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:
Julien Phalip 2011-10-19 04:59:47 +00:00
parent 358e5a8031
commit 26698bc851
10 changed files with 324 additions and 22 deletions

View File

@ -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):

View File

@ -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,7 +427,11 @@ 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
extra_context = options.get('with', {}) if 'context' in options:
message_context = options['context']
else:
message_context = None
extra_context = options.get('with', {})
singular = [] singular = []
plural = [] plural = []
@ -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):

View File

@ -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 = []

View File

@ -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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -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

View File

@ -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):

View File

@ -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 %}

View File

@ -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"

View File

@ -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