django3: Save language preference in a cookie rather than the session.

Support for saving it in the session is dropped in django3, the cookie
is the mechanism that needs to be used. The relevant i18n code doesn't
have access to the response objects and thus needs to delegate setting
the cookie to LocaleMiddleware.

Fixes the LocaleMiddleware point of #16030.
This commit is contained in:
Mateusz Mandera 2020-12-23 13:45:24 +01:00 committed by Tim Abbott
parent 04b6108e71
commit f76202dd59
3 changed files with 14 additions and 6 deletions

View File

@ -100,11 +100,9 @@ There are a few ways to see your translations in the Zulip UI:
prioritization (mostly copied from the Django docs):
1. It looks for the language code as a URL prefix (e.g. `/de/login/`).
2. It looks for the `LANGUAGE_SESSION_KEY` key in the current user's
session (the Zulip language UI option ends up setting this).
3. It looks for the cookie named 'django_language'. You can set a
1. It looks for the cookie named 'django_language'. You can set a
different name through the `LANGUAGE_COOKIE_NAME` setting.
4. It looks for the `Accept-Language` HTTP header in the HTTP request
1. It looks for the `Accept-Language` HTTP header in the HTTP request
(this is how browsers tell Zulip about the OS/browser language).
* Using an HTTP client library like `requests`, `cURL` or `urllib`,

View File

@ -95,8 +95,10 @@ def get_and_set_request_language(
request_language = user_configured_language
translation.activate(request_language)
# We also save the language to the user's session, so that
# We also want to save the language to the user's cookies, so that
# something reasonable will happen in logged-in portico pages.
request.session[translation.LANGUAGE_SESSION_KEY] = translation.get_language()
# We accomplish that by setting a flag on the request which signals
# to LocaleMiddleware to set the cookie on the response.
request._set_language = translation.get_language()
return request_language

View File

@ -371,6 +371,7 @@ def csrf_failure(request: HttpRequest, reason: str="") -> HttpResponse:
class LocaleMiddleware(DjangoLocaleMiddleware):
def process_response(self, request: HttpRequest, response: HttpResponse) -> HttpResponse:
# This is the same as the default LocaleMiddleware, minus the
# logic that redirects 404's that lack a prefixed language in
# the path into having a language. See
@ -382,6 +383,13 @@ class LocaleMiddleware(DjangoLocaleMiddleware):
if not (i18n_patterns_used and language_from_path):
patch_vary_headers(response, ('Accept-Language',))
response.setdefault('Content-Language', language)
# An additional responsibility of our override of this middleware is to save the user's language
# preference in a cookie. That determination is made by code handling the request
# and saved in the _set_language flag so that it can be used here.
if hasattr(request, '_set_language'):
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, request._set_language)
return response
class RateLimitMiddleware(MiddlewareMixin):