Opened 4 years ago
Last modified 4 months ago
#32886 assigned Bug
Translation: clash between language cookie and i18n_patterns URLs
Reported by: | Seb G | Owned by: | Charlie Overton |
---|---|---|---|
Component: | Internationalization | Version: | 3.2 |
Severity: | Normal | Keywords: | |
Cc: | Triage Stage: | Accepted | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
Summary
When using i18n_patterns
, language cookie is disregarded, even in non-i18n_patterns
views.
Description
Given the following project urls.py
file:
from django.conf.urls.i18n import i18n_patterns from django.contrib import admin from django.urls import path, include urlpatterns = [ path("admin/", admin.site.urls), ] urlpatterns += i18n_patterns( path("app/", include("app.urls", namespace="app")), prefix_default_language=False, )
Browsing to /admin/
with a valid django_language
cookie whose value is fr
still returns an english-translated page.
This behavior seems to emerge from the LocaleMiddleware.process_request
method:
def process_request(self, request): urlconf = getattr(request, 'urlconf', settings.ROOT_URLCONF) # This returns (True, False) because i18n_patterns are indeed used (albeit not on the requested route) i18n_patterns_used, prefixed_default_language = is_language_prefix_patterns_used(urlconf) # This returns "fr" as expected from the django_language cookie language = translation.get_language_from_request(request, check_path=i18n_patterns_used) # This returns None since, indeed, this route does not use i18n_patterns language_from_path = translation.get_language_from_path(request.path_info) # This test passes (somewhat unfortunately) if not language_from_path and i18n_patterns_used and not prefixed_default_language: # This gets executed and reverts language to default language (en) language = settings.LANGUAGE_CODE # "en" gets activated instead of "fr" translation.activate(language) request.LANGUAGE_CODE = translation.get_language()
Steps to reproduce
Using the provided test project
python manage.py migrate
python manage.py createsuperuser
python manage.py runserver
- Browse to
/admin/
- Login as superuser
- Edit your own user to set its language to "fr". Verify that you now have a
django_language=fr
cookie - Browse to any admin page => not translated to "fr"
Expected behavior
Cookie language should be respected on routes that do not use i18n_patterns. It is ok though that path language overrides cookie language on routes that do use i18n_patterns.
Attachments (1)
Change History (7)
by , 4 years ago
Attachment: | test_project.zip added |
---|
comment:1 by , 4 years ago
Triage Stage: | Unreviewed → Accepted |
---|
comment:2 by , 4 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
I'll start working on this issue.
comment:3 by , 3 years ago
I've drafted something that I think will solve this issue:
https://github.com/django/django/pull/16142
comment:4 by , 4 months ago
Hi,
The bug still seems present in django 5.2. The code in version 5.2 is still identical to the code posted in the issue.
Was the PR reverted?
Thanks.
comment:6 by , 4 months ago
Quite logically, that revert recreated this bug.
A summary of my understanding of the issue:
The root cause seems to be that in an app where the following routes coexist:
- type A: a language-independant route. The language cannot be inferred from calling such routes.
- type B: a language-dependant route, with a prefix from
i18n_patterns
, withprefix_default_language=False
. The language can be inferred from calling such routes, and will supersede other sources of language.
LocaleMiddleware
cannot tell those apart. With the code currently on master, LocaleMiddleware
detects that at least one URL of type B exists in the app, and will assume that all routes of type A or B are of type B. It will always choose to infer the language.
If I understand correctly, this PR simply inverted the behaviour and caused #34515. Reverting that PR recreated this one.
The actual fix would be for LocaleMiddleware
to be able to know whether the specific route is of type A or B, and based on that, choose to infer the language of the request or not.
As a workaround, we have implemented our own LocaleMiddleware
which:
- uses logic adapted from
django-extensions
'show_urls
command to list all routes in all enabled languages - groups them by name to see whether some routes have more than one distinct pattern
- this tells us whether a route is translated (of type B), or not translated (of type A)
- if type B,
LocaleMiddleware
should infer the language from the route. If type A, it shouldn't.
That solution works for us because we only have two languages. It probably doesn't scale very well to many languages.
Thanks for the well-prepared ticket.