Commits

Carl Meyer committed 13612a6 Merge

Merge support for session locale fallback. Thanks Sylvain Fourmanoit and Alex Marandon.

Comments (0)

Files changed (7)

 #. Add ``'localeurl.middleware.LocaleURLMiddleware'`` to
    ``settings.MIDDLEWARE_CLASSES``. It must come *before*
    ``'django.middleware.common.CommonMiddleware'`` or ``settings.APPEND_SLASH``
-   will not work. Make sure Django's built-in ``LocaleMiddleware`` is **not**
-   in your ``MIDDLEWARE_CLASSES`` setting; ``LocaleURLMiddleware`` replaces it
-   and the two will not work together.
+   will not work. Make sure Django's built-in ``LocaleMiddleware`` is **not** in
+   your ``MIDDLEWARE_CLASSES`` setting; ``LocaleURLMiddleware`` replaces it and
+   the two will not work together. It must also come after
+   ``django.contrib.sessions.middleware.SessionMiddleware`` if you plan using
+   the session fallback (see ``LOCALEURL_USE_SESSION`` below).
 
 #. Add ``'localeurl'`` to ``settings.INSTALLED_APPS``. Because the application
    needs to replace the standard ``urlresolvers.reverse`` function, it is
   URL. (The default behavior, preserved for backwards compatibility,
   is to fallback directly to ``settings.LANGUAGE_CODE``).
 
+``LOCALEURL_USE_SESSION`` (default: ``False``)
+
+  Whether to check for a user-selected locale in the Django session as a
+  fallback in case no locale is specified in the URL. If ``True``, the
+  ``change_locale`` view will save the chosen locale to the user's session
+  under the ``django_language`` session key. When used and available, the
+  session locale takes precedence over the ``Accept-Language`` header fallback
+  (see ``LOCALEURL_USE_ACCEPT_LANGUAGE`` above).
+
+  A localized URL still takes precedence over a locale in the user's
+  session. Following a localized link such as one generated using the
+  ``chlocale`` filter won't switch the session's ``django_language``; only the
+  ``change_locale`` view will.
+
+  To use this feature, `sessions`_ must be in use, and
+  ``django.contrib.sessions.middleware.SessionMiddleware`` must come *before*
+  ``localeurl.middleware.LocaleURLMiddleware`` in
+  ``settings.MIDDLEWARE_CLASSES``.
+
+.. _`sessions` : http://docs.djangoproject.com/en/dev/topics/http/sessions/
+.. _`middleware` : https://docs.djangoproject.com/en/dev/topics/http/middleware/
+
+
 ``LOCALE_REDIRECT_PERMANENT`` (default: ``True``)
   Whether to use a permanent redirect (301 status code) or temporary
   redirect (302) when redirecting users from the no-locale version of a URL
 as ``django.middleware.locale.LocaleMiddleware``) or from
 ``settings.LANGUAGE_CODE``. So a request for ``/about/`` would be redirected to
 ``/fr/about/`` if French is the default language. (This behavior can be changed
-using ``settings.PREFIX_DEFAULT_LOCALE``.)
+using ``settings.PREFIX_DEFAULT_LOCALE``.) Determination of the default locale
+can also take into account the ``Accept-Language`` browser header (see
+``settings.LOCALEURL_USE_ACCEPT_LANGUAGE``), or a previously user-selected
+locale (see ``settings.LOCALEURL_USE_SESSION``, and the ``change_locale`` view
+below).
 
 Templates
 =========
 
 .. _`redirect view`: http://docs.djangoproject.com/en/dev/topics/i18n/#the-set-language-redirect-view
 
+When ``settings.LOCALURL_USE_SESSION`` is set to ``True`` (default is
+``False``), It also records the user-selected locale to the current Django
+session. The last selected locale will then be used as the default locale when
+redirecting from paths missing a locale prefix.
+
 Example
 -------
 

localeurl/middleware.py

 
     def process_request(self, request):
         locale, path = utils.strip_path(request.path_info)
+        if localeurl_settings.USE_SESSION and not locale:
+            slocale = request.session.get('django_language')
+            if slocale and utils.supported_language(slocale):
+                locale = slocale
         if localeurl_settings.USE_ACCEPT_LANGUAGE and not locale:
-            accept_langs = filter(lambda x: x, [utils.supported_language(lang[0])
-                                                for lang in
-                                                parse_accept_lang_header(
-                        request.META.get('HTTP_ACCEPT_LANGUAGE', ''))])
+            accept_lang_header = request.META.get('HTTP_ACCEPT_LANGUAGE', '')
+            header_langs = parse_accept_lang_header(accept_lang_header)
+            accept_langs = filter(
+                None,
+                [utils.supported_language(lang[0]) for lang in header_langs]
+                )
             if accept_langs:
                 locale = accept_langs[0]
         locale_path = utils.locale_path(path, locale)

localeurl/settings.py

 
 USE_ACCEPT_LANGUAGE = getattr(settings, 'LOCALEURL_USE_ACCEPT_LANGUAGE', False)
 
+USE_SESSION = getattr(settings, 'LOCALEURL_USE_SESSION', False)
+
 LOCALE_REDIRECT_PERMANENT = getattr(settings, 'LOCALE_REDIRECT_PERMANENT', True)

localeurl/tests/tests.py

         settings_fixture(self.settings_manager)
         translation.activate("en")
         reload(localeurl_settings)
-        reload(urlresolvers)
 
 
     def tearDown(self):
         self.settings_manager.revert()
         reload(localeurl_settings)
-        reload(urlresolvers)
 
 
 
         self.assertEqual('/fr/test/', r2['Location'])
 
 
+    def test_check_session_enabled(self):
+        self.settings_manager.set(LOCALEURL_USE_SESSION=True)
+        reload(localeurl_settings)
+
+        r1 = self.request_factory.get('/test/')
+        r1.session = {'django_language': 'fr'}
+        r2 = self.middleware.process_request(r1)
+        self.assertEqual(301, r2.status_code)
+        self.assertEqual('/fr/test/', r2['Location'])
+
+
 
 class DefaultPrefixMiddlewareTestCase(MiddlewareTestCase):
     def setUp(self):
         self.assertEqual('/en/test/?somevar=Mudan%E7as_recentes', r2['Location'])
 
 
+    def test_check_session_disabled(self):
+        self.settings_manager.set(LOCALEURL_USE_SESSION=False)
+        reload(localeurl_settings)
+
+        r1 = self.request_factory.get('/test/')
+        r1.session = {'locale': 'fr'}
+        r2 = self.middleware.process_request(r1)
+        self.assertEqual(301, r2.status_code)
+        self.assertEqual('/en/test/', r2['Location'])
+
+
+
+
 
 class NoDefaultPrefixMiddlewareTestCase(MiddlewareTestCase):
     def setUp(self):
         self.assertEqual('fr', r1.LANGUAGE_CODE)
         self.assertEqual('/test/foo/', r1.path_info)
 
+    def test_check_session_disabled(self):
+        self.settings_manager.set(LOCALEURL_USE_SESSION=False)
+        reload(localeurl_settings)
+
+        r1 = self.request_factory.get('/test/')
+        r1.session = {'locale': 'fr'}
+        r2 = self.middleware.process_request(r1)
+        self.assertEqual(None, r2)
+
 
 
 class TagsTestCase(LocaleurlTestCase):
     def test_kwargs_none(self):
         url = reverse("dummy1", args=["test"], kwargs=None)
         self.assertEqual(url, "/en/dummy/test")
+
+
+class ViewsTestCase(LocaleurlTestCase):
+
+    urls = 'localeurl.urls'
+
+    def setUp(self):
+        super(LocaleurlTestCase, self).setUp()
+        self.settings_manager = test_utils.TestSettingsManager()
+
+    def test_change_locale_check_session_enabled(self):
+        self.settings_manager.set(LOCALEURL_USE_SESSION=True)
+        reload(localeurl_settings)
+        self.client.post('/change/', data={'locale': 'de', 'next': '/foo'})
+        self.assertEqual("de", self.client.session['django_language'])
+
+    def test_change_locale_check_session_disabled(self):
+        self.settings_manager.set(LOCALEURL_USE_SESSION=False)
+        reload(localeurl_settings)
+        self.client.post('/change/', data={'locale': 'de', 'next': '/foo'})
+        self.assertNotEqual("de", self.client.session.get('django_language'))

localeurl/views.py

 from django import http
 from django.utils.translation import check_for_language
 from localeurl import utils
+from localeurl import settings as localeurl_settings
 
 def change_locale(request):
     """
     if request.method == 'POST':
         locale = request.POST.get('locale', None)
         if locale and check_for_language(locale):
+            if localeurl_settings.USE_SESSION:
+                request.session['django_language'] = locale
             path = utils.locale_path(path, locale)
 
     response = http.HttpResponseRedirect(path)
             'localeurl',
             'localeurl.tests',
             'django.contrib.sites', # for sitemap test
+            'django.contrib.sessions', # for USE_SESSION
             ),
         ROOT_URLCONF='localeurl.tests.test_urls',
         SITE_ID=1,