Code

Ticket #19160: 19160.resolvable-ungettext.diff

File 19160.resolvable-ungettext.diff, 6.7 KB (added by void, 18 months ago)

Another implementation

Line 
1diff --git a/django/utils/functional.py b/django/utils/functional.py
2index 085a8fc..4fff693 100644
3--- a/django/utils/functional.py
4+++ b/django/utils/functional.py
5@@ -51,7 +51,7 @@ class Promise(object):
6     """
7     pass
8 
9-def lazy(func, *resultclasses):
10+def lazy(func, *resultclasses, **kwargs):
11     """
12     Turns any callable into a lazy evaluated callable. You need to give result
13     classes or types -- at least one is needed so that the automatic forcing of
14@@ -59,8 +59,9 @@ def lazy(func, *resultclasses):
15     function is evaluated on every access.
16     """
17 
18+    mixin = kwargs.get('mixin', object)
19     @total_ordering
20-    class __proxy__(Promise):
21+    class __proxy__(Promise, mixin):
22         """
23         Encapsulate a function call and act as a proxy for methods that are
24         called on the result of that function. The function is not evaluated
25@@ -80,6 +81,7 @@ def lazy(func, *resultclasses):
26                 (func, self.__args, self.__kw) + resultclasses
27             )
28 
29+        @classmethod
30         def __prepare_class__(cls):
31             cls.__dispatch = {}
32             for resultclass in resultclasses:
33@@ -106,8 +108,8 @@ def lazy(func, *resultclasses):
34                     cls.__bytes__ = cls.__bytes_cast
35                 else:
36                     cls.__str__ = cls.__bytes_cast
37-        __prepare_class__ = classmethod(__prepare_class__)
38 
39+        @classmethod
40         def __promise__(cls, klass, funcname, method):
41             # Builds a wrapper around some magic method and registers that magic
42             # method for the given type and method name.
43@@ -124,7 +126,6 @@ def lazy(func, *resultclasses):
44                 cls.__dispatch[klass] = {}
45             cls.__dispatch[klass][funcname] = method
46             return __wrapper__
47-        __promise__ = classmethod(__promise__)
48 
49         def __text_cast(self):
50             return func(*self.__args, **self.__kw)
51diff --git a/django/utils/translation/__init__.py b/django/utils/translation/__init__.py
52index f3cc634..8e87e93 100644
53--- a/django/utils/translation/__init__.py
54+++ b/django/utils/translation/__init__.py
55@@ -80,11 +80,40 @@ def npgettext(context, singular, plural, number):
56     return _trans.npgettext(context, singular, plural, number)
57 
58 gettext_lazy = lazy(gettext, str)
59-ngettext_lazy = lazy(ngettext, str)
60 ugettext_lazy = lazy(ugettext, six.text_type)
61-ungettext_lazy = lazy(ungettext, six.text_type)
62 pgettext_lazy = lazy(pgettext, six.text_type)
63-npgettext_lazy = lazy(npgettext, six.text_type)
64+
65+
66+def lazy_number(func, resultclasses, number=None, **kwargs):
67+    class LazyNumberMixin(object):
68+        def __mod__(self, rhs):
69+            assert self._proxy____kw['number'], 'This object is not resolved yet'
70+            return super(LazyNumberMixin, self).__mod__(rhs)
71+
72+        def resolve(self, number):
73+            self._proxy____kw['number'] = number
74+            return self
75+
76+    if number is not None:
77+        kwargs['number'] = number
78+        proxy = lazy(func, *resultclasses)(**kwargs)
79+    else:
80+        proxy = lazy(func, *resultclasses, mixin=LazyNumberMixin)(**kwargs)
81+    return proxy
82+
83+def ngettext_lazy(singular, plural, number=None):
84+    return lazy_number(
85+        ngettext, [str], singular=singular, plural=plural, number=number)
86+
87+def ungettext_lazy(singular, plural, number=None):
88+    return lazy_number(
89+        ungettext, [six.text_type], singular=singular, plural=plural, number=number)
90+
91+def npgettext_lazy(context, singular, plural, number=None):
92+    return lazy_number(
93+        npgettext, [six.text_type], context=context, singular=singular, plural=plural, number=number)
94+
95+
96 
97 def activate(language):
98     return _trans.activate(language)
99diff --git a/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.po b/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.po
100index a471d38..63b5339 100644
101--- a/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.po
102+++ b/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.po
103@@ -35,6 +35,18 @@ msgid "May"
104 msgstr "Kann"
105 
106 #: models.py:11
107+msgid "%d good result"
108+msgid_plural "%d good results"
109+msgstr[0] "%d gutes Resultat"
110+msgstr[1] "%d guten Resultate"
111+
112+#: models.py:11
113+msgid "%(num)d good result"
114+msgid_plural "%(num)d good results"
115+msgstr[0] "%(num)d gutes Resultat"
116+msgstr[1] "%(num)d guten Resultate"
117+
118+#: models.py:11
119 msgctxt "search"
120 msgid "%d result"
121 msgid_plural "%d results"
122@@ -75,4 +87,4 @@ msgstr "Es gibt %(num_comments)s Kommentare"
123 #: models.py:23
124 msgctxt "other comment count"
125 msgid "There are %(num_comments)s comments"
126-msgstr "Andere: Es gibt %(num_comments)s Kommentare"
127\ No newline at end of file
128+msgstr "Andere: Es gibt %(num_comments)s Kommentare"
129diff --git a/tests/regressiontests/i18n/tests.py b/tests/regressiontests/i18n/tests.py
130index 4054f85..7e149a6 100644
131--- a/tests/regressiontests/i18n/tests.py
132+++ b/tests/regressiontests/i18n/tests.py
133@@ -22,7 +22,7 @@ from django.utils.safestring import mark_safe, SafeBytes, SafeString, SafeText
134 from django.utils import six
135 from django.utils.six import PY3
136 from django.utils.translation import (ugettext, ugettext_lazy, activate,
137-    deactivate, gettext_lazy, pgettext, npgettext, to_locale,
138+    deactivate, gettext_lazy, ungettext_lazy, pgettext, npgettext, to_locale,
139     get_language_info, get_language, get_language_from_request, trans_real)
140 
141 
142@@ -50,7 +50,6 @@ extended_locale_paths = settings.LOCALE_PATHS + (
143 )
144 
145 class TranslationTests(TestCase):
146-
147     def test_override(self):
148         activate('de')
149         with translation.override('pl'):
150@@ -90,6 +89,22 @@ class TranslationTests(TestCase):
151         self.assertEqual(six.text_type(s2), "test")
152 
153     @override_settings(LOCALE_PATHS=extended_locale_paths)
154+    def test_ungettext_lazy(self):
155+        s = ungettext_lazy("%d good result", "%d good result")
156+        with translation.override('de'):
157+            self.assertEqual(s.resolve(1) % 1, "1 gutes Resultat")
158+            self.assertEqual(s.resolve(4) % 4, "4 guten Resultate")
159+
160+        s1 = ungettext_lazy("%(num)d good result", "%(num)d good results", 4)
161+        s2 = ungettext_lazy("%(num)d good result", "%(num)d good results")
162+        s3 = ungettext_lazy("%(num)d good result", "%(num)d good results")
163+        with translation.override('de'):
164+            self.assertEqual(s1 % {'num': 4}, "4 guten Resultate")
165+            self.assertEqual(s2.resolve(1) % {'num': 1}, "1 gutes Resultat")
166+            self.assertEqual(s2.resolve(4) % {'num': 4}, "4 guten Resultate")
167+            self.assertEqual(s3.resolve(5) % {'num': 5}, "5 guten Resultate")
168+
169+    @override_settings(LOCALE_PATHS=extended_locale_paths)
170     def test_pgettext(self):
171         trans_real._active = local()
172         trans_real._translations = {}
173
174