diff --git a/babel/messages/catalog.py b/babel/messages/catalog.py index 5e6c28255..125381ec1 100644 --- a/babel/messages/catalog.py +++ b/babel/messages/catalog.py @@ -74,10 +74,11 @@ def get_close_matches(word, possibilities, n=3, cutoff=0.6): \% (?:\(([\w]*)\))? ( - [-#0\ +]?(?:\*|[\d]+)? - (?:\.(?:\*|[\d]+))? - [hlL]? + (?:[-#0+]?(?:\*|[\d]+)?(?:\.(?:\*|[\d]+))?) + | + (?:\ +(?:\.[\d]+|[\d]+(?:\.[\d]+)?)) ) + [hlL]? ([diouxXeEfFgGcrs%]) ''', re.VERBOSE, diff --git a/tests/messages/test_catalog.py b/tests/messages/test_catalog.py index 7c730d325..817b42825 100644 --- a/tests/messages/test_catalog.py +++ b/tests/messages/test_catalog.py @@ -38,6 +38,18 @@ def test_message_python_format(): assert catalog.PYTHON_FORMAT.search('foo %()s') +def test_message_python_format_prose_percent(): + # literal percent signs in prose must not be mistaken for placeholders + assert not catalog.PYTHON_FORMAT.search('100 % done') + assert not catalog.PYTHON_FORMAT.search('50 % off') + assert not catalog.PYTHON_FORMAT.search('10% of') + assert not catalog.PYTHON_FORMAT.search('10% der') + # space-flag placeholders with explicit width/precision are still valid + assert catalog.PYTHON_FORMAT.search('% 5d') + assert catalog.PYTHON_FORMAT.search('% .2f') + assert catalog.PYTHON_FORMAT.search('% 5.2f') + + def test_message_python_brace_format(): assert not catalog._has_python_brace_format('') assert not catalog._has_python_brace_format('foo') diff --git a/tests/messages/test_checkers.py b/tests/messages/test_checkers.py index 9b0b4b779..a66f39dd2 100644 --- a/tests/messages/test_checkers.py +++ b/tests/messages/test_checkers.py @@ -390,6 +390,7 @@ def test_python_format_valid(msgid, msgstr): '%(foo)d', "incompatible format for placeholder 'foo': 'd' and 's' are not compatible", ), + ('% 5d', '% 5s', "incompatible format for placeholder 1: 'd' and 's' are not compatible"), ], ) def test__validate_format_invalid(msgid, msgstr, error): @@ -412,6 +413,11 @@ def test__validate_format_invalid(msgid, msgstr, error): ('%(foo)s', 'foo'), ('%(foo)s', '%(foo)s %(foo)s'), ('%(bar)s foo %(n)d', '%(n)d foo %(bar)s'), + # literal percent signs in prose are not placeholders + ('100 % done', '100 % erledigt'), + ('50 % off', '50 % Rabatt'), + # space-flag placeholders with explicit width/precision are still checked + ('% 5d', '% 5d'), ], ) def test__validate_format_valid(msgid, msgstr):