Fixed #16332 -- Added language template tag that switches the activate language for the enclosed template section, e.g. to allow translation of URLs as added in r16405. Many thanks to Florian Apolloner and Orne Brocaar.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@16501 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Jannis Leidel 2011-07-04 09:52:06 +00:00
parent a34e67031a
commit 286a1cad88
4 changed files with 120 additions and 7 deletions

View File

@ -1,15 +1,16 @@
from __future__ import with_statement
import re import re
from django.template import Node, Variable, VariableNode from django.template import (Node, Variable, TemplateSyntaxError,
from django.template import TemplateSyntaxError, TokenParser, Library TokenParser, Library, TOKEN_TEXT, TOKEN_VAR)
from django.template import TOKEN_TEXT, TOKEN_VAR
from django.template.base import _render_value_in_context from django.template.base import _render_value_in_context
from django.utils import translation
from django.utils.encoding import force_unicode
from django.template.defaulttags import token_kwargs from django.template.defaulttags import token_kwargs
from django.utils import translation
register = Library() register = Library()
class GetAvailableLanguagesNode(Node): class GetAvailableLanguagesNode(Node):
def __init__(self, variable): def __init__(self, variable):
self.variable = variable self.variable = variable
@ -19,6 +20,7 @@ class GetAvailableLanguagesNode(Node):
context[self.variable] = [(k, translation.ugettext(v)) for k, v in settings.LANGUAGES] context[self.variable] = [(k, translation.ugettext(v)) for k, v in settings.LANGUAGES]
return '' return ''
class GetLanguageInfoNode(Node): class GetLanguageInfoNode(Node):
def __init__(self, lang_code, variable): def __init__(self, lang_code, variable):
self.lang_code = Variable(lang_code) self.lang_code = Variable(lang_code)
@ -29,6 +31,7 @@ class GetLanguageInfoNode(Node):
context[self.variable] = translation.get_language_info(lang_code) context[self.variable] = translation.get_language_info(lang_code)
return '' return ''
class GetLanguageInfoListNode(Node): class GetLanguageInfoListNode(Node):
def __init__(self, languages, variable): def __init__(self, languages, variable):
self.languages = Variable(languages) self.languages = Variable(languages)
@ -47,6 +50,7 @@ class GetLanguageInfoListNode(Node):
context[self.variable] = [self.get_language_info(lang) for lang in langs] context[self.variable] = [self.get_language_info(lang) for lang in langs]
return '' return ''
class GetCurrentLanguageNode(Node): class GetCurrentLanguageNode(Node):
def __init__(self, variable): def __init__(self, variable):
self.variable = variable self.variable = variable
@ -55,6 +59,7 @@ class GetCurrentLanguageNode(Node):
context[self.variable] = translation.get_language() context[self.variable] = translation.get_language()
return '' return ''
class GetCurrentLanguageBidiNode(Node): class GetCurrentLanguageBidiNode(Node):
def __init__(self, variable): def __init__(self, variable):
self.variable = variable self.variable = variable
@ -63,6 +68,7 @@ class GetCurrentLanguageBidiNode(Node):
context[self.variable] = translation.get_language_bidi() context[self.variable] = translation.get_language_bidi()
return '' return ''
class TranslateNode(Node): class TranslateNode(Node):
def __init__(self, filter_expression, noop): def __init__(self, filter_expression, noop):
self.noop = noop self.noop = noop
@ -75,6 +81,7 @@ class TranslateNode(Node):
output = self.filter_expression.resolve(context) output = self.filter_expression.resolve(context)
return _render_value_in_context(output, context) return _render_value_in_context(output, context)
class BlockTranslateNode(Node): class BlockTranslateNode(Node):
def __init__(self, extra_context, singular, plural=None, countervar=None, def __init__(self, extra_context, singular, plural=None, countervar=None,
counter=None): counter=None):
@ -117,6 +124,18 @@ class BlockTranslateNode(Node):
context.pop() context.pop()
return result % data return result % data
class LanguageNode(Node):
def __init__(self, nodelist, language):
self.nodelist = nodelist
self.language = language
def render(self, context):
with translation.override(self.language.resolve(context)):
output = self.nodelist.render(context)
return output
@register.tag("get_available_languages") @register.tag("get_available_languages")
def do_get_available_languages(parser, token): def do_get_available_languages(parser, token):
""" """
@ -271,9 +290,9 @@ def do_translate(parser, token):
# where single quote use is supported. # where single quote use is supported.
if value[0] == "'": if value[0] == "'":
pos = None pos = None
m = re.match("^'([^']+)'(\|.*$)",value) m = re.match("^'([^']+)'(\|.*$)", value)
if m: if m:
value = '"%s"%s' % (m.group(1).replace('"','\\"'),m.group(2)) value = '"%s"%s' % (m.group(1).replace('"','\\"'), m.group(2))
elif value[-1] == "'": elif value[-1] == "'":
value = '"%s"' % value[1:-1].replace('"','\\"') value = '"%s"' % value[1:-1].replace('"','\\"')
@ -366,3 +385,23 @@ def do_block_translate(parser, token):
return BlockTranslateNode(extra_context, singular, plural, countervar, return BlockTranslateNode(extra_context, singular, plural, countervar,
counter) counter)
@register.tag
def language(parser, token):
"""
This will enable the given language just for this block.
Usage::
{% language "de" %}
This is {{ bar }} and {{ boo }}.
{% endlanguage %}
"""
bits = token.split_contents()
if len(bits) < 2:
raise TemplateSyntaxError("'%s' takes one argument (language)" % bits[0])
language = parser.compile_filter(bits[1])
nodelist = parser.parse(('endlanguage',))
parser.delete_first_token()
return LanguageNode(nodelist, language)

View File

@ -28,6 +28,12 @@ This mapping can be as short or as long as needed. It can reference other
mappings. And, because it's pure Python code, it can be constructed mappings. And, because it's pure Python code, it can be constructed
dynamically. dynamically.
.. versionadded:: 1.4
Django also allows to translate URLs according to the active language.
This process is described in the
:ref:`internationalization docs <url-internationalization>`.
.. _how-django-processes-a-request: .. _how-django-processes-a-request:
How Django processes a request How Django processes a request

View File

@ -887,6 +887,32 @@ return the URL in the active language. Example::
that a carelessly translated URL causes a collision with a non-translated that a carelessly translated URL causes a collision with a non-translated
URL pattern. URL pattern.
.. _reversing_in_templates:
.. templatetag:: language
Reversing in templates
----------------------
If localized URLs get reversed in templates they always use the current
language. To link to a URL in another language use the ``language``
template tag. It enables the given language in the enclosed template section:
.. code-block:: html+django
{% load i18n %}
{% get_available_languages as languages %}
{% trans "View this category in:" %}
{% for lang_code, lang_name in languages %}
{% language lang_code %}
<a href="{% url category slug=category.slug %}">{{ lang_name }}</a>
{% endlanguage %}
{% endfor %}
The :ttag:`language` tag expects the language code as the only argument.
.. _set_language-redirect-view: .. _set_language-redirect-view:
The ``set_language`` redirect view The ``set_language`` redirect view

View File

@ -6,6 +6,7 @@ from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import reverse, clear_url_caches from django.core.urlresolvers import reverse, clear_url_caches
from django.test import TestCase from django.test import TestCase
from django.test.utils import override_settings from django.test.utils import override_settings
from django.template import Template, Context
from django.utils import translation from django.utils import translation
@ -241,3 +242,44 @@ class URLResponseTests(URLTestCaseBase):
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response['content-language'], 'pt-br') self.assertEqual(response['content-language'], 'pt-br')
self.assertEqual(response.context['LANGUAGE_CODE'], 'pt-br') self.assertEqual(response.context['LANGUAGE_CODE'], 'pt-br')
class URLTagTests(URLTestCaseBase):
"""
Test if the language tag works.
"""
def test_strings_only(self):
t = Template("""{% load i18n %}
{% language 'nl' %}{% url no-prefix-translated %}{% endlanguage %}
{% language 'pt-br' %}{% url no-prefix-translated %}{% endlanguage %}""")
self.assertEqual(t.render(Context({})).strip().split(),
[u'/vertaald/', u'/traduzidos/'])
def test_context(self):
ctx = Context({'lang1':'nl', 'lang2':'pt-br'})
tpl = Template("""{% load i18n %}
{% language lang1 %}{% url no-prefix-translated %}{% endlanguage %}
{% language lang2 %}{% url no-prefix-translated %}{% endlanguage %}""")
self.assertEqual(tpl.render(ctx).strip().split(),
[u'/vertaald/', u'/traduzidos/'])
def test_args(self):
tpl = Template("""{% load i18n %}
{% language 'nl' %}{% url no-prefix-translated-slug 'apo' %}{% endlanguage %}
{% language 'pt-br' %}{% url no-prefix-translated-slug 'apo' %}{% endlanguage %}""")
self.assertEqual(tpl.render(Context({})).strip().split(),
[u'/vertaald/apo/', u'/traduzidos/apo/'])
def test_kwargs(self):
tpl = Template("""{% load i18n %}
{% language 'nl' %}{% url no-prefix-translated-slug slug='apo' %}{% endlanguage %}
{% language 'pt-br' %}{% url no-prefix-translated-slug slug='apo' %}{% endlanguage %}""")
self.assertEqual(tpl.render(Context({})).strip().split(),
[u'/vertaald/apo/', u'/traduzidos/apo/'])
def test_future_kwargs(self):
tpl = Template("""{% load i18n %}{% load url from future %}
{% language 'nl' %}{% url 'no-prefix-translated-slug' slug='apo' %}{% endlanguage %}
{% language 'pt-br' %}{% url 'no-prefix-translated-slug' slug='apo' %}{% endlanguage %}""")
self.assertEqual(tpl.render(Context({})).strip().split(),
[u'/vertaald/apo/', u'/traduzidos/apo/'])