Ticket #11585: i18nurls.diff

File i18nurls.diff, 34.3 KB (added by Orne Brocaar, 13 years ago)

Implementation for language-prefix and internationalization of URL patterns

  • AUTHORS

     
    9494    Sean Brant
    9595    Andrew Brehaut <http://brehaut.net/blog>
    9696    David Brenneman <http://davidbrenneman.com>
     97    Orne Brocaar <http://brocaar.com/>
    9798    brut.alll@gmail.com
    9899    bthomas
    99100    btoll@bestweb.net
  • docs/topics/i18n/internationalization.txt

     
    769769cases where you really need it (for example, in conjunction with ``ngettext``
    770770to produce proper pluralizations).
    771771
     772.. _url-internationalization:
     773
     774Specifying translation strings: In URL patterns
     775===============================================
     776
     777..  versionadded:: 1.4
     778
     779Django provides two meganism to internationalize URL patterns:
     780
     781* Adding the language-prefix to the root of the URL patterns to make it possible
     782  for the ``LocaleMiddleware`` to detect the language to activate from the requested
     783  URL.
     784
     785* Making URL patterns translatable by using the ``ugettext_lazy()`` function.
     786
     787
     788Language prefix in URL patterns
     789-------------------------------
     790
     791By using the ``i18n_patterns()`` function instead of the ``patterns()`` function,
     792Django will add the current active language automatically to all the URL patterns
     793defined within this function. Example URL patterns::
     794
     795    from django.conf.urls.defaults import patterns, i18n_patterns, include, url
     796   
     797    urlpatterns = patterns(''
     798        url(r'^sitemap\.xml$', 'sitemap.view', name='sitemap_xml'),
     799    )
     800   
     801    news_patterns = patterns(''
     802        url(r'^$', 'news.views.index', name='index'),
     803        url(r'^category/(?P<slug>[\w-]+)/$', 'news.views.category', name='category'),
     804        url(r'^(?P<slug>[\w-]+)/$', 'news.views.details', name='detail'),
     805    )
     806   
     807    urlpatterns += i18n_patterns('',
     808        url(r'^about/$', 'about.view', name='about'),
     809        url(r'^news/$', include(news_patterns, namespace='news')),
     810    )
     811
     812
     813After defining these URL patterns, Django will automatically add the language-prefix
     814to the URL patterns within the ``i18n_patterns`` function. Example::
     815
     816    from django.core.urlresolvers import reverse
     817    from django.utils.translation import activate
     818   
     819    >>> activate('en')
     820    >>> reverse('sitemap_xml')
     821    '/sitemap.xml'
     822    >>> reverse('news:index')
     823    '/en/news/'
     824   
     825    >>> activate('nl')
     826    >>> reverse('news:detail', kwargs={'slug': 'news-slug'})
     827    '/nl/news/news-slug/'
     828
     829.. warning::
     830
     831    * The ``i18n_patterns()`` function is only allowed at rootlevel of the URL patterns.
     832      When including a pattern which contains one or more ``i18n_patterns()`` patterns,
     833      an exception will be thrown.
     834   
     835    * Make sure the automatically added language-prefix does not cause collision
     836      with other non-prefixed URL patterns.
     837
     838
     839Translating URL patterns
     840------------------------
     841
     842URL patterns can be marked translatable by using the ``ugettext_lazy()`` function.
     843Example::
     844
     845    from django.conf.urls.defaults import patterns, i18n_patterns, include, url
     846    from django.utils.translation import ugettext_lazy
     847   
     848    urlpatterns = patterns(''
     849        url(r'^sitemap\.xml$', 'sitemap.view', name='sitemap_xml'),
     850    )
     851   
     852    news_patterns = patterns(''
     853        url(r'^$', 'news.views.index', name='index'),
     854        url(_(r'^category/(?P<slug>[\w-]+)/$'), 'news.views.category', name='category'),
     855        url(r'^(?P<slug>[\w-]+)/$', 'news.views.details', name='detail'),
     856    )
     857   
     858    urlpatterns += i18n_patterns('',
     859        url(_(r'^about/$'), 'about.view', name='about'),
     860        url(_(r'^news/$'), include(news_patterns, namespace='news')),
     861    )
     862
     863
     864After creating the translations (see :doc:`localization` for more information),
     865the ``reverse()`` function will return the URL in the active language. Example::
     866
     867    from django.core.urlresolvers import reverse
     868    from django.utils.translation import activate
     869   
     870    >>> activate('en')
     871    >>> reverse('news:category', kwargs={'slug': 'recent'})
     872    '/en/news/news-slug/'
     873   
     874    >>> activate('nl')
     875    >>> reverse('news:category', kwargs={'slug': 'recent'})
     876    '/nl/nieuws/categorie/recent/'
     877
     878.. warning::
     879
     880    Unless you are using your own middleware to detect the language to activate
     881    from the requested URL, always use translatable URL patterns together with
     882    the ``i18n_patterns()`` function. Django is not able to resolve a URL when
     883    the correct language is not activated.
     884
     885
    772886.. _set_language-redirect-view:
    773887
    774888The ``set_language`` redirect view
  • docs/topics/i18n/deployment.txt

     
    5959
    6060    * Make sure it's one of the first middlewares installed.
    6161    * It should come after ``SessionMiddleware``, because ``LocaleMiddleware``
    62       makes use of session data.
     62      makes use of session data. As well it should come before ``CommonMiddleware``
     63      because ``CommonMiddleware`` depends on a activated language when it needs
     64      to resolve the requested URL.
    6365    * If you use ``CacheMiddleware``, put ``LocaleMiddleware`` after it.
    6466
    6567For example, your :setting:`MIDDLEWARE_CLASSES` might look like this::
     
    7678``LocaleMiddleware`` tries to determine the user's language preference by
    7779following this algorithm:
    7880
    79     * First, it looks for a ``django_language`` key in the current user's
     81    * First, it looks for the language-prefix in the requested URL.
     82      This is only performed when you are using the ``i18n_patterns`` function.
     83      See :ref:`url-internationalization` for more information about the
     84      language-prefix and how to internationalize URL patterns.
     85
     86    * Failing that, it looks for a ``django_language`` key in the current user's
    8087      session.
    8188
    8289    * Failing that, it looks for a cookie.
  • django/conf/urls/defaults.py

     
    1 from django.core.urlresolvers import RegexURLPattern, RegexURLResolver
     1from django.core.urlresolvers import (RegexURLPattern, RegexURLResolver,
     2    LanguagePrefixedRegexURLResolver, get_resolver)
    23from django.core.exceptions import ImproperlyConfigured
     4from django.utils.importlib import import_module
    35
     6
    47__all__ = ['handler404', 'handler500', 'include', 'patterns', 'url']
    58
    69handler404 = 'django.views.defaults.page_not_found'
     
    1518    else:
    1619        # No namespace hint - use manually provided namespace
    1720        urlconf_module = arg
     21   
     22    # Test if the LanguagePrefixedRegexURLResolver is used within the include,
     23    # this should throw an error since this is not allowed!
     24    if isinstance(urlconf_module, basestring):
     25        urlconf_module = import_module(urlconf_module)
     26    patterns = getattr(urlconf_module, 'urlpatterns', urlconf_module)
     27   
     28    # Make sure we can iterate through the patterns (without this, some testcases
     29    # will break).
     30    if isinstance(patterns, (list, tuple)):
     31        for url_pattern in patterns:
     32            if isinstance(url_pattern, LanguagePrefixedRegexURLResolver):
     33                raise ImproperlyConfigured('Using i18n_patterns inside an include is not allowed')
     34   
    1835    return (urlconf_module, app_name, namespace)
    1936
    2037def patterns(prefix, *args):
     
    2744        pattern_list.append(t)
    2845    return pattern_list
    2946
     47def i18n_patterns(prefix, *args):
     48    """
     49    This will add the language-code prefix to every URLPattern within this
     50    function. It is only allowed to use this at rootlevel of your URLConf.
     51    """
     52    pattern_list = patterns(prefix, *args)
     53    return [LanguagePrefixedRegexURLResolver(pattern_list)]
     54
    3055def url(regex, view, kwargs=None, name=None, prefix=''):
    3156    if isinstance(view, (list,tuple)):
    3257        # For include(...) processing.
     
    3964            if prefix:
    4065                view = prefix + '.' + view
    4166        return RegexURLPattern(regex, view, kwargs, name)
    42 
  • django/core/urlresolvers.py

     
    1818from django.utils.functional import memoize, lazy
    1919from django.utils.importlib import import_module
    2020from django.utils.regex_helper import normalize
     21from django.utils.translation import get_language
    2122
     23
    2224_resolver_cache = {} # Maps URLconf modules to RegexURLResolver instances.
    2325_callable_cache = {} # Maps view and url pattern names to their view functions.
    2426
     
    117119
    118120class RegexURLPattern(object):
    119121    def __init__(self, regex, callback, default_args=None, name=None):
    120         # regex is a string representing a regular expression.
     122        # regex is eighter a string representing a regular expression, or a
     123        # translatable string (using ugettext_lazy) representing a regular
     124        # expression.
    121125        # callback is either a string like 'foo.views.news.stories.story_detail'
    122126        # which represents the path to a module and a view function name, or a
    123127        # callable object (view).
    124         self.regex = re.compile(regex, re.UNICODE)
     128        self._regex = regex
     129        self._i18n_regex_dict = {}
    125130        if callable(callback):
    126131            self._callback = callback
    127132        else:
     
    129134            self._callback_str = callback
    130135        self.default_args = default_args or {}
    131136        self.name = name
     137   
     138    @property
     139    def regex(self):
     140        """
     141        Returns a compiled regular expression, depending upon the activated
     142        language-code.
     143        """
     144        language_code = get_language()
     145       
     146        if language_code not in self._i18n_regex_dict:
     147            if isinstance(self._regex, basestring):
     148                compiled_regex = re.compile(self._regex, re.UNICODE)
     149            else:
     150                regex = unicode(self._regex)
     151                compiled_regex = re.compile(regex, re.UNICODE)
     152            self._i18n_regex_dict[language_code] = compiled_regex
     153       
     154        return self._i18n_regex_dict[language_code]
    132155
    133156    def __repr__(self):
    134157        return smart_str(u'<%s %s %s>' % (self.__class__.__name__, self.name, self.regex.pattern))
     
    175198    def __init__(self, regex, urlconf_name, default_kwargs=None, app_name=None, namespace=None):
    176199        # regex is a string representing a regular expression.
    177200        # urlconf_name is a string representing the module containing URLconfs.
    178         self.regex = re.compile(regex, re.UNICODE)
     201        self._regex = regex
     202        self._i18n_regex_dict = {}
    179203        self.urlconf_name = urlconf_name
    180204        if not isinstance(urlconf_name, basestring):
    181205            self._urlconf_module = self.urlconf_name
     
    183207        self.default_kwargs = default_kwargs or {}
    184208        self.namespace = namespace
    185209        self.app_name = app_name
    186         self._reverse_dict = None
    187         self._namespace_dict = None
    188         self._app_dict = None
     210        self._i18n_reverse_dict = {}
     211        self._i18n_namespace_dict = {}
     212        self._i18n_app_dict = {}
    189213
     214    @property
     215    def regex(self):
     216        """
     217        Returns a compiled regular expression, depending upon the activated
     218        language-code.
     219        """
     220        language_code = get_language()
     221       
     222        if language_code not in self._i18n_regex_dict:
     223            if isinstance(self._regex, basestring):
     224                compiled_regex = re.compile(self._regex, re.UNICODE)
     225            else:
     226                regex = unicode(self._regex)
     227                compiled_regex = re.compile(regex, re.UNICODE)
     228            self._i18n_regex_dict[language_code] = compiled_regex
     229       
     230        return self._i18n_regex_dict[language_code]
     231
    190232    def __repr__(self):
    191233        return smart_str(u'<%s %s (%s:%s) %s>' % (self.__class__.__name__, self.urlconf_name, self.app_name, self.namespace, self.regex.pattern))
    192234
     
    220262                lookups.appendlist(pattern.callback, (bits, p_pattern, pattern.default_args))
    221263                if pattern.name is not None:
    222264                    lookups.appendlist(pattern.name, (bits, p_pattern, pattern.default_args))
    223         self._reverse_dict = lookups
    224         self._namespace_dict = namespaces
    225         self._app_dict = apps
     265       
     266        language_code = get_language()
     267        self._i18n_reverse_dict[language_code] = lookups
     268        self._i18n_namespace_dict[language_code] = namespaces
     269        self._i18n_app_dict[language_code] = apps
    226270
    227     def _get_reverse_dict(self):
    228         if self._reverse_dict is None:
     271    @property
     272    def reverse_dict(self):
     273        language_code = get_language()
     274        if language_code not in self._i18n_reverse_dict:
    229275            self._populate()
    230         return self._reverse_dict
    231     reverse_dict = property(_get_reverse_dict)
     276        return self._i18n_reverse_dict[language_code]
    232277
    233     def _get_namespace_dict(self):
    234         if self._namespace_dict is None:
     278    @property
     279    def namespace_dict(self):
     280        language_code = get_language()
     281        if language_code not in self._i18n_namespace_dict:
    235282            self._populate()
    236         return self._namespace_dict
    237     namespace_dict = property(_get_namespace_dict)
     283        return self._i18n_namespace_dict[language_code]
    238284
    239     def _get_app_dict(self):
    240         if self._app_dict is None:
     285    @property
     286    def app_dict(self):
     287        language_code = get_language()
     288        if language_code not in self._i18n_app_dict:
    241289            self._populate()
    242         return self._app_dict
    243     app_dict = property(_get_app_dict)
     290        return self._i18n_app_dict[language_code]
    244291
    245292    def resolve(self, path):
    246293        tried = []
     
    343390        raise NoReverseMatch("Reverse for '%s' with arguments '%s' and keyword "
    344391                "arguments '%s' not found." % (lookup_view_s, args, kwargs))
    345392
     393class LanguagePrefixedRegexURLResolver(RegexURLResolver):
     394    """
     395    The ``__init__`` function does not take an regex argument. Instead, we are
     396    overriding the ``regex`` function to always return the active language-code
     397    as regex.
     398    """
     399   
     400    def __init__(self, urlconf_name, default_kwargs=None, app_name=None, namespace=None):
     401        super(LanguagePrefixedRegexURLResolver, self).__init__(None, urlconf_name,
     402            default_kwargs, app_name, namespace)
     403
     404    @property
     405    def regex(self):
     406        language_code = get_language()
     407        if language_code not in self._i18n_regex_dict:
     408            regex_compiled = re.compile('^%s/' % language_code, re.UNICODE)
     409            self._i18n_regex_dict[language_code] = regex_compiled
     410        return self._i18n_regex_dict[language_code]
     411
    346412def resolve(path, urlconf=None):
    347413    if urlconf is None:
    348414        urlconf = get_urlconf()
  • django/middleware/locale.py

     
    11"this is the locale selecting middleware that will look at accept headers"
    22
     3import re
     4
     5from django.conf import settings
     6from django.core import urlresolvers
     7from django.http import HttpResponseRedirect
    38from django.utils.cache import patch_vary_headers
    49from django.utils import translation
    510
     11
     12language_code_prefix_regex = re.compile(r'^/([\w-]+)/')
     13
    614class LocaleMiddleware(object):
    715    """
    816    This is a very simple middleware that parses a request
     
    1321    """
    1422
    1523    def process_request(self, request):
    16         language = translation.get_language_from_request(request)
     24        language = _language_code_from_path(request.path_info)
     25       
     26        if not language:
     27            language = translation.get_language_from_request(request)
     28       
    1729        translation.activate(language)
    1830        request.LANGUAGE_CODE = translation.get_language()
    1931
    2032    def process_response(self, request, response):
     33        language = translation.get_language()
     34        translation.deactivate()
     35       
     36        if (response.status_code == 404 and not _language_code_from_path(request.path_info)
     37            and _is_language_prefix_patterns_used()):
     38            prefixed_request_path = '/%s%s' % (language, request.get_full_path())
     39            return HttpResponseRedirect(prefixed_request_path)
     40       
    2141        patch_vary_headers(response, ('Accept-Language',))
    2242        if 'Content-Language' not in response:
    23             response['Content-Language'] = translation.get_language()
    24         translation.deactivate()
     43            response['Content-Language'] = language
    2544        return response
     45
     46def _language_code_from_path(path):
     47    """
     48    Returns the language-code if there is a valid language-code found in the
     49    `path`.
     50    """
     51    regex_match = language_code_prefix_regex.match(path)
     52    if regex_match:
     53        if regex_match.group(1) in dict(settings.LANGUAGES):
     54            return regex_match.group(1)
     55    return None
     56
     57def _is_language_prefix_patterns_used():
     58    """
     59    Returns `True` if the `LanguagePrefixedRegexURLResolver` is used at rootlevel
     60    of the urlpatterns, else it returns `False`.
     61    """
     62    resolver = urlresolvers.get_resolver(None)
     63    for url_pattern in resolver.url_patterns:
     64        if isinstance(url_pattern, urlresolvers.LanguagePrefixedRegexURLResolver):
     65            return True
     66    return False
  • tests/regressiontests/i18n/wrong_urls_namespace.py

     
     1from django.conf.urls.defaults import i18n_patterns, include, url
     2from django.utils.translation import ugettext_lazy as _
     3from django.views.generic import TemplateView
     4
     5
     6view = TemplateView.as_view(template_name='dummy.html')
     7
     8urlpatterns = i18n_patterns('',
     9    url(_(r'^register/$'), view, name='register'),
     10)
  • tests/regressiontests/i18n/urls_namespace.py

     
     1from django.conf.urls.defaults import patterns, include, url
     2from django.utils.translation import ugettext_lazy as _
     3from django.views.generic import TemplateView
     4
     5
     6view = TemplateView.as_view(template_name='dummy.html')
     7
     8urlpatterns = patterns('',
     9    url(_(r'^register/$'), view, name='register'),
     10)
  • tests/regressiontests/i18n/wrong_urls.py

     
     1from django.conf.urls.defaults import patterns, i18n_patterns, include, url
     2from django.utils.translation import ugettext_lazy as _
     3
     4
     5urlpatterns = i18n_patterns('',
     6    url(_(r'^account/'), include('regressiontests.i18n.wrong_urls_namespace', namespace='account')),
     7)
  • tests/regressiontests/i18n/tests.py

     
    88from threading import local
    99
    1010from django.conf import settings
     11from django.core.exceptions import ImproperlyConfigured
     12from django.core.urlresolvers import reverse, get_resolver, clear_url_caches
    1113from django.template import Template, Context
    1214from django.test import TestCase
     15from django.test.utils import override_settings
    1316from django.utils.formats import (get_format, date_format, time_format,
    1417    localize, localize_input, iter_format_modules, get_format_modules)
    1518from django.utils.importlib import import_module
     
    828831        with translation.override('nl'):
    829832            self.assertEqual(t.render(Context({})), 'Nee')
    830833
     834
     835class URLTestCaseBase(TestCase):
     836    """
     837    TestCase base-class for the URL tests.
     838    """
     839    def setUp(self):
     840        # Make sure the cache is empty before we are doing our tests.
     841        clear_url_caches()
     842   
     843    def tearDown(self):
     844        # Make sure we will leave an empty cache for other testcases.
     845        clear_url_caches()
     846
     847
     848URLTestCaseBase = override_settings(
     849    ROOT_URLCONF='regressiontests.i18n.urls',
     850    LOCALE_PATHS=(os.path.join(os.path.dirname(__file__), 'test_locale'), ),
     851    TEMPLATE_DIRS=(os.path.join(os.path.dirname(__file__), 'test_templates'), ),
     852    LANGUAGE_CODE='en',
     853    LANGUAGES=(
     854        ('nl', 'Dutch'),
     855        ('en', 'English'),
     856        ('pt-br', 'Brazilian Portuguese'),
     857    ),
     858    MIDDLEWARE_CLASSES=(
     859        'django.middleware.locale.LocaleMiddleware',
     860        'django.middleware.common.CommonMiddleware',
     861    ),
     862)(URLTestCaseBase)
     863
     864
     865class URLPrefixTests(URLTestCaseBase):
     866    """
     867    Tests if the `i18n_patterns` is adding the prefix correctly.
     868    """
     869    def test_not_prefixed(self):
     870        with translation.override('en'):
     871            self.assertEqual(reverse('not-prefixed'), '/not-prefixed/')
     872        with translation.override('nl'):
     873            self.assertEqual(reverse('not-prefixed'), '/not-prefixed/')
     874
     875    def test_prefixed(self):
     876        with translation.override('en'):
     877            self.assertEqual(reverse('prefixed'), '/en/prefixed/')
     878        with translation.override('nl'):
     879            self.assertEqual(reverse('prefixed'), '/nl/prefixed/')
     880   
     881    @override_settings(ROOT_URLCONF='regressiontests.i18n.wrong_urls')
     882    def test_invalid_prefix_use(self):
     883        self.assertRaises(ImproperlyConfigured, lambda: reverse('account:register'))
     884
     885
     886class URLTranslationTests(URLTestCaseBase):
     887    """
     888    Tests if the pattern-strings are translated correctly (within the
     889    `i18n_patterns` and the normal `patterns` function).
     890    """
     891    def test_no_prefix_translated(self):
     892        with translation.override('en'):
     893            self.assertEqual(reverse('no-prefix-translated'), '/translated/')
     894
     895        with translation.override('nl'):
     896            self.assertEqual(reverse('no-prefix-translated'), '/vertaald/')
     897
     898        with translation.override('pt-br'):
     899            self.assertEqual(reverse('no-prefix-translated'), '/traduzidos/')
     900
     901    def test_users_url(self):
     902        with translation.override('en'):
     903            self.assertEqual(reverse('users'), '/en/users/')
     904
     905        with translation.override('nl'):
     906            self.assertEqual(reverse('users'), '/nl/gebruikers/')
     907
     908        with translation.override('pt-br'):
     909            self.assertEqual(reverse('users'), '/pt-br/usuarios/')
     910
     911
     912class URLNamespaceTests(URLTestCaseBase):
     913    """
     914    Tests if the translations are still working within namespaces.
     915    """
     916    def test_account_register(self):
     917        with translation.override('en'):
     918            self.assertEqual(reverse('account:register'), '/en/account/register/')
     919
     920        with translation.override('nl'):
     921            self.assertEqual(reverse('account:register'), '/nl/profiel/registeren/')
     922
     923
     924class URLRedirectTests(URLTestCaseBase):
     925    """
     926    Tests if the user gets redirected to the right URL when there is no
     927    language-prefix in the request URL.
     928    """
     929    def test_no_prefix_response(self):
     930        response = self.client.get('/not-prefixed/')
     931        self.assertEqual(response.status_code, 200)
     932
     933    def test_en_redirect(self):
     934        response = self.client.get('/account/register/', HTTP_ACCEPT_LANGUAGE='en')
     935        self.assertRedirects(response, 'http://testserver/en/account/register/')
     936
     937        response = self.client.get(response['location'])
     938        self.assertEqual(response.status_code, 200)
     939
     940    def test_en_redirect_wrong_url(self):
     941        response = self.client.get('/profiel/registeren/', HTTP_ACCEPT_LANGUAGE='en')
     942        self.assertEqual(response.status_code, 302)
     943        self.assertEqual(response['location'], 'http://testserver/en/profiel/registeren/')
     944
     945        response = self.client.get(response['location'])
     946        self.assertEqual(response.status_code, 404)
     947
     948    def test_nl_redirect(self):
     949        response = self.client.get('/profiel/registeren/', HTTP_ACCEPT_LANGUAGE='nl')
     950        self.assertRedirects(response, 'http://testserver/nl/profiel/registeren/')
     951
     952        response = self.client.get(response['location'])
     953        self.assertEqual(response.status_code, 200)
     954
     955    def test_nl_redirect_wrong_url(self):
     956        response = self.client.get('/account/register/', HTTP_ACCEPT_LANGUAGE='nl')
     957        self.assertEqual(response.status_code, 302)
     958        self.assertEqual(response['location'], 'http://testserver/nl/account/register/')
     959
     960        response = self.client.get(response['location'])
     961        self.assertEqual(response.status_code, 404)
     962
     963    def test_pt_br_redirect(self):
     964        response = self.client.get('/conta/registre-se/', HTTP_ACCEPT_LANGUAGE='pt-br')
     965        self.assertRedirects(response, 'http://testserver/pt-br/conta/registre-se/')
     966
     967        response = self.client.get(response['location'])
     968        self.assertEqual(response.status_code, 200)
     969
     970
     971class URLRedirectWithoutTrailingSlashTests(URLTestCaseBase):
     972    """
     973    Tests the redirect when the requested URL doesn't end with a slash
     974    (`settings.APPEND_SLASH=True`).
     975    """
     976    def test_not_prefixed_redirect(self):
     977        response = self.client.get('/not-prefixed', HTTP_ACCEPT_LANGUAGE='en')
     978        self.assertEqual(response.status_code, 301)
     979        self.assertEqual(response['location'], 'http://testserver/not-prefixed/')
     980
     981    def test_en_redirect(self):
     982        response = self.client.get('/account/register', HTTP_ACCEPT_LANGUAGE='en')
     983        self.assertEqual(response.status_code, 302)
     984        self.assertEqual(response['location'], 'http://testserver/en/account/register')
     985
     986        response = self.client.get(response['location'])
     987        self.assertEqual(response.status_code, 301)
     988        self.assertEqual(response['location'], 'http://testserver/en/account/register/')
     989
     990
     991class URLRedirectWithoutTrailingSlashSettingTests(URLTestCaseBase):
     992    """
     993    Tests the redirect when the requested URL doesn't end with a slash
     994    (`settings.APPEND_SLASH=False`).
     995    """
     996    @override_settings(APPEND_SLASH=False)
     997    def test_not_prefixed_redirect(self):
     998        response = self.client.get('/not-prefixed', HTTP_ACCEPT_LANGUAGE='en')
     999        self.assertEqual(response.status_code, 302)
     1000        self.assertEqual(response['location'], 'http://testserver/en/not-prefixed')
     1001
     1002        response = self.client.get(response['location'])
     1003        self.assertEqual(response.status_code, 404)
     1004
     1005    @override_settings(APPEND_SLASH=False)
     1006    def test_en_redirect(self):
     1007        response = self.client.get('/account/register', HTTP_ACCEPT_LANGUAGE='en')
     1008        self.assertEqual(response.status_code, 302)
     1009        self.assertEqual(response['location'], 'http://testserver/en/account/register')
     1010
     1011        response = self.client.get(response['location'])
     1012        self.assertEqual(response.status_code, 404)
     1013
     1014
     1015class URLResponseTests(URLTestCaseBase):
     1016    """
     1017    Tests if the response has the right language-code.
     1018    """
     1019    def test_not_prefixed_with_prefix(self):
     1020        response = self.client.get('/en/not-prefixed/')
     1021        self.assertEqual(response.status_code, 404)
     1022
     1023    def test_en_url(self):
     1024        response = self.client.get('/en/account/register/')
     1025        self.assertEqual(response.status_code, 200)
     1026        self.assertEqual(response['content-language'], 'en')
     1027        self.assertEqual(response.context['LANGUAGE_CODE'], 'en')
     1028
     1029    def test_nl_url(self):
     1030        response = self.client.get('/nl/profiel/registeren/')
     1031        self.assertEqual(response.status_code, 200)
     1032        self.assertEqual(response['content-language'], 'nl')
     1033        self.assertEqual(response.context['LANGUAGE_CODE'], 'nl')
     1034
     1035    def test_wrong_en_prefix(self):
     1036        response = self.client.get('/en/profiel/registeren/')
     1037        self.assertEqual(response.status_code, 404)
     1038
     1039    def test_wrong_nl_prefix(self):
     1040        response = self.client.get('/nl/account/register/')
     1041        self.assertEqual(response.status_code, 404)
     1042
     1043    def test_pt_br_url(self):
     1044        response = self.client.get('/pt-br/conta/registre-se/')
     1045        self.assertEqual(response.status_code, 200)
     1046        self.assertEqual(response['content-language'], 'pt-br')
     1047        self.assertEqual(response.context['LANGUAGE_CODE'], 'pt-br')
  • tests/regressiontests/i18n/urls.py

     
     1from django.conf.urls.defaults import patterns, i18n_patterns, include, url
     2from django.utils.translation import ugettext_lazy as _
     3from django.views.generic import TemplateView
     4
     5
     6view = TemplateView.as_view(template_name='dummy.html')
     7
     8urlpatterns = patterns('',
     9    url(r'^not-prefixed/$', view, name='not-prefixed'),
     10    url(_(r'^translated/$'), view, name='no-prefix-translated'),
     11)
     12
     13urlpatterns += i18n_patterns('',
     14    url(r'^prefixed/$', view, name='prefixed'),
     15    url(_(r'^users/$'), view, name='users'),
     16    url(_(r'^account/'), include('regressiontests.i18n.urls_namespace', namespace='account')),
     17)
  • tests/regressiontests/i18n/test_locale/en/LC_MESSAGES/django.po

     
     1# SOME DESCRIPTIVE TITLE.
     2# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
     3# This file is distributed under the same license as the PACKAGE package.
     4# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
     5#
     6#, fuzzy
     7msgid ""
     8msgstr ""
     9"Project-Id-Version: PACKAGE VERSION\n"
     10"Report-Msgid-Bugs-To: \n"
     11"POT-Creation-Date: 2011-06-10 11:53+0200\n"
     12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
     13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
     14"Language-Team: LANGUAGE <LL@li.org>\n"
     15"Language: \n"
     16"MIME-Version: 1.0\n"
     17"Content-Type: text/plain; charset=UTF-8\n"
     18"Content-Transfer-Encoding: 8bit\n"
     19
     20#: urls.py:10
     21msgid "^translated/$"
     22msgstr "^translated/$"
     23
     24#: urls.py:15
     25msgid "^users/$"
     26msgstr "^users/$"
     27
     28#: urls.py:16
     29msgid "^account/"
     30msgstr "^account/"
     31
     32#: urls_namespace.py:9
     33msgid "^register/$"
     34msgstr "^register/$"
  • tests/regressiontests/i18n/test_locale/pt_BR/LC_MESSAGES/django.po

    Cannot display: file marked as a binary type.
    svn:mime-type = application/octet-stream
    
    Property changes on: tests/regressiontests/i18n/test_locale/en/LC_MESSAGES/django.mo
    ___________________________________________________________________
    Added: svn:mime-type
       + application/octet-stream
    
     
     1# SOME DESCRIPTIVE TITLE.
     2# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
     3# This file is distributed under the same license as the PACKAGE package.
     4# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
     5#
     6#, fuzzy
     7msgid ""
     8msgstr ""
     9"Project-Id-Version: PACKAGE VERSION\n"
     10"Report-Msgid-Bugs-To: \n"
     11"POT-Creation-Date: 2011-06-10 13:56+0200\n"
     12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
     13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
     14"Language-Team: LANGUAGE <LL@li.org>\n"
     15"Language: \n"
     16"MIME-Version: 1.0\n"
     17"Content-Type: text/plain; charset=UTF-8\n"
     18"Content-Transfer-Encoding: 8bit\n"
     19"Plural-Forms: nplurals=2; plural=(n > 1)\n"
     20
     21#: urls.py:10
     22msgid "^translated/$"
     23msgstr "^traduzidos/$"
     24
     25#: urls.py:15
     26msgid "^users/$"
     27msgstr "^usuarios/$"
     28
     29#: urls.py:16 wrong_urls.py:6
     30msgid "^account/"
     31msgstr "^conta/"
     32
     33#: urls_namespace.py:9 wrong_urls_namespace.py:9
     34msgid "^register/$"
     35msgstr "^registre-se/$"
  • tests/regressiontests/i18n/test_locale/nl/LC_MESSAGES/django.po

    Cannot display: file marked as a binary type.
    svn:mime-type = application/octet-stream
    
    Property changes on: tests/regressiontests/i18n/test_locale/pt_BR/LC_MESSAGES/django.mo
    ___________________________________________________________________
    Added: svn:mime-type
       + application/octet-stream
    
     
     1# SOME DESCRIPTIVE TITLE.
     2# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
     3# This file is distributed under the same license as the PACKAGE package.
     4# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
     5#
     6#, fuzzy
     7msgid ""
     8msgstr ""
     9"Project-Id-Version: PACKAGE VERSION\n"
     10"Report-Msgid-Bugs-To: \n"
     11"POT-Creation-Date: 2011-06-10 11:53+0200\n"
     12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
     13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
     14"Language-Team: LANGUAGE <LL@li.org>\n"
     15"Language: \n"
     16"MIME-Version: 1.0\n"
     17"Content-Type: text/plain; charset=UTF-8\n"
     18"Content-Transfer-Encoding: 8bit\n"
     19"Plural-Forms: nplurals=2; plural=(n != 1)\n"
     20
     21#: urls.py:10
     22msgid "^translated/$"
     23msgstr "^vertaald/$"
     24
     25#: urls.py:15
     26msgid "^users/$"
     27msgstr "^gebruikers/$"
     28
     29#: urls.py:16
     30msgid "^account/"
     31msgstr "^profiel/"
     32
     33#: urls_namespace.py:9
     34msgid "^register/$"
     35msgstr "^registeren/$"
Back to Top