1 | diff --git a/django/utils/translation/__init__.py b/django/utils/translation/__init__.py |
---|
2 | index f3cc634..9333ba8 100644 |
---|
3 | --- a/django/utils/translation/__init__.py |
---|
4 | +++ b/django/utils/translation/__init__.py |
---|
5 | @@ -80,11 +80,42 @@ def npgettext(context, singular, plural, number): |
---|
6 | return _trans.npgettext(context, singular, plural, number) |
---|
7 | |
---|
8 | gettext_lazy = lazy(gettext, str) |
---|
9 | -ngettext_lazy = lazy(ngettext, str) |
---|
10 | ugettext_lazy = lazy(ugettext, six.text_type) |
---|
11 | -ungettext_lazy = lazy(ungettext, six.text_type) |
---|
12 | pgettext_lazy = lazy(pgettext, six.text_type) |
---|
13 | -npgettext_lazy = lazy(npgettext, six.text_type) |
---|
14 | + |
---|
15 | + |
---|
16 | +def lazy_number(func, resultclasses, number=None, **kwargs): |
---|
17 | + if isinstance(number, int): |
---|
18 | + kwargs['number'] = number |
---|
19 | + proxy = lazy(func, *resultclasses)(**kwargs) |
---|
20 | + else: |
---|
21 | + proxy = lazy(func, *resultclasses)(**kwargs) |
---|
22 | + def mod(self, rhs): |
---|
23 | + if isinstance(rhs, dict) and number: |
---|
24 | + number_value = rhs[number] |
---|
25 | + else: |
---|
26 | + number_value = rhs |
---|
27 | + self._proxy____kw['number'] = number_value |
---|
28 | + result = original_mod(self, rhs) |
---|
29 | + del self._proxy____kw['number'] |
---|
30 | + return result |
---|
31 | + original_mod = getattr(proxy.__class__, '__mod__') |
---|
32 | + setattr(proxy.__class__, '__mod__', mod) |
---|
33 | + return proxy |
---|
34 | + |
---|
35 | +def ngettext_lazy(singular, plural, number=None): |
---|
36 | + return lazy_number( |
---|
37 | + ngettext, [str], singular=singular, plural=plural, number=number) |
---|
38 | + |
---|
39 | +def ungettext_lazy(singular, plural, number=None): |
---|
40 | + return lazy_number( |
---|
41 | + ungettext, [six.text_type], singular=singular, plural=plural, number=number) |
---|
42 | + |
---|
43 | +def npgettext_lazy(context, singular, plural, number=None): |
---|
44 | + return lazy_number( |
---|
45 | + npgettext, [six.text_type], context=context, singular=singular, plural=plural, number=number) |
---|
46 | + |
---|
47 | + |
---|
48 | |
---|
49 | def activate(language): |
---|
50 | return _trans.activate(language) |
---|
51 | diff --git a/docs/topics/i18n/translation.txt b/docs/topics/i18n/translation.txt |
---|
52 | index 0b13ea1..82199ba 100644 |
---|
53 | --- a/docs/topics/i18n/translation.txt |
---|
54 | +++ b/docs/topics/i18n/translation.txt |
---|
55 | @@ -408,6 +408,33 @@ convert them to strings, because they should be converted as late as possible |
---|
56 | (so that the correct locale is in effect). This necessitates the use of the |
---|
57 | helper function described next. |
---|
58 | |
---|
59 | +Lazy translations and plural |
---|
60 | +---------------------------- |
---|
61 | + |
---|
62 | +.. versionadded:: 1.5 |
---|
63 | + |
---|
64 | +When using lazy translation for a plural string (``[u]n[p]gettext_lazy``), you |
---|
65 | +generally don't know the ``number`` argument at the time of the string |
---|
66 | +definition. Therefore, you are authorized to pass a dictionary key name in place |
---|
67 | +of an integer as the ``number`` argument. Then, when the string is effectively |
---|
68 | +translated with a placeholders dictionary, the ``number`` argument will get |
---|
69 | +substituted by the value of the key in the dictionary. Alternatively, if the |
---|
70 | +string contains only one unnamed placeholder, you can also omit passing the |
---|
71 | +``number`` argument at all. For example:: |
---|
72 | + |
---|
73 | + class MyForm(forms.Form): |
---|
74 | + error1_message = ungettext_lazy("You only provided %(num)d argument", |
---|
75 | + "You only provided %(num)d arguments", 'num') |
---|
76 | + error2_message = ungettext_lazy("You provided %d argument", |
---|
77 | + "You provided %d arguments") |
---|
78 | + |
---|
79 | + def clean(self): |
---|
80 | + if err_1: |
---|
81 | + raise forms.ValidationError(self.error1_message % {'num': number}) |
---|
82 | + if err_2: |
---|
83 | + raise forms.ValidationError(self.error2_message % number) |
---|
84 | + |
---|
85 | + |
---|
86 | Joining strings: string_concat() |
---|
87 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
---|
88 | |
---|
89 | diff --git a/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.mo b/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.mo |
---|
90 | index f825e3918b7fd74b7af37e55e704b260101367b7..b43f2824584826898add21d2c013063b1a869186 100644 |
---|
91 | GIT binary patch |
---|
92 | delta 594 |
---|
93 | zcmajaJ4*vW5Ww-d<b1}Y&_;=dlO&L!AQqyH2paHgg7|1`<RFU%E{S*-EK_PBSOgn8 |
---|
94 | zEqv6ewDo%g5&RH>h5v~`42TZQ{q~mG+1ZEKUA)mAwJ(K8kdx#&IYxev8m=X>i5r;0 |
---|
95 | zD;&Z{T*YV9bNvw!8`C(CIn<texQJz3!)r|98!n49#EOaxvay2uG;>ieyr)|R-*Ey{ |
---|
96 | zeIi9%LLKB3b<kT(;}ddJ+Bk!+SinTT$T)7}0{Tcxxj{?TSrIxwj2s{tOh!n3r4~}x |
---|
97 | z34P6QcdgLB6LG%L=9IPD$d;UgTCL<9o1pH8()$}o!3)h?Sgsgx%T6{|^(rPG%<8IM |
---|
98 | z$@d=puON_}L({#-Iq?FT9GTXI^%BYGt@Y3Z&Q@m%?`sZrs^2@NS+$cz*Yo|_Z*%mX |
---|
99 | O{;TXq%hs7aZG8e%lWzb3 |
---|
100 | |
---|
101 | delta 327 |
---|
102 | zcmXBQyJ`YK7>410SI5&v@lbdQi-$(E5DT$N6)(WX%0etGEIbev(gZAnwOH6#32H6r |
---|
103 | zLaref5Tb>b;05^p`oYIDyE{8GySwPYng1vKD<MzxnSRg&{iB+KCDOqnhVc#W^U%-r |
---|
104 | z8XLI582(}lPxy*yTO^M?e8G1N;TQHTnad9Y-N0qFEKbqI1*UM1W&B1Tp0S1(Y-8Mr |
---|
105 | zWUz}(oFI>6i=06MR5#S<eT{y|BP9`sQIP858s5AAjR<l!S<b8qt%lnJZ&>$6<KfZR |
---|
106 | NS9|H2-0{;CtQ%0kA8G&q |
---|
107 | |
---|
108 | diff --git a/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.po b/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.po |
---|
109 | index a471d38..e138bb0 100644 |
---|
110 | --- a/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.po |
---|
111 | +++ b/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.po |
---|
112 | @@ -35,6 +35,18 @@ msgid "May" |
---|
113 | msgstr "Kann" |
---|
114 | |
---|
115 | #: models.py:11 |
---|
116 | +msgid "%d good result" |
---|
117 | +msgid_plural "%d good results" |
---|
118 | +msgstr[0] "%d gutes Resultat" |
---|
119 | +msgstr[1] "%d guten Resultate" |
---|
120 | + |
---|
121 | +#: models.py:11 |
---|
122 | +msgid "Hi %(name)s, %(num)d good result" |
---|
123 | +msgid_plural "Hi %(name)s, %(num)d good results" |
---|
124 | +msgstr[0] "Hallo %(name)s, %(num)d gutes Resultat" |
---|
125 | +msgstr[1] "Hallo %(name)s, %(num)d guten Resultate" |
---|
126 | + |
---|
127 | +#: models.py:11 |
---|
128 | msgctxt "search" |
---|
129 | msgid "%d result" |
---|
130 | msgid_plural "%d results" |
---|
131 | @@ -75,4 +87,4 @@ msgstr "Es gibt %(num_comments)s Kommentare" |
---|
132 | #: models.py:23 |
---|
133 | msgctxt "other comment count" |
---|
134 | msgid "There are %(num_comments)s comments" |
---|
135 | -msgstr "Andere: Es gibt %(num_comments)s Kommentare" |
---|
136 | \ No newline at end of file |
---|
137 | +msgstr "Andere: Es gibt %(num_comments)s Kommentare" |
---|
138 | diff --git a/tests/regressiontests/i18n/tests.py b/tests/regressiontests/i18n/tests.py |
---|
139 | index 44d84f9..1aa7fd9 100644 |
---|
140 | --- a/tests/regressiontests/i18n/tests.py |
---|
141 | +++ b/tests/regressiontests/i18n/tests.py |
---|
142 | @@ -23,7 +23,7 @@ from django.utils.safestring import mark_safe, SafeBytes, SafeString, SafeText |
---|
143 | from django.utils import six |
---|
144 | from django.utils.six import PY3 |
---|
145 | from django.utils.translation import (ugettext, ugettext_lazy, activate, |
---|
146 | - deactivate, gettext_lazy, pgettext, npgettext, to_locale, |
---|
147 | + deactivate, gettext_lazy, ungettext_lazy, pgettext, npgettext, to_locale, |
---|
148 | get_language_info, get_language, get_language_from_request, trans_real) |
---|
149 | |
---|
150 | |
---|
151 | @@ -51,7 +51,6 @@ extended_locale_paths = settings.LOCALE_PATHS + ( |
---|
152 | ) |
---|
153 | |
---|
154 | class TranslationTests(TestCase): |
---|
155 | - |
---|
156 | def test_override(self): |
---|
157 | activate('de') |
---|
158 | with translation.override('pl'): |
---|
159 | @@ -95,6 +94,20 @@ class TranslationTests(TestCase): |
---|
160 | self.assertEqual(six.text_type(s2), "test") |
---|
161 | |
---|
162 | @override_settings(LOCALE_PATHS=extended_locale_paths) |
---|
163 | + def test_ungettext_lazy(self): |
---|
164 | + s = ungettext_lazy("%d good result", "%d good result") |
---|
165 | + with translation.override('de'): |
---|
166 | + self.assertEqual(s % 1, "1 gutes Resultat") |
---|
167 | + self.assertEqual(s % 4, "4 guten Resultate") |
---|
168 | + |
---|
169 | + s1 = ungettext_lazy("Hi %(name)s, %(num)d good result", "Hi %(name)s, %(num)d good results", 4) |
---|
170 | + s2 = ungettext_lazy("Hi %(name)s, %(num)d good result", "Hi %(name)s, %(num)d good results", 'num') |
---|
171 | + with translation.override('de'): |
---|
172 | + self.assertEqual(s1 % {'num': 4, 'name': 'Jim'}, "Hallo Jim, 4 guten Resultate") |
---|
173 | + self.assertEqual(s2 % {'name': 'Jim', 'num': 1}, "Hallo Jim, 1 gutes Resultat") |
---|
174 | + self.assertEqual(s2 % {'name': 'Jim', 'num': 5}, "Hallo Jim, 5 guten Resultate") |
---|
175 | + |
---|
176 | + @override_settings(LOCALE_PATHS=extended_locale_paths) |
---|
177 | def test_pgettext(self): |
---|
178 | trans_real._active = local() |
---|
179 | trans_real._translations = {} |
---|
180 | -- |
---|
181 | 1.8.0.2 |
---|
182 | |
---|