Commit b5a1c3a6 authored by jasisz's avatar jasisz Committed by Tim Graham
Browse files

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

parent 46338296
Loading
Loading
Loading
Loading
+19 −4
Original line number Diff line number Diff line
@@ -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
+16 −0
Original line number Diff line number Diff line
@@ -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``
+2 −1
Original line number Diff line number Diff line
@@ -166,7 +166,8 @@ Generic Views
Internationalization
~~~~~~~~~~~~~~~~~~~~

* ...
* Number formatting and the :setting:`NUMBER_GROUPING` setting support
  non-uniform digit grouping.

Management Commands
~~~~~~~~~~~~~~~~~~~
+12 −0
Original line number Diff line number Diff line
@@ -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))