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:
parent
a34e67031a
commit
286a1cad88
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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/'])
|
||||||
|
|
Loading…
Reference in New Issue