Fixed #22120 -- Documented persistent activation of languages and cleaned up language session key use

This commit is contained in:
Erik Romijn 2014-02-22 14:27:57 +01:00 committed by Baptiste Mispelon
parent 09b725f51b
commit 8cd32f0965
10 changed files with 63 additions and 22 deletions

View File

@ -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

View File

@ -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

View File

@ -21,6 +21,7 @@ __all__ = [
'npgettext', 'npgettext_lazy',
]
LANGUAGE_SESSION_KEY = '_language'
class TranslatorCommentWarning(SyntaxWarning):
pass

View File

@ -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

View File

@ -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

View File

@ -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``

View File

@ -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

View File

@ -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.

View File

@ -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(

View File

@ -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/')