]> git.ipfire.org Git - thirdparty/babel.git/commitdiff
Added group_separator feature in number formatting (#726)
authorAbdullah Javed Nesar <abduljaved1994@gmail.com>
Tue, 22 Sep 2020 12:14:27 +0000 (17:44 +0530)
committerGitHub <noreply@github.com>
Tue, 22 Sep 2020 12:14:27 +0000 (15:14 +0300)
babel/numbers.py
tests/test_numbers.py

index cf819fc9a1e654a70e2d354d93da6d69837936cd..bbc5e2129200f46b5657efe6e827f27ed82d51f6 100644 (file)
@@ -373,7 +373,7 @@ def get_decimal_quantum(precision):
 
 
 def format_decimal(
-        number, format=None, locale=LC_NUMERIC, decimal_quantization=True):
+        number, format=None, locale=LC_NUMERIC, decimal_quantization=True, group_separator=True):
     u"""Return the given decimal number formatted for a specific locale.
 
     >>> format_decimal(1.2345, locale='en_US')
@@ -401,19 +401,25 @@ def format_decimal(
     u'1.235'
     >>> format_decimal(1.2346, locale='en_US', decimal_quantization=False)
     u'1.2346'
+    >>> format_decimal(12345.67, locale='fr_CA', group_separator=False)
+    u'12345,67'
+    >>> format_decimal(12345.67, locale='en_US', group_separator=True)
+    u'12,345.67'
 
     :param number: the number to format
     :param format:
     :param locale: the `Locale` object or locale identifier
     :param decimal_quantization: Truncate and round high-precision numbers to
                                  the format pattern. Defaults to `True`.
+    :param group_separator: Boolean to switch group separator on/off in a locale's
+                            number format.
     """
     locale = Locale.parse(locale)
     if not format:
         format = locale.decimal_formats.get(format)
     pattern = parse_pattern(format)
     return pattern.apply(
-        number, locale, decimal_quantization=decimal_quantization)
+        number, locale, decimal_quantization=decimal_quantization, group_separator=group_separator)
 
 
 class UnknownCurrencyFormatError(KeyError):
@@ -422,7 +428,7 @@ class UnknownCurrencyFormatError(KeyError):
 
 def format_currency(
         number, currency, format=None, locale=LC_NUMERIC, currency_digits=True,
-        format_type='standard', decimal_quantization=True):
+        format_type='standard', decimal_quantization=True, group_separator=True):
     u"""Return formatted currency value.
 
     >>> format_currency(1099.98, 'USD', locale='en_US')
@@ -472,6 +478,12 @@ def format_currency(
         ...
     UnknownCurrencyFormatError: "'unknown' is not a known currency format type"
 
+    >>> format_currency(101299.98, 'USD', locale='en_US', group_separator=False)
+    u'$101299.98'
+
+    >>> format_currency(101299.98, 'USD', locale='en_US', group_separator=True)
+    u'$101,299.98'
+
     You can also pass format_type='name' to use long display names. The order of
     the number and currency name, along with the correct localized plural form
     of the currency name, is chosen according to locale:
@@ -500,12 +512,14 @@ def format_currency(
     :param format_type: the currency format type to use
     :param decimal_quantization: Truncate and round high-precision numbers to
                                  the format pattern. Defaults to `True`.
+    :param group_separator: Boolean to switch group separator on/off in a locale's
+                            number format.
 
     """
     if format_type == 'name':
         return _format_currency_long_name(number, currency, format=format,
                                           locale=locale, currency_digits=currency_digits,
-                                          decimal_quantization=decimal_quantization)
+                                          decimal_quantization=decimal_quantization, group_separator=group_separator)
     locale = Locale.parse(locale)
     if format:
         pattern = parse_pattern(format)
@@ -518,12 +532,12 @@ def format_currency(
 
     return pattern.apply(
         number, locale, currency=currency, currency_digits=currency_digits,
-        decimal_quantization=decimal_quantization)
+        decimal_quantization=decimal_quantization, group_separator=group_separator)
 
 
 def _format_currency_long_name(
         number, currency, format=None, locale=LC_NUMERIC, currency_digits=True,
-        format_type='standard', decimal_quantization=True):
+        format_type='standard', decimal_quantization=True, group_separator=True):
     # Algorithm described here:
     # https://www.unicode.org/reports/tr35/tr35-numbers.html#Currencies
     locale = Locale.parse(locale)
@@ -552,13 +566,13 @@ def _format_currency_long_name(
 
     number_part = pattern.apply(
         number, locale, currency=currency, currency_digits=currency_digits,
-        decimal_quantization=decimal_quantization)
+        decimal_quantization=decimal_quantization, group_separator=group_separator)
 
     return unit_pattern.format(number_part, display_name)
 
 
 def format_percent(
-        number, format=None, locale=LC_NUMERIC, decimal_quantization=True):
+        number, format=None, locale=LC_NUMERIC, decimal_quantization=True, group_separator=True):
     """Return formatted percent value for a specific locale.
 
     >>> format_percent(0.34, locale='en_US')
@@ -582,18 +596,26 @@ def format_percent(
     >>> format_percent(23.9876, locale='en_US', decimal_quantization=False)
     u'2,398.76%'
 
+    >>> format_percent(229291.1234, locale='pt_BR', group_separator=False)
+    u'22929112%'
+
+    >>> format_percent(229291.1234, locale='pt_BR', group_separator=True)
+    u'22.929.112%'
+
     :param number: the percent number to format
     :param format:
     :param locale: the `Locale` object or locale identifier
     :param decimal_quantization: Truncate and round high-precision numbers to
                                  the format pattern. Defaults to `True`.
+    :param group_separator: Boolean to switch group separator on/off in a locale's
+                            number format.
     """
     locale = Locale.parse(locale)
     if not format:
         format = locale.percent_formats.get(format)
     pattern = parse_pattern(format)
     return pattern.apply(
-        number, locale, decimal_quantization=decimal_quantization)
+        number, locale, decimal_quantization=decimal_quantization, group_separator=group_separator)
 
 
 def format_scientific(
@@ -913,6 +935,7 @@ class NumberPattern(object):
         currency_digits=True,
         decimal_quantization=True,
         force_frac=None,
+        group_separator=True,
     ):
         """Renders into a string a number following the defined pattern.
 
@@ -952,8 +975,8 @@ class NumberPattern(object):
         if self.exp_prec:
             value, exp, exp_sign = self.scientific_notation_elements(value, locale)
 
-        # Adjust the precision of the fractionnal part and force it to the
-        # currency's if neccessary.
+        # Adjust the precision of the fractional part and force it to the
+        # currency's if necessary.
         if force_frac:
             # TODO (3.x?): Remove this parameter
             warnings.warn('The force_frac parameter to NumberPattern.apply() is deprecated.', DeprecationWarning)
@@ -975,7 +998,7 @@ class NumberPattern(object):
         # Render scientific notation.
         if self.exp_prec:
             number = ''.join([
-                self._quantize_value(value, locale, frac_prec),
+                self._quantize_value(value, locale, frac_prec, group_separator),
                 get_exponential_symbol(locale),
                 exp_sign,
                 self._format_int(
@@ -993,7 +1016,7 @@ class NumberPattern(object):
 
         # A normal number pattern.
         else:
-            number = self._quantize_value(value, locale, frac_prec)
+            number = self._quantize_value(value, locale, frac_prec, group_separator)
 
         retval = ''.join([
             self.prefix[is_negative],
@@ -1060,13 +1083,14 @@ class NumberPattern(object):
             gsize = self.grouping[1]
         return value + ret
 
-    def _quantize_value(self, value, locale, frac_prec):
+    def _quantize_value(self, value, locale, frac_prec, group_separator):
         quantum = get_decimal_quantum(frac_prec[1])
         rounded = value.quantize(quantum)
         a, sep, b = "{:f}".format(rounded).partition(".")
-        number = (self._format_int(a, self.int_prec[0],
-                                   self.int_prec[1], locale) +
-                  self._format_frac(b or '0', locale, frac_prec))
+        integer_part = a
+        if group_separator:
+            integer_part = self._format_int(a, self.int_prec[0], self.int_prec[1], locale)
+        number = integer_part + self._format_frac(b or '0', locale, frac_prec)
         return number
 
     def _format_frac(self, value, locale, force_frac=None):
index a980a66ad380c8b69a90b8b3188a66911d1678f4..3db5f33072d2627a9941f3041ec062e6c68df3dc 100644 (file)
@@ -153,6 +153,36 @@ class FormatDecimalTestCase(unittest.TestCase):
         fmt = numbers.format_decimal(number, format="@@@", locale='en_US')
         self.assertEqual('0.000000700', fmt)
 
+    def test_group_separator(self):
+        self.assertEqual('29567.12', numbers.format_decimal(29567.12,
+                                                                     locale='en_US', group_separator=False))
+        self.assertEqual('29567,12', numbers.format_decimal(29567.12,
+                                                                     locale='fr_CA', group_separator=False))
+        self.assertEqual('29567,12', numbers.format_decimal(29567.12,
+                                                                     locale='pt_BR', group_separator=False))
+        self.assertEqual(u'$1099.98', numbers.format_currency(1099.98, 'USD',
+                                                             locale='en_US', group_separator=False))
+        self.assertEqual(u'101299,98\xa0€', numbers.format_currency(101299.98, 'EUR',
+                                                            locale='fr_CA', group_separator=False))
+        self.assertEqual('101299.98 euros', numbers.format_currency(101299.98, 'EUR',
+                                                            locale='en_US', group_separator=False, format_type='name'))
+        self.assertEqual(u'25123412\xa0%', numbers.format_percent(251234.1234, locale='sv_SE', group_separator=False))
+
+        self.assertEqual(u'29,567.12', numbers.format_decimal(29567.12,
+                                                            locale='en_US', group_separator=True))
+        self.assertEqual(u'29\u202f567,12', numbers.format_decimal(29567.12,
+                                                            locale='fr_CA', group_separator=True))
+        self.assertEqual(u'29.567,12', numbers.format_decimal(29567.12,
+                                                            locale='pt_BR', group_separator=True))
+        self.assertEqual(u'$1,099.98', numbers.format_currency(1099.98, 'USD',
+                                                              locale='en_US', group_separator=True))
+        self.assertEqual(u'101\u202f299,98\xa0\u20ac', numbers.format_currency(101299.98, 'EUR',
+                                                                    locale='fr_CA', group_separator=True))
+        self.assertEqual(u'101,299.98 euros', numbers.format_currency(101299.98, 'EUR',
+                                                                    locale='en_US', group_separator=True,
+                                                                    format_type='name'))
+        self.assertEqual(u'25\xa0123\xa0412\xa0%', numbers.format_percent(251234.1234, locale='sv_SE', group_separator=True))
+
 
 class NumberParsingTestCase(unittest.TestCase):