summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaude Paroz <claude@2xlibre.net>2015-03-12 09:32:29 +0100
committerClaude Paroz <claude@2xlibre.net>2015-03-13 16:46:40 +0100
commitaa5ab114e34645823b219f9a866fd82fc26b427b (patch)
tree2b286ba43dc34a103ccd7e69d1969518df2243f8
parenta8991b9b9f11e2d5308f677d0ad1768b88e2d2ec (diff)
Fixed #24122 -- Redirected to translated url after setting language
Thanks gbdlin for the initial patch and Tim Graham for the review.
-rw-r--r--django/core/urlresolvers.py26
-rw-r--r--django/views/i18n.py4
-rw-r--r--docs/releases/1.9.txt3
-rw-r--r--tests/i18n/patterns/tests.py14
-rw-r--r--tests/view_tests/locale/nl/LC_MESSAGES/django.mobin515 -> 557 bytes
-rw-r--r--tests/view_tests/locale/nl/LC_MESSAGES/django.po4
-rw-r--r--tests/view_tests/tests/test_i18n.py17
-rw-r--r--tests/view_tests/urls.py6
8 files changed, 71 insertions, 3 deletions
diff --git a/django/core/urlresolvers.py b/django/core/urlresolvers.py
index 59114795bc..cf42768add 100644
--- a/django/core/urlresolvers.py
+++ b/django/core/urlresolvers.py
@@ -24,7 +24,8 @@ from django.utils.functional import cached_property, lazy
from django.utils.http import RFC3986_SUBDELIMS, urlquote
from django.utils.module_loading import module_has_submodule
from django.utils.regex_helper import normalize
-from django.utils.translation import get_language
+from django.utils.six.moves.urllib.parse import urlsplit, urlunsplit
+from django.utils.translation import get_language, override
# SCRIPT_NAME prefixes for each thread are stored here. If there's no entry for
# the current thread (which is the only one we ever access), it is assumed to
@@ -652,3 +653,26 @@ def is_valid_path(path, urlconf=None):
return True
except Resolver404:
return False
+
+
+def translate_url(url, lang_code):
+ """
+ Given a URL (absolute or relative), try to get its translated version in
+ the `lang_code` language (either by i18n_patterns or by translated regex).
+ Return the original URL if no translated version is found.
+ """
+ parsed = urlsplit(url)
+ try:
+ match = resolve(parsed.path)
+ except Resolver404:
+ pass
+ else:
+ to_be_reversed = "%s:%s" % (match.namespace, match.url_name) if match.namespace else match.url_name
+ with override(lang_code):
+ try:
+ url = reverse(to_be_reversed, args=match.args, kwargs=match.kwargs)
+ except NoReverseMatch:
+ pass
+ else:
+ url = urlunsplit((parsed.scheme, parsed.netloc, url, parsed.query, parsed.fragment))
+ return url
diff --git a/django/views/i18n.py b/django/views/i18n.py
index 7e21035319..30ecbbeaf3 100644
--- a/django/views/i18n.py
+++ b/django/views/i18n.py
@@ -6,6 +6,7 @@ import os
from django import http
from django.apps import apps
from django.conf import settings
+from django.core.urlresolvers import translate_url
from django.template import Context, Engine
from django.utils import six
from django.utils._os import upath
@@ -37,6 +38,9 @@ def set_language(request):
if request.method == 'POST':
lang_code = request.POST.get('language', None)
if lang_code and check_for_language(lang_code):
+ next_trans = translate_url(next, lang_code)
+ if next_trans != next:
+ response = http.HttpResponseRedirect(next_trans)
if hasattr(request, 'session'):
request.session[LANGUAGE_SESSION_KEY] = lang_code
else:
diff --git a/docs/releases/1.9.txt b/docs/releases/1.9.txt
index da4984a9be..e8dd955e29 100644
--- a/docs/releases/1.9.txt
+++ b/docs/releases/1.9.txt
@@ -127,7 +127,8 @@ Generic Views
Internationalization
^^^^^^^^^^^^^^^^^^^^
-* ...
+* The :func:`django.views.i18n.set_language` view now properly redirects to
+ :ref:`translated URLs <url-internationalization>`, when available.
Management Commands
^^^^^^^^^^^^^^^^^^^
diff --git a/tests/i18n/patterns/tests.py b/tests/i18n/patterns/tests.py
index 1ac0e8a4a9..62ca49b67d 100644
--- a/tests/i18n/patterns/tests.py
+++ b/tests/i18n/patterns/tests.py
@@ -4,7 +4,7 @@ import os
from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import (
- clear_url_caches, reverse, set_script_prefix,
+ clear_url_caches, reverse, set_script_prefix, translate_url,
)
from django.http import HttpResponsePermanentRedirect
from django.middleware.locale import LocaleMiddleware
@@ -135,6 +135,18 @@ class URLTranslationTests(URLTestCaseBase):
with translation.override('pt-br'):
self.assertEqual(reverse('users'), '/pt-br/usuarios/')
+ def test_translate_url_utility(self):
+ with translation.override('en'):
+ self.assertEqual(translate_url('/en/non-existent/', 'nl'), '/en/non-existent/')
+ self.assertEqual(translate_url('/en/users/', 'nl'), '/nl/gebruikers/')
+ # Namespaced URL
+ self.assertEqual(translate_url('/en/account/register/', 'nl'), '/nl/profiel/registeren/')
+ self.assertEqual(translation.get_language(), 'en')
+
+ with translation.override('nl'):
+ self.assertEqual(translate_url('/nl/gebruikers/', 'en'), '/en/users/')
+ self.assertEqual(translation.get_language(), 'nl')
+
class URLNamespaceTests(URLTestCaseBase):
"""
diff --git a/tests/view_tests/locale/nl/LC_MESSAGES/django.mo b/tests/view_tests/locale/nl/LC_MESSAGES/django.mo
index 90066d70ec..ef12f2ade8 100644
--- a/tests/view_tests/locale/nl/LC_MESSAGES/django.mo
+++ b/tests/view_tests/locale/nl/LC_MESSAGES/django.mo
Binary files differ
diff --git a/tests/view_tests/locale/nl/LC_MESSAGES/django.po b/tests/view_tests/locale/nl/LC_MESSAGES/django.po
index e85f08ee80..4e5f7e2fb5 100644
--- a/tests/view_tests/locale/nl/LC_MESSAGES/django.po
+++ b/tests/view_tests/locale/nl/LC_MESSAGES/django.po
@@ -16,6 +16,10 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+#: urls.py:78
+msgid "^translated/$"
+msgstr "^vertaald/$"
+
#: views/csrf.py:98
msgid "Forbidden"
msgstr "Verboden"
diff --git a/tests/view_tests/tests/test_i18n.py b/tests/view_tests/tests/test_i18n.py
index 2f47007b04..ef11d7d82a 100644
--- a/tests/view_tests/tests/test_i18n.py
+++ b/tests/view_tests/tests/test_i18n.py
@@ -67,6 +67,23 @@ class I18NTests(TestCase):
self.assertEqual(language_cookie['path'], '/test/')
self.assertEqual(language_cookie['max-age'], 3600 * 7 * 2)
+ @modify_settings(MIDDLEWARE_CLASSES={
+ 'append': 'django.middleware.locale.LocaleMiddleware',
+ })
+ def test_lang_from_translated_i18n_pattern(self):
+ response = self.client.post(
+ '/i18n/setlang/', data={'language': 'nl'},
+ follow=True, HTTP_REFERER='/en/translated/'
+ )
+ self.assertEqual(self.client.session[LANGUAGE_SESSION_KEY], 'nl')
+ self.assertRedirects(response, 'http://testserver/nl/vertaald/')
+ # And reverse
+ response = self.client.post(
+ '/i18n/setlang/', data={'language': 'en'},
+ follow=True, HTTP_REFERER='/nl/vertaald/'
+ )
+ self.assertRedirects(response, 'http://testserver/en/translated/')
+
def test_jsi18n(self):
"""The javascript_catalog can be deployed with language settings"""
for lang_code in ['es', 'fr', 'ru']:
diff --git a/tests/view_tests/urls.py b/tests/view_tests/urls.py
index 0bfcd587c2..061dd19751 100644
--- a/tests/view_tests/urls.py
+++ b/tests/view_tests/urls.py
@@ -2,7 +2,9 @@
from os import path
from django.conf.urls import include, url
+from django.conf.urls.i18n import i18n_patterns
from django.utils._os import upath
+from django.utils.translation import ugettext_lazy as _
from django.views import defaults, i18n, static
from . import views
@@ -73,6 +75,10 @@ urlpatterns = [
url(r'^site_media/(?P<path>.*)$', static.serve, {'document_root': media_dir}),
]
+urlpatterns += i18n_patterns(
+ url(_(r'^translated/$'), views.index_page, name='i18n_prefixed'),
+)
+
urlpatterns += [
url(r'view_exception/(?P<n>[0-9]+)/$', views.view_exception, name='view_exception'),
url(r'template_exception/(?P<n>[0-9]+)/$', views.template_exception, name='template_exception'),