Fixed #11585 -- Added ability to translate and prefix URL patterns with a language code as an alternative method for language discovery. Many thanks to Orne Brocaar for his initial work and Carl Meyer for feedback.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@16405 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
62bb4b8c37
commit
896e3c69c7
1
AUTHORS
1
AUTHORS
|
@ -94,6 +94,7 @@ answer newbie questions, and generally made Django that much better:
|
|||
Sean Brant
|
||||
Andrew Brehaut <http://brehaut.net/blog>
|
||||
David Brenneman <http://davidbrenneman.com>
|
||||
Orne Brocaar <http://brocaar.com/>
|
||||
brut.alll@gmail.com
|
||||
bthomas
|
||||
btoll@bestweb.net
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
from django.core.urlresolvers import RegexURLPattern, RegexURLResolver
|
||||
from django.core.urlresolvers import (RegexURLPattern,
|
||||
RegexURLResolver, LocaleRegexURLResolver)
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.utils.importlib import import_module
|
||||
|
||||
|
||||
__all__ = ['handler404', 'handler500', 'include', 'patterns', 'url']
|
||||
|
||||
|
@ -15,6 +18,21 @@ def include(arg, namespace=None, app_name=None):
|
|||
else:
|
||||
# No namespace hint - use manually provided namespace
|
||||
urlconf_module = arg
|
||||
|
||||
if isinstance(urlconf_module, basestring):
|
||||
urlconf_module = import_module(urlconf_module)
|
||||
patterns = getattr(urlconf_module, 'urlpatterns', urlconf_module)
|
||||
|
||||
# Make sure we can iterate through the patterns (without this, some
|
||||
# testcases will break).
|
||||
if isinstance(patterns, (list, tuple)):
|
||||
for url_pattern in patterns:
|
||||
# Test if the LocaleRegexURLResolver is used within the include;
|
||||
# this should throw an error since this is not allowed!
|
||||
if isinstance(url_pattern, LocaleRegexURLResolver):
|
||||
raise ImproperlyConfigured(
|
||||
'Using i18n_patterns in an included URLconf is not allowed.')
|
||||
|
||||
return (urlconf_module, app_name, namespace)
|
||||
|
||||
def patterns(prefix, *args):
|
||||
|
|
|
@ -1,4 +1,19 @@
|
|||
from django.conf.urls.defaults import *
|
||||
from django.conf import settings
|
||||
from django.conf.urls.defaults import patterns
|
||||
from django.core.urlresolvers import LocaleRegexURLResolver
|
||||
|
||||
def i18n_patterns(prefix, *args):
|
||||
"""
|
||||
Adds the language code prefix to every URL pattern within this
|
||||
function. This may only be used in the root URLconf, not in an included
|
||||
URLconf.
|
||||
|
||||
"""
|
||||
pattern_list = patterns(prefix, *args)
|
||||
if not settings.USE_I18N:
|
||||
return pattern_list
|
||||
return [LocaleRegexURLResolver(pattern_list)]
|
||||
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^setlang/$', 'django.views.i18n.set_language'),
|
||||
|
|
|
@ -346,12 +346,12 @@ def extract_views_from_urlpatterns(urlpatterns, base=''):
|
|||
"""
|
||||
views = []
|
||||
for p in urlpatterns:
|
||||
if hasattr(p, '_get_callback'):
|
||||
if hasattr(p, 'callback'):
|
||||
try:
|
||||
views.append((p._get_callback(), base + p.regex.pattern))
|
||||
views.append((p.callback, base + p.regex.pattern))
|
||||
except ViewDoesNotExist:
|
||||
continue
|
||||
elif hasattr(p, '_get_url_patterns'):
|
||||
elif hasattr(p, 'url_patterns'):
|
||||
try:
|
||||
patterns = p.url_patterns
|
||||
except ImportError:
|
||||
|
|
|
@ -11,13 +11,14 @@ import re
|
|||
from threading import local
|
||||
|
||||
from django.http import Http404
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist
|
||||
from django.utils.datastructures import MultiValueDict
|
||||
from django.utils.encoding import iri_to_uri, force_unicode, smart_str
|
||||
from django.utils.functional import memoize, lazy
|
||||
from django.utils.importlib import import_module
|
||||
from django.utils.regex_helper import normalize
|
||||
from django.utils.translation import get_language
|
||||
|
||||
|
||||
_resolver_cache = {} # Maps URLconf modules to RegexURLResolver instances.
|
||||
_callable_cache = {} # Maps view and url pattern names to their view functions.
|
||||
|
@ -50,13 +51,13 @@ class ResolverMatch(object):
|
|||
url_name = '.'.join([func.__module__, func.__name__])
|
||||
self.url_name = url_name
|
||||
|
||||
@property
|
||||
def namespace(self):
|
||||
return ':'.join(self.namespaces)
|
||||
namespace = property(namespace)
|
||||
|
||||
@property
|
||||
def view_name(self):
|
||||
return ':'.join([ x for x in [ self.namespace, self.url_name ] if x ])
|
||||
view_name = property(view_name)
|
||||
|
||||
def __getitem__(self, index):
|
||||
return (self.func, self.args, self.kwargs)[index]
|
||||
|
@ -115,13 +116,43 @@ def get_mod_func(callback):
|
|||
return callback, ''
|
||||
return callback[:dot], callback[dot+1:]
|
||||
|
||||
class RegexURLPattern(object):
|
||||
class LocaleRegexProvider(object):
|
||||
"""
|
||||
A mixin to provide a default regex property which can vary by active
|
||||
language.
|
||||
|
||||
"""
|
||||
def __init__(self, regex):
|
||||
# regex is either a string representing a regular expression, or a
|
||||
# translatable string (using ugettext_lazy) representing a regular
|
||||
# expression.
|
||||
self._regex = regex
|
||||
self._regex_dict = {}
|
||||
|
||||
|
||||
@property
|
||||
def regex(self):
|
||||
"""
|
||||
Returns a compiled regular expression, depending upon the activated
|
||||
language-code.
|
||||
"""
|
||||
language_code = get_language()
|
||||
if language_code not in self._regex_dict:
|
||||
if isinstance(self._regex, basestring):
|
||||
compiled_regex = re.compile(self._regex, re.UNICODE)
|
||||
else:
|
||||
regex = force_unicode(self._regex)
|
||||
compiled_regex = re.compile(regex, re.UNICODE)
|
||||
self._regex_dict[language_code] = compiled_regex
|
||||
return self._regex_dict[language_code]
|
||||
|
||||
|
||||
class RegexURLPattern(LocaleRegexProvider):
|
||||
def __init__(self, regex, callback, default_args=None, name=None):
|
||||
# regex is a string representing a regular expression.
|
||||
LocaleRegexProvider.__init__(self, regex)
|
||||
# callback is either a string like 'foo.views.news.stories.story_detail'
|
||||
# which represents the path to a module and a view function name, or a
|
||||
# callable object (view).
|
||||
self.regex = re.compile(regex, re.UNICODE)
|
||||
if callable(callback):
|
||||
self._callback = callback
|
||||
else:
|
||||
|
@ -157,7 +188,8 @@ class RegexURLPattern(object):
|
|||
|
||||
return ResolverMatch(self.callback, args, kwargs, self.name)
|
||||
|
||||
def _get_callback(self):
|
||||
@property
|
||||
def callback(self):
|
||||
if self._callback is not None:
|
||||
return self._callback
|
||||
try:
|
||||
|
@ -169,13 +201,11 @@ class RegexURLPattern(object):
|
|||
mod_name, func_name = get_mod_func(self._callback_str)
|
||||
raise ViewDoesNotExist("Tried %s in module %s. Error was: %s" % (func_name, mod_name, str(e)))
|
||||
return self._callback
|
||||
callback = property(_get_callback)
|
||||
|
||||
class RegexURLResolver(object):
|
||||
class RegexURLResolver(LocaleRegexProvider):
|
||||
def __init__(self, regex, urlconf_name, default_kwargs=None, app_name=None, namespace=None):
|
||||
# regex is a string representing a regular expression.
|
||||
LocaleRegexProvider.__init__(self, regex)
|
||||
# urlconf_name is a string representing the module containing URLconfs.
|
||||
self.regex = re.compile(regex, re.UNICODE)
|
||||
self.urlconf_name = urlconf_name
|
||||
if not isinstance(urlconf_name, basestring):
|
||||
self._urlconf_module = self.urlconf_name
|
||||
|
@ -183,9 +213,9 @@ class RegexURLResolver(object):
|
|||
self.default_kwargs = default_kwargs or {}
|
||||
self.namespace = namespace
|
||||
self.app_name = app_name
|
||||
self._reverse_dict = None
|
||||
self._namespace_dict = None
|
||||
self._app_dict = None
|
||||
self._reverse_dict = {}
|
||||
self._namespace_dict = {}
|
||||
self._app_dict = {}
|
||||
|
||||
def __repr__(self):
|
||||
return smart_str(u'<%s %s (%s:%s) %s>' % (self.__class__.__name__, self.urlconf_name, self.app_name, self.namespace, self.regex.pattern))
|
||||
|
@ -194,6 +224,7 @@ class RegexURLResolver(object):
|
|||
lookups = MultiValueDict()
|
||||
namespaces = {}
|
||||
apps = {}
|
||||
language_code = get_language()
|
||||
for pattern in reversed(self.url_patterns):
|
||||
p_pattern = pattern.regex.pattern
|
||||
if p_pattern.startswith('^'):
|
||||
|
@ -220,27 +251,30 @@ class RegexURLResolver(object):
|
|||
lookups.appendlist(pattern.callback, (bits, p_pattern, pattern.default_args))
|
||||
if pattern.name is not None:
|
||||
lookups.appendlist(pattern.name, (bits, p_pattern, pattern.default_args))
|
||||
self._reverse_dict = lookups
|
||||
self._namespace_dict = namespaces
|
||||
self._app_dict = apps
|
||||
self._reverse_dict[language_code] = lookups
|
||||
self._namespace_dict[language_code] = namespaces
|
||||
self._app_dict[language_code] = apps
|
||||
|
||||
def _get_reverse_dict(self):
|
||||
if self._reverse_dict is None:
|
||||
@property
|
||||
def reverse_dict(self):
|
||||
language_code = get_language()
|
||||
if language_code not in self._reverse_dict:
|
||||
self._populate()
|
||||
return self._reverse_dict
|
||||
reverse_dict = property(_get_reverse_dict)
|
||||
return self._reverse_dict[language_code]
|
||||
|
||||
def _get_namespace_dict(self):
|
||||
if self._namespace_dict is None:
|
||||
@property
|
||||
def namespace_dict(self):
|
||||
language_code = get_language()
|
||||
if language_code not in self._namespace_dict:
|
||||
self._populate()
|
||||
return self._namespace_dict
|
||||
namespace_dict = property(_get_namespace_dict)
|
||||
return self._namespace_dict[language_code]
|
||||
|
||||
def _get_app_dict(self):
|
||||
if self._app_dict is None:
|
||||
@property
|
||||
def app_dict(self):
|
||||
language_code = get_language()
|
||||
if language_code not in self._app_dict:
|
||||
self._populate()
|
||||
return self._app_dict
|
||||
app_dict = property(_get_app_dict)
|
||||
return self._app_dict[language_code]
|
||||
|
||||
def resolve(self, path):
|
||||
tried = []
|
||||
|
@ -267,22 +301,22 @@ class RegexURLResolver(object):
|
|||
raise Resolver404({'tried': tried, 'path': new_path})
|
||||
raise Resolver404({'path' : path})
|
||||
|
||||
def _get_urlconf_module(self):
|
||||
@property
|
||||
def urlconf_module(self):
|
||||
try:
|
||||
return self._urlconf_module
|
||||
except AttributeError:
|
||||
self._urlconf_module = import_module(self.urlconf_name)
|
||||
return self._urlconf_module
|
||||
urlconf_module = property(_get_urlconf_module)
|
||||
|
||||
def _get_url_patterns(self):
|
||||
@property
|
||||
def url_patterns(self):
|
||||
patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module)
|
||||
try:
|
||||
iter(patterns)
|
||||
except TypeError:
|
||||
raise ImproperlyConfigured("The included urlconf %s doesn't have any patterns in it" % self.urlconf_name)
|
||||
return patterns
|
||||
url_patterns = property(_get_url_patterns)
|
||||
|
||||
def _resolve_special(self, view_type):
|
||||
callback = getattr(self.urlconf_module, 'handler%s' % view_type, None)
|
||||
|
@ -343,6 +377,25 @@ class RegexURLResolver(object):
|
|||
raise NoReverseMatch("Reverse for '%s' with arguments '%s' and keyword "
|
||||
"arguments '%s' not found." % (lookup_view_s, args, kwargs))
|
||||
|
||||
class LocaleRegexURLResolver(RegexURLResolver):
|
||||
"""
|
||||
A URL resolver that always matches the active language code as URL prefix.
|
||||
|
||||
Rather than taking a regex argument, we just override the ``regex``
|
||||
function to always return the active language-code as regex.
|
||||
"""
|
||||
def __init__(self, urlconf_name, default_kwargs=None, app_name=None, namespace=None):
|
||||
super(LocaleRegexURLResolver, self).__init__(
|
||||
None, urlconf_name, default_kwargs, app_name, namespace)
|
||||
|
||||
@property
|
||||
def regex(self):
|
||||
language_code = get_language()
|
||||
if language_code not in self._regex_dict:
|
||||
regex_compiled = re.compile('^%s/' % language_code, re.UNICODE)
|
||||
self._regex_dict[language_code] = regex_compiled
|
||||
return self._regex_dict[language_code]
|
||||
|
||||
def resolve(path, urlconf=None):
|
||||
if urlconf is None:
|
||||
urlconf = get_urlconf()
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
"this is the locale selecting middleware that will look at accept headers"
|
||||
"This is the locale selecting middleware that will look at accept headers"
|
||||
|
||||
from django.core.urlresolvers import get_resolver, LocaleRegexURLResolver
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.utils.cache import patch_vary_headers
|
||||
from django.utils import translation
|
||||
|
||||
|
@ -18,8 +20,26 @@ class LocaleMiddleware(object):
|
|||
request.LANGUAGE_CODE = translation.get_language()
|
||||
|
||||
def process_response(self, request, response):
|
||||
language = translation.get_language()
|
||||
translation.deactivate()
|
||||
|
||||
if (response.status_code == 404 and
|
||||
not translation.get_language_from_path(request.path_info)
|
||||
and self.is_language_prefix_patterns_used()):
|
||||
return HttpResponseRedirect(
|
||||
'/%s%s' % (language, request.get_full_path()))
|
||||
|
||||
patch_vary_headers(response, ('Accept-Language',))
|
||||
if 'Content-Language' not in response:
|
||||
response['Content-Language'] = translation.get_language()
|
||||
translation.deactivate()
|
||||
response['Content-Language'] = language
|
||||
return response
|
||||
|
||||
def is_language_prefix_patterns_used(self):
|
||||
"""
|
||||
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:
|
||||
if isinstance(url_pattern, LocaleRegexURLResolver):
|
||||
return True
|
||||
return False
|
||||
|
|
|
@ -144,6 +144,9 @@ def to_locale(language):
|
|||
def get_language_from_request(request):
|
||||
return _trans.get_language_from_request(request)
|
||||
|
||||
def get_language_from_path(path):
|
||||
return _trans.get_language_from_path(path)
|
||||
|
||||
def templatize(src, origin=None):
|
||||
return _trans.templatize(src, origin)
|
||||
|
||||
|
|
|
@ -58,3 +58,7 @@ def to_locale(language):
|
|||
|
||||
def get_language_from_request(request):
|
||||
return settings.LANGUAGE_CODE
|
||||
|
||||
def get_language_from_path(request):
|
||||
return None
|
||||
|
||||
|
|
|
@ -35,6 +35,8 @@ accept_language_re = re.compile(r'''
|
|||
(?:\s*,\s*|$) # Multiple accepts per header.
|
||||
''', re.VERBOSE)
|
||||
|
||||
language_code_prefix_re = re.compile(r'^/([\w-]+)/')
|
||||
|
||||
def to_locale(language, to_lower=False):
|
||||
"""
|
||||
Turns a language name (en-us) into a locale name (en_US). If 'to_lower' is
|
||||
|
@ -336,14 +338,28 @@ def check_for_language(lang_code):
|
|||
"""
|
||||
Checks whether there is a global language file for the given language
|
||||
code. This is used to decide whether a user-provided language is
|
||||
available. This is only used for language codes from either the cookies or
|
||||
session and during format localization.
|
||||
available. This is only used for language codes from either the cookies
|
||||
or session and during format localization.
|
||||
"""
|
||||
for path in all_locale_paths():
|
||||
if gettext_module.find('django', path, [to_locale(lang_code)]) is not None:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_language_from_path(path, supported=None):
|
||||
"""
|
||||
Returns the language-code if there is a valid language-code
|
||||
found in the `path`.
|
||||
"""
|
||||
if supported is None:
|
||||
from django.conf import settings
|
||||
supported = dict(settings.LANGUAGES)
|
||||
regex_match = language_code_prefix_re.match(path)
|
||||
if regex_match:
|
||||
lang_code = regex_match.group(1)
|
||||
if lang_code in supported and check_for_language(lang_code):
|
||||
return lang_code
|
||||
|
||||
def get_language_from_request(request):
|
||||
"""
|
||||
Analyzes the request to find what language the user wants the system to
|
||||
|
@ -355,6 +371,10 @@ def get_language_from_request(request):
|
|||
from django.conf import settings
|
||||
supported = dict(settings.LANGUAGES)
|
||||
|
||||
lang_code = get_language_from_path(request.path_info, supported)
|
||||
if lang_code is not None:
|
||||
return lang_code
|
||||
|
||||
if hasattr(request, 'session'):
|
||||
lang_code = request.session.get('django_language', None)
|
||||
if lang_code in supported and lang_code is not None and check_for_language(lang_code):
|
||||
|
|
|
@ -167,6 +167,16 @@ a :class:`~django.forms.fields.GenericIPAddressField` form field and
|
|||
the validators :data:`~django.core.validators.validate_ipv46_address` and
|
||||
:data:`~django.core.validators.validate_ipv6_address`
|
||||
|
||||
Translating URL patterns
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Django 1.4 gained the ability to look for a language prefix in the URL pattern
|
||||
when using the new :func:`django.conf.urls.i18n.i18n_patterns` helper function.
|
||||
Additionally, it's now possible to define translatable URL patterns using
|
||||
:func:`~django.utils.translation.ugettext_lazy`. See
|
||||
:ref:`url-internationalization` for more information about the language prefix
|
||||
and how to internationalize URL patterns.
|
||||
|
||||
Minor features
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -59,7 +59,9 @@ matters, you should follow these guidelines:
|
|||
|
||||
* Make sure it's one of the first middlewares installed.
|
||||
* It should come after ``SessionMiddleware``, because ``LocaleMiddleware``
|
||||
makes use of session data.
|
||||
makes use of session data. And it should come before ``CommonMiddleware``
|
||||
because ``CommonMiddleware`` needs an activated language in order
|
||||
to resolve the requested URL.
|
||||
* If you use ``CacheMiddleware``, put ``LocaleMiddleware`` after it.
|
||||
|
||||
For example, your :setting:`MIDDLEWARE_CLASSES` might look like this::
|
||||
|
@ -76,8 +78,15 @@ For example, your :setting:`MIDDLEWARE_CLASSES` might look like this::
|
|||
``LocaleMiddleware`` tries to determine the user's language preference by
|
||||
following this algorithm:
|
||||
|
||||
* First, it looks for a ``django_language`` key in the current user's
|
||||
session.
|
||||
.. versionchanged:: 1.4
|
||||
|
||||
* First, it looks for the language prefix in the requested URL. This is
|
||||
only performed when you are using the ``i18n_patterns`` function in your
|
||||
root URLconf. See :ref:`url-internationalization` for more information
|
||||
about the language prefix and how to internationalize URL patterns.
|
||||
|
||||
* Failing that, it looks for a ``django_language`` key in the current
|
||||
user's session.
|
||||
|
||||
* Failing that, it looks for a cookie.
|
||||
|
||||
|
|
|
@ -753,6 +753,138 @@ This isn't as fast as string interpolation in Python, so keep it to those
|
|||
cases where you really need it (for example, in conjunction with ``ngettext``
|
||||
to produce proper pluralizations).
|
||||
|
||||
.. _url-internationalization:
|
||||
|
||||
Specifying translation strings: In URL patterns
|
||||
===============================================
|
||||
|
||||
.. versionadded:: 1.4
|
||||
|
||||
.. module:: django.conf.urls.i18n
|
||||
|
||||
Django provides two mechanisms to internationalize URL patterns:
|
||||
|
||||
* Adding the language prefix to the root of the URL patterns to make it
|
||||
possible for :class:`~django.middleware.locale.LocaleMiddleware` to detect
|
||||
the language to activate from the requested URL.
|
||||
|
||||
* Making URL patterns themselves translatable via the
|
||||
:func:`django.utils.translation.ugettext_lazy()` function.
|
||||
|
||||
.. warning::
|
||||
|
||||
Using either one of these features requires that an active language be set
|
||||
for each request; in other words, you need to have
|
||||
:class:`django.middleware.locale.LocaleMiddleware` in your
|
||||
:setting:`MIDDLEWARE_CLASSES` setting.
|
||||
|
||||
Language prefix in URL patterns
|
||||
-------------------------------
|
||||
|
||||
.. function:: i18n_patterns(prefix, pattern_description, ...)
|
||||
|
||||
This function can be used in your root URLconf as a replacement for the normal
|
||||
:func:`django.conf.urls.defaults.patterns` function. 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::
|
||||
|
||||
from django.conf.urls.defaults import patterns, include, url
|
||||
from django.conf.urls.i18n import i18n_patterns
|
||||
|
||||
urlpatterns = patterns(''
|
||||
url(r'^sitemap\.xml$', 'sitemap.view', name='sitemap_xml'),
|
||||
)
|
||||
|
||||
news_patterns = patterns(''
|
||||
url(r'^$', 'news.views.index', name='index'),
|
||||
url(r'^category/(?P<slug>[\w-]+)/$', 'news.views.category', name='category'),
|
||||
url(r'^(?P<slug>[\w-]+)/$', 'news.views.details', name='detail'),
|
||||
)
|
||||
|
||||
urlpatterns += i18n_patterns('',
|
||||
url(r'^about/$', 'about.view', name='about'),
|
||||
url(r'^news/$', include(news_patterns, namespace='news')),
|
||||
)
|
||||
|
||||
|
||||
After defining these URL patterns, Django will automatically add the
|
||||
language prefix to the URL patterns that were added by the ``i18n_patterns``
|
||||
function. Example::
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import activate
|
||||
|
||||
>>> activate('en')
|
||||
>>> reverse('sitemap_xml')
|
||||
'/sitemap.xml'
|
||||
>>> reverse('news:index')
|
||||
'/en/news/'
|
||||
|
||||
>>> activate('nl')
|
||||
>>> reverse('news:detail', kwargs={'slug': 'news-slug'})
|
||||
'/nl/news/news-slug/'
|
||||
|
||||
.. warning::
|
||||
|
||||
:func:`~django.conf.urls.i18n.i18n_patterns` is only allowed in your root
|
||||
URLconf. Using it within an included URLconf will throw an
|
||||
:exc:`ImproperlyConfigured` exception.
|
||||
|
||||
.. warning::
|
||||
|
||||
Ensure that you don't have non-prefixed URL patterns that might collide
|
||||
with an automatically-added language prefix.
|
||||
|
||||
|
||||
Translating URL patterns
|
||||
------------------------
|
||||
|
||||
URL patterns can also be marked translatable using the
|
||||
:func:`~django.utils.translation.ugettext_lazy` function. Example::
|
||||
|
||||
from django.conf.urls.defaults import patterns, include, url
|
||||
from django.conf.urls.i18n import i18n_patterns
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
urlpatterns = patterns(''
|
||||
url(r'^sitemap\.xml$', 'sitemap.view', name='sitemap_xml'),
|
||||
)
|
||||
|
||||
news_patterns = patterns(''
|
||||
url(r'^$', 'news.views.index', name='index'),
|
||||
url(_(r'^category/(?P<slug>[\w-]+)/$'), 'news.views.category', name='category'),
|
||||
url(r'^(?P<slug>[\w-]+)/$', 'news.views.details', name='detail'),
|
||||
)
|
||||
|
||||
urlpatterns += i18n_patterns('',
|
||||
url(_(r'^about/$'), 'about.view', name='about'),
|
||||
url(_(r'^news/$'), include(news_patterns, namespace='news')),
|
||||
)
|
||||
|
||||
|
||||
After you've created the translations (see :doc:`localization` for more
|
||||
information), the :func:`~django.core.urlresolvers.reverse` function will
|
||||
return the URL in the active language. Example::
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import activate
|
||||
|
||||
>>> activate('en')
|
||||
>>> reverse('news:category', kwargs={'slug': 'recent'})
|
||||
'/en/news/category/recent/'
|
||||
|
||||
>>> activate('nl')
|
||||
>>> reverse('news:category', kwargs={'slug': 'recent'})
|
||||
'/nl/nieuws/categorie/recent/'
|
||||
|
||||
.. warning::
|
||||
|
||||
In most cases, it's best to use translated URLs only within a
|
||||
language-code-prefixed block of patterns (using
|
||||
:func:`~django.conf.urls.i18n.i18n_patterns`), to avoid the possibility
|
||||
that a carelessly translated URL causes a collision with a non-translated
|
||||
URL pattern.
|
||||
|
||||
.. _set_language-redirect-view:
|
||||
|
||||
The ``set_language`` redirect view
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,37 @@
|
|||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2011-06-15 11:33+0200\n"
|
||||
"PO-Revision-Date: 2011-06-14 16:16+0100\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: \n"
|
||||
|
||||
#: urls/default.py:11
|
||||
msgid "^translated/$"
|
||||
msgstr "^translated/$"
|
||||
|
||||
#: urls/default.py:12
|
||||
msgid "^translated/(?P<slug>[\\w-]+)/$"
|
||||
msgstr "^translated/(?P<slug>[\\w-]+)/$"
|
||||
|
||||
#: urls/default.py:17
|
||||
msgid "^users/$"
|
||||
msgstr "^users/$"
|
||||
|
||||
#: urls/default.py:18 urls/wrong.py:7
|
||||
msgid "^account/"
|
||||
msgstr "^account/"
|
||||
|
||||
#: urls/namespace.py:9 urls/wrong_namespace.py:10
|
||||
msgid "^register/$"
|
||||
msgstr "^register/$"
|
Binary file not shown.
|
@ -0,0 +1,38 @@
|
|||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2011-06-15 11:33+0200\n"
|
||||
"PO-Revision-Date: 2011-06-14 16:16+0100\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: \n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
|
||||
#: urls/default.py:11
|
||||
msgid "^translated/$"
|
||||
msgstr "^vertaald/$"
|
||||
|
||||
#: urls/default.py:12
|
||||
msgid "^translated/(?P<slug>[\\w-]+)/$"
|
||||
msgstr "^vertaald/(?P<slug>[\\w-]+)/$"
|
||||
|
||||
#: urls/default.py:17
|
||||
msgid "^users/$"
|
||||
msgstr "^gebruikers/$"
|
||||
|
||||
#: urls/default.py:18 urls/wrong.py:7
|
||||
msgid "^account/"
|
||||
msgstr "^profiel/"
|
||||
|
||||
#: urls/namespace.py:9 urls/wrong_namespace.py:10
|
||||
msgid "^register/$"
|
||||
msgstr "^registeren/$"
|
Binary file not shown.
|
@ -0,0 +1,38 @@
|
|||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2011-06-15 11:34+0200\n"
|
||||
"PO-Revision-Date: 2011-06-14 16:17+0100\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: \n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1)\n"
|
||||
|
||||
#: urls/default.py:11
|
||||
msgid "^translated/$"
|
||||
msgstr "^traduzidos/$"
|
||||
|
||||
#: urls/default.py:12
|
||||
msgid "^translated/(?P<slug>[\\w-]+)/$"
|
||||
msgstr "^traduzidos/(?P<slug>[\\w-]+)/$"
|
||||
|
||||
#: urls/default.py:17
|
||||
msgid "^users/$"
|
||||
msgstr "^usuarios/$"
|
||||
|
||||
#: urls/default.py:18 urls/wrong.py:7
|
||||
msgid "^account/"
|
||||
msgstr "^conta/"
|
||||
|
||||
#: urls/namespace.py:9 urls/wrong_namespace.py:10
|
||||
msgid "^register/$"
|
||||
msgstr "^registre-se/$"
|
|
@ -0,0 +1,241 @@
|
|||
import os
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.core.urlresolvers import reverse, clear_url_caches
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.utils import translation
|
||||
|
||||
|
||||
class URLTestCaseBase(TestCase):
|
||||
"""
|
||||
TestCase base-class for the URL tests.
|
||||
"""
|
||||
urls = 'regressiontests.i18n.patterns.urls.default'
|
||||
|
||||
def setUp(self):
|
||||
# Make sure the cache is empty before we are doing our tests.
|
||||
clear_url_caches()
|
||||
|
||||
def tearDown(self):
|
||||
# Make sure we will leave an empty cache for other testcases.
|
||||
clear_url_caches()
|
||||
|
||||
URLTestCaseBase = override_settings(
|
||||
USE_I18N=True,
|
||||
LOCALE_PATHS=(
|
||||
os.path.join(os.path.dirname(__file__), 'locale'),
|
||||
),
|
||||
TEMPLATE_DIRS=(
|
||||
os.path.join(os.path.dirname(__file__), 'templates'),
|
||||
),
|
||||
LANGUAGE_CODE='en',
|
||||
LANGUAGES=(
|
||||
('nl', 'Dutch'),
|
||||
('en', 'English'),
|
||||
('pt-br', 'Brazilian Portuguese'),
|
||||
),
|
||||
MIDDLEWARE_CLASSES=(
|
||||
'django.middleware.locale.LocaleMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
),
|
||||
)(URLTestCaseBase)
|
||||
|
||||
|
||||
class URLPrefixTests(URLTestCaseBase):
|
||||
"""
|
||||
Tests if the `i18n_patterns` is adding the prefix correctly.
|
||||
"""
|
||||
def test_not_prefixed(self):
|
||||
with translation.override('en'):
|
||||
self.assertEqual(reverse('not-prefixed'), '/not-prefixed/')
|
||||
with translation.override('nl'):
|
||||
self.assertEqual(reverse('not-prefixed'), '/not-prefixed/')
|
||||
|
||||
def test_prefixed(self):
|
||||
with translation.override('en'):
|
||||
self.assertEqual(reverse('prefixed'), '/en/prefixed/')
|
||||
with translation.override('nl'):
|
||||
self.assertEqual(reverse('prefixed'), '/nl/prefixed/')
|
||||
|
||||
@override_settings(ROOT_URLCONF='regressiontests.i18n.patterns.urls.wrong')
|
||||
def test_invalid_prefix_use(self):
|
||||
self.assertRaises(ImproperlyConfigured, lambda: reverse('account:register'))
|
||||
|
||||
|
||||
class URLDisabledTests(URLTestCaseBase):
|
||||
urls = 'regressiontests.i18n.patterns.urls.disabled'
|
||||
|
||||
@override_settings(USE_I18N=False)
|
||||
def test_prefixed_i18n_disabled(self):
|
||||
with translation.override('en'):
|
||||
self.assertEqual(reverse('prefixed'), '/prefixed/')
|
||||
with translation.override('nl'):
|
||||
self.assertEqual(reverse('prefixed'), '/prefixed/')
|
||||
|
||||
|
||||
class URLTranslationTests(URLTestCaseBase):
|
||||
"""
|
||||
Tests if the pattern-strings are translated correctly (within the
|
||||
`i18n_patterns` and the normal `patterns` function).
|
||||
"""
|
||||
def test_no_prefix_translated(self):
|
||||
with translation.override('en'):
|
||||
self.assertEqual(reverse('no-prefix-translated'), '/translated/')
|
||||
self.assertEqual(reverse('no-prefix-translated-slug', kwargs={'slug': 'yeah'}), '/translated/yeah/')
|
||||
|
||||
with translation.override('nl'):
|
||||
self.assertEqual(reverse('no-prefix-translated'), '/vertaald/')
|
||||
self.assertEqual(reverse('no-prefix-translated-slug', kwargs={'slug': 'yeah'}), '/vertaald/yeah/')
|
||||
|
||||
with translation.override('pt-br'):
|
||||
self.assertEqual(reverse('no-prefix-translated'), '/traduzidos/')
|
||||
self.assertEqual(reverse('no-prefix-translated-slug', kwargs={'slug': 'yeah'}), '/traduzidos/yeah/')
|
||||
|
||||
def test_users_url(self):
|
||||
with translation.override('en'):
|
||||
self.assertEqual(reverse('users'), '/en/users/')
|
||||
|
||||
with translation.override('nl'):
|
||||
self.assertEqual(reverse('users'), '/nl/gebruikers/')
|
||||
|
||||
with translation.override('pt-br'):
|
||||
self.assertEqual(reverse('users'), '/pt-br/usuarios/')
|
||||
|
||||
|
||||
class URLNamespaceTests(URLTestCaseBase):
|
||||
"""
|
||||
Tests if the translations are still working within namespaces.
|
||||
"""
|
||||
def test_account_register(self):
|
||||
with translation.override('en'):
|
||||
self.assertEqual(reverse('account:register'), '/en/account/register/')
|
||||
|
||||
with translation.override('nl'):
|
||||
self.assertEqual(reverse('account:register'), '/nl/profiel/registeren/')
|
||||
|
||||
|
||||
class URLRedirectTests(URLTestCaseBase):
|
||||
"""
|
||||
Tests if the user gets redirected to the right URL when there is no
|
||||
language-prefix in the request URL.
|
||||
"""
|
||||
def test_no_prefix_response(self):
|
||||
response = self.client.get('/not-prefixed/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_en_redirect(self):
|
||||
response = self.client.get('/account/register/', HTTP_ACCEPT_LANGUAGE='en')
|
||||
self.assertRedirects(response, 'http://testserver/en/account/register/')
|
||||
|
||||
response = self.client.get(response['location'])
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_en_redirect_wrong_url(self):
|
||||
response = self.client.get('/profiel/registeren/', HTTP_ACCEPT_LANGUAGE='en')
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response['location'], 'http://testserver/en/profiel/registeren/')
|
||||
|
||||
response = self.client.get(response['location'])
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
def test_nl_redirect(self):
|
||||
response = self.client.get('/profiel/registeren/', HTTP_ACCEPT_LANGUAGE='nl')
|
||||
self.assertRedirects(response, 'http://testserver/nl/profiel/registeren/')
|
||||
|
||||
response = self.client.get(response['location'])
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_nl_redirect_wrong_url(self):
|
||||
response = self.client.get('/account/register/', HTTP_ACCEPT_LANGUAGE='nl')
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response['location'], 'http://testserver/nl/account/register/')
|
||||
|
||||
response = self.client.get(response['location'])
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
def test_pt_br_redirect(self):
|
||||
response = self.client.get('/conta/registre-se/', HTTP_ACCEPT_LANGUAGE='pt-br')
|
||||
self.assertRedirects(response, 'http://testserver/pt-br/conta/registre-se/')
|
||||
|
||||
response = self.client.get(response['location'])
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
||||
class URLRedirectWithoutTrailingSlashTests(URLTestCaseBase):
|
||||
"""
|
||||
Tests the redirect when the requested URL doesn't end with a slash
|
||||
(`settings.APPEND_SLASH=True`).
|
||||
"""
|
||||
def test_not_prefixed_redirect(self):
|
||||
response = self.client.get('/not-prefixed', HTTP_ACCEPT_LANGUAGE='en')
|
||||
self.assertEqual(response.status_code, 301)
|
||||
self.assertEqual(response['location'], 'http://testserver/not-prefixed/')
|
||||
|
||||
def test_en_redirect(self):
|
||||
response = self.client.get('/account/register', HTTP_ACCEPT_LANGUAGE='en')
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response['location'], 'http://testserver/en/account/register')
|
||||
|
||||
response = self.client.get(response['location'])
|
||||
self.assertEqual(response.status_code, 301)
|
||||
self.assertEqual(response['location'], 'http://testserver/en/account/register/')
|
||||
|
||||
|
||||
class URLRedirectWithoutTrailingSlashSettingTests(URLTestCaseBase):
|
||||
"""
|
||||
Tests the redirect when the requested URL doesn't end with a slash
|
||||
(`settings.APPEND_SLASH=False`).
|
||||
"""
|
||||
@override_settings(APPEND_SLASH=False)
|
||||
def test_not_prefixed_redirect(self):
|
||||
response = self.client.get('/not-prefixed', HTTP_ACCEPT_LANGUAGE='en')
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response['location'], 'http://testserver/en/not-prefixed')
|
||||
|
||||
response = self.client.get(response['location'])
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
@override_settings(APPEND_SLASH=False)
|
||||
def test_en_redirect(self):
|
||||
response = self.client.get('/account/register', HTTP_ACCEPT_LANGUAGE='en')
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response['location'], 'http://testserver/en/account/register')
|
||||
|
||||
response = self.client.get(response['location'])
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
|
||||
class URLResponseTests(URLTestCaseBase):
|
||||
"""
|
||||
Tests if the response has the right language-code.
|
||||
"""
|
||||
def test_not_prefixed_with_prefix(self):
|
||||
response = self.client.get('/en/not-prefixed/')
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
def test_en_url(self):
|
||||
response = self.client.get('/en/account/register/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response['content-language'], 'en')
|
||||
self.assertEqual(response.context['LANGUAGE_CODE'], 'en')
|
||||
|
||||
def test_nl_url(self):
|
||||
response = self.client.get('/nl/profiel/registeren/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response['content-language'], 'nl')
|
||||
self.assertEqual(response.context['LANGUAGE_CODE'], 'nl')
|
||||
|
||||
def test_wrong_en_prefix(self):
|
||||
response = self.client.get('/en/profiel/registeren/')
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
def test_wrong_nl_prefix(self):
|
||||
response = self.client.get('/nl/account/register/')
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
def test_pt_br_url(self):
|
||||
response = self.client.get('/pt-br/conta/registre-se/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response['content-language'], 'pt-br')
|
||||
self.assertEqual(response.context['LANGUAGE_CODE'], 'pt-br')
|
|
@ -0,0 +1,19 @@
|
|||
from django.conf.urls.defaults import patterns, include, url
|
||||
from django.conf.urls.i18n import i18n_patterns
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
|
||||
view = TemplateView.as_view(template_name='dummy.html')
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^not-prefixed/$', view, name='not-prefixed'),
|
||||
url(_(r'^translated/$'), view, name='no-prefix-translated'),
|
||||
url(_(r'^translated/(?P<slug>[\w-]+)/$'), view, name='no-prefix-translated-slug'),
|
||||
)
|
||||
|
||||
urlpatterns += i18n_patterns('',
|
||||
url(r'^prefixed/$', view, name='prefixed'),
|
||||
url(_(r'^users/$'), view, name='users'),
|
||||
url(_(r'^account/'), include('regressiontests.i18n.patterns.urls.namespace', namespace='account')),
|
||||
)
|
|
@ -0,0 +1,9 @@
|
|||
from django.conf.urls.defaults import url
|
||||
from django.conf.urls.i18n import i18n_patterns
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
view = TemplateView.as_view(template_name='dummy.html')
|
||||
|
||||
urlpatterns = i18n_patterns('',
|
||||
url(r'^prefixed/$', view, name='prefixed'),
|
||||
)
|
|
@ -0,0 +1,10 @@
|
|||
from django.conf.urls.defaults import patterns, include, url
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
|
||||
view = TemplateView.as_view(template_name='dummy.html')
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(_(r'^register/$'), view, name='register'),
|
||||
)
|
|
@ -0,0 +1,8 @@
|
|||
from django.conf.urls.defaults import patterns, include, url
|
||||
from django.conf.urls.i18n import i18n_patterns
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
urlpatterns = i18n_patterns('',
|
||||
url(_(r'^account/'), include('regressiontests.i18n.patterns.urls.wrong_namespace', namespace='account')),
|
||||
)
|
|
@ -0,0 +1,11 @@
|
|||
from django.conf.urls.defaults import include, url
|
||||
from django.conf.urls.i18n import i18n_patterns
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
|
||||
view = TemplateView.as_view(template_name='dummy.html')
|
||||
|
||||
urlpatterns = i18n_patterns('',
|
||||
url(_(r'^register/$'), view, name='register'),
|
||||
)
|
|
@ -3,13 +3,12 @@ from __future__ import with_statement
|
|||
import datetime
|
||||
import decimal
|
||||
import os
|
||||
import sys
|
||||
import pickle
|
||||
from threading import local
|
||||
|
||||
from django.conf import settings
|
||||
from django.template import Template, Context
|
||||
from django.test import TestCase
|
||||
from django.test import TestCase, RequestFactory
|
||||
from django.utils.formats import (get_format, date_format, time_format,
|
||||
localize, localize_input, iter_format_modules, get_format_modules)
|
||||
from django.utils.importlib import import_module
|
||||
|
@ -18,14 +17,14 @@ from django.utils.safestring import mark_safe, SafeString, SafeUnicode
|
|||
from django.utils import translation
|
||||
from django.utils.translation import (ugettext, ugettext_lazy, activate,
|
||||
deactivate, gettext_lazy, pgettext, npgettext, to_locale,
|
||||
get_language_info, get_language)
|
||||
get_language_info, get_language, get_language_from_request)
|
||||
|
||||
|
||||
from forms import I18nForm, SelectDateForm, SelectDateWidget, CompanyForm
|
||||
from models import Company, TestModel
|
||||
|
||||
from commands.tests import *
|
||||
|
||||
from patterns.tests import *
|
||||
from test_warnings import DeprecationWarningTests
|
||||
|
||||
class TranslationTests(TestCase):
|
||||
|
@ -494,6 +493,9 @@ class FormattingTests(TestCase):
|
|||
|
||||
class MiscTests(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.rf = RequestFactory()
|
||||
|
||||
def test_parse_spec_http_header(self):
|
||||
"""
|
||||
Testing HTTP header parsing. First, we test that we can parse the
|
||||
|
@ -534,10 +536,8 @@ class MiscTests(TestCase):
|
|||
"""
|
||||
Now test that we parse a literal HTTP header correctly.
|
||||
"""
|
||||
from django.utils.translation.trans_real import get_language_from_request
|
||||
g = get_language_from_request
|
||||
from django.http import HttpRequest
|
||||
r = HttpRequest
|
||||
r = self.rf.get('/')
|
||||
r.COOKIES = {}
|
||||
r.META = {'HTTP_ACCEPT_LANGUAGE': 'pt-br'}
|
||||
self.assertEqual('pt-br', g(r))
|
||||
|
@ -569,10 +569,8 @@ class MiscTests(TestCase):
|
|||
"""
|
||||
Now test that we parse language preferences stored in a cookie correctly.
|
||||
"""
|
||||
from django.utils.translation.trans_real import get_language_from_request
|
||||
g = get_language_from_request
|
||||
from django.http import HttpRequest
|
||||
r = HttpRequest
|
||||
r = self.rf.get('/')
|
||||
r.COOKIES = {settings.LANGUAGE_COOKIE_NAME: 'pt-br'}
|
||||
r.META = {}
|
||||
self.assertEqual('pt-br', g(r))
|
||||
|
@ -827,4 +825,3 @@ class MultipleLocaleActivationTests(TestCase):
|
|||
t = Template("{% load i18n %}{% blocktrans %}No{% endblocktrans %}")
|
||||
with translation.override('nl'):
|
||||
self.assertEqual(t.render(Context({})), 'Nee')
|
||||
|
||||
|
|
Loading…
Reference in New Issue