Commit d0451e4c authored by Simon Charette's avatar Simon Charette
Browse files

Fixed #26295 -- Allowed using i18n_patterns() in any root URLconf.

Thanks Tim for the review.
parent 2404d209
Loading
Loading
Loading
Loading
+10 −9
Original line number Diff line number Diff line
@@ -5,9 +5,8 @@ from django.http import HttpResponseRedirect
from django.urls import (
    LocaleRegexURLResolver, get_resolver, get_script_prefix, is_valid_path,
)
from django.utils import translation
from django.utils import lru_cache, translation
from django.utils.cache import patch_vary_headers
from django.utils.functional import cached_property


class LocaleMiddleware(object):
@@ -21,17 +20,19 @@ class LocaleMiddleware(object):
    response_redirect_class = HttpResponseRedirect

    def process_request(self, request):
        urlconf = getattr(request, 'urlconf', settings.ROOT_URLCONF)
        language = translation.get_language_from_request(
            request, check_path=self.is_language_prefix_patterns_used)
            request, check_path=self.is_language_prefix_patterns_used(urlconf)
        )
        translation.activate(language)
        request.LANGUAGE_CODE = translation.get_language()

    def process_response(self, request, response):
        language = translation.get_language()
        language_from_path = translation.get_language_from_path(request.path_info)
        urlconf = getattr(request, 'urlconf', settings.ROOT_URLCONF)
        if (response.status_code == 404 and not language_from_path
                and self.is_language_prefix_patterns_used):
            urlconf = getattr(request, 'urlconf', None)
                and self.is_language_prefix_patterns_used(urlconf)):
            language_path = '/%s%s' % (language, request.path_info)
            path_valid = is_valid_path(language_path, urlconf)
            path_needs_slash = (
@@ -52,20 +53,20 @@ class LocaleMiddleware(object):
                )
                return self.response_redirect_class(language_url)

        if not (self.is_language_prefix_patterns_used
        if not (self.is_language_prefix_patterns_used(urlconf)
                and language_from_path):
            patch_vary_headers(response, ('Accept-Language',))
        if 'Content-Language' not in response:
            response['Content-Language'] = language
        return response

    @cached_property
    def is_language_prefix_patterns_used(self):
    @lru_cache.lru_cache(maxsize=None)
    def is_language_prefix_patterns_used(self, urlconf):
        """
        Returns `True` if the `LocaleRegexURLResolver` is used
        at root level of the urlpatterns, else it returns `False`.
        """
        for url_pattern in get_resolver(None).url_patterns:
        for url_pattern in get_resolver(urlconf).url_patterns:
            if isinstance(url_pattern, LocaleRegexURLResolver):
                return True
        return False
+3 −1
Original line number Diff line number Diff line
@@ -244,7 +244,9 @@ Generic Views
Internationalization
~~~~~~~~~~~~~~~~~~~~

* ...
* The :func:`~django.conf.urls.i18n.i18n_patterns` helper function can now be
  used in a root URLConf specified using :attr:`request.urlconf
  <django.http.HttpRequest.urlconf>`.

Management Commands
~~~~~~~~~~~~~~~~~~~
+8 −2
Original line number Diff line number Diff line
@@ -1328,7 +1328,7 @@ Language prefix in URL patterns

.. function:: i18n_patterns(*pattern_list)

This function can be used in your root URLconf and Django will automatically
This function can be used in a root URLconf and Django will automatically
prepend the current active language code to all url patterns defined within
:func:`~django.conf.urls.i18n.i18n_patterns`. Example URL patterns::

@@ -1373,10 +1373,16 @@ function. Example::

.. warning::

    :func:`~django.conf.urls.i18n.i18n_patterns` is only allowed in your root
    :func:`~django.conf.urls.i18n.i18n_patterns` is only allowed in a root
    URLconf. Using it within an included URLconf will throw an
    :exc:`~django.core.exceptions.ImproperlyConfigured` exception.

.. versionchanged:: 1.10

    In older version, using ``i18n_patterns`` in a root URLconf different from
    :setting:`ROOT_URLCONF` by setting :attr:`request.urlconf
    <django.http.HttpRequest.urlconf>` wasn't supported.

.. warning::

    Ensure that you don't have non-prefixed URL patterns that might collide
+13 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@ from django.http import HttpResponsePermanentRedirect
from django.middleware.locale import LocaleMiddleware
from django.template import Context, Template
from django.test import SimpleTestCase, override_settings
from django.test.client import RequestFactory
from django.test.utils import override_script_prefix
from django.urls import clear_url_caches, reverse, translate_url
from django.utils import translation
@@ -92,6 +93,18 @@ class URLDisabledTests(URLTestCaseBase):
            self.assertEqual(reverse('prefixed'), '/prefixed/')


class RequestURLConfTests(SimpleTestCase):

    @override_settings(ROOT_URLCONF='i18n.patterns.urls.path_unused')
    def test_request_urlconf_considered(self):
        request = RequestFactory().get('/nl/')
        request.urlconf = 'i18n.patterns.urls.default'
        middleware = LocaleMiddleware()
        with translation.override('nl'):
            middleware.process_request(request)
        self.assertEqual(request.LANGUAGE_CODE, 'nl')


@override_settings(ROOT_URLCONF='i18n.patterns.urls.path_unused')
class PathUnusedTests(URLTestCaseBase):
    """