summaryrefslogtreecommitdiff
path: root/babel
diff options
context:
space:
mode:
authorJonah Lawrence <jonah@freshidea.com>2022-11-04 09:16:24 -0600
committerGitHub <noreply@github.com>2022-11-04 17:16:24 +0200
commit5fcc2535f96bfce9c1a1ecf9d19a976b9bf6ab6b (patch)
treea7871ea693786fe78eb10c5ff92e30926a71bfbb /babel
parent3add2c141783b1f590c753f475b8cba64d15cd0c (diff)
downloadbabel-5fcc2535f96bfce9c1a1ecf9d19a976b9bf6ab6b.tar.gz
feat: Support for short compact currency formats (#926)
Co-authored-by: Jun Omae (大前 潤) <42682+jun66j5@users.noreply.github.com>
Diffstat (limited to 'babel')
-rw-r--r--babel/core.py12
-rw-r--r--babel/numbers.py53
-rw-r--r--babel/support.py9
3 files changed, 68 insertions, 6 deletions
diff --git a/babel/core.py b/babel/core.py
index 220cbaf..2a01c30 100644
--- a/babel/core.py
+++ b/babel/core.py
@@ -591,6 +591,18 @@ class Locale:
return self._data['currency_formats']
@property
+ def compact_currency_formats(self):
+ """Locale patterns for compact currency number formatting.
+
+ .. note:: The format of the value returned may change between
+ Babel versions.
+
+ >>> Locale('en', 'US').compact_currency_formats["short"]["one"]["1000"]
+ <NumberPattern u'¤0K'>
+ """
+ return self._data['compact_currency_formats']
+
+ @property
def percent_formats(self):
"""Locale patterns for percent number formatting.
diff --git a/babel/numbers.py b/babel/numbers.py
index 8a341c4..8baf110 100644
--- a/babel/numbers.py
+++ b/babel/numbers.py
@@ -440,18 +440,21 @@ def format_compact_decimal(number, *, format_type="short", locale=LC_NUMERIC, fr
:param fraction_digits: Number of digits after the decimal point to use. Defaults to `0`.
"""
locale = Locale.parse(locale)
- number, format = _get_compact_format(number, format_type, locale, fraction_digits)
+ compact_format = locale.compact_decimal_formats[format_type]
+ number, format = _get_compact_format(number, compact_format, locale, fraction_digits)
+ # Did not find a format, fall back.
+ if format is None:
+ format = locale.decimal_formats.get(None)
pattern = parse_pattern(format)
return pattern.apply(number, locale, decimal_quantization=False)
-def _get_compact_format(number, format_type, locale, fraction_digits=0):
+def _get_compact_format(number, compact_format, locale, fraction_digits=0):
"""Returns the number after dividing by the unit and the format pattern to use.
The algorithm is described here:
https://www.unicode.org/reports/tr35/tr35-45/tr35-numbers.html#Compact_Number_Formats.
"""
format = None
- compact_format = locale.compact_decimal_formats[format_type]
for magnitude in sorted([int(m) for m in compact_format["other"]], reverse=True):
if abs(number) >= magnitude:
# check the pattern using "other" as the amount
@@ -470,8 +473,6 @@ def _get_compact_format(number, format_type, locale, fraction_digits=0):
plural_form = plural_form if plural_form in compact_format else "other"
format = compact_format[plural_form][str(magnitude)]
break
- if format is None: # Did not find a format, fall back.
- format = locale.decimal_formats.get(None)
return number, format
@@ -624,6 +625,44 @@ def _format_currency_long_name(
return unit_pattern.format(number_part, display_name)
+def format_compact_currency(number, currency, *, format_type="short", locale=LC_NUMERIC, fraction_digits=0):
+ u"""Format a number as a currency value in compact form.
+
+ >>> format_compact_currency(12345, 'USD', locale='en_US')
+ u'$12K'
+ >>> format_compact_currency(123456789, 'USD', locale='en_US', fraction_digits=2)
+ u'$123.46M'
+ >>> format_compact_currency(123456789, 'EUR', locale='de_DE', fraction_digits=1)
+ '123,5\xa0Mio.\xa0€'
+
+ :param number: the number to format
+ :param currency: the currency code
+ :param format_type: the compact format type to use. Defaults to "short".
+ :param locale: the `Locale` object or locale identifier
+ :param fraction_digits: Number of digits after the decimal point to use. Defaults to `0`.
+ """
+ locale = Locale.parse(locale)
+ try:
+ compact_format = locale.compact_currency_formats[format_type]
+ except KeyError as error:
+ raise UnknownCurrencyFormatError(f"{format_type!r} is not a known compact currency format type") from error
+ number, format = _get_compact_format(number, compact_format, locale, fraction_digits)
+ # Did not find a format, fall back.
+ if format is None or "¤" not in str(format):
+ # find first format that has a currency symbol
+ for magnitude in compact_format['other']:
+ format = compact_format['other'][magnitude].pattern
+ if '¤' not in format:
+ continue
+ # remove characters that are not the currency symbol, 0's or spaces
+ format = re.sub(r'[^0\s\¤]', '', format)
+ # compress adjacent spaces into one
+ format = re.sub(r'(\s)\s+', r'\1', format).strip()
+ break
+ pattern = parse_pattern(format)
+ return pattern.apply(number, locale, currency=currency, currency_digits=False, decimal_quantization=False)
+
+
def format_percent(
number, format=None, locale=LC_NUMERIC, decimal_quantization=True, group_separator=True):
"""Return formatted percent value for a specific locale.
@@ -1082,6 +1121,10 @@ class NumberPattern:
retval = retval.replace(u'¤¤', currency.upper())
retval = retval.replace(u'¤', get_currency_symbol(currency, locale))
+ # remove single quotes around text, except for doubled single quotes
+ # which are replaced with a single quote
+ retval = re.sub(r"'([^']*)'", lambda m: m.group(1) or "'", retval)
+
return retval
#
diff --git a/babel/support.py b/babel/support.py
index 3efc003..50f2752 100644
--- a/babel/support.py
+++ b/babel/support.py
@@ -17,7 +17,7 @@ import locale
from babel.core import Locale
from babel.dates import format_date, format_datetime, format_time, \
format_timedelta
-from babel.numbers import format_decimal, format_currency, \
+from babel.numbers import format_decimal, format_currency, format_compact_currency, \
format_percent, format_scientific, format_compact_decimal
@@ -124,6 +124,13 @@ class Format:
"""
return format_currency(number, currency, locale=self.locale)
+ def compact_currency(self, number, currency, format_type='short', fraction_digits=0):
+ """Return a number in the given currency formatted for the locale
+ using the compact number format.
+ """
+ return format_compact_currency(number, currency, format_type=format_type,
+ fraction_digits=fraction_digits, locale=self.locale)
+
def percent(self, number, format=None):
"""Return a number formatted as percentage for the locale.