From 26698bc85123935d5926a87a45cae25efe683639 Mon Sep 17 00:00:00 2001
From: Julien Phalip
Date: Wed, 19 Oct 2011 04:59:47 +0000
Subject: [PATCH] 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
---
django/template/base.py | 8 +-
django/templatetags/i18n.py | 77 +++++++++--
django/utils/translation/trans_real.py | 45 ++++++-
docs/releases/1.4.txt | 8 ++
docs/topics/i18n/internationalization.txt | 25 +++-
.../i18n/commands/extraction.py | 29 +++++
.../i18n/commands/templates/test.html | 9 ++
.../other/locale/de/LC_MESSAGES/django.mo | Bin 922 -> 1449 bytes
.../other/locale/de/LC_MESSAGES/django.po | 24 ++++
tests/regressiontests/i18n/tests.py | 121 ++++++++++++++++++
10 files changed, 324 insertions(+), 22 deletions(-)
diff --git a/django/template/base.py b/django/template/base.py
index 3e0c14aa03..0fafb2d182 100644
--- a/django/template/base.py
+++ b/django/template/base.py
@@ -12,7 +12,7 @@ from django.utils.itercompat import is_iterable
from django.utils.text import (smart_split, unescape_string_literal,
get_text_list)
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,
mark_for_escaping)
from django.utils.formats import localize
@@ -675,6 +675,7 @@ class Variable(object):
self.literal = None
self.lookups = None
self.translate = False
+ self.message_context = None
try:
# 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"
value = self.literal
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
def __repr__(self):
diff --git a/django/templatetags/i18n.py b/django/templatetags/i18n.py
index 669cdc911a..dc3d93e90f 100644
--- a/django/templatetags/i18n.py
+++ b/django/templatetags/i18n.py
@@ -70,15 +70,21 @@ class GetCurrentLanguageBidiNode(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.asvar = asvar
+ self.message_context = message_context
self.filter_expression = filter_expression
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):
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)
value = _render_value_in_context(output, context)
if self.asvar:
@@ -90,12 +96,13 @@ class TranslateNode(Node):
class BlockTranslateNode(Node):
def __init__(self, extra_context, singular, plural=None, countervar=None,
- counter=None):
+ counter=None, message_context=None):
self.extra_context = extra_context
self.singular = singular
self.plural = plural
self.countervar = countervar
self.counter = counter
+ self.message_context = message_context
def render_token_list(self, tokens):
result = []
@@ -109,6 +116,10 @@ class BlockTranslateNode(Node):
return ''.join(result), vars
def render(self, context):
+ if self.message_context:
+ message_context = self.message_context.resolve(context)
+ else:
+ message_context = None
tmp_context = {}
for var, val in self.extra_context.items():
tmp_context[var] = val.resolve(context)
@@ -123,10 +134,17 @@ class BlockTranslateNode(Node):
context[self.countervar] = count
plural, plural_vars = self.render_token_list(self.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)
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])
context.pop()
try:
@@ -290,6 +308,17 @@ def do_translate(parser, token):
This will just try to translate the contents of
the variable ``variable``. Make sure that the string
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):
def top(self):
@@ -301,7 +330,6 @@ def do_translate(parser, token):
# backwards compatibility with existing uses of ``trans``
# where single quote use is supported.
if value[0] == "'":
- pos = None
m = re.match("^'([^']+)'(\|.*$)", value)
if m:
value = '"%s"%s' % (m.group(1).replace('"','\\"'), m.group(2))
@@ -310,19 +338,24 @@ def do_translate(parser, token):
noop = False
asvar = None
+ message_context = None
while self.more():
tag = self.tag()
if tag == 'noop':
noop = True
+ elif tag == 'context':
+ message_context = parser.compile_filter(self.value())
elif tag == 'as':
asvar = self.tag()
else:
raise TemplateSyntaxError(
- "only options for 'trans' are 'noop' and 'as VAR.")
- return (value, noop, asvar)
- value, noop, asvar = TranslateParser(token.contents).top()
- return TranslateNode(parser.compile_filter(value), noop, asvar)
+ "Only options for 'trans' are 'noop', " \
+ "'context \"xxx\"', and 'as VAR'.")
+ return value, noop, asvar, message_context
+ value, noop, asvar, message_context = TranslateParser(token.contents).top()
+ return TranslateNode(parser.compile_filter(value), noop, asvar,
+ message_context)
@register.tag("blocktrans")
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 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()
@@ -369,6 +411,13 @@ def do_block_translate(parser, token):
if len(value) != 1:
raise TemplateSyntaxError('"count" in %r tag expected exactly '
'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:
raise TemplateSyntaxError('Unknown argument for %r tag: %r.' %
(bits[0], option))
@@ -378,7 +427,11 @@ def do_block_translate(parser, token):
countervar, counter = options['count'].items()[0]
else:
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 = []
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)
return BlockTranslateNode(extra_context, singular, plural, countervar,
- counter)
+ counter, message_context)
@register.tag
def language(parser, token):
diff --git a/django/utils/translation/trans_real.py b/django/utils/translation/trans_real.py
index 71765e74e5..f43cfd9705 100644
--- a/django/utils/translation/trans_real.py
+++ b/django/utils/translation/trans_real.py
@@ -433,12 +433,14 @@ def blankout(src, char):
"""
return dot_re.sub(char, src)
-inline_re = re.compile(r"""^\s*trans\s+((?:".*?")|(?:'.*?'))\s*""")
-block_re = re.compile(r"""^\s*blocktrans(?:\s+|$)""")
+context_re = re.compile(r"""^\s+.*context\s+((?:"[^"]*?")|(?:'[^']*?'))\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$""")
plural_re = re.compile(r"""^\s*plural$""")
constant_re = re.compile(r"""_\(((?:".*?")|(?:'.*?'))\)""")
+
def templatize(src, origin=None):
"""
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,
TOKEN_COMMENT, TRANSLATOR_COMMENT_MARK)
out = StringIO()
+ message_context = None
intrans = False
inplural = False
singular = []
@@ -477,15 +480,22 @@ def templatize(src, origin=None):
pluralmatch = plural_re.match(t.contents)
if endbmatch:
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:
out.write(blankout(part, 'S'))
for part in plural:
out.write(blankout(part, 'P'))
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:
out.write(blankout(part, 'S'))
+ message_context = None
intrans = False
inplural = False
singular = []
@@ -515,12 +525,33 @@ def templatize(src, origin=None):
cmatches = constant_re.findall(t.contents)
if imatch:
g = imatch.group(1)
- if g[0] == '"': g = g.strip('"')
- elif g[0] == "'": g = g.strip("'")
- out.write(' gettext(%r) ' % g)
+ if g[0] == '"':
+ g = g.strip('"')
+ 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:
for fmatch in constant_re.findall(t.contents):
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
inplural = False
singular = []
diff --git a/docs/releases/1.4.txt b/docs/releases/1.4.txt
index 3e364fbe23..00acc65922 100644
--- a/docs/releases/1.4.txt
+++ b/docs/releases/1.4.txt
@@ -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
and how to internationalize URL patterns.
+Contextual translation support for ``{% trans %}`` and ``{% blocktrans %}``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Django 1.3 introduced :ref:`contextual translation` 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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/docs/topics/i18n/internationalization.txt b/docs/topics/i18n/internationalization.txt
index b06c9bdfdb..c8611acff8 100644
--- a/docs/topics/i18n/internationalization.txt
+++ b/docs/topics/i18n/internationalization.txt
@@ -266,6 +266,11 @@ will appear in the .po file as:
msgid "May"
msgstr ""
+.. versionadded:: 1.4
+
+Contextual markers are also supported by the :ttag:`trans` and
+:ttag:`blocktrans` template tags.
+
.. _lazy-translations:
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
``{% blocktrans %}`` instead.
-.. versionchanged:: 1.4
+.. versionadded:: 1.4
If you'd like to retrieve a translated string without displaying it, you can
use the following syntax::
@@ -479,6 +484,15 @@ or should be used as arguments for other template tags or filters::
{% endfor %}
+.. versionadded:: 1.4
+
+``{% trans %}`` also supports :ref:`contextual markers`
+using the ``context`` keyword:
+
+.. code-block:: html+django
+
+ {% trans "May" context "month name" %}
+
.. templatetag:: blocktrans
``blocktrans`` template tag
@@ -560,6 +574,15 @@ be retrieved (and stored) beforehand::
This is a URL: {{ the_url }}
{% endblocktrans %}
+.. versionadded:: 1.4
+
+``{% blocktrans %}`` also supports :ref:`contextual
+markers` using the ``context`` keyword:
+
+.. code-block:: html+django
+
+ {% blocktrans with name=user.username context "greeting" %}Hi {{ name }}{% endblocktrans %}
+
.. _template-translation-vars:
Other tags
diff --git a/tests/regressiontests/i18n/commands/extraction.py b/tests/regressiontests/i18n/commands/extraction.py
index 7d7cdf7485..c49577221e 100644
--- a/tests/regressiontests/i18n/commands/extraction.py
+++ b/tests/regressiontests/i18n/commands/extraction.py
@@ -94,6 +94,35 @@ class BasicExtractorTests(ExtractorTests):
os.remove('./templates/template_with_error.html')
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):
diff --git a/tests/regressiontests/i18n/commands/templates/test.html b/tests/regressiontests/i18n/commands/templates/test.html
index b5d705c132..24fc708621 100644
--- a/tests/regressiontests/i18n/commands/templates/test.html
+++ b/tests/regressiontests/i18n/commands/templates/test.html
@@ -57,3 +57,12 @@ continued here.{% endcomment %}
{% comment %} Translators: Two-line translator comment #5 -- with non ASCII characters: áéíóúö
continued here.{% endcomment %}
{% 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 %}
\ No newline at end of file
diff --git a/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.mo b/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.mo
index a1a93c83296f0fb97d500466a8df30338fdb137e..f825e3918b7fd74b7af37e55e704b260101367b7 100644
GIT binary patch
literal 1449
zcmbu9&2AGh5XTLKuYeFA2SA|86^fJyxy>e^R@(`(Iz5y?RpTHULD_94wpCsfK_!O*x``|h7J-7|cN6^Nf
zK(L!&0#1N0!E4|<@DBI^JOzFO?}FdKW$^N8LT-Rt;C)bn*dlL1gePZU_B=c6cRTDo
zhT{SzXJIzB9dOIuA`c=pLJGycP+cKSNf!!KiM|pVD@_YUnZ_kelg_T_n&av;S>{Ge
zn=lr{>j?zZX=La=w*kjaBA!)rlQ_mT@>t|r
z{D33|KEvIA2s7~0DKes3aMkVQvNJnE;k3c`LF%|T-r@J}nhgE?!%LFFaE87Aio7RO
zC%ax-CHC>zs?Qz^rNczJ)ZgVYNT?Co==_H0C(5vv4nm(jNCVBh#HIQ6y2t8Da03r!
ze-M{e%1)V;E6lk^of)@Uy^D1jLbf6HLK}Icb6DA76^B+Fw>rbhRp^>r8|Eo4b;M1g
zT>6Mh8EV=Tp)VpjkDRbvjI!laD0h+t_{bpTfnc8CG4d94-WQ8eIF+biA>UeUHAa_m
zrpoy`ep(a*^A7sR*NEq4x*zdS&e3jsjN>2dU9)yy+DNjGh+vSDtx>(6>0D2@oUu!X*n^)<);!f1|-k?-H5{#3YbYM_
E1CXVedjJ3c
delta 266
zcmYj~&k8|76o-%Na!FaR@$X7v%jndY3iBu>umm9mdQVo-nqB;X7tcj&+a;*bk{-&L^9xsH>#iSxLJ&8#^d!UjzL
z(-O(zT3Dor8#qmycx+g*qGa$Eo54p+?=R+#8J|R{rpE#ES>)h`_Q~l?2cwc!Q{Qf@
f^`URM*PG>7<+d72)hl)V#c~p;;D7H+OKB}{^SvR9
diff --git a/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.po b/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.po
index 7226ad1943..a471d3814b 100644
--- a/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.po
+++ b/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.po
@@ -52,3 +52,27 @@ msgid "%(percent)s%% represents %(num)s object"
msgid_plural "%(percent)s%% represents %(num)s objects"
msgstr[0] "%(percent)s%% stellt %(num)s Objekt 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"
\ No newline at end of file
diff --git a/tests/regressiontests/i18n/tests.py b/tests/regressiontests/i18n/tests.py
index 17a96ed433..bfed0ec882 100644
--- a/tests/regressiontests/i18n/tests.py
+++ b/tests/regressiontests/i18n/tests.py
@@ -9,6 +9,7 @@ from threading import local
from django.conf import settings
from django.template import Template, Context
+from django.template.base import TemplateSyntaxError
from django.test import TestCase, RequestFactory
from django.test.utils import override_settings
from django.utils import translation
@@ -96,6 +97,126 @@ class TranslationTests(TestCase):
self.assertEqual(pgettext("verb", "May"), u"Kann")
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):
"""
unicode(string_concat(...)) should not raise a TypeError - #4796