mirror of https://github.com/django/django.git
[4.2.x] Fixed #34515 -- Made LocaleMiddleware prefer language from paths when i18n patterns are used.
Regression in94e7f471c4
. This reverts commit94e7f471c4
(refs #34069) and partly reverts commit3b4728310a
. Thanks Anthony Baillard for the report. Co-Authored-By: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com> Backport of0e444e84f8
from main
This commit is contained in:
parent
4f343a10f7
commit
f200d83698
|
@ -16,37 +16,28 @@ class LocaleMiddleware(MiddlewareMixin):
|
||||||
|
|
||||||
response_redirect_class = HttpResponseRedirect
|
response_redirect_class = HttpResponseRedirect
|
||||||
|
|
||||||
def get_fallback_language(self, request):
|
|
||||||
"""
|
|
||||||
Return the fallback language for the current request based on the
|
|
||||||
settings. If LANGUAGE_CODE is a variant not included in the supported
|
|
||||||
languages, get_fallback_language() will try to fallback to a supported
|
|
||||||
generic variant.
|
|
||||||
|
|
||||||
Can be overridden to have a fallback language depending on the request,
|
|
||||||
e.g. based on top level domain.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
return translation.get_supported_language_variant(settings.LANGUAGE_CODE)
|
|
||||||
except LookupError:
|
|
||||||
return settings.LANGUAGE_CODE
|
|
||||||
|
|
||||||
def process_request(self, request):
|
def process_request(self, request):
|
||||||
urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF)
|
urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF)
|
||||||
i18n_patterns_used, _ = is_language_prefix_patterns_used(urlconf)
|
(
|
||||||
|
i18n_patterns_used,
|
||||||
|
prefixed_default_language,
|
||||||
|
) = is_language_prefix_patterns_used(urlconf)
|
||||||
language = translation.get_language_from_request(
|
language = translation.get_language_from_request(
|
||||||
request, check_path=i18n_patterns_used
|
request, check_path=i18n_patterns_used
|
||||||
)
|
)
|
||||||
if not language:
|
language_from_path = translation.get_language_from_path(request.path_info)
|
||||||
language = self.get_fallback_language(request)
|
if (
|
||||||
|
not language_from_path
|
||||||
|
and i18n_patterns_used
|
||||||
|
and not prefixed_default_language
|
||||||
|
):
|
||||||
|
language = settings.LANGUAGE_CODE
|
||||||
translation.activate(language)
|
translation.activate(language)
|
||||||
request.LANGUAGE_CODE = translation.get_language()
|
request.LANGUAGE_CODE = translation.get_language()
|
||||||
|
|
||||||
def process_response(self, request, response):
|
def process_response(self, request, response):
|
||||||
language = translation.get_language()
|
language = translation.get_language()
|
||||||
language_from_path = translation.get_language_from_path(request.path_info)
|
language_from_path = translation.get_language_from_path(request.path_info)
|
||||||
language_from_request = translation.get_language_from_request(request)
|
|
||||||
urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF)
|
urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF)
|
||||||
(
|
(
|
||||||
i18n_patterns_used,
|
i18n_patterns_used,
|
||||||
|
@ -57,7 +48,7 @@ class LocaleMiddleware(MiddlewareMixin):
|
||||||
response.status_code == 404
|
response.status_code == 404
|
||||||
and not language_from_path
|
and not language_from_path
|
||||||
and i18n_patterns_used
|
and i18n_patterns_used
|
||||||
and (prefixed_default_language or language_from_request)
|
and prefixed_default_language
|
||||||
):
|
):
|
||||||
# Maybe the language code is missing in the URL? Try adding the
|
# Maybe the language code is missing in the URL? Try adding the
|
||||||
# language prefix and redirecting to that URL.
|
# language prefix and redirecting to that URL.
|
||||||
|
|
|
@ -23,7 +23,7 @@ from django.utils.datastructures import MultiValueDict
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils.http import RFC3986_SUBDELIMS, escape_leading_slashes
|
from django.utils.http import RFC3986_SUBDELIMS, escape_leading_slashes
|
||||||
from django.utils.regex_helper import _lazy_re_compile, normalize
|
from django.utils.regex_helper import _lazy_re_compile, normalize
|
||||||
from django.utils.translation import get_language, get_supported_language_variant
|
from django.utils.translation import get_language
|
||||||
|
|
||||||
from .converters import get_converter
|
from .converters import get_converter
|
||||||
from .exceptions import NoReverseMatch, Resolver404
|
from .exceptions import NoReverseMatch, Resolver404
|
||||||
|
@ -351,8 +351,7 @@ class LocalePrefixPattern:
|
||||||
@property
|
@property
|
||||||
def language_prefix(self):
|
def language_prefix(self):
|
||||||
language_code = get_language() or settings.LANGUAGE_CODE
|
language_code = get_language() or settings.LANGUAGE_CODE
|
||||||
default_language = get_supported_language_variant(settings.LANGUAGE_CODE)
|
if language_code == settings.LANGUAGE_CODE and not self.prefix_default_language:
|
||||||
if language_code == default_language and not self.prefix_default_language:
|
|
||||||
return ""
|
return ""
|
||||||
else:
|
else:
|
||||||
return "%s/" % language_code
|
return "%s/" % language_code
|
||||||
|
|
|
@ -17,7 +17,6 @@ __all__ = [
|
||||||
"get_language_from_request",
|
"get_language_from_request",
|
||||||
"get_language_info",
|
"get_language_info",
|
||||||
"get_language_bidi",
|
"get_language_bidi",
|
||||||
"get_supported_language_variant",
|
|
||||||
"check_for_language",
|
"check_for_language",
|
||||||
"to_language",
|
"to_language",
|
||||||
"to_locale",
|
"to_locale",
|
||||||
|
|
|
@ -53,7 +53,7 @@ def check_for_language(x):
|
||||||
|
|
||||||
|
|
||||||
def get_language_from_request(request, check_path=False):
|
def get_language_from_request(request, check_path=False):
|
||||||
return None
|
return settings.LANGUAGE_CODE
|
||||||
|
|
||||||
|
|
||||||
def get_language_from_path(request):
|
def get_language_from_path(request):
|
||||||
|
|
|
@ -583,7 +583,11 @@ def get_language_from_request(request, check_path=False):
|
||||||
return get_supported_language_variant(accept_lang)
|
return get_supported_language_variant(accept_lang)
|
||||||
except LookupError:
|
except LookupError:
|
||||||
continue
|
continue
|
||||||
return None
|
|
||||||
|
try:
|
||||||
|
return get_supported_language_variant(settings.LANGUAGE_CODE)
|
||||||
|
except LookupError:
|
||||||
|
return settings.LANGUAGE_CODE
|
||||||
|
|
||||||
|
|
||||||
@functools.lru_cache(maxsize=1000)
|
@functools.lru_cache(maxsize=1000)
|
||||||
|
|
|
@ -31,6 +31,11 @@ Bugfixes
|
||||||
``prefix_default_language`` argument when a fallback language of the default
|
``prefix_default_language`` argument when a fallback language of the default
|
||||||
language was used (:ticket:`34455`).
|
language was used (:ticket:`34455`).
|
||||||
|
|
||||||
|
* Fixed a regression in Django 4.2 where translated URLs of the default
|
||||||
|
language from ``i18n_patterns()`` with ``prefix_default_language`` set to
|
||||||
|
``False`` raised 404 errors for a request with a different language
|
||||||
|
(:ticket:`34515`).
|
||||||
|
|
||||||
* Fixed a regression in Django 4.2 where creating copies and deep copies of
|
* Fixed a regression in Django 4.2 where creating copies and deep copies of
|
||||||
``HttpRequest``, ``HttpResponse``, and their subclasses didn't always work
|
``HttpRequest``, ``HttpResponse``, and their subclasses didn't always work
|
||||||
correctly (:ticket:`34482`, :ticket:`34484`).
|
correctly (:ticket:`34482`, :ticket:`34484`).
|
||||||
|
|
|
@ -248,10 +248,6 @@ Internationalization
|
||||||
|
|
||||||
* Added support and translations for the Central Kurdish (Sorani) language.
|
* Added support and translations for the Central Kurdish (Sorani) language.
|
||||||
|
|
||||||
* The :class:`~django.middleware.locale.LocaleMiddleware` now respects a
|
|
||||||
language from the request when :func:`~django.conf.urls.i18n.i18n_patterns`
|
|
||||||
is used with the ``prefix_default_language`` argument set to ``False``.
|
|
||||||
|
|
||||||
Logging
|
Logging
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -431,6 +431,27 @@ class URLResponseTests(URLTestCaseBase):
|
||||||
self.assertEqual(response.context["LANGUAGE_CODE"], "nl")
|
self.assertEqual(response.context["LANGUAGE_CODE"], "nl")
|
||||||
|
|
||||||
|
|
||||||
|
@override_settings(ROOT_URLCONF="i18n.urls_default_unprefixed", LANGUAGE_CODE="nl")
|
||||||
|
class URLPrefixedFalseTranslatedTests(URLTestCaseBase):
|
||||||
|
def test_translated_path_unprefixed_language_other_than_accepted_header(self):
|
||||||
|
response = self.client.get("/gebruikers/", headers={"accept-language": "en"})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_translated_path_unprefixed_language_other_than_cookie_language(self):
|
||||||
|
self.client.cookies.load({settings.LANGUAGE_COOKIE_NAME: "en"})
|
||||||
|
response = self.client.get("/gebruikers/")
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_translated_path_prefixed_language_other_than_accepted_header(self):
|
||||||
|
response = self.client.get("/en/users/", headers={"accept-language": "nl"})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_translated_path_prefixed_language_other_than_cookie_language(self):
|
||||||
|
self.client.cookies.load({settings.LANGUAGE_COOKIE_NAME: "nl"})
|
||||||
|
response = self.client.get("/en/users/")
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
class URLRedirectWithScriptAliasTests(URLTestCaseBase):
|
class URLRedirectWithScriptAliasTests(URLTestCaseBase):
|
||||||
"""
|
"""
|
||||||
#21579 - LocaleMiddleware should respect the script prefix.
|
#21579 - LocaleMiddleware should respect the script prefix.
|
||||||
|
|
|
@ -2142,22 +2142,8 @@ class UnprefixedDefaultLanguageTests(SimpleTestCase):
|
||||||
response = self.client.get("/fr/simple/")
|
response = self.client.get("/fr/simple/")
|
||||||
self.assertEqual(response.content, b"Oui")
|
self.assertEqual(response.content, b"Oui")
|
||||||
|
|
||||||
def test_unprefixed_language_with_accept_language(self):
|
def test_unprefixed_language_other_than_accept_language(self):
|
||||||
"""'Accept-Language' is respected."""
|
response = self.client.get("/simple/", HTTP_ACCEPT_LANGUAGE="fr")
|
||||||
response = self.client.get("/simple/", headers={"accept-language": "fr"})
|
|
||||||
self.assertRedirects(response, "/fr/simple/")
|
|
||||||
|
|
||||||
def test_unprefixed_language_with_cookie_language(self):
|
|
||||||
"""A language set in the cookies is respected."""
|
|
||||||
self.client.cookies.load({settings.LANGUAGE_COOKIE_NAME: "fr"})
|
|
||||||
response = self.client.get("/simple/")
|
|
||||||
self.assertRedirects(response, "/fr/simple/")
|
|
||||||
|
|
||||||
def test_unprefixed_language_with_non_valid_language(self):
|
|
||||||
response = self.client.get("/simple/", headers={"accept-language": "fi"})
|
|
||||||
self.assertEqual(response.content, b"Yes")
|
|
||||||
self.client.cookies.load({settings.LANGUAGE_COOKIE_NAME: "fi"})
|
|
||||||
response = self.client.get("/simple/")
|
|
||||||
self.assertEqual(response.content, b"Yes")
|
self.assertEqual(response.content, b"Yes")
|
||||||
|
|
||||||
def test_page_with_dash(self):
|
def test_page_with_dash(self):
|
||||||
|
@ -2233,7 +2219,10 @@ class CountrySpecificLanguageTests(SimpleTestCase):
|
||||||
|
|
||||||
def test_get_language_from_request_null(self):
|
def test_get_language_from_request_null(self):
|
||||||
lang = trans_null.get_language_from_request(None)
|
lang = trans_null.get_language_from_request(None)
|
||||||
self.assertEqual(lang, None)
|
self.assertEqual(lang, "en")
|
||||||
|
with override_settings(LANGUAGE_CODE="de"):
|
||||||
|
lang = trans_null.get_language_from_request(None)
|
||||||
|
self.assertEqual(lang, "de")
|
||||||
|
|
||||||
def test_specific_language_codes(self):
|
def test_specific_language_codes(self):
|
||||||
# issue 11915
|
# issue 11915
|
||||||
|
|
|
@ -7,5 +7,6 @@ urlpatterns = i18n_patterns(
|
||||||
re_path(r"^(?P<arg>[\w-]+)-page", lambda request, **arg: HttpResponse(_("Yes"))),
|
re_path(r"^(?P<arg>[\w-]+)-page", lambda request, **arg: HttpResponse(_("Yes"))),
|
||||||
path("simple/", lambda r: HttpResponse(_("Yes"))),
|
path("simple/", lambda r: HttpResponse(_("Yes"))),
|
||||||
re_path(r"^(.+)/(.+)/$", lambda *args: HttpResponse()),
|
re_path(r"^(.+)/(.+)/$", lambda *args: HttpResponse()),
|
||||||
|
re_path(_(r"^users/$"), lambda *args: HttpResponse(), name="users"),
|
||||||
prefix_default_language=False,
|
prefix_default_language=False,
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue