close Warning: HTML preview using PatchRenderer failed (ValueError: need more than 1 value to unpack)

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

File 14806.template-tags-pgettext.diff, 14.5 KB (added by julien, 4 years ago)
Line 
1diff --git a/django/template/base.py b/django/template/base.py
2index 8001e93..3129886 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.trans_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.trans_context:
28+                return pgettext_lazy(self.trans_context, value)
29+            else:
30+                return ugettext_lazy(value)
31         return value
32 
33     def __repr__(self):
34@@ -814,11 +818,10 @@ class NodeList(list):
35         bits = []
36         for node in self:
37             if isinstance(node, Node):
38-                bit = self.render_node(node, context)
39+                bits.append(self.render_node(node, context))
40             else:
41-                bit = node
42-            bits.append(force_unicode(bit))
43-        return mark_safe(u''.join(bits))
44+                bits.append(node)
45+        return mark_safe(''.join([force_unicode(b) for b in bits]))
46 
47     def get_nodes_by_type(self, nodetype):
48         "Return a list of all nodes of the given type"
49diff --git a/django/templatetags/i18n.py b/django/templatetags/i18n.py
50index 669cdc9..4f64316 100644
51--- a/django/templatetags/i18n.py
52+++ b/django/templatetags/i18n.py
53@@ -70,15 +70,21 @@ class GetCurrentLanguageBidiNode(Node):
54 
55 
56 class TranslateNode(Node):
57-    def __init__(self, filter_expression, noop, asvar=None):
58+    def __init__(self, filter_expression, noop, asvar=None,
59+                 trans_context=None):
60         self.noop = noop
61         self.asvar = asvar
62+        self.trans_context = trans_context
63         self.filter_expression = filter_expression
64         if isinstance(self.filter_expression.var, basestring):
65-            self.filter_expression.var = Variable(u"'%s'" % self.filter_expression.var)
66+            self.filter_expression.var = Variable(u"'%s'" %
67+                                                  self.filter_expression.var)
68 
69     def render(self, context):
70         self.filter_expression.var.translate = not self.noop
71+        if self.trans_context:
72+            self.filter_expression.var.trans_context = (
73+                self.trans_context.resolve(context))
74         output = self.filter_expression.resolve(context)
75         value = _render_value_in_context(output, context)
76         if self.asvar:
77@@ -90,12 +96,13 @@ class TranslateNode(Node):
78 
79 class BlockTranslateNode(Node):
80     def __init__(self, extra_context, singular, plural=None, countervar=None,
81-            counter=None):
82+            counter=None, trans_context=None):
83         self.extra_context = extra_context
84         self.singular = singular
85         self.plural = plural
86         self.countervar = countervar
87         self.counter = counter
88+        self.trans_context = trans_context
89 
90     def render_token_list(self, tokens):
91         result = []
92@@ -109,6 +116,10 @@ class BlockTranslateNode(Node):
93         return ''.join(result), vars
94 
95     def render(self, context):
96+        if self.trans_context:
97+            trans_context = self.trans_context.resolve(context)
98+        else:
99+            trans_context = None
100         tmp_context = {}
101         for var, val in self.extra_context.items():
102             tmp_context[var] = val.resolve(context)
103@@ -123,10 +134,17 @@ class BlockTranslateNode(Node):
104             context[self.countervar] = count
105             plural, plural_vars = self.render_token_list(self.plural)
106             plural = re.sub(u'%(?!\()', u'%%', plural)
107-            result = translation.ungettext(singular, plural, count)
108+            if trans_context:
109+                result = translation.npgettext(trans_context, singular, plural,
110+                                               count)
111+            else:
112+                result = translation.ungettext(singular, plural, count)
113             vars.extend(plural_vars)
114         else:
115-            result = translation.ugettext(singular)
116+            if trans_context:
117+                result = translation.pgettext(trans_context, singular)
118+            else:
119+                result = translation.ugettext(singular)
120         data = dict([(v, _render_value_in_context(context.get(v, ''), context)) for v in vars])
121         context.pop()
122         try:
123@@ -301,7 +319,6 @@ def do_translate(parser, token):
124             # backwards compatibility with existing uses of ``trans``
125             # where single quote use is supported.
126             if value[0] == "'":
127-                pos = None
128                 m = re.match("^'([^']+)'(\|.*$)", value)
129                 if m:
130                     value = '"%s"%s' % (m.group(1).replace('"','\\"'), m.group(2))
131@@ -310,19 +327,23 @@ def do_translate(parser, token):
132 
133             noop = False
134             asvar = None
135+            trans_context = None
136 
137             while self.more():
138                 tag = self.tag()
139                 if tag == 'noop':
140                     noop = True
141+                elif tag == 'context':
142+                    trans_context = parser.compile_filter(self.tag())
143                 elif tag == 'as':
144                     asvar = self.tag()
145                 else:
146                     raise TemplateSyntaxError(
147                         "only options for 'trans' are 'noop' and 'as VAR.")
148-            return (value, noop, asvar)
149-    value, noop, asvar = TranslateParser(token.contents).top()
150-    return TranslateNode(parser.compile_filter(value), noop, asvar)
151+            return value, noop, asvar, trans_context
152+    value, noop, asvar, trans_context = TranslateParser(token.contents).top()
153+    return TranslateNode(parser.compile_filter(value), noop, asvar,
154+                         trans_context)
155 
156 @register.tag("blocktrans")
157 def do_block_translate(parser, token):
158@@ -369,6 +390,13 @@ def do_block_translate(parser, token):
159             if len(value) != 1:
160                 raise TemplateSyntaxError('"count" in %r tag expected exactly '
161                                           'one keyword argument.' % bits[0])
162+        elif option == "context":
163+            try:
164+                value = remaining_bits.pop(0)
165+                value = parser.compile_filter(value)
166+            except Exception:
167+                raise TemplateSyntaxError('"context" in %r tag expected '
168+                                          'exactly one argument.' % bits[0])
169         else:
170             raise TemplateSyntaxError('Unknown argument for %r tag: %r.' %
171                                       (bits[0], option))
172@@ -378,7 +406,11 @@ def do_block_translate(parser, token):
173         countervar, counter = options['count'].items()[0]
174     else:
175         countervar, counter = None, None
176-    extra_context = options.get('with', {})
177+    if 'context' in options:
178+        trans_context = options['context']
179+    else:
180+        trans_context = None
181+    extra_context = options.get('with', {})
182 
183     singular = []
184     plural = []
185@@ -401,7 +433,7 @@ def do_block_translate(parser, token):
186         raise TemplateSyntaxError("'blocktrans' doesn't allow other block tags (seen %r) inside it" % token.contents)
187 
188     return BlockTranslateNode(extra_context, singular, plural, countervar,
189-            counter)
190+            counter, trans_context)
191 
192 @register.tag
193 def language(parser, token):
194diff --git a/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.mo b/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.mo
195index a1a93c83296f0fb97d500466a8df30338fdb137e..9ab2f4672535fe38d864ec37cbd7815cac8db453 100644
196GIT binary patch
197delta 554
198zcmaKnu}T9$5QZnoX^dbBMbSj$5KofA(n@TMR$2%?fLxNr3&vG;bCt+7d4V7{zJ{F$
199z;zL;KD;Qru{5J70N_5$Ozq#4jf97uUkA>uQ*1HkbG+IV&G=m<HTYYqh-cbSVWQKJI
200z(Br!Wi%`QFyn<!;1h?S}oPy=7$T9I6bbj5O$dX7R&jc$Z-ryW8;z}s1a2Kw_MR*D~
201zVHdLHo&!IbKyFPAF*xZlXzB*eBlmO8)4h|Ra-mE^=}y%KLGPo~>#O9v6Di}{m>b`!
202z&@>`1sOb2tu1wW_<&Cm&yTkLbc`NC=rk_@(5qJMPFO0Xoi!$@s+IPQDH<eM^KXPsx
203jd*(#db<2)UG_3elVAb_yuCz5W_}>OUZ>*C2P;2=F0$+a)
204
205delta 225
206zcmdnbIg7pio)F7a1|Z-7Vi_Qg0b*_-o&&@nZ~}+}fcPX3a{{pxBSf7FkY)k$9f33-
207zkah#o(m*;CNQ2BO1k&+9Tm{4+{kNDH7$kwTHZuc*9FTSe(!xM(3@Jbc5HVB$2?-!w
208z1EfI)PX*FIy>I{$1DXK_Y(NsEhaHGPma$HJJ7MxcMw!jhOgv1Jb66x;y%Y2D7y!I`
209B6te&T
210
211diff --git a/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.po b/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.po
212index 7226ad1..9bf79ce 100644
213--- a/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.po
214+++ b/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.po
215@@ -52,3 +52,18 @@ msgid "%(percent)s%% represents %(num)s object"
216 msgid_plural "%(percent)s%% represents %(num)s objects"
217 msgstr[0] "%(percent)s%% stellt %(num)s Objekt dar"
218 msgstr[1] "%(percent)s%% stellt %(num)s Objekte dar"
219+
220+#: models.py:17
221+msgctxt "super search"
222+msgid "%(number)s super result"
223+msgid_plural "%(number)s super results"
224+msgstr[0] "%(number)s Super-Ergebnis"
225+msgstr[1] "%(number)s Super-Ergebnisse"
226+
227+#: models.py:19
228+msgctxt "other super search"
229+msgid "%(number)s super result"
230+msgid_plural "%(number)s super results"
231+msgstr[0] "%(number)s anderen Super-Ergebnis"
232+msgstr[1] "%(number)s andere Super-Ergebnisse"
233+
234diff --git a/tests/regressiontests/i18n/tests.py b/tests/regressiontests/i18n/tests.py
235index fc69d9c..7b0e285 100644
236--- a/tests/regressiontests/i18n/tests.py
237+++ b/tests/regressiontests/i18n/tests.py
238@@ -9,6 +9,7 @@ from threading import local
239 
240 from django.conf import settings
241 from django.template import Template, Context
242+from django.template.base import TemplateSyntaxError
243 from django.test import TestCase, RequestFactory
244 from django.test.utils import override_settings
245 from django.utils import translation
246@@ -87,6 +88,69 @@ class TranslationTests(TestCase):
247                 self.assertEqual(pgettext("verb", "May"), u"Kann")
248                 self.assertEqual(npgettext("search", "%d result", "%d results", 4) % 4, u"4 Resultate")
249 
250+    def test_template_tags_pgettext(self):
251+        # Reset translation catalog to include other/locale/de
252+        extended_locale_paths = settings.LOCALE_PATHS + (
253+            os.path.join(here, 'other', 'locale'),
254+        )
255+        with self.settings(LOCALE_PATHS=extended_locale_paths):
256+            from django.utils.translation import trans_real
257+            trans_real._active = local()
258+            trans_real._translations = {}
259+            with translation.override('de'):
260+
261+                # Blocktrans ---------
262+
263+                # Inexisting context...
264+                t = Template('{% load i18n %}{% blocktrans context "unexisting" %}May{% endblocktrans %}')
265+                rendered = t.render(Context())
266+                self.assertEqual(rendered, 'May')
267+
268+                # Existing context...
269+                # Using a literal
270+                t = Template('{% load i18n %}{% blocktrans context "month name" %}May{% endblocktrans %}')
271+                rendered = t.render(Context())
272+                self.assertEqual(rendered, 'Mai')
273+                t = Template('{% load i18n %}{% blocktrans context "verb" %}May{% endblocktrans %}')
274+                rendered = t.render(Context())
275+                self.assertEqual(rendered, 'Kann')
276+
277+                # Using a variable
278+                t = Template('{% load i18n %}{% blocktrans context trans_context %}May{% endblocktrans %}')
279+                rendered = t.render(Context({'trans_context': 'month name'}))
280+                self.assertEqual(rendered, 'Mai')
281+                t = Template('{% load i18n %}{% blocktrans context trans_context %}May{% endblocktrans %}')
282+                rendered = t.render(Context({'trans_context': 'verb'}))
283+                self.assertEqual(rendered, 'Kann')
284+
285+                # Using a filter
286+                t = Template('{% load i18n %}{% blocktrans context trans_context|lower %}May{% endblocktrans %}')
287+                rendered = t.render(Context({'trans_context': 'MONTH NAME'}))
288+                self.assertEqual(rendered, 'Mai')
289+                t = Template('{% load i18n %}{% blocktrans context trans_context|lower %}May{% endblocktrans %}')
290+                rendered = t.render(Context({'trans_context': 'VERB'}))
291+                self.assertEqual(rendered, 'Kann')
292+
293+                # Using 'count'
294+                t = Template('{% load i18n %}{% blocktrans count number=1 context "super search" %}{{ number }} super result{% plural %}{{ number }} super results{% endblocktrans %}')
295+                rendered = t.render(Context())
296+                self.assertEqual(rendered, '1 Super-Ergebnis')
297+                t = Template('{% load i18n %}{% blocktrans count number=2 context "super search" %}{{ number }} super result{% plural %}{{ number }} super results{% endblocktrans %}')
298+                rendered = t.render(Context())
299+                self.assertEqual(rendered, '2 Super-Ergebnisse')
300+                t = Template('{% load i18n %}{% blocktrans count number=1 context "other super search" %}{{ number }} super result{% plural %}{{ number }} super results{% endblocktrans %}')
301+                rendered = t.render(Context())
302+                self.assertEqual(rendered, '1 anderen Super-Ergebnis')
303+                t = Template('{% load i18n %}{% blocktrans count number=2 context "other super search" %}{{ number }} super result{% plural %}{{ number }} super results{% endblocktrans %}')
304+                rendered = t.render(Context())
305+                self.assertEqual(rendered, '2 andere Super-Ergebnisse')
306+
307+                # Mis-uses
308+                self.assertRaises(TemplateSyntaxError, Template, '{% load i18n %}{% blocktrans context with month="May" %}{{ month }}{% endblocktrans %}')
309+                self.assertRaises(TemplateSyntaxError, Template, '{% load i18n %}{% blocktrans context %}{% endblocktrans %}')
310+                self.assertRaises(TemplateSyntaxError, Template, '{% load i18n %}{% blocktrans count number=2 context %}{{ number }} super result{% plural %}{{ number }} super results{% endblocktrans %}')
311+
312+
313     def test_string_concat(self):
314         """
315         unicode(string_concat(...)) should not raise a TypeError - #4796
Back to Top