close Warning: HTML preview using PatchRenderer failed (ValueError: not enough values to unpack (expected 2, got 1))

Ticket #14806: 14806.template-tags-pgettext.4.diff

File 14806.template-tags-pgettext.4.diff, 29.8 KB (added by Julien Phalip, 13 years ago)
Line 
1diff --git a/django/template/base.py b/django/template/base.py
2index 8001e93..3a2b740 100644
3--- a/django/template/base.py
4+++ b/django/template/base.py
5@@ -10,7 +10,7 @@ from django.utils.itercompat import is_iterable
6 from django.utils.text import (smart_split, unescape_string_literal,
7 get_text_list)
8 from django.utils.encoding import smart_unicode, force_unicode, smart_str
9-from django.utils.translation import ugettext_lazy
10+from django.utils.translation import ugettext_lazy, pgettext_lazy
11 from django.utils.safestring import (SafeData, EscapeData, mark_safe,
12 mark_for_escaping)
13 from django.utils.formats import localize
14@@ -673,6 +673,7 @@ class Variable(object):
15 self.literal = None
16 self.lookups = None
17 self.translate = False
18+ self.message_context = None
19
20 try:
21 # First try to treat this variable as a number.
22@@ -720,7 +721,10 @@ class Variable(object):
23 # We're dealing with a literal, so it's already been "resolved"
24 value = self.literal
25 if self.translate:
26- return ugettext_lazy(value)
27+ if self.message_context:
28+ return pgettext_lazy(self.message_context, value)
29+ else:
30+ return ugettext_lazy(value)
31 return value
32
33 def __repr__(self):
34diff --git a/django/templatetags/i18n.py b/django/templatetags/i18n.py
35index 669cdc9..dc3d93e 100644
36--- a/django/templatetags/i18n.py
37+++ b/django/templatetags/i18n.py
38@@ -70,15 +70,21 @@ class GetCurrentLanguageBidiNode(Node):
39
40
41 class TranslateNode(Node):
42- def __init__(self, filter_expression, noop, asvar=None):
43+ def __init__(self, filter_expression, noop, asvar=None,
44+ message_context=None):
45 self.noop = noop
46 self.asvar = asvar
47+ self.message_context = message_context
48 self.filter_expression = filter_expression
49 if isinstance(self.filter_expression.var, basestring):
50- self.filter_expression.var = Variable(u"'%s'" % self.filter_expression.var)
51+ self.filter_expression.var = Variable(u"'%s'" %
52+ self.filter_expression.var)
53
54 def render(self, context):
55 self.filter_expression.var.translate = not self.noop
56+ if self.message_context:
57+ self.filter_expression.var.message_context = (
58+ self.message_context.resolve(context))
59 output = self.filter_expression.resolve(context)
60 value = _render_value_in_context(output, context)
61 if self.asvar:
62@@ -90,12 +96,13 @@ class TranslateNode(Node):
63
64 class BlockTranslateNode(Node):
65 def __init__(self, extra_context, singular, plural=None, countervar=None,
66- counter=None):
67+ counter=None, message_context=None):
68 self.extra_context = extra_context
69 self.singular = singular
70 self.plural = plural
71 self.countervar = countervar
72 self.counter = counter
73+ self.message_context = message_context
74
75 def render_token_list(self, tokens):
76 result = []
77@@ -109,6 +116,10 @@ class BlockTranslateNode(Node):
78 return ''.join(result), vars
79
80 def render(self, context):
81+ if self.message_context:
82+ message_context = self.message_context.resolve(context)
83+ else:
84+ message_context = None
85 tmp_context = {}
86 for var, val in self.extra_context.items():
87 tmp_context[var] = val.resolve(context)
88@@ -123,10 +134,17 @@ class BlockTranslateNode(Node):
89 context[self.countervar] = count
90 plural, plural_vars = self.render_token_list(self.plural)
91 plural = re.sub(u'%(?!\()', u'%%', plural)
92- result = translation.ungettext(singular, plural, count)
93+ if message_context:
94+ result = translation.npgettext(message_context, singular,
95+ plural, count)
96+ else:
97+ result = translation.ungettext(singular, plural, count)
98 vars.extend(plural_vars)
99 else:
100- result = translation.ugettext(singular)
101+ if message_context:
102+ result = translation.pgettext(message_context, singular)
103+ else:
104+ result = translation.ugettext(singular)
105 data = dict([(v, _render_value_in_context(context.get(v, ''), context)) for v in vars])
106 context.pop()
107 try:
108@@ -290,6 +308,17 @@ def do_translate(parser, token):
109 This will just try to translate the contents of
110 the variable ``variable``. Make sure that the string
111 in there is something that is in the .po file.
112+
113+ It is possible to store the translated string into a variable::
114+
115+ {% trans "this is a test" as var %}
116+ {{ var }}
117+
118+ Contextual translations are also supported::
119+
120+ {% trans "this is a test" context "greeting" %}
121+
122+ This is equivalent to calling pgettext instead of (u)gettext.
123 """
124 class TranslateParser(TokenParser):
125 def top(self):
126@@ -301,7 +330,6 @@ def do_translate(parser, token):
127 # backwards compatibility with existing uses of ``trans``
128 # where single quote use is supported.
129 if value[0] == "'":
130- pos = None
131 m = re.match("^'([^']+)'(\|.*$)", value)
132 if m:
133 value = '"%s"%s' % (m.group(1).replace('"','\\"'), m.group(2))
134@@ -310,19 +338,24 @@ def do_translate(parser, token):
135
136 noop = False
137 asvar = None
138+ message_context = None
139
140 while self.more():
141 tag = self.tag()
142 if tag == 'noop':
143 noop = True
144+ elif tag == 'context':
145+ message_context = parser.compile_filter(self.value())
146 elif tag == 'as':
147 asvar = self.tag()
148 else:
149 raise TemplateSyntaxError(
150- "only options for 'trans' are 'noop' and 'as VAR.")
151- return (value, noop, asvar)
152- value, noop, asvar = TranslateParser(token.contents).top()
153- return TranslateNode(parser.compile_filter(value), noop, asvar)
154+ "Only options for 'trans' are 'noop', " \
155+ "'context \"xxx\"', and 'as VAR'.")
156+ return value, noop, asvar, message_context
157+ value, noop, asvar, message_context = TranslateParser(token.contents).top()
158+ return TranslateNode(parser.compile_filter(value), noop, asvar,
159+ message_context)
160
161 @register.tag("blocktrans")
162 def do_block_translate(parser, token):
163@@ -349,6 +382,15 @@ def do_block_translate(parser, token):
164
165 {% blocktrans with foo|filter as bar and baz|filter as boo %}
166 {% blocktrans count var|length as count %}
167+
168+ Contextual translations are supported::
169+
170+ {% blocktrans with bar=foo|filter context "greeting" %}
171+ This is {{ bar }}.
172+ {% endblocktrans %}
173+
174+ This is equivalent to calling pgettext/npgettext instead of
175+ (u)gettext/(u)ngettext.
176 """
177 bits = token.split_contents()
178
179@@ -369,6 +411,13 @@ def do_block_translate(parser, token):
180 if len(value) != 1:
181 raise TemplateSyntaxError('"count" in %r tag expected exactly '
182 'one keyword argument.' % bits[0])
183+ elif option == "context":
184+ try:
185+ value = remaining_bits.pop(0)
186+ value = parser.compile_filter(value)
187+ except Exception:
188+ raise TemplateSyntaxError('"context" in %r tag expected '
189+ 'exactly one argument.' % bits[0])
190 else:
191 raise TemplateSyntaxError('Unknown argument for %r tag: %r.' %
192 (bits[0], option))
193@@ -378,7 +427,11 @@ def do_block_translate(parser, token):
194 countervar, counter = options['count'].items()[0]
195 else:
196 countervar, counter = None, None
197- extra_context = options.get('with', {})
198+ if 'context' in options:
199+ message_context = options['context']
200+ else:
201+ message_context = None
202+ extra_context = options.get('with', {})
203
204 singular = []
205 plural = []
206@@ -401,7 +454,7 @@ def do_block_translate(parser, token):
207 raise TemplateSyntaxError("'blocktrans' doesn't allow other block tags (seen %r) inside it" % token.contents)
208
209 return BlockTranslateNode(extra_context, singular, plural, countervar,
210- counter)
211+ counter, message_context)
212
213 @register.tag
214 def language(parser, token):
215diff --git a/django/utils/translation/trans_real.py b/django/utils/translation/trans_real.py
216index 71765e7..f43cfd9 100644
217--- a/django/utils/translation/trans_real.py
218+++ b/django/utils/translation/trans_real.py
219@@ -433,12 +433,14 @@ def blankout(src, char):
220 """
221 return dot_re.sub(char, src)
222
223-inline_re = re.compile(r"""^\s*trans\s+((?:".*?")|(?:'.*?'))\s*""")
224-block_re = re.compile(r"""^\s*blocktrans(?:\s+|$)""")
225+context_re = re.compile(r"""^\s+.*context\s+((?:"[^"]*?")|(?:'[^']*?'))\s*""")
226+inline_re = re.compile(r"""^\s*trans\s+((?:"[^"]*?")|(?:'[^']*?'))(\s+.*context\s+(?:"[^"]*?")|(?:'[^']*?'))?\s*""")
227+block_re = re.compile(r"""^\s*blocktrans(\s+.*context\s+(?:"[^"]*?")|(?:'[^']*?'))?(?:\s+|$)""")
228 endblock_re = re.compile(r"""^\s*endblocktrans$""")
229 plural_re = re.compile(r"""^\s*plural$""")
230 constant_re = re.compile(r"""_\(((?:".*?")|(?:'.*?'))\)""")
231
232+
233 def templatize(src, origin=None):
234 """
235 Turns a Django template into something that is understood by xgettext. It
236@@ -448,6 +450,7 @@ def templatize(src, origin=None):
237 from django.template import (Lexer, TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK,
238 TOKEN_COMMENT, TRANSLATOR_COMMENT_MARK)
239 out = StringIO()
240+ message_context = None
241 intrans = False
242 inplural = False
243 singular = []
244@@ -477,15 +480,22 @@ def templatize(src, origin=None):
245 pluralmatch = plural_re.match(t.contents)
246 if endbmatch:
247 if inplural:
248- out.write(' ngettext(%r,%r,count) ' % (''.join(singular), ''.join(plural)))
249+ if message_context:
250+ out.write(' npgettext(%r, %r, %r,count) ' % (message_context, ''.join(singular), ''.join(plural)))
251+ else:
252+ out.write(' ngettext(%r, %r, count) ' % (''.join(singular), ''.join(plural)))
253 for part in singular:
254 out.write(blankout(part, 'S'))
255 for part in plural:
256 out.write(blankout(part, 'P'))
257 else:
258- out.write(' gettext(%r) ' % ''.join(singular))
259+ if message_context:
260+ out.write(' pgettext(%r, %r) ' % (message_context, ''.join(singular)))
261+ else:
262+ out.write(' gettext(%r) ' % ''.join(singular))
263 for part in singular:
264 out.write(blankout(part, 'S'))
265+ message_context = None
266 intrans = False
267 inplural = False
268 singular = []
269@@ -515,12 +525,33 @@ def templatize(src, origin=None):
270 cmatches = constant_re.findall(t.contents)
271 if imatch:
272 g = imatch.group(1)
273- if g[0] == '"': g = g.strip('"')
274- elif g[0] == "'": g = g.strip("'")
275- out.write(' gettext(%r) ' % g)
276+ if g[0] == '"':
277+ g = g.strip('"')
278+ elif g[0] == "'":
279+ g = g.strip("'")
280+ if imatch.group(2):
281+ # A context is provided
282+ context_match = context_re.match(imatch.group(2))
283+ message_context = context_match.group(1)
284+ if message_context[0] == '"':
285+ message_context = message_context.strip('"')
286+ elif message_context[0] == "'":
287+ message_context = message_context.strip("'")
288+ out.write(' pgettext(%r, %r) ' % (message_context, g))
289+ message_context = None
290+ else:
291+ out.write(' gettext(%r) ' % g)
292 elif bmatch:
293 for fmatch in constant_re.findall(t.contents):
294 out.write(' _(%s) ' % fmatch)
295+ if bmatch.group(1):
296+ # A context is provided
297+ context_match = context_re.match(bmatch.group(1))
298+ message_context = context_match.group(1)
299+ if message_context[0] == '"':
300+ message_context = message_context.strip('"')
301+ elif message_context[0] == "'":
302+ message_context = message_context.strip("'")
303 intrans = True
304 inplural = False
305 singular = []
306diff --git a/docs/releases/1.4.txt b/docs/releases/1.4.txt
307index 3e364fb..00acc65 100644
308--- a/docs/releases/1.4.txt
309+++ b/docs/releases/1.4.txt
310@@ -191,6 +191,14 @@ Additionally, it's now possible to define translatable URL patterns using
311 :ref:`url-internationalization` for more information about the language prefix
312 and how to internationalize URL patterns.
313
314+Contextual translation support for ``{% trans %}`` and ``{% blocktrans %}``
315+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
316+
317+Django 1.3 introduced :ref:`contextual translation<contextual-markers>` support
318+in Python files via the ``pgettext`` function. This is now also supported by
319+the :ttag:`trans` and :ttag:`blocktrans` template tags using the new
320+``context`` keyword.
321+
322 Customizable ``SingleObjectMixin`` URLConf kwargs
323 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
324
325diff --git a/docs/topics/i18n/internationalization.txt b/docs/topics/i18n/internationalization.txt
326index b06c9bd..c8611ac 100644
327--- a/docs/topics/i18n/internationalization.txt
328+++ b/docs/topics/i18n/internationalization.txt
329@@ -266,6 +266,11 @@ will appear in the .po file as:
330 msgid "May"
331 msgstr ""
332
333+.. versionadded:: 1.4
334+
335+Contextual markers are also supported by the :ttag:`trans` and
336+:ttag:`blocktrans` template tags.
337+
338 .. _lazy-translations:
339
340 Lazy translation
341@@ -453,7 +458,7 @@ It's not possible to mix a template variable inside a string within ``{% trans
342 %}``. If your translations require strings with variables (placeholders), use
343 ``{% blocktrans %}`` instead.
344
345-.. versionchanged:: 1.4
346+.. versionadded:: 1.4
347
348 If you'd like to retrieve a translated string without displaying it, you can
349 use the following syntax::
350@@ -479,6 +484,15 @@ or should be used as arguments for other template tags or filters::
351 {% endfor %}
352 </p>
353
354+.. versionadded:: 1.4
355+
356+``{% trans %}`` also supports :ref:`contextual markers<contextual-markers>`
357+using the ``context`` keyword:
358+
359+.. code-block:: html+django
360+
361+ {% trans "May" context "month name" %}
362+
363 .. templatetag:: blocktrans
364
365 ``blocktrans`` template tag
366@@ -560,6 +574,15 @@ be retrieved (and stored) beforehand::
367 This is a URL: {{ the_url }}
368 {% endblocktrans %}
369
370+.. versionadded:: 1.4
371+
372+``{% blocktrans %}`` also supports :ref:`contextual
373+markers<contextual-markers>` using the ``context`` keyword:
374+
375+.. code-block:: html+django
376+
377+ {% blocktrans with name=user.username context "greeting" %}Hi {{ name }}{% endblocktrans %}
378+
379 .. _template-translation-vars:
380
381 Other tags
382diff --git a/tests/regressiontests/i18n/commands/extraction.py b/tests/regressiontests/i18n/commands/extraction.py
383index 7d7cdf7..c495772 100644
384--- a/tests/regressiontests/i18n/commands/extraction.py
385+++ b/tests/regressiontests/i18n/commands/extraction.py
386@@ -94,6 +94,35 @@ class BasicExtractorTests(ExtractorTests):
387 os.remove('./templates/template_with_error.html')
388 os.remove('./templates/template_with_error.html.py') # Waiting for #8536 to be fixed
389
390+ def test_template_message_context_extractor(self):
391+ """
392+ Ensure that message contexts are correctly extracted for the
393+ {% trans %} and {% blocktrans %} template tags.
394+ Refs #14806.
395+ """
396+ os.chdir(self.test_dir)
397+ management.call_command('makemessages', locale=LOCALE, verbosity=0)
398+ self.assertTrue(os.path.exists(self.PO_FILE))
399+ po_contents = open(self.PO_FILE, 'r').read()
400+ # {% trans %}
401+ self.assertTrue('msgctxt "Special trans context #1"' in po_contents)
402+ self.assertTrue("Translatable literal #7a" in po_contents)
403+ self.assertTrue('msgctxt "Special trans context #2"' in po_contents)
404+ self.assertTrue("Translatable literal #7b" in po_contents)
405+ self.assertTrue('msgctxt "Special trans context #3"' in po_contents)
406+ self.assertTrue("Translatable literal #7c" in po_contents)
407+
408+ # {% blocktrans %}
409+ self.assertTrue('msgctxt "Special blocktrans context #1"' in po_contents)
410+ self.assertTrue("Translatable literal #8a" in po_contents)
411+ self.assertTrue('msgctxt "Special blocktrans context #2"' in po_contents)
412+ self.assertTrue("Translatable literal #8b-singular" in po_contents)
413+ self.assertTrue("Translatable literal #8b-plural" in po_contents)
414+ self.assertTrue('msgctxt "Special blocktrans context #3"' in po_contents)
415+ self.assertTrue("Translatable literal #8c-singular" in po_contents)
416+ self.assertTrue("Translatable literal #8c-plural" in po_contents)
417+ self.assertTrue('msgctxt "Special blocktrans context #4"' in po_contents)
418+ self.assertTrue("Translatable literal #8d" in po_contents)
419
420 class JavascriptExtractorTests(ExtractorTests):
421
422diff --git a/tests/regressiontests/i18n/commands/templates/test.html b/tests/regressiontests/i18n/commands/templates/test.html
423index b5d705c..24fc708 100644
424--- a/tests/regressiontests/i18n/commands/templates/test.html
425+++ b/tests/regressiontests/i18n/commands/templates/test.html
426@@ -57,3 +57,12 @@ continued here.{% endcomment %}
427 {% comment %} Translators: Two-line translator comment #5 -- with non ASCII characters: áéíóúö
428 continued here.{% endcomment %}
429 {% trans "Translatable literal #6b" %}
430+
431+{% trans "Translatable literal #7a" context "Special trans context #1" %}
432+{% trans "Translatable literal #7b" as var context "Special trans context #2" %}
433+{% trans "Translatable literal #7c" context "Special trans context #3" as var %}
434+
435+{% blocktrans context "Special blocktrans context #1" %}Translatable literal #8a{% endblocktrans %}
436+{% blocktrans count 2 context "Special blocktrans context #2" %}Translatable literal #8b-singular{% plural %}Translatable literal #8b-plural{% endblocktrans %}
437+{% blocktrans context "Special blocktrans context #3" count 2 %}Translatable literal #8c-singular{% plural %}Translatable literal #8c-plural{% endblocktrans %}
438+{% blocktrans with a=1 context "Special blocktrans context #4" %}Translatable literal #8d {{ a }}{% endblocktrans %}
439\ No newline at end of file
440diff --git a/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.mo b/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.mo
441index a1a93c83296f0fb97d500466a8df30338fdb137e..f825e3918b7fd74b7af37e55e704b260101367b7 100644
442GIT binary patch
443literal 1449
444zcmbu9&2AGh5XTLKuYeFA2SA|86^fJyxy>e^R@<g(NmEgps8PG+0Ef!Sx|=P#-pKa0
445zRN@7A01jL@ao`2GA)bK;K)`_q;qOgDLIJ4=OQWB?{?B;q%;d|}+$(}{5q2B)0(J%V
446z8D<APK}Z{%1kZyC%yeK5>(`(Iz5y?RpTHULD_94wpCsfK_!O*x``|h7J-7|cN6^Nf
447zK(L!&0#1N0!E4|<@DBI^JOzFO?}FdKW$^N8LT-Rt;C)bn*dlL1gePZU_B=c6cRTDo
448zhT{SzXJIzB9dOIuA`c=pLJGycP+cKSNf!!KiM|pVD@_YUnZ_kelg_T_n&av;S>{Ge
449zn=lr{>j?zZX=La=w*kjaBA!)rlQ_m<w40>T<e*oAa#(iu_TjEZ62mEp6KQ%>@>t|r
450z{D33|KEvIA2s7~0DKes3aMkVQvNJnE;k3c`LF%|T-r@J}nhgE?!%LFFaE87Aio7RO
451zC%ax-CHC>zs?Qz^rNczJ)ZgVYNT?Co==_H0C(5vv4nm(jNCVBh#HIQ6y2t8Da03r!
452ze-M{e%1)V;E6lk^of)@Uy^D1jLbf6HLK}Icb6DA76^B+Fw>rbhRp^>r8|Eo4b;M1g
453zT>6Mh8EV=Tp)VpjkDRbvjI!laD0h+t_{bpTfnc8CG4d94-WQ8eIF+biA>UeUHAa_m
454zrpoy`ep(a*^A7sR*NEq4x*zdS&e3j<D=kcI%UfZyBb`lWM<~{i-NX-N;L_PnX!7kS
455zRXk!ViHfyLWj|Z$T4jz7rnRD^lQrs;@<)nV8xf&ahlRh6!hdF{&((2sA`{}R(|G!c
456z2n{VZ*Xv8o#&&zjd$?IbTWH!)f#}$0+vf_$jJLy|TpF4NVdpTR@orcfv<*6iw0KCC
457zq>sjN>2dU9)yy+DNjGh+vSDtx>(6>0D2@oUu!X*n^)<);!f1|-k?-H5{<?#3YbYM_
458E1CXVedjJ3c
459
460delta 266
461zcmYj~&k8|76o-%Na!FaR@$X7v<tZ%KNJ-g9Y3kmh7|B&g$ur1CUcg%3fmg8bEWV-S
462z*L>%jndY3iBu>umm9mdQVo-nqB;X7tcj&+a;*bk{-&L^9xsH>#iSxLJ&8#^d!UjzL
463z(-O(zT3Dor8#qmycx+g*qGa$Eo54p+?=R+#8J|R{rpE#ES>)h`_Q~l?2cwc!Q{Qf@
464f^`URM*PG>7<+d72)hl)V#c~p;;D7H+OKB}{^SvR9
465
466diff --git a/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.po b/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.po
467index 7226ad1..a471d38 100644
468--- a/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.po
469+++ b/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.po
470@@ -52,3 +52,27 @@ msgid "%(percent)s%% represents %(num)s object"
471 msgid_plural "%(percent)s%% represents %(num)s objects"
472 msgstr[0] "%(percent)s%% stellt %(num)s Objekt dar"
473 msgstr[1] "%(percent)s%% stellt %(num)s Objekte dar"
474+
475+#: models.py:17
476+msgctxt "super search"
477+msgid "%(number)s super result"
478+msgid_plural "%(number)s super results"
479+msgstr[0] "%(number)s Super-Ergebnis"
480+msgstr[1] "%(number)s Super-Ergebnisse"
481+
482+#: models.py:19
483+msgctxt "other super search"
484+msgid "%(number)s super result"
485+msgid_plural "%(number)s super results"
486+msgstr[0] "%(number)s anderen Super-Ergebnis"
487+msgstr[1] "%(number)s andere Super-Ergebnisse"
488+
489+#: models.py:21
490+msgctxt "comment count"
491+msgid "There are %(num_comments)s comments"
492+msgstr "Es gibt %(num_comments)s Kommentare"
493+
494+#: models.py:23
495+msgctxt "other comment count"
496+msgid "There are %(num_comments)s comments"
497+msgstr "Andere: Es gibt %(num_comments)s Kommentare"
498\ No newline at end of file
499diff --git a/tests/regressiontests/i18n/tests.py b/tests/regressiontests/i18n/tests.py
500index fc69d9c..10e9935 100644
501--- a/tests/regressiontests/i18n/tests.py
502+++ b/tests/regressiontests/i18n/tests.py
503@@ -9,6 +9,7 @@ from threading import local
504
505 from django.conf import settings
506 from django.template import Template, Context
507+from django.template.base import TemplateSyntaxError
508 from django.test import TestCase, RequestFactory
509 from django.test.utils import override_settings
510 from django.utils import translation
511@@ -87,6 +88,126 @@ class TranslationTests(TestCase):
512 self.assertEqual(pgettext("verb", "May"), u"Kann")
513 self.assertEqual(npgettext("search", "%d result", "%d results", 4) % 4, u"4 Resultate")
514
515+ def test_template_tags_pgettext(self):
516+ """
517+ Ensure that message contexts are taken into account the {% trans %} and
518+ {% blocktrans %} template tags.
519+ Refs #14806.
520+ """
521+ # Reset translation catalog to include other/locale/de
522+ extended_locale_paths = settings.LOCALE_PATHS + (
523+ os.path.join(here, 'other', 'locale'),
524+ )
525+ with self.settings(LOCALE_PATHS=extended_locale_paths):
526+ from django.utils.translation import trans_real
527+ trans_real._active = local()
528+ trans_real._translations = {}
529+ with translation.override('de'):
530+
531+ # {% trans %} -----------------------------------
532+
533+ # Inexisting context...
534+ t = Template('{% load i18n %}{% trans "May" context "unexisting" %}')
535+ rendered = t.render(Context())
536+ self.assertEqual(rendered, 'May')
537+
538+ # Existing context...
539+ # Using a literal
540+ t = Template('{% load i18n %}{% trans "May" context "month name" %}')
541+ rendered = t.render(Context())
542+ self.assertEqual(rendered, 'Mai')
543+ t = Template('{% load i18n %}{% trans "May" context "verb" %}')
544+ rendered = t.render(Context())
545+ self.assertEqual(rendered, 'Kann')
546+
547+ # Using a variable
548+ t = Template('{% load i18n %}{% trans "May" context message_context %}')
549+ rendered = t.render(Context({'message_context': 'month name'}))
550+ self.assertEqual(rendered, 'Mai')
551+ t = Template('{% load i18n %}{% trans "May" context message_context %}')
552+ rendered = t.render(Context({'message_context': 'verb'}))
553+ self.assertEqual(rendered, 'Kann')
554+
555+ # Using a filter
556+ t = Template('{% load i18n %}{% trans "May" context message_context|lower %}')
557+ rendered = t.render(Context({'message_context': 'MONTH NAME'}))
558+ self.assertEqual(rendered, 'Mai')
559+ t = Template('{% load i18n %}{% trans "May" context message_context|lower %}')
560+ rendered = t.render(Context({'message_context': 'VERB'}))
561+ self.assertEqual(rendered, 'Kann')
562+
563+ # Using 'as'
564+ t = Template('{% load i18n %}{% trans "May" context "month name" as var %}Value: {{ var }}')
565+ rendered = t.render(Context())
566+ self.assertEqual(rendered, 'Value: Mai')
567+ t = Template('{% load i18n %}{% trans "May" as var context "verb" %}Value: {{ var }}')
568+ rendered = t.render(Context())
569+ self.assertEqual(rendered, 'Value: Kann')
570+
571+ # Mis-uses
572+ self.assertRaises(TemplateSyntaxError, Template, '{% load i18n %}{% trans "May" context as var %}{{ var }}')
573+ self.assertRaises(TemplateSyntaxError, Template, '{% load i18n %}{% trans "May" as var context %}{{ var }}')
574+
575+ # {% blocktrans %} ------------------------------
576+
577+ # Inexisting context...
578+ t = Template('{% load i18n %}{% blocktrans context "unexisting" %}May{% endblocktrans %}')
579+ rendered = t.render(Context())
580+ self.assertEqual(rendered, 'May')
581+
582+ # Existing context...
583+ # Using a literal
584+ t = Template('{% load i18n %}{% blocktrans context "month name" %}May{% endblocktrans %}')
585+ rendered = t.render(Context())
586+ self.assertEqual(rendered, 'Mai')
587+ t = Template('{% load i18n %}{% blocktrans context "verb" %}May{% endblocktrans %}')
588+ rendered = t.render(Context())
589+ self.assertEqual(rendered, 'Kann')
590+
591+ # Using a variable
592+ t = Template('{% load i18n %}{% blocktrans context message_context %}May{% endblocktrans %}')
593+ rendered = t.render(Context({'message_context': 'month name'}))
594+ self.assertEqual(rendered, 'Mai')
595+ t = Template('{% load i18n %}{% blocktrans context message_context %}May{% endblocktrans %}')
596+ rendered = t.render(Context({'message_context': 'verb'}))
597+ self.assertEqual(rendered, 'Kann')
598+
599+ # Using a filter
600+ t = Template('{% load i18n %}{% blocktrans context message_context|lower %}May{% endblocktrans %}')
601+ rendered = t.render(Context({'message_context': 'MONTH NAME'}))
602+ self.assertEqual(rendered, 'Mai')
603+ t = Template('{% load i18n %}{% blocktrans context message_context|lower %}May{% endblocktrans %}')
604+ rendered = t.render(Context({'message_context': 'VERB'}))
605+ self.assertEqual(rendered, 'Kann')
606+
607+ # Using 'count'
608+ t = Template('{% load i18n %}{% blocktrans count number=1 context "super search" %}{{ number }} super result{% plural %}{{ number }} super results{% endblocktrans %}')
609+ rendered = t.render(Context())
610+ self.assertEqual(rendered, '1 Super-Ergebnis')
611+ t = Template('{% load i18n %}{% blocktrans count number=2 context "super search" %}{{ number }} super result{% plural %}{{ number }} super results{% endblocktrans %}')
612+ rendered = t.render(Context())
613+ self.assertEqual(rendered, '2 Super-Ergebnisse')
614+ t = Template('{% load i18n %}{% blocktrans context "other super search" count number=1 %}{{ number }} super result{% plural %}{{ number }} super results{% endblocktrans %}')
615+ rendered = t.render(Context())
616+ self.assertEqual(rendered, '1 anderen Super-Ergebnis')
617+ t = Template('{% load i18n %}{% blocktrans context "other super search" count number=2 %}{{ number }} super result{% plural %}{{ number }} super results{% endblocktrans %}')
618+ rendered = t.render(Context())
619+ self.assertEqual(rendered, '2 andere Super-Ergebnisse')
620+
621+ # Using 'with'
622+ t = Template('{% load i18n %}{% blocktrans with num_comments=5 context "comment count" %}There are {{ num_comments }} comments{% endblocktrans %}')
623+ rendered = t.render(Context())
624+ self.assertEqual(rendered, 'Es gibt 5 Kommentare')
625+ t = Template('{% load i18n %}{% blocktrans with num_comments=5 context "other comment count" %}There are {{ num_comments }} comments{% endblocktrans %}')
626+ rendered = t.render(Context())
627+ self.assertEqual(rendered, 'Andere: Es gibt 5 Kommentare')
628+
629+ # Mis-uses
630+ self.assertRaises(TemplateSyntaxError, Template, '{% load i18n %}{% blocktrans context with month="May" %}{{ month }}{% endblocktrans %}')
631+ self.assertRaises(TemplateSyntaxError, Template, '{% load i18n %}{% blocktrans context %}{% endblocktrans %}')
632+ self.assertRaises(TemplateSyntaxError, Template, '{% load i18n %}{% blocktrans count number=2 context %}{{ number }} super result{% plural %}{{ number }} super results{% endblocktrans %}')
633+
634+
635 def test_string_concat(self):
636 """
637 unicode(string_concat(...)) should not raise a TypeError - #4796
Back to Top