Skip to content

Commit 56ef7c7

Browse files
akxafh
andcommitted
Prefer LC_MONETARY when formatting currency
Co-authored-by: Alexis Hildebrandt <afh@surryhill.net>
1 parent aee6d69 commit 56ef7c7

2 files changed

Lines changed: 46 additions & 24 deletions

File tree

babel/numbers.py

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
The default locale for the functions in this module is determined by the
88
following environment variables, in that order:
99
10-
* ``LC_NUMERIC``,
10+
* ``LC_MONETARY`` for currency related functions,
11+
* ``LC_NUMERIC``, and
1112
* ``LC_ALL``, and
1213
* ``LANG``
1314
@@ -28,6 +29,7 @@
2829
from babel.core import Locale, default_locale, get_global
2930
from babel.localedata import LocaleDataDict
3031

32+
LC_MONETARY = default_locale(('LC_MONETARY', 'LC_NUMERIC'))
3133
LC_NUMERIC = default_locale('LC_NUMERIC')
3234

3335

@@ -117,9 +119,10 @@ def get_currency_name(
117119
:param currency: the currency code.
118120
:param count: the optional count. If provided the currency name
119121
will be pluralized to that number if possible.
120-
:param locale: the `Locale` object or locale identifier. Defaults to the system numeric locale.
122+
:param locale: the `Locale` object or locale identifier.
123+
Defaults to the system currency locale or numeric locale.
121124
"""
122-
loc = Locale.parse(locale or LC_NUMERIC)
125+
loc = Locale.parse(locale or LC_MONETARY)
123126
if count is not None:
124127
try:
125128
plural_form = loc.plural_form(count)
@@ -142,9 +145,10 @@ def get_currency_symbol(currency: str, locale: Locale | str | None = None) -> st
142145
u'$'
143146
144147
:param currency: the currency code.
145-
:param locale: the `Locale` object or locale identifier. Defaults to the system numeric locale.
148+
:param locale: the `Locale` object or locale identifier.
149+
Defaults to the system currency locale or numeric locale.
146150
"""
147-
return Locale.parse(locale or LC_NUMERIC).currency_symbols.get(currency, currency)
151+
return Locale.parse(locale or LC_MONETARY).currency_symbols.get(currency, currency)
148152

149153

150154
def get_currency_precision(currency: str) -> int:
@@ -181,9 +185,10 @@ def get_currency_unit_pattern(
181185
:param currency: the currency code.
182186
:param count: the optional count. If provided the unit
183187
pattern for that number will be returned.
184-
:param locale: the `Locale` object or locale identifier. Defaults to the system numeric locale.
188+
:param locale: the `Locale` object or locale identifier.
189+
Defaults to the system currency locale or numeric locale.
185190
"""
186-
loc = Locale.parse(locale or LC_NUMERIC)
191+
loc = Locale.parse(locale or LC_MONETARY)
187192
if count is not None:
188193
plural_form = loc.plural_form(count)
189194
try:
@@ -760,7 +765,8 @@ def format_currency(
760765
:param number: the number to format
761766
:param currency: the currency code
762767
:param format: the format string to use
763-
:param locale: the `Locale` object or locale identifier. Defaults to the system numeric locale.
768+
:param locale: the `Locale` object or locale identifier.
769+
Defaults to the system currency locale or numeric locale.
764770
:param currency_digits: use the currency's natural number of decimal digits
765771
:param format_type: the currency format type to use
766772
:param decimal_quantization: Truncate and round high-precision numbers to
@@ -771,7 +777,7 @@ def format_currency(
771777
The special value "default" will use the default numbering system of the locale.
772778
:raise `UnsupportedNumberingSystemError`: If the numbering system is not supported by the locale.
773779
"""
774-
locale = Locale.parse(locale or LC_NUMERIC)
780+
locale = Locale.parse(locale or LC_MONETARY)
775781

776782
if format_type == 'name':
777783
return _format_currency_long_name(
@@ -860,13 +866,14 @@ def format_compact_currency(
860866
:param number: the number to format
861867
:param currency: the currency code
862868
:param format_type: the compact format type to use. Defaults to "short".
863-
:param locale: the `Locale` object or locale identifier. Defaults to the system numeric locale.
869+
:param locale: the `Locale` object or locale identifier.
870+
Defaults to the system currency locale or numeric locale.
864871
:param fraction_digits: Number of digits after the decimal point to use. Defaults to `0`.
865872
:param numbering_system: The numbering system used for formatting number symbols. Defaults to "latn".
866873
The special value "default" will use the default numbering system of the locale.
867874
:raise `UnsupportedNumberingSystemError`: If the numbering system is not supported by the locale.
868875
"""
869-
locale = Locale.parse(locale or LC_NUMERIC)
876+
locale = Locale.parse(locale or LC_MONETARY)
870877
try:
871878
compact_format = locale.compact_currency_formats[format_type]
872879
except KeyError as error:

tests/test_numbers.py

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -485,19 +485,6 @@ def test_format_currency():
485485
== 'US$0,00') # other
486486

487487

488-
def test_format_currency_with_none_locale_with_default(monkeypatch):
489-
"""Test that the default locale is used when locale is None."""
490-
monkeypatch.setattr(numbers, "LC_NUMERIC", "fi_FI")
491-
assert numbers.format_currency(0, "USD", locale=None) == "0,00\xa0$"
492-
493-
494-
def test_format_currency_with_none_locale(monkeypatch):
495-
"""Test that the API raises the "Empty locale identifier" error when locale is None, and the default is too."""
496-
monkeypatch.setattr(numbers, "LC_NUMERIC", None) # Pretend we couldn't find any locale when importing the module
497-
with pytest.raises(TypeError, match="Empty"):
498-
numbers.format_currency(0, "USD", locale=None)
499-
500-
501488
def test_format_currency_format_type():
502489
assert (numbers.format_currency(1099.98, 'USD', locale='en_US',
503490
format_type="standard")
@@ -867,3 +854,31 @@ def test_single_quotes_in_pattern():
867854
assert numbers.format_decimal(123, "'$'''0", locale='en') == "$'123"
868855

869856
assert numbers.format_decimal(12, "'#'0 o''clock", locale='en') == "#12 o'clock"
857+
858+
859+
def test_format_currency_with_none_locale_with_default(monkeypatch):
860+
"""Test that the default locale is used when locale is None."""
861+
monkeypatch.setattr(numbers, "LC_MONETARY", "fi_FI")
862+
monkeypatch.setattr(numbers, "LC_NUMERIC", None)
863+
assert numbers.format_currency(0, "USD", locale=None) == "0,00\xa0$"
864+
865+
866+
def test_format_currency_with_none_locale(monkeypatch):
867+
"""Test that the API raises the "Empty locale identifier" error when locale is None, and the default is too."""
868+
monkeypatch.setattr(numbers, "LC_MONETARY", None) # Pretend we couldn't find any locale when importing the module
869+
with pytest.raises(TypeError, match="Empty"):
870+
numbers.format_currency(0, "USD", locale=None)
871+
872+
873+
def test_format_decimal_with_none_locale_with_default(monkeypatch):
874+
"""Test that the default locale is used when locale is None."""
875+
monkeypatch.setattr(numbers, "LC_NUMERIC", "fi_FI")
876+
monkeypatch.setattr(numbers, "LC_MONETARY", None)
877+
assert numbers.format_decimal("1.23", locale=None) == "1,23"
878+
879+
880+
def test_format_decimal_with_none_locale(monkeypatch):
881+
"""Test that the API raises the "Empty locale identifier" error when locale is None, and the default is too."""
882+
monkeypatch.setattr(numbers, "LC_NUMERIC", None) # Pretend we couldn't find any locale when importing the module
883+
with pytest.raises(TypeError, match="Empty"):
884+
numbers.format_decimal(0, locale=None)

0 commit comments

Comments
 (0)