Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Fixes #19160 - Make ungettext_lazy really usable #684

Closed
wants to merge 2 commits into from

1 participant

@uruz

No description provided.

@uruz

Pull request has been merged at @3f1a0c0, closing.

@uruz uruz closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
3  django/utils/functional.py
@@ -157,8 +157,7 @@ def __mod__(self, rhs):
return bytes(self) % rhs
elif self._delegate_text:
return six.text_type(self) % rhs
- else:
- raise AssertionError('__mod__ not supported for non-string types')
+ return self.__cast() % rhs
def __deepcopy__(self, memo):
# Instances of this class are effectively immutable. It's just a
View
33 django/utils/translation/__init__.py
@@ -80,11 +80,38 @@ 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, resultclass, number=None, **kwargs):
+ if isinstance(number, int):
+ kwargs['number'] = number
+ proxy = lazy(func, resultclass)(**kwargs)
+ else:
+ class NumberAwareString(resultclass):
+ def __mod__(self, rhs):
+ if isinstance(rhs, dict) and number:
+ try:
+ number_value = rhs[number]
+ except KeyError:
+ raise KeyError('Your dictionary lacks key \'%s\'. Please provide it, because '
+ 'it is required to determine whether string is singular or plural.' % number)
+ else:
+ number_value = rhs
+ kwargs['number'] = number_value
+ return func(**kwargs) % rhs
+
+ proxy = lazy(lambda **kwargs:NumberAwareString(), NumberAwareString)(**kwargs)
+ 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)
View
27 docs/topics/i18n/translation.txt
@@ -406,6 +406,33 @@ convert them to strings, because they should be converted as late as possible
(so that the correct locale is in effect). This necessitates the use of the
helper function described next.
+Lazy translations and plural
+----------------------------
+
+.. versionadded:: 1.6
+
+When using lazy translation for a plural string (``[u]n[p]gettext_lazy``), you
+generally don't know the ``number`` argument at the time of the string
+definition. Therefore, you are authorized to pass a dictionary key name in place
+of an integer as the ``number`` argument. Then, when the string is effectively
+translated with a placeholders dictionary, the ``number`` argument will get
+substituted by the value of the key in the dictionary. Alternatively, if the
+string contains only one unnamed placeholder, you can also omit passing the
+``number`` argument at all. For example::
+
+ class MyForm(forms.Form):
+ error1_message = ungettext_lazy("You only provided %(num)d argument",
+ "You only provided %(num)d arguments", 'num')
+ error2_message = ungettext_lazy("You provided %d argument",
+ "You provided %d arguments")
+
+ def clean(self):
+ if err_1:
+ raise forms.ValidationError(self.error1_message % {'num': number})
+ if err_2:
+ raise forms.ValidationError(self.error2_message % number)
+
+
Joining strings: string_concat()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
View
BIN  tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.mo
Binary file not shown
View
26 tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.po
@@ -41,6 +41,32 @@ msgid_plural "%d results"
msgstr[0] "%d Resultat"
msgstr[1] "%d Resultate"
+#: 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
+msgctxt "Exclamation"
+msgid "%d good result"
+msgid_plural "%d good results"
+msgstr[0] "%d gutes Resultat!"
+msgstr[1] "%d guten Resultate!"
+
+#: models.py:11
+msgid "Hi %(name)s, %(num)d good result"
+msgid_plural "Hi %(name)s, %(num)d good results"
+msgstr[0] "Hallo %(name)s, %(num)d gutes Resultat"
+msgstr[1] "Hallo %(name)s, %(num)d guten Resultate"
+
+#: models.py:11
+msgctxt "Greeting"
+msgid "Hi %(name)s, %(num)d good result"
+msgid_plural "Hi %(name)s, %(num)d good results"
+msgstr[0] "Willkommen %(name)s, %(num)d gutes Resultat"
+msgstr[1] "Willkommen %(name)s, %(num)d guten Resultate"
+
#: models.py:13
#, python-format
msgid "The result was %(percent)s%%"
View
38 tests/regressiontests/i18n/tests.py
@@ -23,7 +23,7 @@
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, ngettext_lazy, npgettext_lazy, pgettext, npgettext, to_locale,
get_language_info, get_language, get_language_from_request, trans_real)
@@ -96,6 +96,42 @@ def test_lazy_pickle(self):
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 results")
+ s1 = ngettext_lazy(str("%d good result"), str("%d good results"))
+ s2 = npgettext_lazy('Exclamation', '%d good result', '%d good results')
+ with translation.override('de'):
+ self.assertEqual(s0 % 1, "1 gutes Resultat")
+ self.assertEqual(s0 % 4, "4 guten Resultate")
+ self.assertEqual(s1 % 1, str("1 gutes Resultat"))
+ self.assertEqual(s1 % 4, str("4 guten Resultate"))
+ self.assertEqual(s2 % 1, "1 gutes Resultat!")
+ self.assertEqual(s2 % 4, "4 guten Resultate!")
+
+ s3 = ungettext_lazy("Hi %(name)s, %(num)d good result", "Hi %(name)s, %(num)d good results", 4)
+ s4 = ungettext_lazy("Hi %(name)s, %(num)d good result", "Hi %(name)s, %(num)d good results", 'num')
+ s5 = ngettext_lazy(str("Hi %(name)s, %(num)d good result"), str("Hi %(name)s, %(num)d good results"), 4)
+ s6 = ngettext_lazy(str("Hi %(name)s, %(num)d good result"), str("Hi %(name)s, %(num)d good results"), 'num')
+ s7 = npgettext_lazy('Greeting', "Hi %(name)s, %(num)d good result", "Hi %(name)s, %(num)d good results", 4)
+ s8 = npgettext_lazy('Greeting', "Hi %(name)s, %(num)d good result", "Hi %(name)s, %(num)d good results", 'num')
+ with translation.override('de'):
+ self.assertEqual(s3 % {'num': 4, 'name': 'Jim'}, "Hallo Jim, 4 guten Resultate")
+ self.assertEqual(s4 % {'name': 'Jim', 'num': 1}, "Hallo Jim, 1 gutes Resultat")
+ self.assertEqual(s4 % {'name': 'Jim', 'num': 5}, "Hallo Jim, 5 guten Resultate")
+ with self.assertRaisesRegexp(KeyError, 'Your dictionary lacks key.*'):
+ s4 % {'name': 'Jim'}
+ self.assertEqual(s5 % {'num': 4, 'name': 'Jim'}, str("Hallo Jim, 4 guten Resultate"))
+ self.assertEqual(s6 % {'name': 'Jim', 'num': 1}, str("Hallo Jim, 1 gutes Resultat"))
+ self.assertEqual(s6 % {'name': 'Jim', 'num': 5}, str("Hallo Jim, 5 guten Resultate"))
+ with self.assertRaisesRegexp(KeyError, 'Your dictionary lacks key.*'):
+ s6 % {'name': 'Jim'}
+ self.assertEqual(s7 % {'num': 4, 'name': 'Jim'}, "Willkommen Jim, 4 guten Resultate")
+ self.assertEqual(s8 % {'name': 'Jim', 'num': 1}, "Willkommen Jim, 1 gutes Resultat")
+ self.assertEqual(s8 % {'name': 'Jim', 'num': 5}, "Willkommen Jim, 5 guten Resultate")
+ with self.assertRaisesRegexp(KeyError, 'Your dictionary lacks key.*'):
+ s8 % {'name': 'Jim'}
+
+ @override_settings(LOCALE_PATHS=extended_locale_paths)
def test_pgettext(self):
trans_real._active = local()
trans_real._translations = {}
Something went wrong with that request. Please try again.