mirror of https://github.com/django/django.git
Fixed #22120 -- Documented persistent activation of languages and cleaned up language session key use
This commit is contained in:
parent
09b725f51b
commit
8cd32f0965
|
@ -5,6 +5,7 @@ from django.apps import apps as django_apps
|
|||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
|
||||
from django.utils.module_loading import import_string
|
||||
from django.utils.translation import LANGUAGE_SESSION_KEY
|
||||
from django.middleware.csrf import rotate_token
|
||||
|
||||
from .signals import user_logged_in, user_logged_out, user_login_failed
|
||||
|
@ -108,12 +109,12 @@ def logout(request):
|
|||
|
||||
# remember language choice saved to session
|
||||
# for backwards compatibility django_language is also checked (remove in 1.8)
|
||||
language = request.session.get('_language', request.session.get('django_language'))
|
||||
language = request.session.get(LANGUAGE_SESSION_KEY, request.session.get('django_language'))
|
||||
|
||||
request.session.flush()
|
||||
|
||||
if language is not None:
|
||||
request.session['_language'] = language
|
||||
request.session[LANGUAGE_SESSION_KEY] = language
|
||||
|
||||
if hasattr(request, 'user'):
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
|
|
|
@ -14,6 +14,7 @@ from django.http import QueryDict, HttpRequest
|
|||
from django.utils.encoding import force_text
|
||||
from django.utils.http import urlquote
|
||||
from django.utils.six.moves.urllib.parse import urlparse, ParseResult
|
||||
from django.utils.translation import LANGUAGE_SESSION_KEY
|
||||
from django.utils._os import upath
|
||||
from django.test import TestCase, override_settings
|
||||
from django.test.utils import patch_logger
|
||||
|
@ -718,12 +719,12 @@ class LogoutTest(AuthViewsTestCase):
|
|||
# Create a new session with language
|
||||
engine = import_module(settings.SESSION_ENGINE)
|
||||
session = engine.SessionStore()
|
||||
session['_language'] = 'pl'
|
||||
session[LANGUAGE_SESSION_KEY] = 'pl'
|
||||
session.save()
|
||||
self.client.cookies[settings.SESSION_COOKIE_NAME] = session.session_key
|
||||
|
||||
self.client.get('/logout/')
|
||||
self.assertEqual(self.client.session['_language'], 'pl')
|
||||
self.assertEqual(self.client.session[LANGUAGE_SESSION_KEY], 'pl')
|
||||
|
||||
|
||||
@skipIfCustomUser
|
||||
|
|
|
@ -21,6 +21,7 @@ __all__ = [
|
|||
'npgettext', 'npgettext_lazy',
|
||||
]
|
||||
|
||||
LANGUAGE_SESSION_KEY = '_language'
|
||||
|
||||
class TranslatorCommentWarning(SyntaxWarning):
|
||||
pass
|
||||
|
|
|
@ -18,7 +18,7 @@ from django.utils._os import upath
|
|||
from django.utils.safestring import mark_safe, SafeData
|
||||
from django.utils import six, lru_cache
|
||||
from django.utils.six import StringIO
|
||||
from django.utils.translation import TranslatorCommentWarning, trim_whitespace
|
||||
from django.utils.translation import TranslatorCommentWarning, trim_whitespace, LANGUAGE_SESSION_KEY
|
||||
|
||||
|
||||
# Translations are cached in a dictionary for every language+app tuple.
|
||||
|
@ -478,7 +478,7 @@ def get_language_from_request(request, check_path=False):
|
|||
|
||||
if hasattr(request, 'session'):
|
||||
# for backwards compatibility django_language is also checked (remove in 1.8)
|
||||
lang_code = request.session.get('_language', request.session.get('django_language'))
|
||||
lang_code = request.session.get(LANGUAGE_SESSION_KEY, request.session.get('django_language'))
|
||||
if lang_code in supported and lang_code is not None and check_for_language(lang_code):
|
||||
return lang_code
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ from django import http
|
|||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
from django.template import Context, Template
|
||||
from django.utils.translation import check_for_language, to_locale, get_language
|
||||
from django.utils.translation import check_for_language, to_locale, get_language, LANGUAGE_SESSION_KEY
|
||||
from django.utils.encoding import smart_text
|
||||
from django.utils.formats import get_format_modules, get_format
|
||||
from django.utils._os import upath
|
||||
|
@ -36,7 +36,7 @@ def set_language(request):
|
|||
lang_code = request.POST.get('language', None)
|
||||
if lang_code and check_for_language(lang_code):
|
||||
if hasattr(request, 'session'):
|
||||
request.session['_language'] = lang_code
|
||||
request.session[LANGUAGE_SESSION_KEY] = lang_code
|
||||
else:
|
||||
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, lang_code)
|
||||
return response
|
||||
|
|
|
@ -937,6 +937,10 @@ For a complete discussion on the usage of the following see the
|
|||
so by translating the Django translation tags into standard gettext function
|
||||
invocations.
|
||||
|
||||
.. data:: LANGUAGE_SESSION_KEY
|
||||
|
||||
Session key under which the active language for the current session is stored.
|
||||
|
||||
.. _time-zone-selection-functions:
|
||||
|
||||
``django.utils.timezone``
|
||||
|
|
|
@ -537,11 +537,13 @@ Internationalization
|
|||
attribute allows you to customize the redirects issued by the middleware.
|
||||
|
||||
* The :class:`~django.middleware.locale.LocaleMiddleware` now stores the user's
|
||||
selected language with the session key ``_language``. Previously it was
|
||||
stored with the key ``django_language``, but keys reserved for Django should
|
||||
start with an underscore. For backwards compatibility ``django_language`` is
|
||||
still read from in 1.7. Sessions will be migrated to the new ``_language``
|
||||
key as they are written.
|
||||
selected language with the session key ``_language``. This should only be
|
||||
accessed using the :data:`~django.utils.translation.LANGUAGE_SESSION_KEY`
|
||||
constant. Previously it was stored with the key ``django_language`` and the
|
||||
``LANGUAGE_SESSION_KEY`` constant did not exist, but keys reserved for Django
|
||||
should start with an underscore. For backwards compatibility ``django_language``
|
||||
is still read from in 1.7. Sessions will be migrated to the new key
|
||||
as they are written.
|
||||
|
||||
* The :ttag:`blocktrans` now supports a ``trimmed`` option. This
|
||||
option will remove newline characters from the beginning and the end of the
|
||||
|
|
|
@ -1510,6 +1510,38 @@ Here's example HTML template code:
|
|||
In this example, Django looks up the URL of the page to which the user will be
|
||||
redirected in the ``redirect_to`` context variable.
|
||||
|
||||
Explicitly setting the active language
|
||||
--------------------------------------
|
||||
|
||||
.. highlightlang:: python
|
||||
|
||||
You may want to set the active language for the current session explicitly. Perhaps
|
||||
a user's language preference is retrieved from another system, for example.
|
||||
You've already been introduced to :func:`django.utils.translation.activate()`. That
|
||||
applies to the current thread only. To persist the language for the entire
|
||||
session, also modify :data:`~django.utils.translation.LANGUAGE_SESSION_KEY`
|
||||
in the session::
|
||||
|
||||
from django.utils import translation
|
||||
user_language = 'fr'
|
||||
translation.activate(user_language)
|
||||
request.session[translation.LANGUAGE_SESSION_KEY] = user_language
|
||||
|
||||
You would typically want to use both: :func:`django.utils.translation.activate()`
|
||||
will change the language for this thread, and modifying the session makes this
|
||||
preference persist in future requests.
|
||||
|
||||
If you are not using sessions, the language will persist in a cookie, whose name
|
||||
is configured in :setting:`LANGUAGE_COOKIE_NAME`. For example::
|
||||
|
||||
from django.utils import translation
|
||||
from django import http
|
||||
from django.conf import settings
|
||||
user_language = 'fr'
|
||||
translation.activate(user_language)
|
||||
response = http.HttpResponse(...)
|
||||
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, user_language)
|
||||
|
||||
Using translations outside views and templates
|
||||
----------------------------------------------
|
||||
|
||||
|
@ -1621,13 +1653,13 @@ following this algorithm:
|
|||
root URLconf. See :ref:`url-internationalization` for more information
|
||||
about the language prefix and how to internationalize URL patterns.
|
||||
|
||||
* Failing that, it looks for a ``_language`` key in the current user's session.
|
||||
* Failing that, it looks for the :data:`~django.utils.translation.LANGUAGE_SESSION_KEY`
|
||||
key in the current user's session.
|
||||
|
||||
.. versionchanged:: 1.7
|
||||
|
||||
In previous versions, the key was named ``django_language`` but it was
|
||||
renamed to start with an underscore to denote a Django reserved session
|
||||
key.
|
||||
In previous versions, the key was named ``django_language``, and the
|
||||
``LANGUAGE_SESSION_KEY`` constant did not exist.
|
||||
|
||||
* Failing that, it looks for a cookie.
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ from django.utils.translation import (activate, deactivate,
|
|||
pgettext,
|
||||
npgettext, npgettext_lazy,
|
||||
check_for_language,
|
||||
string_concat)
|
||||
string_concat, LANGUAGE_SESSION_KEY)
|
||||
|
||||
from .forms import I18nForm, SelectDateForm, SelectDateWidget, CompanyForm
|
||||
from .models import Company, TestModel
|
||||
|
@ -1267,7 +1267,7 @@ class LocaleMiddlewareTests(TestCase):
|
|||
session on every request."""
|
||||
# Regression test for #21473
|
||||
self.client.get('/fr/simple/')
|
||||
self.assertNotIn('_language', self.client.session)
|
||||
self.assertNotIn(LANGUAGE_SESSION_KEY, self.client.session)
|
||||
|
||||
|
||||
@override_settings(
|
||||
|
|
|
@ -11,7 +11,7 @@ from django.test import (
|
|||
LiveServerTestCase, TestCase, modify_settings, override_settings)
|
||||
from django.utils import six
|
||||
from django.utils._os import upath
|
||||
from django.utils.translation import override
|
||||
from django.utils.translation import override, LANGUAGE_SESSION_KEY
|
||||
|
||||
try:
|
||||
from selenium.webdriver.firefox import webdriver as firefox
|
||||
|
@ -35,7 +35,7 @@ class I18NTests(TestCase):
|
|||
post_data = dict(language=lang_code, next='/')
|
||||
response = self.client.post('/i18n/setlang/', data=post_data)
|
||||
self.assertRedirects(response, 'http://testserver/')
|
||||
self.assertEqual(self.client.session['_language'], lang_code)
|
||||
self.assertEqual(self.client.session[LANGUAGE_SESSION_KEY], lang_code)
|
||||
|
||||
def test_setlang_unsafe_next(self):
|
||||
"""
|
||||
|
@ -46,7 +46,7 @@ class I18NTests(TestCase):
|
|||
post_data = dict(language=lang_code, next='//unsafe/redirection/')
|
||||
response = self.client.post('/i18n/setlang/', data=post_data)
|
||||
self.assertEqual(response.url, 'http://testserver/')
|
||||
self.assertEqual(self.client.session['_language'], lang_code)
|
||||
self.assertEqual(self.client.session[LANGUAGE_SESSION_KEY], lang_code)
|
||||
|
||||
def test_setlang_reversal(self):
|
||||
self.assertEqual(reverse('set_language'), '/i18n/setlang/')
|
||||
|
|
Loading…
Reference in New Issue