#32797: model_ngettext incorrectly tries to translate already translated words
-------------------------------------+-------------------------------------
               Reporter:  Maciej     |          Owner:  Maciej Olko
  Olko                               |
                   Type:  Bug        |         Status:  assigned
              Component:             |        Version:  3.2
  contrib.admin                      |       Keywords:  i18n, gettext,
               Severity:  Normal     |  ngettext
           Triage Stage:             |      Has patch:  1
  Unreviewed                         |
    Needs documentation:  0          |    Needs tests:  0
Patch needs improvement:  0          |  Easy pickings:  0
                  UI/UX:  1          |
-------------------------------------+-------------------------------------
 `model_ngettext()` util function doesn't handle `gettext_lazy` objects as
 model's `verbose_name` and `verbose_name_plural`.

 `translation`'s module `ngettext()` function is intended to be called with
 not translated source strings, whereas `model_ngettext()` puts
 `verbose_name` and `verbose_name_plural`, which may be a `gettext_lazy`
 objects, as its arguments.

 Effectively it makes Django not use correct plural translations for
 verbose name for any language that has other plural rules then English for
 phrases
 * `Successfully deleted %(count)d %(items)s.`
 * and `%(count)s %(name)s were changed successfully.`
 in admin panel (they use `model_ngettext` function to render `items` and
 `name` respectively).

 Following test:

 {{{
 Index: tests/admin_utils/tests.py
 IDEA additional info:
 Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
 <+>UTF-8
 ===================================================================
 diff --git a/tests/admin_utils/tests.py b/tests/admin_utils/tests.py
 --- a/tests/admin_utils/tests.py        (revision
 d270dd584e0af12fe6229fb712d0704c232dc7e5)
 +++ b/tests/admin_utils/tests.py        (date 1622333679821)
 @@ -1,5 +1,6 @@
  from datetime import datetime
  from decimal import Decimal
 +from unittest.mock import patch

  from django import forms
  from django.conf import settings
 @@ -8,7 +9,7 @@
  from django.contrib.admin.utils import (
      NestedObjects, display_for_field, display_for_value, flatten,
      flatten_fieldsets, help_text_for_field, label_for_field,
 lookup_field,
 -    quote,
 +    quote, model_ngettext,
  )
  from django.db import DEFAULT_DB_ALIAS, models
  from django.test import SimpleTestCase, TestCase, override_settings
 @@ -16,7 +17,7 @@
  from django.utils.safestring import mark_safe

  from .models import (
 -    Article, Car, Count, Event, EventGuide, Location, Site, Vehicle,
 +    Article, Car, Count, Event, EventGuide, Location, Site, Vehicle, Foo,
  )


 @@ -410,3 +411,9 @@

      def test_quote(self):
          self.assertEqual(quote('something\nor\nother'),
 'something_0Aor_0Aother')
 +
 +    @patch('django.contrib.admin.utils.ngettext')
 +    def test_model_ngettext(self, ngettext):
 +        model_ngettext(Foo(), None)
 +        self.assertIsInstance(ngettext.call_args.args[0], str,
 type(ngettext.call_args.args[0]))
 +        self.assertIsInstance(ngettext.call_args.args[1], str,
 type(ngettext.call_args.args[1]))
 Index: tests/admin_utils/models.py
 IDEA additional info:
 Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
 <+>UTF-8
 ===================================================================
 diff --git a/tests/admin_utils/models.py b/tests/admin_utils/models.py
 --- a/tests/admin_utils/models.py       (revision
 d270dd584e0af12fe6229fb712d0704c232dc7e5)
 +++ b/tests/admin_utils/models.py       (date 1622333679823)
 @@ -85,3 +85,9 @@

  class Car(VehicleMixin):
      pass
 +
 +
 +class Foo(models.Model):
 +    class Meta:
 +        verbose_name = _('foo')
 +        verbose_name_plural = _('foos')
 }}}

 Fails with:

 {{{
 Traceback (most recent call last):
   File "my-venv/versions/3.9.1/lib/python3.9/unittest/mock.py", line 1337,
 in patched
     return func(*newargs, **newkeywargs)
   File "django/tests/admin_utils/tests.py", line 419, in
 test_model_ngettext
     self.assertIsInstance(ngettext.call_args.args[0], str,
 type(ngettext.call_args.args[0]))
 AssertionError: 'foo' is not an instance of <class 'str'> : <class
 'django.utils.functional.lazy.<locals>.__proxy__'>
 }}}

 A fix requires us to recognize `gettext_lazy` objects as verbose names,
 and passing their source strings instead of translations to ngettext
 function.

 **Backwards compatibility**
 As third party libraries does not provide us with plural translations of
 models, we should keep the current behavior in case of missing plural
 translation of model name.

 Let me create a pull request after having ticket number assigned.

-- 
Ticket URL: <https://code.djangoproject.com/ticket/32797>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-updates+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/050.1a19ffb104f6f6d1bbc365c00239f0e4%40djangoproject.com.

Reply via email to