diff --git a/django/contrib/humanize/templatetags/humanize.py b/django/contrib/humanize/templatetags/humanize.py index 19000c185c..a69b820c85 100644 --- a/django/contrib/humanize/templatetags/humanize.py +++ b/django/contrib/humanize/templatetags/humanize.py @@ -173,7 +173,7 @@ def apnumber(value): # Perform the comparison in the default time zone when USE_TZ = True # (unless a specific time zone has been applied with the |timezone filter). @register.filter(expects_localtime=True) -def naturalday(value, arg=None): +def naturalday(value, arg=None, **kwargs): """ For date values that are tomorrow, today or yesterday compared to present day return representing string. Otherwise, return a string diff --git a/django/template/base.py b/django/template/base.py index 0f1eca58db..b0720c456a 100644 --- a/django/template/base.py +++ b/django/template/base.py @@ -737,12 +737,14 @@ class FilterExpression: arg_vals.append(mark_safe(arg)) else: arg_vals.append(arg.resolve(context)) + kwargs = {} if getattr(func, "expects_localtime", False): obj = template_localtime(obj, context.use_tz) + kwargs["use_l10n"] = context.use_l10n if getattr(func, "needs_autoescape", False): - new_obj = func(obj, autoescape=context.autoescape, *arg_vals) + new_obj = func(obj, autoescape=context.autoescape, *arg_vals, **kwargs) else: - new_obj = func(obj, *arg_vals) + new_obj = func(obj, *arg_vals, **kwargs) if getattr(func, "is_safe", False) and isinstance(obj, SafeData): obj = mark_safe(new_obj) else: diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py index a08ce2710d..795c01e232 100644 --- a/django/template/defaultfilters.py +++ b/django/template/defaultfilters.py @@ -763,12 +763,12 @@ def get_digit(value, arg): @register.filter(expects_localtime=True, is_safe=False) -def date(value, arg=None): +def date(value, arg=None, use_l10n=None): """Format a date according to the given format.""" if value in (None, ""): return "" try: - return formats.date_format(value, arg) + return formats.date_format(value, arg, use_l10n=use_l10n) except AttributeError: try: return format(value, arg) @@ -777,12 +777,12 @@ def date(value, arg=None): @register.filter(expects_localtime=True, is_safe=False) -def time(value, arg=None): +def time(value, arg=None, use_l10n=None): """Format a time according to the given format.""" if value in (None, ""): return "" try: - return formats.time_format(value, arg) + return formats.time_format(value, arg, use_l10n=use_l10n) except (AttributeError, TypeError): try: return time_format(value, arg) diff --git a/tests/i18n/tests.py b/tests/i18n/tests.py index 56d73eb778..90cf4630bf 100644 --- a/tests/i18n/tests.py +++ b/tests/i18n/tests.py @@ -1353,20 +1353,49 @@ class FormattingTests(SimpleTestCase): def test_unlocalize_honor_date_settings(self): filter_template = Template( - "{% load l10n %}Localized: {{ date }}. Unlocalized: {{ date|unlocalize }}." + "{% load l10n %}Localized: {{ my_value }}. " + "Unlocalized: {{ my_value|unlocalize }}." ) tag_template = Template( - "{% load l10n %}Localized: {{ date }}. {% localize off %}Unlocalized: " - "{{ date }}{% endlocalize %}." + "{% load l10n %}Localized: {{ my_value }}. {% localize off %}" + "Unlocalized: {{ my_value }}{% endlocalize %}." ) - context = Context({"date": datetime.date(2024, 12, 15)}) + filter_inside_unlocalize = Template( + "{% load l10n %}Localized: {{ my_value|date }}. {% localize off %}" + "Unlocalized: {{ my_value|date:'DATE_FORMAT' }}{% endlocalize %}." + ) + context = Context({"my_value": datetime.date(2024, 12, 15)}) expected_result = "Localized: 15. Dezember 2024. Unlocalized: 15-12-2024." - with ( - translation.override("de", deactivate=True), - self.settings(DATE_FORMAT="j-m-Y"), - ): - self.assertEqual(filter_template.render(context), expected_result) - self.assertEqual(tag_template.render(context), expected_result) + for case in (filter_template, tag_template, filter_inside_unlocalize): + with ( + self.subTest(case=str(case)), + translation.override("de", deactivate=True), + self.settings(DATE_FORMAT="j-m-Y"), + ): + self.assertEqual(case.render(context), expected_result) + + def test_unlocalize_honor_time_settings(self): + filter_template = Template( + "{% load l10n %}Localized: {{ my_value }}. " + "Unlocalized: {{ my_value|unlocalize }}." + ) + tag_template = Template( + "{% load l10n %}Localized: {{ my_value }}. {% localize off %}" + "Unlocalized: {{ my_value }}{% endlocalize %}." + ) + filter_inside_unlocalize = Template( + "{% load l10n %}Localized: {{ my_value|time }}. {% localize off %}" + "Unlocalized: {{ my_value|time }}{% endlocalize %}." + ) + context = Context({"my_value": datetime.time(1, 2, 3)}) + expected_result = "Localized: 01:02. Unlocalized: 01h 02m." + for case in (filter_template, tag_template, filter_inside_unlocalize): + with ( + self.subTest(case=str(case)), + translation.override("de", deactivate=True), + self.settings(TIME_FORMAT="H\\h i\\m"), + ): + self.assertEqual(case.render(context), expected_result) def test_localized_off_numbers(self): """A string representation is returned for unlocalized numbers."""