Browse Source

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

Mariusz Felisiak 3 years ago
parent
commit
4a43335d30

+ 31 - 9
django/template/defaultfilters.py

@@ -126,13 +126,29 @@ def floatformat(text, arg=-1):
     * {{ 6666.6666|floatformat:"2g" }} displays "6,666.67"
     * {{ 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
     of that value.
     """
     force_grouping = False
-    if isinstance(arg, str) and arg.endswith('g'):
-        force_grouping = True
-        arg = arg[:-1] or -1
+    use_l10n = True
+    if isinstance(arg, str):
+        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:
         input_val = repr(text)
         d = Decimal(input_val)
@@ -152,9 +168,12 @@ def floatformat(text, arg=-1):
         return input_val
 
     if not m and p < 0:
-        return mark_safe(
-            formats.number_format('%d' % (int(d)), 0, force_grouping=force_grouping),
-        )
+        return mark_safe(formats.number_format(
+            '%d' % (int(d)),
+            0,
+            use_l10n=use_l10n,
+            force_grouping=force_grouping,
+        ))
 
     exp = Decimal(1).scaleb(-abs(p))
     # Set the precision high enough to avoid an exception (#15789).
@@ -174,9 +193,12 @@ def floatformat(text, arg=-1):
     if sign and rounded_d:
         digits.append('-')
     number = ''.join(reversed(digits))
-    return mark_safe(
-        formats.number_format(number, abs(p), force_grouping=force_grouping),
-    )
+    return mark_safe(formats.number_format(
+        number,
+        abs(p),
+        use_l10n=use_l10n,
+        force_grouping=force_grouping,
+    ))
 
 
 @register.filter(is_safe=True)

+ 19 - 0
docs/ref/templates/builtins.txt

@@ -1736,6 +1736,18 @@ example, when the active locale is ``en`` (English):
 ``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``
 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.
 
+.. 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
 
 ``force_escape``

+ 6 - 1
docs/releases/4.0.txt

@@ -355,7 +355,8 @@ Signals
 Templates
 ~~~~~~~~~
 
-* ...
+* :tfilter:`floatformat` template filter now allows using the ``u`` suffix to
+  force disabling localization.
 
 Tests
 ~~~~~
@@ -574,6 +575,10 @@ Miscellaneous
   <overriding-built-in-widget-templates>` with the appropriate template from
   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:
 
 Features deprecated in 4.0

+ 9 - 9
tests/i18n/tests.py

@@ -523,15 +523,15 @@ class FormattingTests(SimpleTestCase):
             self.assertEqual('99999.999', Template('{{ f }}').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('66666.67', Template('{{ n|floatformat:2 }}').render(self.ctxt))
-            self.assertEqual('100000.0', Template('{{ f|floatformat }}').render(self.ctxt))
+            self.assertEqual('66666.67', Template('{{ n|floatformat:"2u" }}').render(self.ctxt))
+            self.assertEqual('100000.0', Template('{{ f|floatformat:"u" }}').render(self.ctxt))
             self.assertEqual(
                 '66666.67',
-                Template('{{ n|floatformat:"2g" }}').render(self.ctxt),
+                Template('{{ n|floatformat:"2gu" }}').render(self.ctxt),
             )
             self.assertEqual(
                 '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('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:
-            # thousand separator and grouping when USE_L10N is False even
-            # if the USE_THOUSAND_SEPARATOR, NUMBER_GROUPING and
-            # THOUSAND_SEPARATOR settings are specified
+            # thousand separator and grouping when localization is disabled
+            # even if the USE_THOUSAND_SEPARATOR, NUMBER_GROUPING and
+            # THOUSAND_SEPARATOR settings are specified.
             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('100000.0', Template('{{ f|floatformat }}').render(self.ctxt))
+                self.assertEqual('66666.67', Template('{{ n|floatformat:"2u" }}').render(self.ctxt))
+                self.assertEqual('100000.0', Template('{{ f|floatformat:"u" }}').render(self.ctxt))
 
     def test_false_like_locale_formats(self):
         """

+ 14 - 2
tests/template_tests/filter_tests/test_floatformat.py

@@ -2,7 +2,6 @@ from decimal import Decimal, localcontext
 
 from django.template.defaultfilters import floatformat
 from django.test import SimpleTestCase
-from django.test.utils import override_settings
 from django.utils import translation
 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.00000000000000015, 16), '1.0000000000000002')
 
-    @override_settings(USE_L10N=True)
     def test_force_grouping(self):
         with translation.override('en'):
             self.assertEqual(floatformat(10000, 'g'), '10,000')
@@ -73,6 +71,20 @@ class FunctionTests(SimpleTestCase):
             # Invalid suffix.
             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):
         self.assertEqual(floatformat(0, 6), '0.000000')
         self.assertEqual(floatformat(0, 7), '0.0000000')