Fixed #14806 -- Added support for contextual translations to the `trans` and `blocktrans` template tags. Thanks to jtiai for the report.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@17015 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
358e5a8031
commit
26698bc851
|
@ -12,7 +12,7 @@ from django.utils.itercompat import is_iterable
|
||||||
from django.utils.text import (smart_split, unescape_string_literal,
|
from django.utils.text import (smart_split, unescape_string_literal,
|
||||||
get_text_list)
|
get_text_list)
|
||||||
from django.utils.encoding import smart_unicode, force_unicode, smart_str
|
from django.utils.encoding import smart_unicode, force_unicode, smart_str
|
||||||
from django.utils.translation import ugettext_lazy
|
from django.utils.translation import ugettext_lazy, pgettext_lazy
|
||||||
from django.utils.safestring import (SafeData, EscapeData, mark_safe,
|
from django.utils.safestring import (SafeData, EscapeData, mark_safe,
|
||||||
mark_for_escaping)
|
mark_for_escaping)
|
||||||
from django.utils.formats import localize
|
from django.utils.formats import localize
|
||||||
|
@ -675,6 +675,7 @@ class Variable(object):
|
||||||
self.literal = None
|
self.literal = None
|
||||||
self.lookups = None
|
self.lookups = None
|
||||||
self.translate = False
|
self.translate = False
|
||||||
|
self.message_context = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# First try to treat this variable as a number.
|
# First try to treat this variable as a number.
|
||||||
|
@ -722,7 +723,10 @@ class Variable(object):
|
||||||
# We're dealing with a literal, so it's already been "resolved"
|
# We're dealing with a literal, so it's already been "resolved"
|
||||||
value = self.literal
|
value = self.literal
|
||||||
if self.translate:
|
if self.translate:
|
||||||
return ugettext_lazy(value)
|
if self.message_context:
|
||||||
|
return pgettext_lazy(self.message_context, value)
|
||||||
|
else:
|
||||||
|
return ugettext_lazy(value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
|
|
@ -70,15 +70,21 @@ class GetCurrentLanguageBidiNode(Node):
|
||||||
|
|
||||||
|
|
||||||
class TranslateNode(Node):
|
class TranslateNode(Node):
|
||||||
def __init__(self, filter_expression, noop, asvar=None):
|
def __init__(self, filter_expression, noop, asvar=None,
|
||||||
|
message_context=None):
|
||||||
self.noop = noop
|
self.noop = noop
|
||||||
self.asvar = asvar
|
self.asvar = asvar
|
||||||
|
self.message_context = message_context
|
||||||
self.filter_expression = filter_expression
|
self.filter_expression = filter_expression
|
||||||
if isinstance(self.filter_expression.var, basestring):
|
if isinstance(self.filter_expression.var, basestring):
|
||||||
self.filter_expression.var = Variable(u"'%s'" % self.filter_expression.var)
|
self.filter_expression.var = Variable(u"'%s'" %
|
||||||
|
self.filter_expression.var)
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
self.filter_expression.var.translate = not self.noop
|
self.filter_expression.var.translate = not self.noop
|
||||||
|
if self.message_context:
|
||||||
|
self.filter_expression.var.message_context = (
|
||||||
|
self.message_context.resolve(context))
|
||||||
output = self.filter_expression.resolve(context)
|
output = self.filter_expression.resolve(context)
|
||||||
value = _render_value_in_context(output, context)
|
value = _render_value_in_context(output, context)
|
||||||
if self.asvar:
|
if self.asvar:
|
||||||
|
@ -90,12 +96,13 @@ class TranslateNode(Node):
|
||||||
|
|
||||||
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, message_context=None):
|
||||||
self.extra_context = extra_context
|
self.extra_context = extra_context
|
||||||
self.singular = singular
|
self.singular = singular
|
||||||
self.plural = plural
|
self.plural = plural
|
||||||
self.countervar = countervar
|
self.countervar = countervar
|
||||||
self.counter = counter
|
self.counter = counter
|
||||||
|
self.message_context = message_context
|
||||||
|
|
||||||
def render_token_list(self, tokens):
|
def render_token_list(self, tokens):
|
||||||
result = []
|
result = []
|
||||||
|
@ -109,6 +116,10 @@ class BlockTranslateNode(Node):
|
||||||
return ''.join(result), vars
|
return ''.join(result), vars
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
|
if self.message_context:
|
||||||
|
message_context = self.message_context.resolve(context)
|
||||||
|
else:
|
||||||
|
message_context = None
|
||||||
tmp_context = {}
|
tmp_context = {}
|
||||||
for var, val in self.extra_context.items():
|
for var, val in self.extra_context.items():
|
||||||
tmp_context[var] = val.resolve(context)
|
tmp_context[var] = val.resolve(context)
|
||||||
|
@ -123,10 +134,17 @@ class BlockTranslateNode(Node):
|
||||||
context[self.countervar] = count
|
context[self.countervar] = count
|
||||||
plural, plural_vars = self.render_token_list(self.plural)
|
plural, plural_vars = self.render_token_list(self.plural)
|
||||||
plural = re.sub(u'%(?!\()', u'%%', plural)
|
plural = re.sub(u'%(?!\()', u'%%', plural)
|
||||||
result = translation.ungettext(singular, plural, count)
|
if message_context:
|
||||||
|
result = translation.npgettext(message_context, singular,
|
||||||
|
plural, count)
|
||||||
|
else:
|
||||||
|
result = translation.ungettext(singular, plural, count)
|
||||||
vars.extend(plural_vars)
|
vars.extend(plural_vars)
|
||||||
else:
|
else:
|
||||||
result = translation.ugettext(singular)
|
if message_context:
|
||||||
|
result = translation.pgettext(message_context, singular)
|
||||||
|
else:
|
||||||
|
result = translation.ugettext(singular)
|
||||||
data = dict([(v, _render_value_in_context(context.get(v, ''), context)) for v in vars])
|
data = dict([(v, _render_value_in_context(context.get(v, ''), context)) for v in vars])
|
||||||
context.pop()
|
context.pop()
|
||||||
try:
|
try:
|
||||||
|
@ -290,6 +308,17 @@ def do_translate(parser, token):
|
||||||
This will just try to translate the contents of
|
This will just try to translate the contents of
|
||||||
the variable ``variable``. Make sure that the string
|
the variable ``variable``. Make sure that the string
|
||||||
in there is something that is in the .po file.
|
in there is something that is in the .po file.
|
||||||
|
|
||||||
|
It is possible to store the translated string into a variable::
|
||||||
|
|
||||||
|
{% trans "this is a test" as var %}
|
||||||
|
{{ var }}
|
||||||
|
|
||||||
|
Contextual translations are also supported::
|
||||||
|
|
||||||
|
{% trans "this is a test" context "greeting" %}
|
||||||
|
|
||||||
|
This is equivalent to calling pgettext instead of (u)gettext.
|
||||||
"""
|
"""
|
||||||
class TranslateParser(TokenParser):
|
class TranslateParser(TokenParser):
|
||||||
def top(self):
|
def top(self):
|
||||||
|
@ -301,7 +330,6 @@ def do_translate(parser, token):
|
||||||
# backwards compatibility with existing uses of ``trans``
|
# backwards compatibility with existing uses of ``trans``
|
||||||
# where single quote use is supported.
|
# where single quote use is supported.
|
||||||
if value[0] == "'":
|
if value[0] == "'":
|
||||||
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))
|
||||||
|
@ -310,19 +338,24 @@ def do_translate(parser, token):
|
||||||
|
|
||||||
noop = False
|
noop = False
|
||||||
asvar = None
|
asvar = None
|
||||||
|
message_context = None
|
||||||
|
|
||||||
while self.more():
|
while self.more():
|
||||||
tag = self.tag()
|
tag = self.tag()
|
||||||
if tag == 'noop':
|
if tag == 'noop':
|
||||||
noop = True
|
noop = True
|
||||||
|
elif tag == 'context':
|
||||||
|
message_context = parser.compile_filter(self.value())
|
||||||
elif tag == 'as':
|
elif tag == 'as':
|
||||||
asvar = self.tag()
|
asvar = self.tag()
|
||||||
else:
|
else:
|
||||||
raise TemplateSyntaxError(
|
raise TemplateSyntaxError(
|
||||||
"only options for 'trans' are 'noop' and 'as VAR.")
|
"Only options for 'trans' are 'noop', " \
|
||||||
return (value, noop, asvar)
|
"'context \"xxx\"', and 'as VAR'.")
|
||||||
value, noop, asvar = TranslateParser(token.contents).top()
|
return value, noop, asvar, message_context
|
||||||
return TranslateNode(parser.compile_filter(value), noop, asvar)
|
value, noop, asvar, message_context = TranslateParser(token.contents).top()
|
||||||
|
return TranslateNode(parser.compile_filter(value), noop, asvar,
|
||||||
|
message_context)
|
||||||
|
|
||||||
@register.tag("blocktrans")
|
@register.tag("blocktrans")
|
||||||
def do_block_translate(parser, token):
|
def do_block_translate(parser, token):
|
||||||
|
@ -349,6 +382,15 @@ def do_block_translate(parser, token):
|
||||||
|
|
||||||
{% blocktrans with foo|filter as bar and baz|filter as boo %}
|
{% blocktrans with foo|filter as bar and baz|filter as boo %}
|
||||||
{% blocktrans count var|length as count %}
|
{% blocktrans count var|length as count %}
|
||||||
|
|
||||||
|
Contextual translations are supported::
|
||||||
|
|
||||||
|
{% blocktrans with bar=foo|filter context "greeting" %}
|
||||||
|
This is {{ bar }}.
|
||||||
|
{% endblocktrans %}
|
||||||
|
|
||||||
|
This is equivalent to calling pgettext/npgettext instead of
|
||||||
|
(u)gettext/(u)ngettext.
|
||||||
"""
|
"""
|
||||||
bits = token.split_contents()
|
bits = token.split_contents()
|
||||||
|
|
||||||
|
@ -369,6 +411,13 @@ def do_block_translate(parser, token):
|
||||||
if len(value) != 1:
|
if len(value) != 1:
|
||||||
raise TemplateSyntaxError('"count" in %r tag expected exactly '
|
raise TemplateSyntaxError('"count" in %r tag expected exactly '
|
||||||
'one keyword argument.' % bits[0])
|
'one keyword argument.' % bits[0])
|
||||||
|
elif option == "context":
|
||||||
|
try:
|
||||||
|
value = remaining_bits.pop(0)
|
||||||
|
value = parser.compile_filter(value)
|
||||||
|
except Exception:
|
||||||
|
raise TemplateSyntaxError('"context" in %r tag expected '
|
||||||
|
'exactly one argument.' % bits[0])
|
||||||
else:
|
else:
|
||||||
raise TemplateSyntaxError('Unknown argument for %r tag: %r.' %
|
raise TemplateSyntaxError('Unknown argument for %r tag: %r.' %
|
||||||
(bits[0], option))
|
(bits[0], option))
|
||||||
|
@ -378,6 +427,10 @@ def do_block_translate(parser, token):
|
||||||
countervar, counter = options['count'].items()[0]
|
countervar, counter = options['count'].items()[0]
|
||||||
else:
|
else:
|
||||||
countervar, counter = None, None
|
countervar, counter = None, None
|
||||||
|
if 'context' in options:
|
||||||
|
message_context = options['context']
|
||||||
|
else:
|
||||||
|
message_context = None
|
||||||
extra_context = options.get('with', {})
|
extra_context = options.get('with', {})
|
||||||
|
|
||||||
singular = []
|
singular = []
|
||||||
|
@ -401,7 +454,7 @@ def do_block_translate(parser, token):
|
||||||
raise TemplateSyntaxError("'blocktrans' doesn't allow other block tags (seen %r) inside it" % token.contents)
|
raise TemplateSyntaxError("'blocktrans' doesn't allow other block tags (seen %r) inside it" % token.contents)
|
||||||
|
|
||||||
return BlockTranslateNode(extra_context, singular, plural, countervar,
|
return BlockTranslateNode(extra_context, singular, plural, countervar,
|
||||||
counter)
|
counter, message_context)
|
||||||
|
|
||||||
@register.tag
|
@register.tag
|
||||||
def language(parser, token):
|
def language(parser, token):
|
||||||
|
|
|
@ -433,12 +433,14 @@ def blankout(src, char):
|
||||||
"""
|
"""
|
||||||
return dot_re.sub(char, src)
|
return dot_re.sub(char, src)
|
||||||
|
|
||||||
inline_re = re.compile(r"""^\s*trans\s+((?:".*?")|(?:'.*?'))\s*""")
|
context_re = re.compile(r"""^\s+.*context\s+((?:"[^"]*?")|(?:'[^']*?'))\s*""")
|
||||||
block_re = re.compile(r"""^\s*blocktrans(?:\s+|$)""")
|
inline_re = re.compile(r"""^\s*trans\s+((?:"[^"]*?")|(?:'[^']*?'))(\s+.*context\s+(?:"[^"]*?")|(?:'[^']*?'))?\s*""")
|
||||||
|
block_re = re.compile(r"""^\s*blocktrans(\s+.*context\s+(?:"[^"]*?")|(?:'[^']*?'))?(?:\s+|$)""")
|
||||||
endblock_re = re.compile(r"""^\s*endblocktrans$""")
|
endblock_re = re.compile(r"""^\s*endblocktrans$""")
|
||||||
plural_re = re.compile(r"""^\s*plural$""")
|
plural_re = re.compile(r"""^\s*plural$""")
|
||||||
constant_re = re.compile(r"""_\(((?:".*?")|(?:'.*?'))\)""")
|
constant_re = re.compile(r"""_\(((?:".*?")|(?:'.*?'))\)""")
|
||||||
|
|
||||||
|
|
||||||
def templatize(src, origin=None):
|
def templatize(src, origin=None):
|
||||||
"""
|
"""
|
||||||
Turns a Django template into something that is understood by xgettext. It
|
Turns a Django template into something that is understood by xgettext. It
|
||||||
|
@ -448,6 +450,7 @@ def templatize(src, origin=None):
|
||||||
from django.template import (Lexer, TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK,
|
from django.template import (Lexer, TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK,
|
||||||
TOKEN_COMMENT, TRANSLATOR_COMMENT_MARK)
|
TOKEN_COMMENT, TRANSLATOR_COMMENT_MARK)
|
||||||
out = StringIO()
|
out = StringIO()
|
||||||
|
message_context = None
|
||||||
intrans = False
|
intrans = False
|
||||||
inplural = False
|
inplural = False
|
||||||
singular = []
|
singular = []
|
||||||
|
@ -477,15 +480,22 @@ def templatize(src, origin=None):
|
||||||
pluralmatch = plural_re.match(t.contents)
|
pluralmatch = plural_re.match(t.contents)
|
||||||
if endbmatch:
|
if endbmatch:
|
||||||
if inplural:
|
if inplural:
|
||||||
out.write(' ngettext(%r,%r,count) ' % (''.join(singular), ''.join(plural)))
|
if message_context:
|
||||||
|
out.write(' npgettext(%r, %r, %r,count) ' % (message_context, ''.join(singular), ''.join(plural)))
|
||||||
|
else:
|
||||||
|
out.write(' ngettext(%r, %r, count) ' % (''.join(singular), ''.join(plural)))
|
||||||
for part in singular:
|
for part in singular:
|
||||||
out.write(blankout(part, 'S'))
|
out.write(blankout(part, 'S'))
|
||||||
for part in plural:
|
for part in plural:
|
||||||
out.write(blankout(part, 'P'))
|
out.write(blankout(part, 'P'))
|
||||||
else:
|
else:
|
||||||
out.write(' gettext(%r) ' % ''.join(singular))
|
if message_context:
|
||||||
|
out.write(' pgettext(%r, %r) ' % (message_context, ''.join(singular)))
|
||||||
|
else:
|
||||||
|
out.write(' gettext(%r) ' % ''.join(singular))
|
||||||
for part in singular:
|
for part in singular:
|
||||||
out.write(blankout(part, 'S'))
|
out.write(blankout(part, 'S'))
|
||||||
|
message_context = None
|
||||||
intrans = False
|
intrans = False
|
||||||
inplural = False
|
inplural = False
|
||||||
singular = []
|
singular = []
|
||||||
|
@ -515,12 +525,33 @@ def templatize(src, origin=None):
|
||||||
cmatches = constant_re.findall(t.contents)
|
cmatches = constant_re.findall(t.contents)
|
||||||
if imatch:
|
if imatch:
|
||||||
g = imatch.group(1)
|
g = imatch.group(1)
|
||||||
if g[0] == '"': g = g.strip('"')
|
if g[0] == '"':
|
||||||
elif g[0] == "'": g = g.strip("'")
|
g = g.strip('"')
|
||||||
out.write(' gettext(%r) ' % g)
|
elif g[0] == "'":
|
||||||
|
g = g.strip("'")
|
||||||
|
if imatch.group(2):
|
||||||
|
# A context is provided
|
||||||
|
context_match = context_re.match(imatch.group(2))
|
||||||
|
message_context = context_match.group(1)
|
||||||
|
if message_context[0] == '"':
|
||||||
|
message_context = message_context.strip('"')
|
||||||
|
elif message_context[0] == "'":
|
||||||
|
message_context = message_context.strip("'")
|
||||||
|
out.write(' pgettext(%r, %r) ' % (message_context, g))
|
||||||
|
message_context = None
|
||||||
|
else:
|
||||||
|
out.write(' gettext(%r) ' % g)
|
||||||
elif bmatch:
|
elif bmatch:
|
||||||
for fmatch in constant_re.findall(t.contents):
|
for fmatch in constant_re.findall(t.contents):
|
||||||
out.write(' _(%s) ' % fmatch)
|
out.write(' _(%s) ' % fmatch)
|
||||||
|
if bmatch.group(1):
|
||||||
|
# A context is provided
|
||||||
|
context_match = context_re.match(bmatch.group(1))
|
||||||
|
message_context = context_match.group(1)
|
||||||
|
if message_context[0] == '"':
|
||||||
|
message_context = message_context.strip('"')
|
||||||
|
elif message_context[0] == "'":
|
||||||
|
message_context = message_context.strip("'")
|
||||||
intrans = True
|
intrans = True
|
||||||
inplural = False
|
inplural = False
|
||||||
singular = []
|
singular = []
|
||||||
|
|
|
@ -191,6 +191,14 @@ Additionally, it's now possible to define translatable URL patterns using
|
||||||
:ref:`url-internationalization` for more information about the language prefix
|
:ref:`url-internationalization` for more information about the language prefix
|
||||||
and how to internationalize URL patterns.
|
and how to internationalize URL patterns.
|
||||||
|
|
||||||
|
Contextual translation support for ``{% trans %}`` and ``{% blocktrans %}``
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Django 1.3 introduced :ref:`contextual translation<contextual-markers>` support
|
||||||
|
in Python files via the ``pgettext`` function. This is now also supported by
|
||||||
|
the :ttag:`trans` and :ttag:`blocktrans` template tags using the new
|
||||||
|
``context`` keyword.
|
||||||
|
|
||||||
Customizable ``SingleObjectMixin`` URLConf kwargs
|
Customizable ``SingleObjectMixin`` URLConf kwargs
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -266,6 +266,11 @@ will appear in the .po file as:
|
||||||
msgid "May"
|
msgid "May"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
.. versionadded:: 1.4
|
||||||
|
|
||||||
|
Contextual markers are also supported by the :ttag:`trans` and
|
||||||
|
:ttag:`blocktrans` template tags.
|
||||||
|
|
||||||
.. _lazy-translations:
|
.. _lazy-translations:
|
||||||
|
|
||||||
Lazy translation
|
Lazy translation
|
||||||
|
@ -453,7 +458,7 @@ It's not possible to mix a template variable inside a string within ``{% trans
|
||||||
%}``. If your translations require strings with variables (placeholders), use
|
%}``. If your translations require strings with variables (placeholders), use
|
||||||
``{% blocktrans %}`` instead.
|
``{% blocktrans %}`` instead.
|
||||||
|
|
||||||
.. versionchanged:: 1.4
|
.. versionadded:: 1.4
|
||||||
|
|
||||||
If you'd like to retrieve a translated string without displaying it, you can
|
If you'd like to retrieve a translated string without displaying it, you can
|
||||||
use the following syntax::
|
use the following syntax::
|
||||||
|
@ -479,6 +484,15 @@ or should be used as arguments for other template tags or filters::
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
.. versionadded:: 1.4
|
||||||
|
|
||||||
|
``{% trans %}`` also supports :ref:`contextual markers<contextual-markers>`
|
||||||
|
using the ``context`` keyword:
|
||||||
|
|
||||||
|
.. code-block:: html+django
|
||||||
|
|
||||||
|
{% trans "May" context "month name" %}
|
||||||
|
|
||||||
.. templatetag:: blocktrans
|
.. templatetag:: blocktrans
|
||||||
|
|
||||||
``blocktrans`` template tag
|
``blocktrans`` template tag
|
||||||
|
@ -560,6 +574,15 @@ be retrieved (and stored) beforehand::
|
||||||
This is a URL: {{ the_url }}
|
This is a URL: {{ the_url }}
|
||||||
{% endblocktrans %}
|
{% endblocktrans %}
|
||||||
|
|
||||||
|
.. versionadded:: 1.4
|
||||||
|
|
||||||
|
``{% blocktrans %}`` also supports :ref:`contextual
|
||||||
|
markers<contextual-markers>` using the ``context`` keyword:
|
||||||
|
|
||||||
|
.. code-block:: html+django
|
||||||
|
|
||||||
|
{% blocktrans with name=user.username context "greeting" %}Hi {{ name }}{% endblocktrans %}
|
||||||
|
|
||||||
.. _template-translation-vars:
|
.. _template-translation-vars:
|
||||||
|
|
||||||
Other tags
|
Other tags
|
||||||
|
|
|
@ -94,6 +94,35 @@ class BasicExtractorTests(ExtractorTests):
|
||||||
os.remove('./templates/template_with_error.html')
|
os.remove('./templates/template_with_error.html')
|
||||||
os.remove('./templates/template_with_error.html.py') # Waiting for #8536 to be fixed
|
os.remove('./templates/template_with_error.html.py') # Waiting for #8536 to be fixed
|
||||||
|
|
||||||
|
def test_template_message_context_extractor(self):
|
||||||
|
"""
|
||||||
|
Ensure that message contexts are correctly extracted for the
|
||||||
|
{% trans %} and {% blocktrans %} template tags.
|
||||||
|
Refs #14806.
|
||||||
|
"""
|
||||||
|
os.chdir(self.test_dir)
|
||||||
|
management.call_command('makemessages', locale=LOCALE, verbosity=0)
|
||||||
|
self.assertTrue(os.path.exists(self.PO_FILE))
|
||||||
|
po_contents = open(self.PO_FILE, 'r').read()
|
||||||
|
# {% trans %}
|
||||||
|
self.assertTrue('msgctxt "Special trans context #1"' in po_contents)
|
||||||
|
self.assertTrue("Translatable literal #7a" in po_contents)
|
||||||
|
self.assertTrue('msgctxt "Special trans context #2"' in po_contents)
|
||||||
|
self.assertTrue("Translatable literal #7b" in po_contents)
|
||||||
|
self.assertTrue('msgctxt "Special trans context #3"' in po_contents)
|
||||||
|
self.assertTrue("Translatable literal #7c" in po_contents)
|
||||||
|
|
||||||
|
# {% blocktrans %}
|
||||||
|
self.assertTrue('msgctxt "Special blocktrans context #1"' in po_contents)
|
||||||
|
self.assertTrue("Translatable literal #8a" in po_contents)
|
||||||
|
self.assertTrue('msgctxt "Special blocktrans context #2"' in po_contents)
|
||||||
|
self.assertTrue("Translatable literal #8b-singular" in po_contents)
|
||||||
|
self.assertTrue("Translatable literal #8b-plural" in po_contents)
|
||||||
|
self.assertTrue('msgctxt "Special blocktrans context #3"' in po_contents)
|
||||||
|
self.assertTrue("Translatable literal #8c-singular" in po_contents)
|
||||||
|
self.assertTrue("Translatable literal #8c-plural" in po_contents)
|
||||||
|
self.assertTrue('msgctxt "Special blocktrans context #4"' in po_contents)
|
||||||
|
self.assertTrue("Translatable literal #8d" in po_contents)
|
||||||
|
|
||||||
class JavascriptExtractorTests(ExtractorTests):
|
class JavascriptExtractorTests(ExtractorTests):
|
||||||
|
|
||||||
|
|
|
@ -57,3 +57,12 @@ continued here.{% endcomment %}
|
||||||
{% comment %} Translators: Two-line translator comment #5 -- with non ASCII characters: áéíóúö
|
{% comment %} Translators: Two-line translator comment #5 -- with non ASCII characters: áéíóúö
|
||||||
continued here.{% endcomment %}
|
continued here.{% endcomment %}
|
||||||
{% trans "Translatable literal #6b" %}
|
{% trans "Translatable literal #6b" %}
|
||||||
|
|
||||||
|
{% trans "Translatable literal #7a" context "Special trans context #1" %}
|
||||||
|
{% trans "Translatable literal #7b" as var context "Special trans context #2" %}
|
||||||
|
{% trans "Translatable literal #7c" context "Special trans context #3" as var %}
|
||||||
|
|
||||||
|
{% blocktrans context "Special blocktrans context #1" %}Translatable literal #8a{% endblocktrans %}
|
||||||
|
{% blocktrans count 2 context "Special blocktrans context #2" %}Translatable literal #8b-singular{% plural %}Translatable literal #8b-plural{% endblocktrans %}
|
||||||
|
{% blocktrans context "Special blocktrans context #3" count 2 %}Translatable literal #8c-singular{% plural %}Translatable literal #8c-plural{% endblocktrans %}
|
||||||
|
{% blocktrans with a=1 context "Special blocktrans context #4" %}Translatable literal #8d {{ a }}{% endblocktrans %}
|
Binary file not shown.
|
@ -52,3 +52,27 @@ msgid "%(percent)s%% represents %(num)s object"
|
||||||
msgid_plural "%(percent)s%% represents %(num)s objects"
|
msgid_plural "%(percent)s%% represents %(num)s objects"
|
||||||
msgstr[0] "%(percent)s%% stellt %(num)s Objekt dar"
|
msgstr[0] "%(percent)s%% stellt %(num)s Objekt dar"
|
||||||
msgstr[1] "%(percent)s%% stellt %(num)s Objekte dar"
|
msgstr[1] "%(percent)s%% stellt %(num)s Objekte dar"
|
||||||
|
|
||||||
|
#: models.py:17
|
||||||
|
msgctxt "super search"
|
||||||
|
msgid "%(number)s super result"
|
||||||
|
msgid_plural "%(number)s super results"
|
||||||
|
msgstr[0] "%(number)s Super-Ergebnis"
|
||||||
|
msgstr[1] "%(number)s Super-Ergebnisse"
|
||||||
|
|
||||||
|
#: models.py:19
|
||||||
|
msgctxt "other super search"
|
||||||
|
msgid "%(number)s super result"
|
||||||
|
msgid_plural "%(number)s super results"
|
||||||
|
msgstr[0] "%(number)s anderen Super-Ergebnis"
|
||||||
|
msgstr[1] "%(number)s andere Super-Ergebnisse"
|
||||||
|
|
||||||
|
#: models.py:21
|
||||||
|
msgctxt "comment count"
|
||||||
|
msgid "There are %(num_comments)s comments"
|
||||||
|
msgstr "Es gibt %(num_comments)s Kommentare"
|
||||||
|
|
||||||
|
#: models.py:23
|
||||||
|
msgctxt "other comment count"
|
||||||
|
msgid "There are %(num_comments)s comments"
|
||||||
|
msgstr "Andere: Es gibt %(num_comments)s Kommentare"
|
|
@ -9,6 +9,7 @@ from threading import local
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.template import Template, Context
|
from django.template import Template, Context
|
||||||
|
from django.template.base import TemplateSyntaxError
|
||||||
from django.test import TestCase, RequestFactory
|
from django.test import TestCase, RequestFactory
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
from django.utils import translation
|
from django.utils import translation
|
||||||
|
@ -96,6 +97,126 @@ class TranslationTests(TestCase):
|
||||||
self.assertEqual(pgettext("verb", "May"), u"Kann")
|
self.assertEqual(pgettext("verb", "May"), u"Kann")
|
||||||
self.assertEqual(npgettext("search", "%d result", "%d results", 4) % 4, u"4 Resultate")
|
self.assertEqual(npgettext("search", "%d result", "%d results", 4) % 4, u"4 Resultate")
|
||||||
|
|
||||||
|
def test_template_tags_pgettext(self):
|
||||||
|
"""
|
||||||
|
Ensure that message contexts are taken into account the {% trans %} and
|
||||||
|
{% blocktrans %} template tags.
|
||||||
|
Refs #14806.
|
||||||
|
"""
|
||||||
|
# Reset translation catalog to include other/locale/de
|
||||||
|
extended_locale_paths = settings.LOCALE_PATHS + (
|
||||||
|
os.path.join(here, 'other', 'locale'),
|
||||||
|
)
|
||||||
|
with self.settings(LOCALE_PATHS=extended_locale_paths):
|
||||||
|
from django.utils.translation import trans_real
|
||||||
|
trans_real._active = local()
|
||||||
|
trans_real._translations = {}
|
||||||
|
with translation.override('de'):
|
||||||
|
|
||||||
|
# {% trans %} -----------------------------------
|
||||||
|
|
||||||
|
# Inexisting context...
|
||||||
|
t = Template('{% load i18n %}{% trans "May" context "unexisting" %}')
|
||||||
|
rendered = t.render(Context())
|
||||||
|
self.assertEqual(rendered, 'May')
|
||||||
|
|
||||||
|
# Existing context...
|
||||||
|
# Using a literal
|
||||||
|
t = Template('{% load i18n %}{% trans "May" context "month name" %}')
|
||||||
|
rendered = t.render(Context())
|
||||||
|
self.assertEqual(rendered, 'Mai')
|
||||||
|
t = Template('{% load i18n %}{% trans "May" context "verb" %}')
|
||||||
|
rendered = t.render(Context())
|
||||||
|
self.assertEqual(rendered, 'Kann')
|
||||||
|
|
||||||
|
# Using a variable
|
||||||
|
t = Template('{% load i18n %}{% trans "May" context message_context %}')
|
||||||
|
rendered = t.render(Context({'message_context': 'month name'}))
|
||||||
|
self.assertEqual(rendered, 'Mai')
|
||||||
|
t = Template('{% load i18n %}{% trans "May" context message_context %}')
|
||||||
|
rendered = t.render(Context({'message_context': 'verb'}))
|
||||||
|
self.assertEqual(rendered, 'Kann')
|
||||||
|
|
||||||
|
# Using a filter
|
||||||
|
t = Template('{% load i18n %}{% trans "May" context message_context|lower %}')
|
||||||
|
rendered = t.render(Context({'message_context': 'MONTH NAME'}))
|
||||||
|
self.assertEqual(rendered, 'Mai')
|
||||||
|
t = Template('{% load i18n %}{% trans "May" context message_context|lower %}')
|
||||||
|
rendered = t.render(Context({'message_context': 'VERB'}))
|
||||||
|
self.assertEqual(rendered, 'Kann')
|
||||||
|
|
||||||
|
# Using 'as'
|
||||||
|
t = Template('{% load i18n %}{% trans "May" context "month name" as var %}Value: {{ var }}')
|
||||||
|
rendered = t.render(Context())
|
||||||
|
self.assertEqual(rendered, 'Value: Mai')
|
||||||
|
t = Template('{% load i18n %}{% trans "May" as var context "verb" %}Value: {{ var }}')
|
||||||
|
rendered = t.render(Context())
|
||||||
|
self.assertEqual(rendered, 'Value: Kann')
|
||||||
|
|
||||||
|
# Mis-uses
|
||||||
|
self.assertRaises(TemplateSyntaxError, Template, '{% load i18n %}{% trans "May" context as var %}{{ var }}')
|
||||||
|
self.assertRaises(TemplateSyntaxError, Template, '{% load i18n %}{% trans "May" as var context %}{{ var }}')
|
||||||
|
|
||||||
|
# {% blocktrans %} ------------------------------
|
||||||
|
|
||||||
|
# Inexisting context...
|
||||||
|
t = Template('{% load i18n %}{% blocktrans context "unexisting" %}May{% endblocktrans %}')
|
||||||
|
rendered = t.render(Context())
|
||||||
|
self.assertEqual(rendered, 'May')
|
||||||
|
|
||||||
|
# Existing context...
|
||||||
|
# Using a literal
|
||||||
|
t = Template('{% load i18n %}{% blocktrans context "month name" %}May{% endblocktrans %}')
|
||||||
|
rendered = t.render(Context())
|
||||||
|
self.assertEqual(rendered, 'Mai')
|
||||||
|
t = Template('{% load i18n %}{% blocktrans context "verb" %}May{% endblocktrans %}')
|
||||||
|
rendered = t.render(Context())
|
||||||
|
self.assertEqual(rendered, 'Kann')
|
||||||
|
|
||||||
|
# Using a variable
|
||||||
|
t = Template('{% load i18n %}{% blocktrans context message_context %}May{% endblocktrans %}')
|
||||||
|
rendered = t.render(Context({'message_context': 'month name'}))
|
||||||
|
self.assertEqual(rendered, 'Mai')
|
||||||
|
t = Template('{% load i18n %}{% blocktrans context message_context %}May{% endblocktrans %}')
|
||||||
|
rendered = t.render(Context({'message_context': 'verb'}))
|
||||||
|
self.assertEqual(rendered, 'Kann')
|
||||||
|
|
||||||
|
# Using a filter
|
||||||
|
t = Template('{% load i18n %}{% blocktrans context message_context|lower %}May{% endblocktrans %}')
|
||||||
|
rendered = t.render(Context({'message_context': 'MONTH NAME'}))
|
||||||
|
self.assertEqual(rendered, 'Mai')
|
||||||
|
t = Template('{% load i18n %}{% blocktrans context message_context|lower %}May{% endblocktrans %}')
|
||||||
|
rendered = t.render(Context({'message_context': 'VERB'}))
|
||||||
|
self.assertEqual(rendered, 'Kann')
|
||||||
|
|
||||||
|
# Using 'count'
|
||||||
|
t = Template('{% load i18n %}{% blocktrans count number=1 context "super search" %}{{ number }} super result{% plural %}{{ number }} super results{% endblocktrans %}')
|
||||||
|
rendered = t.render(Context())
|
||||||
|
self.assertEqual(rendered, '1 Super-Ergebnis')
|
||||||
|
t = Template('{% load i18n %}{% blocktrans count number=2 context "super search" %}{{ number }} super result{% plural %}{{ number }} super results{% endblocktrans %}')
|
||||||
|
rendered = t.render(Context())
|
||||||
|
self.assertEqual(rendered, '2 Super-Ergebnisse')
|
||||||
|
t = Template('{% load i18n %}{% blocktrans context "other super search" count number=1 %}{{ number }} super result{% plural %}{{ number }} super results{% endblocktrans %}')
|
||||||
|
rendered = t.render(Context())
|
||||||
|
self.assertEqual(rendered, '1 anderen Super-Ergebnis')
|
||||||
|
t = Template('{% load i18n %}{% blocktrans context "other super search" count number=2 %}{{ number }} super result{% plural %}{{ number }} super results{% endblocktrans %}')
|
||||||
|
rendered = t.render(Context())
|
||||||
|
self.assertEqual(rendered, '2 andere Super-Ergebnisse')
|
||||||
|
|
||||||
|
# Using 'with'
|
||||||
|
t = Template('{% load i18n %}{% blocktrans with num_comments=5 context "comment count" %}There are {{ num_comments }} comments{% endblocktrans %}')
|
||||||
|
rendered = t.render(Context())
|
||||||
|
self.assertEqual(rendered, 'Es gibt 5 Kommentare')
|
||||||
|
t = Template('{% load i18n %}{% blocktrans with num_comments=5 context "other comment count" %}There are {{ num_comments }} comments{% endblocktrans %}')
|
||||||
|
rendered = t.render(Context())
|
||||||
|
self.assertEqual(rendered, 'Andere: Es gibt 5 Kommentare')
|
||||||
|
|
||||||
|
# Mis-uses
|
||||||
|
self.assertRaises(TemplateSyntaxError, Template, '{% load i18n %}{% blocktrans context with month="May" %}{{ month }}{% endblocktrans %}')
|
||||||
|
self.assertRaises(TemplateSyntaxError, Template, '{% load i18n %}{% blocktrans context %}{% endblocktrans %}')
|
||||||
|
self.assertRaises(TemplateSyntaxError, Template, '{% load i18n %}{% blocktrans count number=2 context %}{{ number }} super result{% plural %}{{ number }} super results{% endblocktrans %}')
|
||||||
|
|
||||||
|
|
||||||
def test_string_concat(self):
|
def test_string_concat(self):
|
||||||
"""
|
"""
|
||||||
unicode(string_concat(...)) should not raise a TypeError - #4796
|
unicode(string_concat(...)) should not raise a TypeError - #4796
|
||||||
|
|
Loading…
Reference in New Issue