From 286a1cad8839da3219dc3901540fb9422686e5ab Mon Sep 17 00:00:00 2001 From: Jannis Leidel Date: Mon, 4 Jul 2011 09:52:06 +0000 Subject: [PATCH] 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 --- django/templatetags/i18n.py | 53 +++++++++++++++++--- docs/topics/http/urls.txt | 6 +++ docs/topics/i18n/internationalization.txt | 26 ++++++++++ tests/regressiontests/i18n/patterns/tests.py | 42 ++++++++++++++++ 4 files changed, 120 insertions(+), 7 deletions(-) diff --git a/django/templatetags/i18n.py b/django/templatetags/i18n.py index 17821367a6..edb297404a 100644 --- a/django/templatetags/i18n.py +++ b/django/templatetags/i18n.py @@ -1,15 +1,16 @@ +from __future__ import with_statement import re -from django.template import Node, Variable, VariableNode -from django.template import TemplateSyntaxError, TokenParser, Library -from django.template import TOKEN_TEXT, TOKEN_VAR +from django.template import (Node, Variable, TemplateSyntaxError, + TokenParser, Library, TOKEN_TEXT, TOKEN_VAR) 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.utils import translation + register = Library() + class GetAvailableLanguagesNode(Node): def __init__(self, variable): self.variable = variable @@ -19,6 +20,7 @@ class GetAvailableLanguagesNode(Node): context[self.variable] = [(k, translation.ugettext(v)) for k, v in settings.LANGUAGES] return '' + class GetLanguageInfoNode(Node): def __init__(self, lang_code, variable): self.lang_code = Variable(lang_code) @@ -29,6 +31,7 @@ class GetLanguageInfoNode(Node): context[self.variable] = translation.get_language_info(lang_code) return '' + class GetLanguageInfoListNode(Node): def __init__(self, languages, variable): self.languages = Variable(languages) @@ -47,6 +50,7 @@ class GetLanguageInfoListNode(Node): context[self.variable] = [self.get_language_info(lang) for lang in langs] return '' + class GetCurrentLanguageNode(Node): def __init__(self, variable): self.variable = variable @@ -55,6 +59,7 @@ class GetCurrentLanguageNode(Node): context[self.variable] = translation.get_language() return '' + class GetCurrentLanguageBidiNode(Node): def __init__(self, variable): self.variable = variable @@ -63,6 +68,7 @@ class GetCurrentLanguageBidiNode(Node): context[self.variable] = translation.get_language_bidi() return '' + class TranslateNode(Node): def __init__(self, filter_expression, noop): self.noop = noop @@ -75,6 +81,7 @@ class TranslateNode(Node): output = self.filter_expression.resolve(context) return _render_value_in_context(output, context) + class BlockTranslateNode(Node): def __init__(self, extra_context, singular, plural=None, countervar=None, counter=None): @@ -117,6 +124,18 @@ class BlockTranslateNode(Node): context.pop() 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") def do_get_available_languages(parser, token): """ @@ -271,9 +290,9 @@ def do_translate(parser, token): # where single quote use is supported. if value[0] == "'": pos = None - m = re.match("^'([^']+)'(\|.*$)",value) + m = re.match("^'([^']+)'(\|.*$)", value) 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] == "'": value = '"%s"' % value[1:-1].replace('"','\\"') @@ -366,3 +385,23 @@ def do_block_translate(parser, token): return BlockTranslateNode(extra_context, singular, plural, countervar, 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) diff --git a/docs/topics/http/urls.txt b/docs/topics/http/urls.txt index 1f880262fa..f06dbe1506 100644 --- a/docs/topics/http/urls.txt +++ b/docs/topics/http/urls.txt @@ -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 dynamically. +.. versionadded:: 1.4 + + Django also allows to translate URLs according to the active language. + This process is described in the + :ref:`internationalization docs `. + .. _how-django-processes-a-request: How Django processes a request diff --git a/docs/topics/i18n/internationalization.txt b/docs/topics/i18n/internationalization.txt index 4850f7e7bc..5e9c1cd3b0 100644 --- a/docs/topics/i18n/internationalization.txt +++ b/docs/topics/i18n/internationalization.txt @@ -887,6 +887,32 @@ return the URL in the active language. Example:: that a carelessly translated URL causes a collision with a non-translated 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 %} + {{ lang_name }} + {% endlanguage %} + {% endfor %} + +The :ttag:`language` tag expects the language code as the only argument. + .. _set_language-redirect-view: The ``set_language`` redirect view diff --git a/tests/regressiontests/i18n/patterns/tests.py b/tests/regressiontests/i18n/patterns/tests.py index 091eb0f233..724eb03e27 100644 --- a/tests/regressiontests/i18n/patterns/tests.py +++ b/tests/regressiontests/i18n/patterns/tests.py @@ -6,6 +6,7 @@ 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.template import Template, Context from django.utils import translation @@ -241,3 +242,44 @@ class URLResponseTests(URLTestCaseBase): self.assertEqual(response.status_code, 200) self.assertEqual(response['content-language'], '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/'])