From: Abdullah Javed Nesar Date: Tue, 22 Sep 2020 12:14:27 +0000 (+0530) Subject: Added group_separator feature in number formatting (#726) X-Git-Tag: v2.9.0~11 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e0e6aa614546855ccb76637b8d910382b6e94dba;p=thirdparty%2Fbabel.git Added group_separator feature in number formatting (#726) --- diff --git a/babel/numbers.py b/babel/numbers.py index cf819fc9..bbc5e212 100644 --- a/babel/numbers.py +++ b/babel/numbers.py @@ -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): diff --git a/tests/test_numbers.py b/tests/test_numbers.py index a980a66a..3db5f330 100644 --- a/tests/test_numbers.py +++ b/tests/test_numbers.py @@ -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):