Skip to content

Instantly share code, notes, and snippets.

@uruz
Created October 24, 2012 11:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save uruz/3945679 to your computer and use it in GitHub Desktop.
Save uruz/3945679 to your computer and use it in GitHub Desktop.
Patch on 19160
diff --git a/django/utils/functional.py b/django/utils/functional.py
index 085a8fc..d27f034 100644
--- a/django/utils/functional.py
+++ b/django/utils/functional.py
@@ -51,14 +51,17 @@ class Promise(object):
"""
pass
-def lazy(func, *resultclasses):
+def lazy(func, *resultclasses, **kwargs):
"""
Turns any callable into a lazy evaluated callable. You need to give result
classes or types -- at least one is needed so that the automatic forcing of
the lazy evaluation code is triggered. Results are not memoized; the
function is evaluated on every access.
"""
+ class ChildPromise(Promise):
+ pass
+ mixin = kwargs.get('mixin', ChildPromise)
@total_ordering
class __proxy__(Promise):
"""
@@ -80,6 +83,7 @@ def lazy(func, *resultclasses):
(func, self.__args, self.__kw) + resultclasses
)
+ @classmethod
def __prepare_class__(cls):
cls.__dispatch = {}
for resultclass in resultclasses:
@@ -106,8 +110,8 @@ def lazy(func, *resultclasses):
cls.__bytes__ = cls.__bytes_cast
else:
cls.__str__ = cls.__bytes_cast
- __prepare_class__ = classmethod(__prepare_class__)
+ @classmethod
def __promise__(cls, klass, funcname, method):
# Builds a wrapper around some magic method and registers that magic
# method for the given type and method name.
@@ -124,7 +128,6 @@ def lazy(func, *resultclasses):
cls.__dispatch[klass] = {}
cls.__dispatch[klass][funcname] = method
return __wrapper__
- __promise__ = classmethod(__promise__)
def __text_cast(self):
return func(*self.__args, **self.__kw)
@@ -167,10 +170,13 @@ def lazy(func, *resultclasses):
memo[id(self)] = self
return self
+ class __childproxy__(mixin, __proxy__):
+ pass
+
@wraps(func)
def __wrapper__(*args, **kw):
# Creates the proxy object, instead of the actual value.
- return __proxy__(args, kw)
+ return __childproxy__(args, kw)
return __wrapper__
diff --git a/django/utils/translation/__init__.py b/django/utils/translation/__init__.py
index f3cc634..108ca66 100644
--- a/django/utils/translation/__init__.py
+++ b/django/utils/translation/__init__.py
@@ -2,10 +2,11 @@
Internationalization support.
"""
from __future__ import unicode_literals
-
+from django.utils.functional import Promise
+from django.utils import six
from django.utils.encoding import force_text
from django.utils.functional import lazy
-from django.utils import six
+
__all__ = [
@@ -80,11 +81,48 @@ def npgettext(context, singular, plural, number):
return _trans.npgettext(context, singular, plural, number)
gettext_lazy = lazy(gettext, str)
-ngettext_lazy = lazy(ngettext, str)
ugettext_lazy = lazy(ugettext, six.text_type)
-ungettext_lazy = lazy(ungettext, six.text_type)
pgettext_lazy = lazy(pgettext, six.text_type)
-npgettext_lazy = lazy(npgettext, six.text_type)
+
+
+def lazy_number(func, resultclasses, number=None, **kwargs):
+ class LazyNumberMixin(Promise):
+ def __mod__(self, rhs):
+ if isinstance(rhs, dict):
+ number = rhs[self._proxy____mod_key]
+ elif hasattr(rhs, '__getitem__'):
+ number = rhs[0]
+ else:
+ number = rhs
+ self._proxy____kw['number'] = number
+ try:
+ return super(LazyNumberMixin, self).__mod__(rhs)
+ except TypeError:
+ # String does not contain a number, so
+ # TypeError: not all arguments converted during string formatting is raised
+ return super(LazyNumberMixin, self).__mod__({})
+
+ if isinstance(number, int):
+ kwargs['number'] = number
+ proxy = lazy(func, *resultclasses)(**kwargs)
+ else:
+ proxy = lazy(func, *resultclasses, mixin=LazyNumberMixin)(**kwargs)
+ proxy._proxy____mod_key = number
+ return proxy
+
+def ngettext_lazy(singular, plural, number=None):
+ return lazy_number(
+ ngettext, [str], singular=singular, plural=plural, number=number)
+
+def ungettext_lazy(singular, plural, number=None):
+ return lazy_number(
+ ungettext, [six.text_type], singular=singular, plural=plural, number=number)
+
+def npgettext_lazy(context, singular, plural, number=None):
+ return lazy_number(
+ npgettext, [six.text_type], context=context, singular=singular, plural=plural, number=number)
+
+
def activate(language):
return _trans.activate(language)
--- a/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.po
+++ b/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.po
@@ -35,6 +35,18 @@ msgid "May"
msgstr "Kann"
#: models.py:11
+msgid "%d good result"
+msgid_plural "%d good results"
+msgstr[0] "%d gutes Resultat"
+msgstr[1] "%d guten Resultate"
+
+#: models.py:11
+msgid "%(num)d good result"
+msgid_plural "%(num)d good results"
+msgstr[0] "%(num)d gutes Resultat"
+msgstr[1] "%(num)d guten Resultate"
+
+#: models.py:11
msgctxt "search"
msgid "%d result"
msgid_plural "%d results"
@@ -75,4 +87,4 @@ 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
+msgstr "Andere: Es gibt %(num_comments)s Kommentare"
diff --git a/tests/regressiontests/i18n/other/locale/ru/LC_MESSAGES/django.po b/tests/regressiontests/i18n/other/locale/ru/LC_MESSAGES/django.po
new file mode 100644
index 0000000..fd42d71
--- /dev/null
+++ b/tests/regressiontests/i18n/other/locale/ru/LC_MESSAGES/django.po
@@ -0,0 +1,106 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-10-24 14:14+0400\n"
+"PO-Revision-Date: 2012-10-24 14:30+0300\n"
+"Last-Translator: alexey boriskin <sun.void@gmail.com>\n"
+"Language-Team: <sun.void@gmail.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
+"X-Poedit-Language: Russian\n"
+"X-Poedit-Country: RUSSIAN FEDERATION\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+#: tests.py:67
+#: tests.py:76
+#: tests.py:78
+#: tests.py:79
+#, python-format
+msgid "Add %(name)s"
+msgstr ""
+
+#: tests.py:82
+msgid "Some other string"
+msgstr ""
+
+#: tests.py:86
+msgid "test"
+msgstr ""
+
+#: tests.py:93
+#: tests.py:94
+#, python-format
+msgid "%d good result"
+msgid_plural "%d good result"
+msgstr[0] ""
+msgstr[1] ""
+
+#: tests.py:100
+#: tests.py:101
+#: tests.py:102
+#, python-format
+msgid "%(num)d good result"
+msgid_plural "%(num)d good results"
+msgstr[0] ""
+msgstr[1] ""
+
+#: tests.py:113
+msgid "There was not so much apples"
+msgid_plural "There were a lot of apples"
+msgstr[0] "Было мало яблок"
+msgstr[1] "Было довольно много яблок"
+msgstr[2] "Было много яблок"
+
+#: tests.py:114
+#, python-format
+msgid "There were %(apple)s apple, but I have eaten %(num)s"
+msgid_plural "There were %(apple)s apples, but I have eaten %(num)s"
+msgstr[0] "Было %(apple)s яблоко, но я съел %(num)s"
+msgstr[1] "Было %(apple)s яблока, но я съел %(num)s"
+msgstr[2] "Было %(apple)s яблок, но я съел %(num)s"
+
+#: tests.py:124
+msgctxt "unexisting"
+msgid "May"
+msgstr ""
+
+#: tests.py:125
+msgctxt "month name"
+msgid "May"
+msgstr ""
+
+#: tests.py:126
+msgctxt "verb"
+msgid "May"
+msgstr ""
+
+#: tests.py:127
+#, python-format
+msgctxt "search"
+msgid "%d result"
+msgid_plural "%d results"
+msgstr[0] ""
+msgstr[1] ""
+
+#: tests.py:274
+msgid "Mac\rEOF\r"
+msgstr ""
+
+#: tests.py:275
+msgid ""
+"Win\r\n"
+"EOF\r\n"
+msgstr ""
+
+#: tests.py:909
+msgid "Date/time"
+msgstr ""
+
diff --git a/tests/regressiontests/i18n/tests.py b/tests/regressiontests/i18n/tests.py
index 4054f85..9d40f92 100644
--- a/tests/regressiontests/i18n/tests.py
+++ b/tests/regressiontests/i18n/tests.py
@@ -22,7 +22,7 @@ from django.utils.safestring import mark_safe, SafeBytes, SafeString, SafeText
from django.utils import six
from django.utils.six import PY3
from django.utils.translation import (ugettext, ugettext_lazy, activate,
- deactivate, gettext_lazy, pgettext, npgettext, to_locale,
+ deactivate, gettext_lazy, ungettext_lazy, pgettext, npgettext, to_locale,
get_language_info, get_language, get_language_from_request, trans_real)
@@ -50,7 +50,6 @@ extended_locale_paths = settings.LOCALE_PATHS + (
)
class TranslationTests(TestCase):
-
def test_override(self):
activate('de')
with translation.override('pl'):
@@ -90,6 +89,38 @@ class TranslationTests(TestCase):
self.assertEqual(six.text_type(s2), "test")
@override_settings(LOCALE_PATHS=extended_locale_paths)
+ def test_ungettext_lazy(self):
+ s0 = ungettext_lazy("%d good result", "%d good result")
+ s1 = ungettext_lazy("%d good result", "%d good result", 'num')
+ with translation.override('de'):
+ self.assertEqual(s0 % 1, "1 gutes Resultat")
+ self.assertEqual(s0 % 4, "4 guten Resultate")
+ self.assertEqual(s1 % 4, "4 guten Resultate")
+
+ s2 = ungettext_lazy("%(num)d good result", "%(num)d good results", 4)
+ s3 = ungettext_lazy("%(num)d good result", "%(num)d good results", 'num')
+ s4 = ungettext_lazy("%(num)d good result", "%(num)d good results")
+ with translation.override('de'):
+ self.assertEqual(s2 % {'num': 4}, "4 guten Resultate")
+ self.assertEqual(s3 % {'num': 1}, "1 gutes Resultat")
+ self.assertEqual(s3 % {'num': 4}, "4 guten Resultate")
+ with self.assertRaises(KeyError):
+ s4 % {'num': 4}
+
+ s5 = ungettext_lazy('There was not so much apples', 'There were a lot of apples', 'num')
+ s6 = ungettext_lazy('There were %(apple)s apple, but I have eaten %(num)s', 'There were %(apple)s apples, but I have eaten %(num)s', 'apple')
+ s7 = ungettext_lazy('There were %(apple)s apple, but I have eaten %(num)s', 'There were %(apple)s apples, but I have eaten %(num)s', 'num')
+ with translation.override('ru'):
+ self.assertEqual(s5 % {'num': 1}, 'Было мало яблок')
+ self.assertEqual(s5 % 1, 'Было мало яблок')
+ self.assertEqual(s6 % {'apple': 1, 'num': 1}, 'Было 1 яблоко, но я съел 1')
+ self.assertEqual(s6 % {'apple': 2, 'num': 1}, 'Было 2 яблока, но я съел 1')
+ self.assertEqual(s6 % {'apple': 5, 'num': 3}, 'Было 5 яблок, но я съел 3')
+ self.assertEqual(s7 % {'apple': 1, 'num': 1}, 'Было 1 яблоко, но я съел 1')
+ self.assertEqual(s7 % {'apple': 2, 'num': 1}, 'Было 2 яблоко, но я съел 1')
+ self.assertEqual(s7 % {'apple': 1, 'num': 5}, 'Было 1 яблок, но я съел 5')
+
+ @override_settings(LOCALE_PATHS=extended_locale_paths)
def test_pgettext(self):
trans_real._active = local()
trans_real._translations = {}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment