Browse Source

Fixed #32873 -- Deprecated settings.USE_L10N.

Co-authored-by: Mariusz Felisiak <felisiak.mariusz@gmail.com>
Claude Paroz 3 years ago
parent
commit
676bd084f2
48 changed files with 328 additions and 141 deletions
  1. 34 0
      django/conf/__init__.py
  2. 1 1
      django/conf/global_settings.py
  3. 27 5
      django/conf/locale/en/formats.py
  4. 0 2
      django/conf/project_template/project_name/settings.py-tpl
  5. 10 2
      django/utils/formats.py
  6. 2 0
      docs/internals/deprecation.txt
  7. 9 4
      docs/ref/settings.txt
  8. 1 1
      docs/releases/1.10.txt
  9. 1 1
      docs/releases/1.2.txt
  10. 1 1
      docs/releases/2.0.txt
  11. 1 1
      docs/releases/3.2.txt
  12. 19 2
      docs/releases/4.0.txt
  13. 7 9
      docs/topics/i18n/formatting.txt
  14. 0 6
      docs/topics/i18n/timezones.txt
  15. 0 6
      docs/topics/i18n/translation.txt
  16. 1 1
      tests/admin_inlines/tests.py
  17. 0 1
      tests/admin_utils/test_logentry.py
  18. 2 2
      tests/admin_utils/tests.py
  19. 1 1
      tests/admin_views/test_actions.py
  20. 7 7
      tests/admin_views/tests.py
  21. 2 2
      tests/admin_widgets/tests.py
  22. 43 0
      tests/deprecation/test_use_l10n.py
  23. 1 4
      tests/forms_tests/field_tests/test_datefield.py
  24. 13 2
      tests/forms_tests/field_tests/test_decimalfield.py
  25. 13 2
      tests/forms_tests/field_tests/test_floatfield.py
  26. 4 3
      tests/forms_tests/tests/test_input_formats.py
  27. 1 1
      tests/forms_tests/widget_tests/test_checkboxselectmultiple.py
  28. 0 2
      tests/forms_tests/widget_tests/test_dateinput.py
  29. 15 8
      tests/forms_tests/widget_tests/test_datetimeinput.py
  30. 0 2
      tests/forms_tests/widget_tests/test_nullbooleanselect.py
  31. 1 1
      tests/forms_tests/widget_tests/test_numberinput.py
  32. 1 1
      tests/forms_tests/widget_tests/test_radioselect.py
  33. 1 1
      tests/forms_tests/widget_tests/test_select.py
  34. 8 3
      tests/forms_tests/widget_tests/test_selectdatewidget.py
  35. 0 2
      tests/forms_tests/widget_tests/test_splithiddendatetimewidget.py
  36. 0 2
      tests/forms_tests/widget_tests/test_timeinput.py
  37. 1 1
      tests/gis_tests/test_geoforms.py
  38. 5 5
      tests/humanize_tests/tests.py
  39. 52 23
      tests/i18n/tests.py
  40. 5 5
      tests/settings_tests/tests.py
  41. 1 1
      tests/sitemaps_tests/test_http.py
  42. 2 6
      tests/template_tests/filter_tests/test_date.py
  43. 1 1
      tests/template_tests/filter_tests/test_filesizeformat.py
  44. 1 2
      tests/template_tests/filter_tests/test_time.py
  45. 28 2
      tests/timezones/tests.py
  46. 1 2
      tests/utils_tests/test_numberformat.py
  47. 1 1
      tests/view_tests/tests/test_debug.py
  48. 3 3
      tests/view_tests/tests/test_i18n.py

+ 34 - 0
django/conf/__init__.py

@@ -9,9 +9,11 @@ for a list of all possible variables.
 import importlib
 import os
 import time
+import traceback
 import warnings
 from pathlib import Path
 
+import django
 from django.conf import global_settings
 from django.core.exceptions import ImproperlyConfigured
 from django.utils.deprecation import RemovedInDjango50Warning
@@ -19,6 +21,12 @@ from django.utils.functional import LazyObject, empty
 
 ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE"
 
+USE_L10N_DEPRECATED_MSG = (
+    'The USE_L10N setting is deprecated. Starting with Django 5.0, localized '
+    'formatting of data will always be enabled. For example Django will '
+    'display numbers and dates using the format of the current locale.'
+)
+
 
 class SettingsReference(str):
     """
@@ -129,6 +137,27 @@ class LazySettings(LazyObject):
         """Return True if the settings have already been configured."""
         return self._wrapped is not empty
 
+    @property
+    def USE_L10N(self):
+        stack = traceback.extract_stack()
+        # Show a warning if the setting is used outside of Django.
+        # Stack index: -1 this line, -2 the caller.
+        filename, _, _, _ = stack[-2]
+        if not filename.startswith(os.path.dirname(django.__file__)):
+            warnings.warn(
+                USE_L10N_DEPRECATED_MSG,
+                RemovedInDjango50Warning,
+                stacklevel=2,
+            )
+        return self.__getattr__('USE_L10N')
+
+    # RemovedInDjango50Warning.
+    @property
+    def _USE_L10N_INTERNAL(self):
+        # Special hook to avoid checking a traceback in internal use on hot
+        # paths.
+        return self.__getattr__('USE_L10N')
+
 
 class Settings:
     def __init__(self, settings_module):
@@ -179,6 +208,9 @@ class Settings:
             os.environ['TZ'] = self.TIME_ZONE
             time.tzset()
 
+        if self.is_overridden('USE_L10N'):
+            warnings.warn(USE_L10N_DEPRECATED_MSG, RemovedInDjango50Warning)
+
     def is_overridden(self, setting):
         return setting in self._explicit_settings
 
@@ -210,6 +242,8 @@ class UserSettingsHolder:
 
     def __setattr__(self, name, value):
         self._deleted.discard(name)
+        if name == 'USE_L10N':
+            warnings.warn(USE_L10N_DEPRECATED_MSG, RemovedInDjango50Warning)
         super().__setattr__(name, value)
 
     def __delattr__(self, name):

+ 1 - 1
django/conf/global_settings.py

@@ -167,7 +167,7 @@ LANGUAGE_COOKIE_SAMESITE = None
 
 # If you set this to True, Django will format dates, numbers and calendars
 # according to user current locale.
-USE_L10N = False
+USE_L10N = True
 
 # Not-necessarily-technical managers of the site. They get broken link
 # notifications and other various emails.

+ 27 - 5
django/conf/locale/en/formats.py

@@ -2,24 +2,36 @@
 #
 # The *_FORMAT strings use the Django date format syntax,
 # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
+
+# Formatting for date objects.
 DATE_FORMAT = 'N j, Y'
+# Formatting for time objects.
 TIME_FORMAT = 'P'
+# Formatting for datetime objects.
 DATETIME_FORMAT = 'N j, Y, P'
+# Formatting for date objects when only the year and month are relevant.
 YEAR_MONTH_FORMAT = 'F Y'
+# Formatting for date objects when only the month and day are relevant.
 MONTH_DAY_FORMAT = 'F j'
+# Short formatting for date objects.
 SHORT_DATE_FORMAT = 'm/d/Y'
+# Short formatting for datetime objects.
 SHORT_DATETIME_FORMAT = 'm/d/Y P'
-FIRST_DAY_OF_WEEK = 0  # Sunday
+# First day of week, to be used on calendars.
+# 0 means Sunday, 1 means Monday...
+FIRST_DAY_OF_WEEK = 0
 
+# Formats to be used when parsing dates from input boxes, in order.
 # The *_INPUT_FORMATS strings use the Python strftime format syntax,
 # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior
+# Note that these format strings are different from the ones to display dates.
 # Kept ISO formats as they are in first position
 DATE_INPUT_FORMATS = [
     '%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y',  # '2006-10-25', '10/25/2006', '10/25/06'
-    # '%b %d %Y', '%b %d, %Y',            # 'Oct 25 2006', 'Oct 25, 2006'
-    # '%d %b %Y', '%d %b, %Y',            # '25 Oct 2006', '25 Oct, 2006'
-    # '%B %d %Y', '%B %d, %Y',            # 'October 25 2006', 'October 25, 2006'
-    # '%d %B %Y', '%d %B, %Y',            # '25 October 2006', '25 October, 2006'
+    '%b %d %Y', '%b %d, %Y',             # 'Oct 25 2006', 'Oct 25, 2006'
+    '%d %b %Y', '%d %b, %Y',             # '25 Oct 2006', '25 Oct, 2006'
+    '%B %d %Y', '%B %d, %Y',             # 'October 25 2006', 'October 25, 2006'
+    '%d %B %Y', '%d %B, %Y',             # '25 October 2006', '25 October, 2006'
 ]
 DATETIME_INPUT_FORMATS = [
     '%Y-%m-%d %H:%M:%S',     # '2006-10-25 14:30:59'
@@ -32,6 +44,16 @@ DATETIME_INPUT_FORMATS = [
     '%m/%d/%y %H:%M:%S.%f',  # '10/25/06 14:30:59.000200'
     '%m/%d/%y %H:%M',        # '10/25/06 14:30'
 ]
+TIME_INPUT_FORMATS = [
+    '%H:%M:%S',     # '14:30:59'
+    '%H:%M:%S.%f',  # '14:30:59.000200'
+    '%H:%M',        # '14:30'
+]
+
+# Decimal separator symbol.
 DECIMAL_SEPARATOR = '.'
+# Thousand separator symbol.
 THOUSAND_SEPARATOR = ','
+# Number of digits that will be together, when splitting them by
+# THOUSAND_SEPARATOR. 0 means no grouping, 3 means splitting by thousands.
 NUMBER_GROUPING = 3

+ 0 - 2
django/conf/project_template/project_name/settings.py-tpl

@@ -109,8 +109,6 @@ TIME_ZONE = 'UTC'
 
 USE_I18N = True
 
-USE_L10N = True
-
 USE_TZ = True
 
 

+ 10 - 2
django/utils/formats.py

@@ -104,7 +104,11 @@ def get_format(format_type, lang=None, use_l10n=None):
     If use_l10n is provided and is not None, it forces the value to
     be localized (or not), overriding the value of settings.USE_L10N.
     """
-    use_l10n = use_l10n or (use_l10n is None and settings.USE_L10N)
+    use_l10n = use_l10n or (use_l10n is None and (
+        settings._USE_L10N_INTERNAL
+        if hasattr(settings, '_USE_L10N_INTERNAL')
+        else settings.USE_L10N
+    ))
     if use_l10n and lang is None:
         lang = get_language()
     cache_key = (format_type, lang)
@@ -168,7 +172,11 @@ def number_format(value, decimal_pos=None, use_l10n=None, force_grouping=False):
     If use_l10n is provided and is not None, it forces the value to
     be localized (or not), overriding the value of settings.USE_L10N.
     """
-    use_l10n = use_l10n or (use_l10n is None and settings.USE_L10N)
+    use_l10n = use_l10n or (use_l10n is None and (
+        settings._USE_L10N_INTERNAL
+        if hasattr(settings, '_USE_L10N_INTERNAL')
+        else settings.USE_L10N
+    ))
     lang = get_language() if use_l10n else None
     return numberformat.format(
         value,

+ 2 - 0
docs/internals/deprecation.txt

@@ -34,6 +34,8 @@ details on these changes.
   ``StringAgg`` aggregates will return ``None`` when there are no rows instead
   of ``[]``, ``[]``, and ``''`` respectively.
 
+* The ``USE_L10N`` setting will be removed.
+
 .. _deprecation-removed-in-4.1:
 
 4.1

+ 9 - 4
docs/ref/settings.txt

@@ -2774,7 +2774,7 @@ See also :setting:`LANGUAGE_CODE`, :setting:`USE_L10N` and :setting:`USE_TZ`.
 ``USE_L10N``
 ------------
 
-Default: ``False``
+Default: ``True``
 
 A boolean that specifies if localized formatting of data will be enabled by
 default or not. If this is set to ``True``, e.g. Django will display numbers and
@@ -2782,10 +2782,15 @@ dates using the format of the current locale.
 
 See also :setting:`LANGUAGE_CODE`, :setting:`USE_I18N` and :setting:`USE_TZ`.
 
-.. note::
+.. versionchanged:: 4.0
 
-    The default :file:`settings.py` file created by :djadmin:`django-admin
-    startproject <startproject>` includes ``USE_L10N = True`` for convenience.
+    In older versions, the default value is ``False``.
+
+.. deprecated:: 4.0
+
+    This setting is deprecated. Starting with Django 5.0, localized formatting
+    of data will always be enabled. For example Django will display numbers and
+    dates using the format of the current locale.
 
 .. setting:: USE_THOUSAND_SEPARATOR
 

+ 1 - 1
docs/releases/1.10.txt

@@ -918,7 +918,7 @@ Miscellaneous
   To adapt, move the fragment outside the template tag:
   ``{% static 'img.svg' %}#fragment``.
 
-* When :setting:`USE_L10N` is ``True``, localization is now applied for the
+* When ``USE_L10N`` is ``True``, localization is now applied for the
   :tfilter:`date` and :tfilter:`time` filters when no format string is
   specified. The ``DATE_FORMAT`` and ``TIME_FORMAT`` specifiers from the active
   locale are used instead of the settings of the same name.

+ 1 - 1
docs/releases/1.2.txt

@@ -936,7 +936,7 @@ Date format helper functions
 ``django.utils.translation.get_date_formats()`` and
 ``django.utils.translation.get_partial_date_formats()`` have been deprecated
 in favor of the appropriate calls to ``django.utils.formats.get_format()``,
-which is locale-aware when :setting:`USE_L10N` is set to ``True``, and falls
+which is locale-aware when ``USE_L10N`` is set to ``True``, and falls
 back to default settings if set to ``False``.
 
 To get the different date formats, instead of writing this::

+ 1 - 1
docs/releases/2.0.txt

@@ -558,7 +558,7 @@ Miscellaneous
 * :class:`~django.views.generic.base.RedirectView` no longer silences
   ``NoReverseMatch`` if the ``pattern_name`` doesn't exist.
 
-* When :setting:`USE_L10N` is off, :class:`~django.forms.FloatField` and
+* When ``USE_L10N`` is off, :class:`~django.forms.FloatField` and
   :class:`~django.forms.DecimalField` now respect :setting:`DECIMAL_SEPARATOR`
   and :setting:`THOUSAND_SEPARATOR` during validation. For example, with the
   settings::

+ 1 - 1
docs/releases/3.2.txt

@@ -672,7 +672,7 @@ Miscellaneous
   and underscores.
 
 * The :tfilter:`intcomma` and :tfilter:`intword` template filters no longer
-  depend on the :setting:`USE_L10N` setting.
+  depend on the ``USE_L10N`` setting.
 
 * Support for ``argon2-cffi`` < 19.1.0 is removed.
 

+ 19 - 2
docs/releases/4.0.txt

@@ -579,8 +579,11 @@ Miscellaneous
   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.
+  ``USE_L10N`` setting and always returns localized output. Use the ``u``
+  suffix to disable localization.
+
+* The default value of the ``USE_L10N`` setting is changed to ``True``. See the
+  :ref:`Localization section <use_l10n_deprecation>` above for more details.
 
 .. _deprecated-features-4.0:
 
@@ -601,6 +604,20 @@ Note that the default :file:`settings.py` file created by
 You can set ``USE_TZ`` to ``False`` in your project settings before then to
 opt-out.
 
+.. _use_l10n_deprecation:
+
+Localization
+------------
+
+In order to follow good practice, the default value of the ``USE_L10N`` setting
+is changed from ``False`` to ``True``.
+
+Moreover ``USE_L10N`` is deprecated as of this release. Starting with Django
+5.0, by default, any date or number displayed by Django will be localized.
+
+The :ttag:`{% localize %} <localize>` tag and the :tfilter:`localize`/
+:tfilter:`unlocalize` filters will still be honored by Django.
+
 Miscellaneous
 -------------
 

+ 7 - 9
docs/topics/i18n/formatting.txt

@@ -18,18 +18,16 @@ necessary to set :setting:`USE_L10N = True <USE_L10N>` in your settings file.
 
 .. note::
 
-    The default :file:`settings.py` file created by :djadmin:`django-admin
-    startproject <startproject>` includes :setting:`USE_L10N = True <USE_L10N>`
-    for convenience.  Note, however, that to enable number formatting with
-    thousand separators it is necessary to set :setting:`USE_THOUSAND_SEPARATOR
-    = True <USE_THOUSAND_SEPARATOR>` in your settings file. Alternatively, you
-    could use :tfilter:`intcomma` to format numbers in your template.
+    To enable number formatting with thousand separators, it is necessary to
+    set :setting:`USE_THOUSAND_SEPARATOR = True <USE_THOUSAND_SEPARATOR>` in
+    your settings file. Alternatively, you could use :tfilter:`intcomma` to
+    format numbers in your template.
 
 .. note::
 
-    There is also an independent but related :setting:`USE_I18N` setting that
-    controls if Django should activate translation. See
-    :doc:`/topics/i18n/translation` for more details.
+    There is a related :setting:`USE_I18N` setting that controls if Django
+    should activate translation. See :doc:`/topics/i18n/translation` for more
+    details.
 
 Locale aware input in forms
 ===========================

+ 0 - 6
docs/topics/i18n/timezones.txt

@@ -47,12 +47,6 @@ functions in :mod:`django.utils.timezone`.
     startproject <startproject>` includes :setting:`USE_TZ = True <USE_TZ>`
     for convenience.
 
-.. note::
-
-    There is also an independent but related :setting:`USE_L10N` setting that
-    controls whether Django should activate format localization. See
-    :doc:`/topics/i18n/formatting` for more details.
-
 If you're wrestling with a particular problem, start with the :ref:`time zone
 FAQ <time-zones-faq>`.
 

+ 0 - 6
docs/topics/i18n/translation.txt

@@ -29,12 +29,6 @@ use internationalization, you should take the two seconds to set
 :setting:`USE_I18N = False <USE_I18N>` in your settings file. Then Django will
 make some optimizations so as not to load the internationalization machinery.
 
-.. note::
-
-    There is also an independent but related :setting:`USE_L10N` setting that
-    controls if Django should implement format localization. See
-    :doc:`/topics/i18n/formatting` for more details.
-
 .. note::
 
     Make sure you've activated translation for your project (the fastest way is

+ 1 - 1
tests/admin_inlines/tests.py

@@ -382,7 +382,7 @@ class TestInline(TestDataMixin, TestCase):
             html=True
         )
 
-    @override_settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=True)
+    @override_settings(USE_THOUSAND_SEPARATOR=True)
     def test_localize_pk_shortcut(self):
         """
         The "View on Site" link is correct for locales that use thousand

+ 0 - 1
tests/admin_utils/test_logentry.py

@@ -74,7 +74,6 @@ class LogEntryTests(TestCase):
         logentry = LogEntry(change_message='non-JSON string')
         self.assertEqual(logentry.get_change_message(), logentry.change_message)
 
-    @override_settings(USE_L10N=True)
     def test_logentry_change_message_localized_datetime_input(self):
         """
         Localized date/time inputs shouldn't affect changed form data detection.

+ 2 - 2
tests/admin_utils/tests.py

@@ -201,7 +201,7 @@ class UtilsTests(SimpleTestCase):
         display_value = display_for_field(12345, models.IntegerField(), self.empty_value)
         self.assertEqual(display_value, '12345')
 
-    @override_settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=True)
+    @override_settings(USE_THOUSAND_SEPARATOR=True)
     def test_number_formats_with_thousand_separator_display_for_field(self):
         display_value = display_for_field(12345.6789, models.FloatField(), self.empty_value)
         self.assertEqual(display_value, '12,345.6789')
@@ -219,7 +219,7 @@ class UtilsTests(SimpleTestCase):
         display_value = display_for_value([1, 2, 'buckle', 'my', 'shoe'], self.empty_value)
         self.assertEqual(display_value, '1, 2, buckle, my, shoe')
 
-    @override_settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=True)
+    @override_settings(USE_THOUSAND_SEPARATOR=True)
     def test_list_display_for_value_boolean(self):
         self.assertEqual(
             display_for_value(True, '', boolean=True),

+ 1 - 1
tests/admin_views/test_actions.py

@@ -72,7 +72,7 @@ class AdminActionsTest(TestCase):
         self.assertContains(response, 'Are you sure you want to delete the selected subscribers?')
         self.assertContains(response, '<ul></ul>', html=True)
 
-    @override_settings(USE_THOUSAND_SEPARATOR=True, USE_L10N=True, NUMBER_GROUPING=3)
+    @override_settings(USE_THOUSAND_SEPARATOR=True, NUMBER_GROUPING=3)
     def test_non_localized_pk(self):
         """
         If USE_THOUSAND_SEPARATOR is set, the ids for the objects selected for

+ 7 - 7
tests/admin_views/tests.py

@@ -109,7 +109,7 @@ class AdminFieldExtractionMixin:
                 return field
 
 
-@override_settings(ROOT_URLCONF='admin_views.urls', USE_I18N=True, USE_L10N=False, LANGUAGE_CODE='en')
+@override_settings(ROOT_URLCONF='admin_views.urls', USE_I18N=True, LANGUAGE_CODE='en')
 class AdminViewBasicTestCase(TestCase):
 
     @classmethod
@@ -802,12 +802,12 @@ class AdminViewBasicTest(AdminViewBasicTestCase):
         response = self.client.get(reverse('admin-extra-context:jsi18n'))
         self.assertEqual(response.status_code, 200)
 
-    def test_L10N_deactivated(self):
+    def test_jsi18n_format_fallback(self):
         """
-        Check if L10N is deactivated, the JavaScript i18n view doesn't
-        return localized date/time formats. Refs #14824.
+        The JavaScript i18n view doesn't return localized date/time formats
+        when the selected language cannot be found.
         """
-        with self.settings(LANGUAGE_CODE='ru', USE_L10N=False), translation.override('none'):
+        with self.settings(LANGUAGE_CODE='ru'), translation.override('none'):
             response = self.client.get(reverse('admin:jsi18n'))
             self.assertNotContains(response, '%d.%m.%Y %H:%M:%S')
             self.assertContains(response, '%Y-%m-%d %H:%M:%S')
@@ -4541,7 +4541,7 @@ class PrePopulatedTest(TestCase):
             "&quot;id&quot;: &quot;#id_prepopulatedsubpost_set-0-subslug&quot;"
         )
 
-    @override_settings(USE_THOUSAND_SEPARATOR=True, USE_L10N=True)
+    @override_settings(USE_THOUSAND_SEPARATOR=True)
     def test_prepopulated_maxlength_localized(self):
         """
         Regression test for #15938: if USE_THOUSAND_SEPARATOR is set, make sure
@@ -5704,7 +5704,7 @@ class ValidXHTMLTests(TestCase):
         self.assertNotContains(response, ' xml:lang=""')
 
 
-@override_settings(ROOT_URLCONF='admin_views.urls', USE_THOUSAND_SEPARATOR=True, USE_L10N=True)
+@override_settings(ROOT_URLCONF='admin_views.urls', USE_THOUSAND_SEPARATOR=True)
 class DateHierarchyTests(TestCase):
 
     @classmethod

+ 2 - 2
tests/admin_widgets/tests.py

@@ -338,7 +338,7 @@ class AdminSplitDateTimeWidgetTest(SimpleTestCase):
     def test_localization(self):
         w = widgets.AdminSplitDateTime()
 
-        with self.settings(USE_L10N=True), translation.override('de-at'):
+        with translation.override('de-at'):
             w.is_localized = True
             self.assertHTMLEqual(
                 w.render('test', datetime(2007, 12, 1, 9, 30)),
@@ -939,7 +939,7 @@ class DateTimePickerSeleniumTests(AdminWidgetSeleniumTestCase):
             expected_caption = '{:s} {:d}'.format(may_translation.upper(), 1984)
 
             # Test with every locale
-            with override_settings(LANGUAGE_CODE=language_code, USE_L10N=True):
+            with override_settings(LANGUAGE_CODE=language_code):
 
                 # Open a page that has a date picker widget
                 url = reverse('admin:admin_widgets_member_change', args=(member.pk,))

+ 43 - 0
tests/deprecation/test_use_l10n.py

@@ -0,0 +1,43 @@
+import sys
+from types import ModuleType
+
+from django.conf import USE_L10N_DEPRECATED_MSG, Settings, settings
+from django.test import TestCase, ignore_warnings
+from django.utils.deprecation import RemovedInDjango50Warning
+
+
+class DeprecationTests(TestCase):
+    msg = USE_L10N_DEPRECATED_MSG
+
+    def test_override_settings_warning(self):
+        # Warning is raised when USE_L10N is set in UserSettingsHolder (used by
+        # the @override_settings decorator).
+        with self.assertRaisesMessage(RemovedInDjango50Warning, self.msg):
+            with self.settings(USE_L10N=True):
+                pass
+
+    def test_settings_init_warning(self):
+        settings_module = ModuleType('fake_settings_module')
+        settings_module.SECRET_KEY = 'foo'
+        settings_module.USE_TZ = True
+        settings_module.USE_L10N = False
+        sys.modules['fake_settings_module'] = settings_module
+        try:
+            with self.assertRaisesMessage(RemovedInDjango50Warning, self.msg):
+                Settings('fake_settings_module')
+        finally:
+            del sys.modules['fake_settings_module']
+
+    def test_access_warning(self):
+        with self.assertRaisesMessage(RemovedInDjango50Warning, self.msg):
+            settings.USE_L10N
+        # Works a second time.
+        with self.assertRaisesMessage(RemovedInDjango50Warning, self.msg):
+            settings.USE_L10N
+
+    @ignore_warnings(category=RemovedInDjango50Warning)
+    def test_access(self):
+        with self.settings(USE_L10N=False):
+            self.assertIs(settings.USE_L10N, False)
+            # Works a second time.
+            self.assertIs(settings.USE_L10N, False)

+ 1 - 4
tests/forms_tests/field_tests/test_datefield.py

@@ -2,7 +2,7 @@ from datetime import date, datetime
 
 from django.core.exceptions import ValidationError
 from django.forms import DateField, Form, HiddenInput, SelectDateWidget
-from django.test import SimpleTestCase, override_settings
+from django.test import SimpleTestCase
 from django.utils import translation
 
 
@@ -37,7 +37,6 @@ class DateFieldTest(SimpleTestCase):
         d = GetDate({'mydate_month': '1', 'mydate_day': '1', 'mydate_year': '2010'})
         self.assertIn('<label for="id_mydate_month">', d.as_p())
 
-    @override_settings(USE_L10N=True)
     @translation.override('nl')
     def test_l10n_date_changed(self):
         """
@@ -95,7 +94,6 @@ class DateFieldTest(SimpleTestCase):
         }, initial={'mydate': date(2008, 4, 1)})
         self.assertFalse(b.has_changed())
 
-    @override_settings(USE_L10N=True)
     @translation.override('nl')
     def test_l10n_invalid_date_in(self):
         # Invalid dates shouldn't be allowed
@@ -104,7 +102,6 @@ class DateFieldTest(SimpleTestCase):
         # 'Geef een geldige datum op.' = 'Enter a valid date.'
         self.assertEqual(a.errors, {'mydate': ['Voer een geldige datum in.']})
 
-    @override_settings(USE_L10N=True)
     @translation.override('nl')
     def test_form_label_association(self):
         # label tag is correctly associated with first rendered dropdown

+ 13 - 2
tests/forms_tests/field_tests/test_decimalfield.py

@@ -2,8 +2,9 @@ import decimal
 
 from django.core.exceptions import ValidationError
 from django.forms import DecimalField, NumberInput, Widget
-from django.test import SimpleTestCase, override_settings
+from django.test import SimpleTestCase, ignore_warnings, override_settings
 from django.utils import formats, translation
+from django.utils.deprecation import RemovedInDjango50Warning
 
 from . import FormFieldAssertionsMixin
 
@@ -153,17 +154,27 @@ class DecimalFieldTest(FormFieldAssertionsMixin, SimpleTestCase):
         self.assertFalse(f.has_changed(d, '0.10'))
         self.assertTrue(f.has_changed(d, '0.101'))
 
-        with translation.override('fr'), self.settings(USE_L10N=True):
+        with translation.override('fr'):
             f = DecimalField(max_digits=2, decimal_places=2, localize=True)
             localized_d = formats.localize_input(d)  # -> '0,1' in French
             self.assertFalse(f.has_changed(d, localized_d))
 
+    # RemovedInDjango50Warning: When the deprecation ends, remove
+    # @ignore_warnings and USE_L10N=False. The test should remain because
+    # format-related settings will take precedence over locale-dictated
+    # formats.
+    @ignore_warnings(category=RemovedInDjango50Warning)
     @override_settings(USE_L10N=False, DECIMAL_SEPARATOR=',')
     def test_decimalfield_support_decimal_separator(self):
         f = DecimalField(localize=True)
         self.assertEqual(f.clean('1001,10'), decimal.Decimal("1001.10"))
         self.assertEqual(f.clean('1001.10'), decimal.Decimal("1001.10"))
 
+    # RemovedInDjango50Warning: When the deprecation ends, remove
+    # @ignore_warnings and USE_L10N=False. The test should remain because
+    # format-related settings will take precedence over locale-dictated
+    # formats.
+    @ignore_warnings(category=RemovedInDjango50Warning)
     @override_settings(USE_L10N=False, DECIMAL_SEPARATOR=',', USE_THOUSAND_SEPARATOR=True,
                        THOUSAND_SEPARATOR='.')
     def test_decimalfield_support_thousands_separator(self):

+ 13 - 2
tests/forms_tests/field_tests/test_floatfield.py

@@ -1,8 +1,9 @@
 from django.core.exceptions import ValidationError
 from django.forms import FloatField, NumberInput
 from django.test import SimpleTestCase
-from django.test.utils import override_settings
+from django.test.utils import ignore_warnings, override_settings
 from django.utils import formats, translation
+from django.utils.deprecation import RemovedInDjango50Warning
 
 from . import FormFieldAssertionsMixin
 
@@ -81,17 +82,27 @@ class FloatFieldTest(FormFieldAssertionsMixin, SimpleTestCase):
         n = 4.35
         self.assertFalse(f.has_changed(n, '4.3500'))
 
-        with translation.override('fr'), self.settings(USE_L10N=True):
+        with translation.override('fr'):
             f = FloatField(localize=True)
             localized_n = formats.localize_input(n)  # -> '4,35' in French
             self.assertFalse(f.has_changed(n, localized_n))
 
+    # RemovedInDjango50Warning: When the deprecation ends, remove
+    # @ignore_warnings and USE_L10N=False. The test should remain because
+    # format-related settings will take precedence over locale-dictated
+    # formats.
+    @ignore_warnings(category=RemovedInDjango50Warning)
     @override_settings(USE_L10N=False, DECIMAL_SEPARATOR=',')
     def test_decimalfield_support_decimal_separator(self):
         f = FloatField(localize=True)
         self.assertEqual(f.clean('1001,10'), 1001.10)
         self.assertEqual(f.clean('1001.10'), 1001.10)
 
+    # RemovedInDjango50Warning: When the deprecation ends, remove
+    # @ignore_warnings and USE_L10N=False. The test should remain because
+    # format-related settings will take precedence over locale-dictated
+    # formats.
+    @ignore_warnings(category=RemovedInDjango50Warning)
     @override_settings(USE_L10N=False, DECIMAL_SEPARATOR=',', USE_THOUSAND_SEPARATOR=True,
                        THOUSAND_SEPARATOR='.')
     def test_decimalfield_support_thousands_separator(self):

+ 4 - 3
tests/forms_tests/tests/test_input_formats.py

@@ -3,10 +3,10 @@ from datetime import date, datetime, time
 from django import forms
 from django.core.exceptions import ValidationError
 from django.test import SimpleTestCase, override_settings
+from django.utils import translation
 from django.utils.translation import activate, deactivate
 
 
-@override_settings(TIME_INPUT_FORMATS=["%I:%M:%S %p", "%I:%M %p"], USE_L10N=True)
 class LocalizedTimeTests(SimpleTestCase):
     def setUp(self):
         # nl/formats.py has customized TIME_INPUT_FORMATS:
@@ -117,6 +117,7 @@ class LocalizedTimeTests(SimpleTestCase):
         self.assertEqual(text, "13:30:00")
 
 
+@translation.override(None)  # RemovedInDjango50Warning.
 @override_settings(TIME_INPUT_FORMATS=["%I:%M:%S %p", "%I:%M %p"])
 class CustomTimeInputFormatsTests(SimpleTestCase):
     def test_timeField(self):
@@ -310,7 +311,6 @@ class SimpleTimeFormatTests(SimpleTestCase):
         self.assertEqual(text, "13:30:00")
 
 
-@override_settings(DATE_INPUT_FORMATS=["%d/%m/%Y", "%d-%m-%Y"], USE_L10N=True)
 class LocalizedDateTests(SimpleTestCase):
     def setUp(self):
         activate('de')
@@ -422,6 +422,7 @@ class LocalizedDateTests(SimpleTestCase):
         self.assertEqual(text, "21.12.2010")
 
 
+@translation.override(None)  # RemovedInDjango50Warning.
 @override_settings(DATE_INPUT_FORMATS=["%d.%m.%Y", "%d-%m-%Y"])
 class CustomDateInputFormatsTests(SimpleTestCase):
     def test_dateField(self):
@@ -615,7 +616,6 @@ class SimpleDateFormatTests(SimpleTestCase):
         self.assertEqual(text, "2010-12-21")
 
 
-@override_settings(DATETIME_INPUT_FORMATS=["%I:%M:%S %p %d/%m/%Y", "%I:%M %p %d-%m-%Y"], USE_L10N=True)
 class LocalizedDateTimeTests(SimpleTestCase):
     def setUp(self):
         activate('de')
@@ -731,6 +731,7 @@ class LocalizedDateTimeTests(SimpleTestCase):
         self.assertEqual(text, "21.12.2010 13:30:00")
 
 
+@translation.override(None)  # RemovedInDjango50Warning.
 @override_settings(DATETIME_INPUT_FORMATS=["%I:%M:%S %p %d/%m/%Y", "%I:%M %p %d-%m-%Y"])
 class CustomDateTimeInputFormatsTests(SimpleTestCase):
     def test_dateTimeField(self):

+ 1 - 1
tests/forms_tests/widget_tests/test_checkboxselectmultiple.py

@@ -133,7 +133,7 @@ class CheckboxSelectMultipleTest(WidgetTest):
         """
         self.check_html(widget, 'letters', ['a', 'c'], html=html)
 
-    @override_settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=True)
+    @override_settings(USE_THOUSAND_SEPARATOR=True)
     def test_doesnt_localize_input_value(self):
         choices = [
             (1, 'One'),

+ 0 - 2
tests/forms_tests/widget_tests/test_dateinput.py

@@ -1,7 +1,6 @@
 from datetime import date
 
 from django.forms import DateInput
-from django.test import override_settings
 from django.utils import translation
 
 from .base import WidgetTest
@@ -38,7 +37,6 @@ class DateInputTest(WidgetTest):
         widget = DateInput(format='%d/%m/%Y', attrs={'type': 'date'})
         self.check_html(widget, 'date', d, html='<input type="date" name="date" value="17/09/2007">')
 
-    @override_settings(USE_L10N=True)
     @translation.override('de-at')
     def test_l10n(self):
         self.check_html(

+ 15 - 8
tests/forms_tests/widget_tests/test_datetimeinput.py

@@ -1,8 +1,9 @@
 from datetime import datetime
 
 from django.forms import DateTimeInput
-from django.test import override_settings
+from django.test import ignore_warnings
 from django.utils import translation
+from django.utils.deprecation import RemovedInDjango50Warning
 
 from .base import WidgetTest
 
@@ -39,7 +40,6 @@ class DateTimeInputTest(WidgetTest):
         d = datetime(2007, 9, 17, 12, 51, 34, 482548)
         self.check_html(widget, 'date', d, html='<input type="datetime" name="date" value="17/09/2007 12:51">')
 
-    @override_settings(USE_L10N=True)
     @translation.override('de-at')
     def test_l10n(self):
         d = datetime(2007, 9, 17, 12, 51, 34, 482548)
@@ -47,15 +47,22 @@ class DateTimeInputTest(WidgetTest):
             '<input type="text" name="date" value="17.09.2007 12:51:34">'
         ))
 
-    @override_settings(USE_L10N=True)
     @translation.override('de-at')
     def test_locale_aware(self):
         d = datetime(2007, 9, 17, 12, 51, 34, 482548)
-        with self.settings(USE_L10N=False):
-            self.check_html(
-                self.widget, 'date', d,
-                html='<input type="text" name="date" value="2007-09-17 12:51:34">',
-            )
+        # RemovedInDjango50Warning: When the deprecation ends, remove
+        # @ignore_warnings and USE_L10N=False. The assertion should remain
+        # because format-related settings will take precedence over
+        # locale-dictated formats.
+        with ignore_warnings(category=RemovedInDjango50Warning):
+            with self.settings(USE_L10N=False):
+                with self.settings(DATETIME_INPUT_FORMATS=[
+                    '%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M:%S.%f', '%Y-%m-%d %H:%M',
+                ]):
+                    self.check_html(
+                        self.widget, 'date', d,
+                        html='<input type="text" name="date" value="2007-09-17 12:51:34">',
+                    )
         with translation.override('es'):
             self.check_html(
                 self.widget, 'date', d,

+ 0 - 2
tests/forms_tests/widget_tests/test_nullbooleanselect.py

@@ -1,5 +1,4 @@
 from django.forms import NullBooleanSelect
-from django.test import override_settings
 from django.utils import translation
 
 from .base import WidgetTest
@@ -89,7 +88,6 @@ class NullBooleanSelectTest(WidgetTest):
             </select>"""
         ))
 
-    @override_settings(USE_L10N=True)
     def test_l10n(self):
         """
         The NullBooleanSelect widget's options are lazily localized (#17190).

+ 1 - 1
tests/forms_tests/widget_tests/test_numberinput.py

@@ -6,7 +6,7 @@ from .base import WidgetTest
 
 class NumberInputTests(WidgetTest):
 
-    @override_settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=True)
+    @override_settings(USE_THOUSAND_SEPARATOR=True)
     def test_attrs_not_localized(self):
         widget = NumberInput(attrs={'max': 12345, 'min': 1234, 'step': 9999})
         self.check_html(

+ 1 - 1
tests/forms_tests/widget_tests/test_radioselect.py

@@ -101,7 +101,7 @@ class RadioSelectTest(WidgetTest):
         """
         self.check_html(self.widget(choices=self.beatles), 'beatle', 'J', attrs={'class': 'bar'}, html=html)
 
-    @override_settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=True)
+    @override_settings(USE_THOUSAND_SEPARATOR=True)
     def test_doesnt_localize_input_value(self):
         choices = [
             (1, 'One'),

+ 1 - 1
tests/forms_tests/widget_tests/test_select.py

@@ -220,7 +220,7 @@ class SelectTest(WidgetTest):
             </select>"""
         ))
 
-    @override_settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=True)
+    @override_settings(USE_THOUSAND_SEPARATOR=True)
     def test_doesnt_localize_option_value(self):
         choices = [
             (1, 'One'),

+ 8 - 3
tests/forms_tests/widget_tests/test_selectdatewidget.py

@@ -1,9 +1,10 @@
 from datetime import date
 
 from django.forms import DateField, Form, SelectDateWidget
-from django.test import override_settings
+from django.test import ignore_warnings, override_settings
 from django.utils import translation
 from django.utils.dates import MONTHS_AP
+from django.utils.deprecation import RemovedInDjango50Warning
 
 from .base import WidgetTest
 
@@ -387,7 +388,6 @@ class SelectDateWidgetTest(WidgetTest):
         with self.assertRaisesMessage(ValueError, 'empty_label list/tuple must have 3 elements.'):
             SelectDateWidget(years=('2014',), empty_label=('not enough', 'values'))
 
-    @override_settings(USE_L10N=True)
     @translation.override('nl')
     def test_l10n(self):
         w = SelectDateWidget(
@@ -485,6 +485,11 @@ class SelectDateWidgetTest(WidgetTest):
             '13-08-0001',
         )
 
+    # RemovedInDjango50Warning: When the deprecation ends, remove
+    # @ignore_warnings and USE_L10N=False. The test should remain because
+    # format-related settings will take precedence over locale-dictated
+    # formats.
+    @ignore_warnings(category=RemovedInDjango50Warning)
     @override_settings(USE_L10N=False, DATE_INPUT_FORMATS=['%d.%m.%Y'])
     def test_custom_input_format(self):
         w = SelectDateWidget(years=('0001', '1899', '2009', '2010'))
@@ -551,7 +556,7 @@ class SelectDateWidgetTest(WidgetTest):
         data = {'field_day': '1', 'field_month': '12', 'field_year': '2000'}
         self.assertIs(self.widget.value_omitted_from_data(data, {}, 'field'), False)
 
-    @override_settings(USE_THOUSAND_SEPARATOR=True, USE_L10N=True)
+    @override_settings(USE_THOUSAND_SEPARATOR=True)
     def test_years_rendered_without_separator(self):
         widget = SelectDateWidget(years=(2007,))
         self.check_html(widget, 'mydate', '', html=(

+ 0 - 2
tests/forms_tests/widget_tests/test_splithiddendatetimewidget.py

@@ -1,7 +1,6 @@
 from datetime import datetime
 
 from django.forms import SplitHiddenDateTimeWidget
-from django.test import override_settings
 from django.utils import translation
 
 from .base import WidgetTest
@@ -30,7 +29,6 @@ class SplitHiddenDateTimeWidgetTest(WidgetTest):
             '<input type="hidden" name="date_1" value="12:51:00">'
         ))
 
-    @override_settings(USE_L10N=True)
     @translation.override('de-at')
     def test_l10n(self):
         d = datetime(2007, 9, 17, 12, 51)

+ 0 - 2
tests/forms_tests/widget_tests/test_timeinput.py

@@ -1,7 +1,6 @@
 from datetime import time
 
 from django.forms import TimeInput
-from django.test import override_settings
 from django.utils import translation
 
 from .base import WidgetTest
@@ -41,7 +40,6 @@ class TimeInputTest(WidgetTest):
         widget = TimeInput(format='%H:%M', attrs={'type': 'time'})
         self.check_html(widget, 'time', t, html='<input type="time" name="time" value="12:51">')
 
-    @override_settings(USE_L10N=True)
     @translation.override('de-at')
     def test_l10n(self):
         t = time(12, 51, 34, 482548)

+ 1 - 1
tests/gis_tests/test_geoforms.py

@@ -238,7 +238,7 @@ class SpecializedFieldTest(SimpleTestCase):
         self.assertIn(escape(ogr.json), rendered)
 
     # map_srid in openlayers.html template must not be localized.
-    @override_settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=True)
+    @override_settings(USE_THOUSAND_SEPARATOR=True)
     def test_pointfield(self):
         class PointForm(forms.Form):
             p = forms.PointField()

+ 5 - 5
tests/humanize_tests/tests.py

@@ -89,13 +89,13 @@ class HumanizeTests(SimpleTestCase):
             '100', '1,000', '10,123', '10,311', '1,000,000', '1,234,567.1234567',
             '1,234,567.1234567', None,
         )
-        with self.settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=False):
+        with self.settings(USE_THOUSAND_SEPARATOR=False):
             with translation.override('en'):
                 self.humanize_tester(test_list, result_list, 'intcomma')
 
     def test_intcomma_without_number_grouping(self):
         # Regression for #17414
-        with translation.override('ja'), self.settings(USE_L10N=True):
+        with translation.override('ja'):
             self.humanize_tester([100], ['100'], 'intcomma')
 
     def test_intword(self):
@@ -126,7 +126,7 @@ class HumanizeTests(SimpleTestCase):
                      '100', '1000', '10123', '10311', '1000000', None)
         result_list = ('100', '1.000', '10.123', '10.311', '1.000.000', '1.234.567,25',
                        '100', '1.000', '10.123', '10.311', '1.000.000', None)
-        with self.settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=True):
+        with self.settings(USE_THOUSAND_SEPARATOR=True):
             with translation.override('de'):
                 self.humanize_tester(test_list, result_list, 'intcomma')
 
@@ -143,7 +143,7 @@ class HumanizeTests(SimpleTestCase):
         # Negative integers.
         test_list_negative = ('-' + test for test in test_list_positive)
         result_list_negative = ('-' + result for result in result_list_positive)
-        with self.settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=True):
+        with self.settings(USE_THOUSAND_SEPARATOR=True):
             with translation.override('de'):
                 self.humanize_tester(
                     (*test_list_positive, *test_list_negative),
@@ -355,7 +355,7 @@ class HumanizeTests(SimpleTestCase):
         orig_humanize_datetime, humanize.datetime = humanize.datetime, MockDateTime
         try:
             # Choose a language with different naturaltime-past/naturaltime-future translations
-            with translation.override('cs'), self.settings(USE_L10N=True):
+            with translation.override('cs'):
                 self.humanize_tester(test_list, result_list, 'naturaltime')
         finally:
             humanize.datetime = orig_humanize_datetime

+ 52 - 23
tests/i18n/tests.py

@@ -19,9 +19,11 @@ from django.conf.locale import LANG_INFO
 from django.conf.urls.i18n import i18n_patterns
 from django.template import Context, Template
 from django.test import (
-    RequestFactory, SimpleTestCase, TestCase, override_settings,
+    RequestFactory, SimpleTestCase, TestCase, ignore_warnings,
+    override_settings,
 )
 from django.utils import translation
+from django.utils.deprecation import RemovedInDjango50Warning
 from django.utils.formats import (
     date_format, get_format, iter_format_modules, localize, localize_input,
     reset_format_cache, sanitize_separators, sanitize_strftime_format,
@@ -422,7 +424,6 @@ class TranslationThreadSafetyTests(SimpleTestCase):
         self.assertLess(translation_count, len(trans_real._translations))
 
 
-@override_settings(USE_L10N=True)
 class FormattingTests(SimpleTestCase):
 
     def setUp(self):
@@ -498,6 +499,7 @@ class FormattingTests(SimpleTestCase):
             self.assertEqual('31.12.2009 в 20:50', Template('{{ dt|date:"d.m.Y в H:i" }}').render(self.ctxt))
             self.assertEqual('⌚ 10:15', Template('{{ t|time:"⌚ H:i" }}').render(self.ctxt))
 
+    @ignore_warnings(category=RemovedInDjango50Warning)
     @override_settings(USE_L10N=False)
     def test_l10n_disabled(self):
         """
@@ -1135,8 +1137,9 @@ class FormattingTests(SimpleTestCase):
                 self.assertEqual(sanitize_separators('77\xa0777,777'), '77777.777')
                 self.assertEqual(sanitize_separators('12 345'), '12345')
                 self.assertEqual(sanitize_separators('77 777,777'), '77777.777')
-            with self.settings(USE_THOUSAND_SEPARATOR=True, USE_L10N=False):
-                self.assertEqual(sanitize_separators('12\xa0345'), '12\xa0345')
+            with translation.override(None):  # RemovedInDjango50Warning
+                with self.settings(USE_THOUSAND_SEPARATOR=True, THOUSAND_SEPARATOR='.'):
+                    self.assertEqual(sanitize_separators('12\xa0345'), '12\xa0345')
 
         with self.settings(USE_THOUSAND_SEPARATOR=True):
             with patch_formats(get_language(), THOUSAND_SEPARATOR='.', DECIMAL_SEPARATOR=','):
@@ -1144,18 +1147,25 @@ class FormattingTests(SimpleTestCase):
                 # Suspicion that user entered dot as decimal separator (#22171)
                 self.assertEqual(sanitize_separators('10.10'), '10.10')
 
-        with self.settings(USE_L10N=False, DECIMAL_SEPARATOR=','):
-            self.assertEqual(sanitize_separators('1001,10'), '1001.10')
-            self.assertEqual(sanitize_separators('1001.10'), '1001.10')
-
-        with self.settings(
-            USE_L10N=False, DECIMAL_SEPARATOR=',', USE_THOUSAND_SEPARATOR=True,
-            THOUSAND_SEPARATOR='.'
-        ):
-            self.assertEqual(sanitize_separators('1.001,10'), '1001.10')
-            self.assertEqual(sanitize_separators('1001,10'), '1001.10')
-            self.assertEqual(sanitize_separators('1001.10'), '1001.10')
-            self.assertEqual(sanitize_separators('1,001.10'), '1.001.10')  # Invalid output
+        # RemovedInDjango50Warning: When the deprecation ends, remove
+        # @ignore_warnings and USE_L10N=False. The assertions should remain
+        # because format-related settings will take precedence over
+        # locale-dictated formats.
+        with ignore_warnings(category=RemovedInDjango50Warning):
+            with self.settings(USE_L10N=False):
+                with self.settings(DECIMAL_SEPARATOR=','):
+                    self.assertEqual(sanitize_separators('1001,10'), '1001.10')
+                    self.assertEqual(sanitize_separators('1001.10'), '1001.10')
+                with self.settings(
+                    DECIMAL_SEPARATOR=',',
+                    THOUSAND_SEPARATOR='.',
+                    USE_THOUSAND_SEPARATOR=True,
+                ):
+                    self.assertEqual(sanitize_separators('1.001,10'), '1001.10')
+                    self.assertEqual(sanitize_separators('1001,10'), '1001.10')
+                    self.assertEqual(sanitize_separators('1001.10'), '1001.10')
+                    # Invalid output.
+                    self.assertEqual(sanitize_separators('1,001.10'), '1.001.10')
 
     def test_iter_format_modules(self):
         """
@@ -1225,10 +1235,21 @@ class FormattingTests(SimpleTestCase):
         output3 = '; '.join([expected_localized, expected_unlocalized])
         output4 = '; '.join([expected_unlocalized, expected_localized])
         with translation.override('de', deactivate=True):
-            with self.settings(USE_L10N=False, USE_THOUSAND_SEPARATOR=True):
-                self.assertEqual(template1.render(context), output1)
-                self.assertEqual(template4.render(context), output4)
-            with self.settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=True):
+            # RemovedInDjango50Warning: When the deprecation ends, remove
+            # @ignore_warnings and USE_L10N=False. The assertions should remain
+            # because format-related settings will take precedence over
+            # locale-dictated formats.
+            with ignore_warnings(category=RemovedInDjango50Warning):
+                with self.settings(
+                    USE_L10N=False,
+                    DATE_FORMAT='N j, Y',
+                    DECIMAL_SEPARATOR='.',
+                    NUMBER_GROUPING=0,
+                    USE_THOUSAND_SEPARATOR=True,
+                ):
+                    self.assertEqual(template1.render(context), output1)
+                    self.assertEqual(template4.render(context), output4)
+            with self.settings(USE_THOUSAND_SEPARATOR=True):
                 self.assertEqual(template1.render(context), output1)
                 self.assertEqual(template2.render(context), output2)
                 self.assertEqual(template3.render(context), output3)
@@ -1242,9 +1263,17 @@ class FormattingTests(SimpleTestCase):
         context = Context(
             {'int': 1455, 'float': 3.14, 'decimal': decimal.Decimal('24.1567')}
         )
-        for use_l10n in [True, False]:
-            with self.subTest(use_l10n=use_l10n), self.settings(
-                USE_L10N=use_l10n,
+        with self.settings(
+            DECIMAL_SEPARATOR=',',
+            USE_THOUSAND_SEPARATOR=True,
+            THOUSAND_SEPARATOR='°',
+            NUMBER_GROUPING=2,
+        ):
+            self.assertEqual(template.render(context), '1455/3.14/24.1567')
+        # RemovedInDjango50Warning.
+        with ignore_warnings(category=RemovedInDjango50Warning):
+            with self.settings(
+                USE_L10N=False,
                 DECIMAL_SEPARATOR=',',
                 USE_THOUSAND_SEPARATOR=True,
                 THOUSAND_SEPARATOR='°',

+ 5 - 5
tests/settings_tests/tests.py

@@ -248,19 +248,19 @@ class SettingsTests(SimpleTestCase):
         Allow deletion of a setting in an overridden settings set (#18824)
         """
         previous_i18n = settings.USE_I18N
-        previous_l10n = settings.USE_L10N
+        previous_tz = settings.USE_TZ
         with self.settings(USE_I18N=False):
             del settings.USE_I18N
             with self.assertRaises(AttributeError):
                 getattr(settings, 'USE_I18N')
             # Should also work for a non-overridden setting
-            del settings.USE_L10N
+            del settings.USE_TZ
             with self.assertRaises(AttributeError):
-                getattr(settings, 'USE_L10N')
+                getattr(settings, 'USE_TZ')
             self.assertNotIn('USE_I18N', dir(settings))
-            self.assertNotIn('USE_L10N', dir(settings))
+            self.assertNotIn('USE_TZ', dir(settings))
         self.assertEqual(settings.USE_I18N, previous_i18n)
-        self.assertEqual(settings.USE_L10N, previous_l10n)
+        self.assertEqual(settings.USE_TZ, previous_tz)
 
     def test_override_settings_nested(self):
         """

+ 1 - 1
tests/sitemaps_tests/test_http.py

@@ -176,7 +176,7 @@ class HTTPSitemapTests(SitemapTestsBase):
         response = self.client.get('/lastmod-sitemaps/descending.xml')
         self.assertEqual(response.headers['Last-Modified'], 'Sat, 20 Apr 2013 05:00:00 GMT')
 
-    @override_settings(USE_I18N=True, USE_L10N=True)
+    @override_settings(USE_I18N=True)
     def test_localized_priority(self):
         """The priority value should not be localized."""
         with translation.override('fr'):

+ 2 - 6
tests/template_tests/filter_tests/test_date.py

@@ -1,7 +1,7 @@
 from datetime import datetime, time
 
 from django.template.defaultfilters import date
-from django.test import SimpleTestCase, override_settings
+from django.test import SimpleTestCase
 from django.utils import timezone, translation
 
 from ..utils import setup
@@ -20,13 +20,9 @@ class DateTests(TimezoneTestCase):
         output = self.engine.render_to_string('date02', {'d': datetime(2008, 1, 1)})
         self.assertEqual(output, 'Jan. 1, 2008')
 
-    @override_settings(USE_L10N=True)
     @setup({'date02_l10n': '{{ d|date }}'})
     def test_date02_l10n(self):
-        """
-        Without arg and when USE_L10N is True, the active language's DATE_FORMAT
-        is used.
-        """
+        """Without arg, the active language's DATE_FORMAT is used."""
         with translation.override('fr'):
             output = self.engine.render_to_string('date02_l10n', {'d': datetime(2008, 1, 1)})
         self.assertEqual(output, '1 janvier 2008')

+ 1 - 1
tests/template_tests/filter_tests/test_filesizeformat.py

@@ -47,7 +47,7 @@ class FunctionTests(SimpleTestCase):
             ('', '0\xa0Bytes'),
             ('\N{GREEK SMALL LETTER ALPHA}', '0\xa0Bytes'),
         ]
-        with self.settings(USE_L10N=True), translation.override('de'):
+        with translation.override('de'):
             for value, expected in tests:
                 with self.subTest(value=value):
                     self.assertEqual(filesizeformat(value), expected)

+ 1 - 2
tests/template_tests/filter_tests/test_time.py

@@ -1,7 +1,7 @@
 from datetime import time
 
 from django.template.defaultfilters import time as time_filter
-from django.test import SimpleTestCase, override_settings
+from django.test import SimpleTestCase
 from django.utils import timezone, translation
 
 from ..utils import setup
@@ -18,7 +18,6 @@ class TimeTests(TimezoneTestCase):
         output = self.engine.render_to_string('time00', {'dt': time(16, 25)})
         self.assertEqual(output, '4:25 p.m.')
 
-    @override_settings(USE_L10N=True)
     @setup({'time00_l10n': '{{ dt|time }}'})
     def test_time00_l10n(self):
         with translation.override('fr'):

+ 28 - 2
tests/timezones/tests.py

@@ -24,12 +24,13 @@ from django.template import (
     Context, RequestContext, Template, TemplateSyntaxError, context_processors,
 )
 from django.test import (
-    SimpleTestCase, TestCase, TransactionTestCase, override_settings,
-    skipIfDBFeature, skipUnlessDBFeature,
+    SimpleTestCase, TestCase, TransactionTestCase, ignore_warnings,
+    override_settings, skipIfDBFeature, skipUnlessDBFeature,
 )
 from django.test.utils import requires_tz_support
 from django.urls import reverse
 from django.utils import timezone
+from django.utils.deprecation import RemovedInDjango50Warning
 from django.utils.timezone import timedelta
 
 from .forms import (
@@ -812,8 +813,15 @@ class SerializationTests(SimpleTestCase):
                 self.assertEqual(obj.dt, dt)
 
 
+# RemovedInDjango50Warning: When the deprecation ends, remove setUpClass() and
+# USE_L10N=False. The tests should remain because format-related settings will
+# take precedence over locale-dictated formats.
 @override_settings(DATETIME_FORMAT='c', TIME_ZONE='Africa/Nairobi', USE_L10N=False, USE_TZ=True)
 class TemplateTests(SimpleTestCase):
+    @classmethod
+    def setUpClass(cls):
+        with ignore_warnings(category=RemovedInDjango50Warning):
+            super().setUpClass()
 
     @requires_tz_support
     def test_localtime_templatetag_and_filters(self):
@@ -1072,8 +1080,15 @@ class TemplateTests(SimpleTestCase):
             self.assertEqual(tpl.render(Context({})), "+0700")
 
 
+# RemovedInDjango50Warning: When the deprecation ends, remove setUpClass() and
+# USE_L10N=False. The tests should remain because format-related settings will
+# take precedence over locale-dictated formats.
 @override_settings(DATETIME_FORMAT='c', TIME_ZONE='Africa/Nairobi', USE_L10N=False, USE_TZ=False)
 class LegacyFormsTests(TestCase):
+    @classmethod
+    def setUpClass(cls):
+        with ignore_warnings(category=RemovedInDjango50Warning):
+            super().setUpClass()
 
     def test_form(self):
         form = EventForm({'dt': '2011-09-01 13:20:30'})
@@ -1109,8 +1124,15 @@ class LegacyFormsTests(TestCase):
         self.assertEqual(e.dt, datetime.datetime(2011, 9, 1, 13, 20, 30))
 
 
+# RemovedInDjango50Warning: When the deprecation ends, remove setUpClass() and
+# USE_L10N=False. The tests should remain because format-related settings will
+# take precedence over locale-dictated formats.
 @override_settings(DATETIME_FORMAT='c', TIME_ZONE='Africa/Nairobi', USE_L10N=False, USE_TZ=True)
 class NewFormsTests(TestCase):
+    @classmethod
+    def setUpClass(cls):
+        with ignore_warnings(category=RemovedInDjango50Warning):
+            super().setUpClass()
 
     @requires_tz_support
     def test_form(self):
@@ -1183,6 +1205,10 @@ class NewFormsTests(TestCase):
     ROOT_URLCONF='timezones.urls',
 )
 class AdminTests(TestCase):
+    @classmethod
+    def setUpClass(cls):
+        with ignore_warnings(category=RemovedInDjango50Warning):
+            super().setUpClass()
 
     @classmethod
     def setUpTestData(cls):

+ 1 - 2
tests/utils_tests/test_numberformat.py

@@ -15,9 +15,8 @@ class TestNumberFormat(SimpleTestCase):
         self.assertEqual(nformat(1234, '.', grouping=2, thousand_sep=',', force_grouping=True), '12,34')
         self.assertEqual(nformat(-1234.33, '.', decimal_pos=1), '-1234.3')
         # The use_l10n parameter can force thousand grouping behavior.
-        with self.settings(USE_THOUSAND_SEPARATOR=True, USE_L10N=True):
+        with self.settings(USE_THOUSAND_SEPARATOR=True):
             self.assertEqual(nformat(1234, '.', grouping=3, thousand_sep=',', use_l10n=False), '1234')
-        with self.settings(USE_THOUSAND_SEPARATOR=True, USE_L10N=False):
             self.assertEqual(nformat(1234, '.', grouping=3, thousand_sep=',', use_l10n=True), '1,234')
 
     def test_format_string(self):

+ 1 - 1
tests/view_tests/tests/test_debug.py

@@ -195,7 +195,7 @@ class DebugViewTests(SimpleTestCase):
         """
         Numeric IDs and fancy traceback context blocks line numbers shouldn't be localized.
         """
-        with self.settings(DEBUG=True, USE_L10N=True):
+        with self.settings(DEBUG=True):
             with self.assertLogs('django.request', 'ERROR'):
                 response = self.client.get('/raises500/')
             # We look for a HTML fragment of the form

+ 3 - 3
tests/view_tests/tests/test_i18n.py

@@ -206,8 +206,8 @@ class I18NViewTests(SimpleTestCase):
     def test_get_formats(self):
         formats = get_formats()
         # Test 3 possible types in get_formats: integer, string, and list.
-        self.assertEqual(formats['FIRST_DAY_OF_WEEK'], 0)
-        self.assertEqual(formats['DECIMAL_SEPARATOR'], '.')
+        self.assertEqual(formats['FIRST_DAY_OF_WEEK'], 1)
+        self.assertEqual(formats['DECIMAL_SEPARATOR'], ',')
         self.assertEqual(formats['TIME_INPUT_FORMATS'], ['%H:%M:%S', '%H:%M:%S.%f', '%H:%M'])
 
     def test_jsi18n(self):
@@ -243,7 +243,7 @@ class I18NViewTests(SimpleTestCase):
             self.assertIn('catalog', data)
             self.assertIn('formats', data)
             self.assertEqual(data['formats']['TIME_INPUT_FORMATS'], ['%H:%M:%S', '%H:%M:%S.%f', '%H:%M'])
-            self.assertEqual(data['formats']['FIRST_DAY_OF_WEEK'], 0)
+            self.assertEqual(data['formats']['FIRST_DAY_OF_WEEK'], 1)
             self.assertIn('plural', data)
             self.assertEqual(data['catalog']['month name\x04May'], 'Mai')
             self.assertIn('DATETIME_FORMAT', data['formats'])