Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

numbers: implement currency formatting with long display names. #585

Merged

Conversation

spookylukey
Copy link
Contributor

Fixes #578

@codecov-io
Copy link

codecov-io commented Jun 4, 2018

Codecov Report

Merging #585 into master will increase coverage by 0.07%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff            @@
##           master    #585      +/-   ##
=========================================
+ Coverage   90.22%   90.3%   +0.07%     
=========================================
  Files          24      24              
  Lines        4082    4105      +23     
=========================================
+ Hits         3683    3707      +24     
+ Misses        399     398       -1
Impacted Files Coverage Δ
babel/numbers.py 97.55% <100%> (+0.18%) ⬆️
babel/plural.py 96.04% <0%> (+0.35%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update a5ecaa3...4c7e9b6. Read the comment docs.

@spookylukey
Copy link
Contributor Author

Some notes:

Step 1 of the algorithm involves checking for an exact match against number 0 or 1. However, none of the locale data actually has data defined for this. Since I would have been producing untestable code, I thought it best not to implement this. It could be added as a refinement when the need comes up.

Step 5 refers to applying currencyDecimal where appropriate. I have omitted this part, because babel doesn't currently support it all, and that affects other currency formatting, not just this new feature, and therefore ought to be addressed as its own issue. (It also would require a bit of re-plumbing to implement correctly, I think, and therefore would complicate this patch).

It would appear that there is just one affected locale which actually defines this currencyDecimal value - fr_CH, which defines it as .

babel doesn't use it though:

>>> print(format_currency(12345.678, 'USD', locale='fr_CH'))
12 345,68 $US

Contrast this with Intl.NumberFormat (tested in Firefox and Chrome), which correctly has a . for the decimal separator instead of the ,:

> Intl.NumberFormat("fr-CH", {currencyDisplay:"symbol", style:"currency", currency:"USD"}).format(12345.678)
"12 345.68 $US "

Whether you want to address this odd corner case is a matter for another issue I think.

@akx
Copy link
Member

akx commented Jun 4, 2018

Based on a cursory review, this looks great! Clean and simple :)
I'll give it a more thorough review this week, but it's looking good.

Mind making an issue out of that currencyDecimal thing, so it's not lost here in the comments?

@akx akx self-requested a review June 4, 2018 20:34
Copy link
Member

@akx akx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good! 😎

Just please address that non-latn importing issue (just in case!) and we're good to 🚢 this :)

@@ -903,6 +904,14 @@ def parse_currency_formats(data, tree):
currency_formats[type] = numbers.parse_pattern(pattern)


def parse_currency_unit_patterns(data, tree):
currency_unit_patterns = data.setdefault('currency_unit_patterns', {})
for unit_pattern_elem in tree.findall('.//currencyFormats/unitPattern'):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should ignore non-latn currency formats at present, just in case there are both latn and non-latn currencyFormats elements within a single locale.

You can (rebase and) use the _should_skip_number_elem() function introduced in #579 to do that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 4b5097f

babel/numbers.py Outdated

# Step 3.
try:
unit_pattern = locale._data['currency_unit_patterns'][plural_category]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mildly like pulling things out of locale._data from outside the locale object, but we haven't been that strict about it before either.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a partial go at fixing this in 4c7e9b6 - it is at least more consistent now, and the resulting code is much flatter as well.

In detail:

1. Use the already existing get_currency_name function which does
   the plural form logic already.

2. Create a similar `get_currency_unit_pattern` function.
   This function could be useful if someone wanted to write
   something very similar to the _format_currency_long_name
   functionality but with some different customizations,
   so it is now publicly documented.

3. Simplify the _format_currency_long_name function - it
   is now much flatter.

4. Add more tests to ensure certain cases are really covered.
   (e.g. different unit patterns depending on the count)

An upshot of the changes is that we have reduced (and made more consistent)
the number of places where we need to peek into `Locale._data` -
get_currency_name and get_currency_unit_pattern are the only places that
babel.numbers does this.
@spookylukey
Copy link
Contributor Author

Copying my message from the 2nd commit in case it gets a bit lost:

4c7e9b6 Simplify format_currency code by pulling out/using helpers.

In detail:

  1. Use the already existing get_currency_name function which does the plural form logic already.

  2. Create a similar get_currency_unit_pattern function. This function could be useful if someone wanted to write something very similar to the _format_currency_long_name functionality but with some different customizations, so it is now publicly documented.

  3. Simplify the _format_currency_long_name function - it is now much flatter and shorter.

  4. Add more tests to ensure certain cases are really covered. (e.g. different unit patterns depending on the count)

An upshot of the changes is that we have reduced (and made more consistent) the number of places where we need to peek into Locale._data - get_currency_name and get_currency_unit_pattern are the only places that babel.numbers does this.


In fact I already have a use case for having get_currency_unit_pattern as public API, so this seems better all round.

@akx akx merged commit 553a754 into python-babel:master Jun 13, 2018
@akx
Copy link
Member

akx commented Jun 13, 2018

Thank you, @spookylukey! 😻

@spookylukey
Copy link
Contributor Author

Thanks for merging, and maintaining babel - it is one of the those projects that is often a thankless task when it comes to maintaining it, rarely making any headlines, but a dependency of a huge number of projects!

@spookylukey
Copy link
Contributor Author

@akx - do you have plans to do a release soon? I'm not pressuring you to do so, it would just be nice to know - there is another library I'm preparing a patch for that could be improved if I could use the feature you merged in this PR (and be able to refer to the specific babel version it was added in for dependencies). Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants