Fixed #30086, Refs #32873 -- Made floatformat template filter independent of USE_L10N.

This commit is contained in:
Mariusz Felisiak 2021-09-08 08:37:27 +02:00 committed by GitHub
parent 301a85a12f
commit 4a43335d30
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 79 additions and 21 deletions

View File

@ -126,13 +126,29 @@ def floatformat(text, arg=-1):
* {{ 6666.6666|floatformat:"2g" }} displays "6,666.67" * {{ 6666.6666|floatformat:"2g" }} displays "6,666.67"
* {{ 10000|floatformat:"g" }} displays "10,000" * {{ 10000|floatformat:"g" }} displays "10,000"
If arg has the 'u' suffix, force the result to be unlocalized. When the
active locale is pl (Polish):
* {{ 66666.6666|floatformat:"2" }} displays "66666,67"
* {{ 66666.6666|floatformat:"2u" }} displays "66666.67"
If the input float is infinity or NaN, display the string representation If the input float is infinity or NaN, display the string representation
of that value. of that value.
""" """
force_grouping = False force_grouping = False
if isinstance(arg, str) and arg.endswith('g'): use_l10n = True
force_grouping = True if isinstance(arg, str):
arg = arg[:-1] or -1 last_char = arg[-1]
if arg[-2:] in {'gu', 'ug'}:
force_grouping = True
use_l10n = False
arg = arg[:-2] or -1
elif last_char == 'g':
force_grouping = True
arg = arg[:-1] or -1
elif last_char == 'u':
use_l10n = False
arg = arg[:-1] or -1
try: try:
input_val = repr(text) input_val = repr(text)
d = Decimal(input_val) d = Decimal(input_val)
@ -152,9 +168,12 @@ def floatformat(text, arg=-1):
return input_val return input_val
if not m and p < 0: if not m and p < 0:
return mark_safe( return mark_safe(formats.number_format(
formats.number_format('%d' % (int(d)), 0, force_grouping=force_grouping), '%d' % (int(d)),
) 0,
use_l10n=use_l10n,
force_grouping=force_grouping,
))
exp = Decimal(1).scaleb(-abs(p)) exp = Decimal(1).scaleb(-abs(p))
# Set the precision high enough to avoid an exception (#15789). # Set the precision high enough to avoid an exception (#15789).
@ -174,9 +193,12 @@ def floatformat(text, arg=-1):
if sign and rounded_d: if sign and rounded_d:
digits.append('-') digits.append('-')
number = ''.join(reversed(digits)) number = ''.join(reversed(digits))
return mark_safe( return mark_safe(formats.number_format(
formats.number_format(number, abs(p), force_grouping=force_grouping), number,
) abs(p),
use_l10n=use_l10n,
force_grouping=force_grouping,
))
@register.filter(is_safe=True) @register.filter(is_safe=True)

View File

@ -1736,6 +1736,18 @@ example, when the active locale is ``en`` (English):
``34232.00`` ``{{ value|floatformat:"-3g" }}`` ``34,232`` ``34232.00`` ``{{ value|floatformat:"-3g" }}`` ``34,232``
============ ================================= ============= ============ ================================= =============
Output is always localized (independently of the :ttag:`{% localize off %}
<localize>` tag) unless the argument passed to ``floatformat`` has the ``u``
suffix, which will force disabling localization. For example, when the active
locale is ``pl`` (Polish):
============ ================================= =============
``value`` Template Output
============ ================================= =============
``34.23234`` ``{{ value|floatformat:"3" }}`` ``34,232``
``34.23234`` ``{{ value|floatformat:"3u" }}`` ``34.232``
============ ================================= =============
Using ``floatformat`` with no argument is equivalent to using ``floatformat`` Using ``floatformat`` with no argument is equivalent to using ``floatformat``
with an argument of ``-1``. with an argument of ``-1``.
@ -1743,6 +1755,13 @@ with an argument of ``-1``.
The ``g`` suffix to force grouping by thousand separators was added. The ``g`` suffix to force grouping by thousand separators was added.
.. versionchanged:: 4.0
``floatformat`` template filter no longer depends on the
:setting:`USE_L10N` setting and always returns localized output.
The ``u`` suffix to force disabling localization was added.
.. templatefilter:: force_escape .. templatefilter:: force_escape
``force_escape`` ``force_escape``

View File

@ -355,7 +355,8 @@ Signals
Templates Templates
~~~~~~~~~ ~~~~~~~~~
* ... * :tfilter:`floatformat` template filter now allows using the ``u`` suffix to
force disabling localization.
Tests Tests
~~~~~ ~~~~~
@ -574,6 +575,10 @@ Miscellaneous
<overriding-built-in-widget-templates>` with the appropriate template from <overriding-built-in-widget-templates>` with the appropriate template from
Django 3.2. Django 3.2.
* The :tfilter:`floatformat` template filter no longer depends on the
:setting:`USE_L10N` setting and always returns localized output. Use the
``u`` suffix to disable localization.
.. _deprecated-features-4.0: .. _deprecated-features-4.0:
Features deprecated in 4.0 Features deprecated in 4.0

View File

@ -523,15 +523,15 @@ class FormattingTests(SimpleTestCase):
self.assertEqual('99999.999', Template('{{ f }}').render(self.ctxt)) self.assertEqual('99999.999', Template('{{ f }}').render(self.ctxt))
self.assertEqual('Des. 31, 2009', Template('{{ d }}').render(self.ctxt)) self.assertEqual('Des. 31, 2009', Template('{{ d }}').render(self.ctxt))
self.assertEqual('Des. 31, 2009, 8:50 p.m.', Template('{{ dt }}').render(self.ctxt)) self.assertEqual('Des. 31, 2009, 8:50 p.m.', Template('{{ dt }}').render(self.ctxt))
self.assertEqual('66666.67', Template('{{ n|floatformat:2 }}').render(self.ctxt)) self.assertEqual('66666.67', Template('{{ n|floatformat:"2u" }}').render(self.ctxt))
self.assertEqual('100000.0', Template('{{ f|floatformat }}').render(self.ctxt)) self.assertEqual('100000.0', Template('{{ f|floatformat:"u" }}').render(self.ctxt))
self.assertEqual( self.assertEqual(
'66666.67', '66666.67',
Template('{{ n|floatformat:"2g" }}').render(self.ctxt), Template('{{ n|floatformat:"2gu" }}').render(self.ctxt),
) )
self.assertEqual( self.assertEqual(
'100000.0', '100000.0',
Template('{{ f|floatformat:"g" }}').render(self.ctxt), Template('{{ f|floatformat:"ug" }}').render(self.ctxt),
) )
self.assertEqual('10:15 a.m.', Template('{{ t|time:"TIME_FORMAT" }}').render(self.ctxt)) self.assertEqual('10:15 a.m.', Template('{{ t|time:"TIME_FORMAT" }}').render(self.ctxt))
self.assertEqual('12/31/2009', Template('{{ d|date:"SHORT_DATE_FORMAT" }}').render(self.ctxt)) self.assertEqual('12/31/2009', Template('{{ d|date:"SHORT_DATE_FORMAT" }}').render(self.ctxt))
@ -628,12 +628,12 @@ class FormattingTests(SimpleTestCase):
) )
# We shouldn't change the behavior of the floatformat filter re: # We shouldn't change the behavior of the floatformat filter re:
# thousand separator and grouping when USE_L10N is False even # thousand separator and grouping when localization is disabled
# if the USE_THOUSAND_SEPARATOR, NUMBER_GROUPING and # even if the USE_THOUSAND_SEPARATOR, NUMBER_GROUPING and
# THOUSAND_SEPARATOR settings are specified # THOUSAND_SEPARATOR settings are specified.
with self.settings(USE_THOUSAND_SEPARATOR=True, NUMBER_GROUPING=1, THOUSAND_SEPARATOR='!'): with self.settings(USE_THOUSAND_SEPARATOR=True, NUMBER_GROUPING=1, THOUSAND_SEPARATOR='!'):
self.assertEqual('66666.67', Template('{{ n|floatformat:2 }}').render(self.ctxt)) self.assertEqual('66666.67', Template('{{ n|floatformat:"2u" }}').render(self.ctxt))
self.assertEqual('100000.0', Template('{{ f|floatformat }}').render(self.ctxt)) self.assertEqual('100000.0', Template('{{ f|floatformat:"u" }}').render(self.ctxt))
def test_false_like_locale_formats(self): def test_false_like_locale_formats(self):
""" """

View File

@ -2,7 +2,6 @@ from decimal import Decimal, localcontext
from django.template.defaultfilters import floatformat from django.template.defaultfilters import floatformat
from django.test import SimpleTestCase from django.test import SimpleTestCase
from django.test.utils import override_settings
from django.utils import translation from django.utils import translation
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
@ -60,7 +59,6 @@ class FunctionTests(SimpleTestCase):
self.assertEqual(floatformat(1.5e-15, -20), '0.00000000000000150000') self.assertEqual(floatformat(1.5e-15, -20), '0.00000000000000150000')
self.assertEqual(floatformat(1.00000000000000015, 16), '1.0000000000000002') self.assertEqual(floatformat(1.00000000000000015, 16), '1.0000000000000002')
@override_settings(USE_L10N=True)
def test_force_grouping(self): def test_force_grouping(self):
with translation.override('en'): with translation.override('en'):
self.assertEqual(floatformat(10000, 'g'), '10,000') self.assertEqual(floatformat(10000, 'g'), '10,000')
@ -73,6 +71,20 @@ class FunctionTests(SimpleTestCase):
# Invalid suffix. # Invalid suffix.
self.assertEqual(floatformat(10000, 'g2'), '10000') self.assertEqual(floatformat(10000, 'g2'), '10000')
def test_unlocalize(self):
with translation.override('de', deactivate=True):
self.assertEqual(floatformat(66666.666, '2'), '66666,67')
self.assertEqual(floatformat(66666.666, '2u'), '66666.67')
with self.settings(
USE_THOUSAND_SEPARATOR=True,
NUMBER_GROUPING=3,
THOUSAND_SEPARATOR='!',
):
self.assertEqual(floatformat(66666.666, '2gu'), '66!666.67')
self.assertEqual(floatformat(66666.666, '2ug'), '66!666.67')
# Invalid suffix.
self.assertEqual(floatformat(66666.666, 'u2'), '66666.666')
def test_zero_values(self): def test_zero_values(self):
self.assertEqual(floatformat(0, 6), '0.000000') self.assertEqual(floatformat(0, 6), '0.000000')
self.assertEqual(floatformat(0, 7), '0.0000000') self.assertEqual(floatformat(0, 7), '0.0000000')