diff --git a/babel/messages/checkers.py b/babel/messages/checkers.py index 4026ab1b3..dbf53f5f5 100644 --- a/babel/messages/checkers.py +++ b/babel/messages/checkers.py @@ -56,7 +56,11 @@ def python_format(catalog: Catalog | None, message: Message) -> None: msgstrs = (msgstrs,) if msgstrs[0]: - _validate_format(msgids[0], msgstrs[0]) + msgid = msgids[0] + # A single plural form must also support the plural source's placeholders. + if message.pluralizable and catalog is not None and catalog.num_plurals == 1: + msgid = msgids[1] + _validate_format(msgid, msgstrs[0]) if message.pluralizable: for msgstr in msgstrs[1:]: if msgstr: diff --git a/tests/messages/test_checkers.py b/tests/messages/test_checkers.py index 9b0b4b779..e400cee48 100644 --- a/tests/messages/test_checkers.py +++ b/tests/messages/test_checkers.py @@ -19,7 +19,7 @@ from babel.core import Locale, UnknownLocaleError from babel.dates import format_datetime from babel.messages import Message, checkers -from babel.messages.catalog import TranslationError +from babel.messages.catalog import Catalog, TranslationError from babel.messages.checkers import _validate_format, python_format from babel.messages.plurals import PLURALS from babel.messages.pofile import read_po @@ -416,3 +416,34 @@ def test__validate_format_invalid(msgid, msgstr, error): ) def test__validate_format_valid(msgid, msgstr): _validate_format(msgid, msgstr) + + +@pytest.mark.parametrize('locale', ['zh', 'ja', 'ko']) +def test_python_format_single_plural_uses_plural_placeholders(locale): + message = Message( + ('One file for %(owner)s', '%(num)d files for %(owner)s'), + ('%(owner)s: %(num)d',), + ) + python_format(Catalog(locale), message) + + +@pytest.mark.parametrize(('translation', 'error'), [ + ('%(owner)s: %(num)s', "incompatible format for placeholder 'num'"), + ('%(other)s: %(num)d', "unknown named placeholder 'other'"), +]) +def test_python_format_single_plural_rejects_invalid_placeholders(translation, error): + message = Message( + ('One file for %(owner)s', '%(num)d files for %(owner)s'), + (translation,), + ) + with pytest.raises(TranslationError, match=error): + python_format(Catalog('zh'), message) + + +def test_python_format_multiple_plurals_checks_singular_placeholders(): + message = Message( + ('One file for %(owner)s', '%(num)d files for %(owner)s'), + ('%(owner)s: %(num)d', '%(owner)s: %(num)d'), + ) + with pytest.raises(TranslationError, match="unknown named placeholder 'num'"): + python_format(Catalog('en'), message)