Skip to content

Commit f200d83

Browse files
committed
[4.2.x] Fixed #34515 -- Made LocaleMiddleware prefer language from paths when i18n patterns are used.
Regression in 94e7f47. This reverts commit 94e7f47 (refs #34069) and partly reverts commit 3b47283. Thanks Anthony Baillard for the report. Co-Authored-By: Sarah Boyce <[email protected]> Backport of 0e444e8 from main
1 parent 4f343a1 commit f200d83

File tree

10 files changed

+53
-48
lines changed

10 files changed

+53
-48
lines changed

django/middleware/locale.py

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,37 +16,28 @@ class LocaleMiddleware(MiddlewareMixin):
1616

1717
response_redirect_class = HttpResponseRedirect
1818

19-
def get_fallback_language(self, request):
20-
"""
21-
Return the fallback language for the current request based on the
22-
settings. If LANGUAGE_CODE is a variant not included in the supported
23-
languages, get_fallback_language() will try to fallback to a supported
24-
generic variant.
25-
26-
Can be overridden to have a fallback language depending on the request,
27-
e.g. based on top level domain.
28-
"""
29-
try:
30-
return translation.get_supported_language_variant(settings.LANGUAGE_CODE)
31-
except LookupError:
32-
return settings.LANGUAGE_CODE
33-
3419
def process_request(self, request):
3520
urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF)
36-
i18n_patterns_used, _ = is_language_prefix_patterns_used(urlconf)
21+
(
22+
i18n_patterns_used,
23+
prefixed_default_language,
24+
) = is_language_prefix_patterns_used(urlconf)
3725
language = translation.get_language_from_request(
3826
request, check_path=i18n_patterns_used
3927
)
40-
if not language:
41-
language = self.get_fallback_language(request)
42-
28+
language_from_path = translation.get_language_from_path(request.path_info)
29+
if (
30+
not language_from_path
31+
and i18n_patterns_used
32+
and not prefixed_default_language
33+
):
34+
language = settings.LANGUAGE_CODE
4335
translation.activate(language)
4436
request.LANGUAGE_CODE = translation.get_language()
4537

4638
def process_response(self, request, response):
4739
language = translation.get_language()
4840
language_from_path = translation.get_language_from_path(request.path_info)
49-
language_from_request = translation.get_language_from_request(request)
5041
urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF)
5142
(
5243
i18n_patterns_used,
@@ -57,7 +48,7 @@ def process_response(self, request, response):
5748
response.status_code == 404
5849
and not language_from_path
5950
and i18n_patterns_used
60-
and (prefixed_default_language or language_from_request)
51+
and prefixed_default_language
6152
):
6253
# Maybe the language code is missing in the URL? Try adding the
6354
# language prefix and redirecting to that URL.

django/urls/resolvers.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from django.utils.functional import cached_property
2424
from django.utils.http import RFC3986_SUBDELIMS, escape_leading_slashes
2525
from django.utils.regex_helper import _lazy_re_compile, normalize
26-
from django.utils.translation import get_language, get_supported_language_variant
26+
from django.utils.translation import get_language
2727

2828
from .converters import get_converter
2929
from .exceptions import NoReverseMatch, Resolver404
@@ -351,8 +351,7 @@ def regex(self):
351351
@property
352352
def language_prefix(self):
353353
language_code = get_language() or settings.LANGUAGE_CODE
354-
default_language = get_supported_language_variant(settings.LANGUAGE_CODE)
355-
if language_code == default_language and not self.prefix_default_language:
354+
if language_code == settings.LANGUAGE_CODE and not self.prefix_default_language:
356355
return ""
357356
else:
358357
return "%s/" % language_code

django/utils/translation/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
"get_language_from_request",
1818
"get_language_info",
1919
"get_language_bidi",
20-
"get_supported_language_variant",
2120
"check_for_language",
2221
"to_language",
2322
"to_locale",

django/utils/translation/trans_null.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def check_for_language(x):
5353

5454

5555
def get_language_from_request(request, check_path=False):
56-
return None
56+
return settings.LANGUAGE_CODE
5757

5858

5959
def get_language_from_path(request):

django/utils/translation/trans_real.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,11 @@ def get_language_from_request(request, check_path=False):
583583
return get_supported_language_variant(accept_lang)
584584
except LookupError:
585585
continue
586-
return None
586+
587+
try:
588+
return get_supported_language_variant(settings.LANGUAGE_CODE)
589+
except LookupError:
590+
return settings.LANGUAGE_CODE
587591

588592

589593
@functools.lru_cache(maxsize=1000)

docs/releases/4.2.1.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ Bugfixes
3131
``prefix_default_language`` argument when a fallback language of the default
3232
language was used (:ticket:`34455`).
3333

34+
* Fixed a regression in Django 4.2 where translated URLs of the default
35+
language from ``i18n_patterns()`` with ``prefix_default_language`` set to
36+
``False`` raised 404 errors for a request with a different language
37+
(:ticket:`34515`).
38+
3439
* Fixed a regression in Django 4.2 where creating copies and deep copies of
3540
``HttpRequest``, ``HttpResponse``, and their subclasses didn't always work
3641
correctly (:ticket:`34482`, :ticket:`34484`).

docs/releases/4.2.txt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -248,10 +248,6 @@ Internationalization
248248

249249
* Added support and translations for the Central Kurdish (Sorani) language.
250250

251-
* The :class:`~django.middleware.locale.LocaleMiddleware` now respects a
252-
language from the request when :func:`~django.conf.urls.i18n.i18n_patterns`
253-
is used with the ``prefix_default_language`` argument set to ``False``.
254-
255251
Logging
256252
~~~~~~~
257253

tests/i18n/patterns/tests.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,27 @@ def test_nl_path(self):
431431
self.assertEqual(response.context["LANGUAGE_CODE"], "nl")
432432

433433

434+
@override_settings(ROOT_URLCONF="i18n.urls_default_unprefixed", LANGUAGE_CODE="nl")
435+
class URLPrefixedFalseTranslatedTests(URLTestCaseBase):
436+
def test_translated_path_unprefixed_language_other_than_accepted_header(self):
437+
response = self.client.get("/gebruikers/", headers={"accept-language": "en"})
438+
self.assertEqual(response.status_code, 200)
439+
440+
def test_translated_path_unprefixed_language_other_than_cookie_language(self):
441+
self.client.cookies.load({settings.LANGUAGE_COOKIE_NAME: "en"})
442+
response = self.client.get("/gebruikers/")
443+
self.assertEqual(response.status_code, 200)
444+
445+
def test_translated_path_prefixed_language_other_than_accepted_header(self):
446+
response = self.client.get("/en/users/", headers={"accept-language": "nl"})
447+
self.assertEqual(response.status_code, 200)
448+
449+
def test_translated_path_prefixed_language_other_than_cookie_language(self):
450+
self.client.cookies.load({settings.LANGUAGE_COOKIE_NAME: "nl"})
451+
response = self.client.get("/en/users/")
452+
self.assertEqual(response.status_code, 200)
453+
454+
434455
class URLRedirectWithScriptAliasTests(URLTestCaseBase):
435456
"""
436457
#21579 - LocaleMiddleware should respect the script prefix.

tests/i18n/tests.py

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2142,22 +2142,8 @@ def test_other_lang_with_prefix(self):
21422142
response = self.client.get("/fr/simple/")
21432143
self.assertEqual(response.content, b"Oui")
21442144

2145-
def test_unprefixed_language_with_accept_language(self):
2146-
"""'Accept-Language' is respected."""
2147-
response = self.client.get("/simple/", headers={"accept-language": "fr"})
2148-
self.assertRedirects(response, "/fr/simple/")
2149-
2150-
def test_unprefixed_language_with_cookie_language(self):
2151-
"""A language set in the cookies is respected."""
2152-
self.client.cookies.load({settings.LANGUAGE_COOKIE_NAME: "fr"})
2153-
response = self.client.get("/simple/")
2154-
self.assertRedirects(response, "/fr/simple/")
2155-
2156-
def test_unprefixed_language_with_non_valid_language(self):
2157-
response = self.client.get("/simple/", headers={"accept-language": "fi"})
2158-
self.assertEqual(response.content, b"Yes")
2159-
self.client.cookies.load({settings.LANGUAGE_COOKIE_NAME: "fi"})
2160-
response = self.client.get("/simple/")
2145+
def test_unprefixed_language_other_than_accept_language(self):
2146+
response = self.client.get("/simple/", HTTP_ACCEPT_LANGUAGE="fr")
21612147
self.assertEqual(response.content, b"Yes")
21622148

21632149
def test_page_with_dash(self):
@@ -2233,7 +2219,10 @@ def test_get_language_from_request(self):
22332219

22342220
def test_get_language_from_request_null(self):
22352221
lang = trans_null.get_language_from_request(None)
2236-
self.assertEqual(lang, None)
2222+
self.assertEqual(lang, "en")
2223+
with override_settings(LANGUAGE_CODE="de"):
2224+
lang = trans_null.get_language_from_request(None)
2225+
self.assertEqual(lang, "de")
22372226

22382227
def test_specific_language_codes(self):
22392228
# issue 11915

tests/i18n/urls_default_unprefixed.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@
77
re_path(r"^(?P<arg>[\w-]+)-page", lambda request, **arg: HttpResponse(_("Yes"))),
88
path("simple/", lambda r: HttpResponse(_("Yes"))),
99
re_path(r"^(.+)/(.+)/$", lambda *args: HttpResponse()),
10+
re_path(_(r"^users/$"), lambda *args: HttpResponse(), name="users"),
1011
prefix_default_language=False,
1112
)

0 commit comments

Comments
 (0)