﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
32797	model_ngettext incorrectly tries to translate already translated words	Maciej Olko		"`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."	Bug	closed	contrib.admin	3.2	Normal	duplicate	i18n, gettext, ngettext	Claude Paroz	Accepted	1	0	1	1	0	1
