Fixed #25920 -- Added support for non-uniform NUMBER_GROUPING.

This commit is contained in:
jasisz 2016-06-02 12:13:47 -07:00 committed by Tim Graham
parent 46338296aa
commit b5a1c3a6f5
4 changed files with 49 additions and 5 deletions

View File

@ -15,12 +15,15 @@ def format(number, decimal_sep, decimal_pos=None, grouping=0, thousand_sep='',
* decimal_sep: Decimal separator symbol (for example ".")
* decimal_pos: Number of decimal positions
* grouping: Number of digits in every group limited by thousand separator
* grouping: Number of digits in every group limited by thousand separator.
For non-uniform digit grouping, it can be a sequence with the number
of digit group sizes following the format used by the Python locale
module in locale.localeconv() LC_NUMERIC grouping (e.g. (3, 2, 0)).
* thousand_sep: Thousand separator symbol (for example ",")
"""
use_grouping = settings.USE_L10N and settings.USE_THOUSAND_SEPARATOR
use_grouping = use_grouping or force_grouping
use_grouping = use_grouping and grouping > 0
use_grouping = use_grouping and grouping != 0
# Make the common case fast
if isinstance(number, int) and not use_grouping and not decimal_pos:
return mark_safe(six.text_type(number))
@ -46,10 +49,22 @@ def format(number, decimal_sep, decimal_pos=None, grouping=0, thousand_sep='',
dec_part = decimal_sep + dec_part
# grouping
if use_grouping:
try:
# if grouping is a sequence
intervals = list(grouping)
except TypeError:
# grouping is a single value
intervals = [grouping, 0]
active_interval = intervals.pop(0)
int_part_gd = ''
for cnt, digit in enumerate(int_part[::-1]):
if cnt and not cnt % grouping:
cnt = 0
for digit in int_part[::-1]:
if cnt and cnt == active_interval:
if intervals:
active_interval = intervals.pop(0) or active_interval
int_part_gd += thousand_sep[::-1]
cnt = 0
int_part_gd += digit
cnt += 1
int_part = int_part_gd[::-1]
return sign + int_part + dec_part

View File

@ -1943,12 +1943,28 @@ no grouping will be applied to the number. If this setting is greater than
``0``, then :setting:`THOUSAND_SEPARATOR` will be used as the separator between
those groups.
Some locales use non-uniform digit grouping, e.g. ``10,00,00,000`` in
``en_IN``. For this case, you can provide a sequence with the number of digit
group sizes to be applied. The first number defines the size of the group
preceding the decimal delimiter, and each number that follows defines the size
of preceding groups. If the sequence is terminated with ``-1``, no further
grouping is performed. If the sequence terminates with a ``0``, the last group
size is used for the remainder of the number.
Example tuple for ``en_IN``::
NUMBER_GROUPING = (3, 2, 0)
Note that if :setting:`USE_L10N` is set to ``True``, then the locale-dictated
format has higher precedence and will be applied instead.
See also :setting:`DECIMAL_SEPARATOR`, :setting:`THOUSAND_SEPARATOR` and
:setting:`USE_THOUSAND_SEPARATOR`.
.. versionchanged:: 1.11
Support for non-uniform digit grouping was added.
.. setting:: PREPEND_WWW
``PREPEND_WWW``

View File

@ -166,7 +166,8 @@ Generic Views
Internationalization
~~~~~~~~~~~~~~~~~~~~
* ...
* Number formatting and the :setting:`NUMBER_GROUPING` setting support
non-uniform digit grouping.
Management Commands
~~~~~~~~~~~~~~~~~~~

View File

@ -576,6 +576,18 @@ class FormattingTests(SimpleTestCase):
self.assertEqual('-66666.6', nformat(-66666.666, decimal_sep='.', decimal_pos=1))
self.assertEqual('-66666.0', nformat(int('-66666'), decimal_sep='.', decimal_pos=1))
self.assertEqual('10000.0', nformat(self.l, decimal_sep='.', decimal_pos=1))
self.assertEqual(
'10,00,00,000.00',
nformat(100000000.00, decimal_sep='.', decimal_pos=2, grouping=(3, 2, 0), thousand_sep=',')
)
self.assertEqual(
'1,0,00,000,0000.00',
nformat(10000000000.00, decimal_sep='.', decimal_pos=2, grouping=(4, 3, 2, 1, 0), thousand_sep=',')
)
self.assertEqual(
'10000,00,000.00',
nformat(1000000000.00, decimal_sep='.', decimal_pos=2, grouping=(3, 2, -1), thousand_sep=',')
)
# This unusual grouping/force_grouping combination may be triggered by the intcomma filter (#17414)
self.assertEqual('10000', nformat(self.l, decimal_sep='.', decimal_pos=0, grouping=0, force_grouping=True))