Commit 3f1a0c00 authored by Aymeric Augustin's avatar Aymeric Augustin
Browse files

Fixed #19160 -- Made lazy plural translations usable.

Many thanks to Alexey Boriskin, Claude Paroz and Julien Phalip.
parent 47ddd6a4
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -157,8 +157,7 @@ def lazy(func, *resultclasses):
                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
+32 −3
Original line number Diff line number Diff line
@@ -85,11 +85,40 @@ 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)
+4 −0
Original line number Diff line number Diff line
@@ -35,6 +35,10 @@ Minor features
  :class:`~django.forms.URLField` use the new type attributes available in
  HTML5 (type='email', type='url').

* The ``number`` argument for :ref:`lazy plural translations
  <lazy-plural-translations>` can be provided at translation time rather than
  at definition time.

Backwards incompatible changes in 1.6
=====================================

+35 −0
Original line number Diff line number Diff line
@@ -414,6 +414,41 @@ 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-plural-translations:

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 key name instead of an
integer as the ``number`` argument. Then ``number`` will be looked up in the
dictionary under that key during string interpolation. Here's example::

    class MyForm(forms.Form):
        error_message = ungettext_lazy("You only provided %(num)d argument",
            "You only provided %(num)d arguments", 'num')

        def clean(self):
            # ...
            if error:
                raise forms.ValidationError(self.error_message % {'num': number})

If the string contains exactly one unnamed placeholder, you can interpolate
directly with the ``number`` argument::

    class MyForm(forms.Form):
        error_message = ungettext_lazy("You provided %d argument",
            "You provided %d arguments")

        def clean(self):
            # ...
            if error:
                raise forms.ValidationError(self.error_message % number)


Joining strings: string_concat()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Loading