Commits

Anonymous committed 6eaa9b0

Based resolvers on build_locale_url and parse_locale_url.

Comments (0)

Files changed (10)

localeurl/middleware.py

             from django.core.exceptions import MiddlewareNotUsed
             raise MiddlewareNotUsed()
 
-        # Make sure the default language is in the list of supported languages
-        assert resolver.is_supported_locale(settings.LANGUAGE_CODE), \
-                "settings.LANGUAGE_CODE must be in settings.LANGUAGES."
-
     def process_request(self, request):
         response = resolver.process_request(request)
         if response is not None:

localeurl/models.py

 
 if settings.USE_I18N:
     def reverse(viewname, urlconf=None, args=[], kwargs={}, prefix=None):
-        return resolver.reverse(viewname, urlconf, args, kwargs, prefix)
+        return resolver.reverse(django_reverse, viewname, urlconf, args,
+                kwargs, prefix)
+
+    django_reverse = urlresolvers.reverse
+    urlresolvers.reverse = reverse

localeurl/resolver/base.py

 from django.core import urlresolvers
+from django.http import HttpResponseRedirect
+from django.utils import translation
 
 class Resolver(object):
-    def __init__(self, settings, reverse=urlresolvers.reverse):
+    def __init__(self, settings):
         self.settings = settings
-        self.django_reverse = reverse
         self.supported_locales = dict(settings.LANGUAGES)
-        assert self.supported_language(settings.LANGUAGE_CODE) \
-                in self.supported_locales, \
+        assert self.is_supported_locale(settings.LANGUAGE_CODE), \
                 "settings.LANGUAGE_CODE must be in settings.LANGUAGES"
 
     def process_request(self, request):
         """
-        Sets the LANGUAGE_CODE attribute on the request. This method must be
-        implemented in extending classes.
+        Parses the request URL and sets the LANGUAGE_CODE attribute. The
+        default implementation uses the build_locale_url and parse_locale_url
+        methods. It may be overridden to be more efficient in extending
+        classes.
         """
-        raise NotImplementedError
+        url = request.build_absolute_uri()
+        path, locale = self.parse_locale_url(url)
+        if locale is None:
+            locale = self.get_fallback_locale(request)
+        locale_url = self.build_locale_url(path, locale, request)
+        if url != request.build_absolute_uri(locale_url):
+            return HttpResponseRedirect(locale_url)
+        request.path_info = path
+        request.LANGUAGE_CODE = locale
 
     def build_locale_url(self, path, locale=None, request=None, prefix=None):
         """
         """
         raise NotImplementedError
 
-    def reverse(self, viewname, urlconf=None, args=[], kwargs={}, prefix=None):
+    def reverse(self, django_reverse, viewname, urlconf=None, args=[],
+            kwargs={}, prefix=None):
         """
         Returns the URL to a view. This function uses the urlresolvers.reverse
         function, strips off the script prefix and call build_locale_url with
         """
         if prefix is None:
             prefix = urlresolvers.get_script_prefix()
-        url = self.django_reverse(viewname, urlconf, args, kwargs, prefix)
+        url = django_reverse(viewname, urlconf, args, kwargs, prefix)
         assert url.startswith(prefix)
         return self.build_locale_url(url[len(prefix)-1:])
 
         guaranteed to be in settings.LANGUAGES
         """
         if request is not None and hasattr(request, 'LANGUAGE_CODE'):
-            assert self.supported_language(request.LANGUAGE_CODE) \
-                    in self.supported_locales, \
+            assert self.is_supported_locale(request.LANGUAGE_CODE), \
                     "request.LANGUAGE_CODE must be in settings.LANGUAGES"
             return self.supported_language(request.LANGUAGE_CODE)
         else:
         return self.supported_language(self.settings.LANGUAGE_CODE)
     default_locale = property(_default_locale)
 
+    def _current_locale(self):
+        """
+        The currently activated locale.
+        """
+        return self.supported_language(translation.get_language())
+    current_locale = property(_current_locale)
+
     def strip_script_prefix(self, url):
         """
-        Strips the SCRIPT_PREFIX from the URL. The function assumes the URL
+        Strips the script prefix from the URL. The function assumes the URL
         starts with the prefix.
         """
         assert url.startswith(urlresolvers.get_script_prefix()), \
-                "URL does not start with SCRIPT_PREFIX: %s" % url
+                "URL does not start with script prefix: %s" % url
         pos = len(urlresolvers.get_script_prefix()) - 1
         return url[:pos], url[pos:]

localeurl/resolver/domain_component.py

 class DomainComponentResolver(Resolver):
     def __init__(self, settings=django_settings):
         super(DomainComponentResolver, self).__init__(settings)
-        self.reverse = self.django_reverse
         self.default_locale_component = getattr(settings,
                 'DEFAULT_LOCALE_DOMAIN_COMPONENT', self.default_locale)
-        locales_re = '|'.join([self.default_locale_component
-                if self.is_default_locale(locale) else locale
+        locales_re = '|'.join([self.is_default_locale(locale)
+                and self.default_locale_component or locale
                 for locale in self.supported_locales])
         self.domain_re = re.compile(DOMAIN_PATTERN % locales_re)
         self.domain_reverse = urlresolvers.normalize(DOMAIN_PATTERN)[0][0]
         locale, domain = self.split_domain(request.get_host())
         if not self.is_supported_locale(locale):
             locale = self.get_fallback_locale(request)
-        request.get_host = lambda: domain
         request.LANGUAGE_CODE = locale
 
     def build_locale_url(self, path, locale=None, request=None):
         if locale is None:
-            return path
+            return "%s%s" % (urlresolvers.get_script_prefix(), path[1:])
         else:
             assert request is not None, \
                     "Request required for building URL with specified locale"
+            (_, domain) = self.split_domain(request.get_host())
+            domain = self.join_domain(locale, domain)
             return "%s://%s%s%s" % (request.is_secure() and 'https' or 'http',
-                    self.join_domain(locale, request.get_host()),
-                    urlresolvers.get_script_prefix(), path[1:])
+                    domain, urlresolvers.get_script_prefix(), path[1:])
 
     def parse_locale_url(self, url):
         (_, domain, path, query, fragment) = urlparse.urlsplit(url)
     def split_domain(self, domain):
         check = self.domain_re.match(domain)
         if check:
-            return check.group('locale'), check.group('domain')
+            locale = check.group('locale')
+            if locale == self.default_locale_component:
+                locale = self.default_locale
+            return locale, check.group('domain')
         else:
             return '', domain

localeurl/resolver/domains.py

 class DomainsResolver(Resolver):
     def __init__(self, settings=django_settings):
         super(DomainsResolver, self).__init__(settings)
-        self.reverse = self.django_reverse
         self.domains = getattr(settings, 'LOCALEURL_DOMAINS', ())
         self.domain_locale_map = dict(self.domains)
         self.locale_domain_map = dict(map(reversed, self.domains))
                 for locale in self.locale_domain_map]), \
                 "settings.LANGUAGES must contain all settings.LOCALE_DOMAINS"
 
-    def process_request(self, request):
-        try:
-            locale = self.domain_locale_map[request.get_host()]
-        except KeyError:
-            # Redirect to the domain for the fallback locale
-            return HttpResponseRedirect("%s://%s%s" % (
-                    request.is_secure() and 'https' or 'http',
-                    self.locale_domain_map[self.get_fallback_locale(request)],
-                    request.get_full_path()))
-        if not locale:
-            locale = self.get_fallback_locale(request)
-        request.LANGUAGE_CODE = locale
-
     def build_locale_url(self, path, locale=None, request=None):
         if locale is None:
             return ''.join([urlresolvers.get_script_prefix(), path[1:]])

localeurl/resolver/path_prefix.py

 # Licensed under the terms of the MIT License (see LICENSE.txt)
 
 import re
+import urlparse
 from django.conf import settings as django_settings
 from django.core import urlresolvers
 from django.http import HttpResponseRedirect
         self.locale_independent_paths = getattr(settings,
                 'LOCALE_INDEPENDENT_PATHS', ())
 
-    def process_request(self, request):
-        locale, path = self.split_path(request.path_info)
+    def build_locale_url(self, path, locale=None, request=None):
+        if locale is None:
+            locale = self.current_locale
+        if self.is_locale_independent(path):
+            pass
+        elif self.is_default_locale(locale) \
+                and not self.prefix_default_locale:
+            pass
+        else:
+            path = ''.join((u'/', locale, path))
+        return ''.join((urlresolvers.get_script_prefix(), path[1:]))
 
-        # Redirect locale independent paths with locale prefix
-        if locale and self.is_locale_independent(path):
-            return self.redirection(path, locale)
-
-        # Redirect default locale with prefix unless PREFIX_DEFAULT_LOCALE
-        if self.is_default_locale(locale) \
-                and not self.prefix_default_locale:
-            return self.redirection(path, locale)
-
-        # Redirect paths without prefix if PREFIX_DEFAULT_LOCALE
-        if not locale and self.prefix_default_locale \
-                and not self.is_locale_independent(path):
-            return self.redirection(path, self.get_fallback_locale(request))
-
+    def parse_locale_url(self, url):
+        path = self._get_path(url)
+        (_, path) = self.strip_script_prefix(path)
+        (locale, path) = self.split_path(path)
         if not locale:
-            locale = self.get_fallback_locale(request)
-        request.path_info = path
-        request.LANGUAGE_CODE = locale
-
-    def redirection(self, path, locale):
-        return HttpResponseRedirect(self.build_locale_url(path, locale))
+            locale = None
+        return (path, locale)
 
     def split_path(self, path):
         check = self.path_re.match(path)
                 return True
         return False
 
-    def build_locale_url(self, path, locale=None, request=None):
-        if not locale:
-            from django.utils import translation
-            locale = self.supported_language(translation.get_language())
-        if self.is_locale_independent(path):
-            pass
-        elif self.is_default_locale(locale) \
-                and not self.prefix_default_locale:
-            pass
-        else:
-            path = ''.join([u'/', locale, path])
-        return ''.join([urlresolvers.get_script_prefix(), path[1:]])
-
-    def parse_locale_url(self, url):
-        (_, path) = self.strip_script_prefix(url)
-        (locale, path) = self.split_path(path)
-        if not locale:
-            locale = None
-        return (path, locale)
+    def _get_path(self, url):
+        (_, _, path, query, fragment) = urlparse.urlsplit(url)
+        return urlparse.urlunsplit(('', '', path, query, fragment))

localeurl/tests/resolver/test_domain_component.py

+import unittest
 import re
 from django.test import TestCase
+from localeurl.models import django_reverse
 from localeurl.resolver.domain_component import DomainComponentResolver
 from localeurl.tests.resolver.test_base import ResolverTestCase
 from localeurl.tests.utils import RequestFactory, TestSettings
     def test_reverses_path(self):
         resolver = DomainComponentResolver(self.settings)
         self.assertEqual('%s/test/' % (self.script_name),
-                resolver.reverse('localeurl.tests.views.test'))
+                resolver.reverse(django_reverse, 'localeurl.tests.views.test'))
+
+if __name__ == '__main__':
+    unittest.main()

localeurl/tests/resolver/test_domains.py

 from django.test import TestCase
+from localeurl.models import django_reverse
 from localeurl.resolver.domains import DomainsResolver
 from localeurl.tests.resolver.test_base import ResolverTestCase
 from localeurl.tests.utils import RequestFactory, TestSettings
     def test_reverses_path(self):
         resolver = DomainsResolver(self.settings)
         self.assertEqual('%s/test/' % (self.script_name),
-                resolver.reverse('localeurl.tests.views.test'))
+                resolver.reverse(django_reverse, 'localeurl.tests.views.test'))

localeurl/tests/resolver/test_path_prefix.py

 from django.test import TestCase
 from django.utils import translation
 from localeurl.resolver.path_prefix import PathPrefixResolver
+from localeurl.models import django_reverse
 from localeurl.tests.resolver.test_base import ResolverTestCase
 from localeurl.tests.utils import RequestFactory, TestSettings
 
         translation.activate(locale)
         resolver = PathPrefixResolver(self.settings)
         self.assertEqual("%s%s" % (self.script_name, expected_path),
-                resolver.reverse(view))
+                resolver.reverse(django_reverse, view))
 
     def test_reverses_path_if_prefix_default_locale(self):
         self.settings['PREFIX_DEFAULT_LOCALE'] = True

localeurl/tests/settings.py

 DEBUG = True
 
 DATABASE_ENGINE = 'sqlite3'
+DATABASE_NAME = ':memory:'
 
 INSTALLED_APPS = (
     'localeurl',