translations: more validations, ensure named placeholders match

This commit is contained in:
deltragon 2024-08-04 19:54:25 +02:00
parent 3a8f6a0953
commit dc6f333fae
11 changed files with 97 additions and 31 deletions

View File

@ -286,7 +286,7 @@ msgstr "تعبير الوظيفة المجدولة غير صحيح '%s'"
# Settings dialog # Settings dialog
#, python-format #, python-format
msgid "Please add the resource %(resource)s to %(config_resource)s directory" msgid "Please add the resource %(resource)s to %(config_resource)s directory"
msgstr "من فضلك أضف المورد %(resource)s إلى الدليل %(config_resource)" msgstr "من فضلك أضف المورد %(resource)s إلى الدليل %(config_resource)s"
# Settings dialog # Settings dialog
msgid "New Break" msgid "New Break"

View File

@ -287,7 +287,7 @@ msgstr "Ugyldigt cron-udtryk '%s'"
# Settings dialog # Settings dialog
#, python-format #, python-format
msgid "Please add the resource %(resource)s to %(config_resource)s directory" msgid "Please add the resource %(resource)s to %(config_resource)s directory"
msgstr "Tilføj ressource %(ressource)s til %(config_resource)s bibliotek" msgstr "Tilføj ressource %(resource)s til %(config_resource)s bibliotek"
# Settings dialog # Settings dialog
msgid "New Break" msgid "New Break"

View File

@ -285,7 +285,7 @@ msgstr ""
#, python-format #, python-format
msgid "Please add the resource %(resource)s to %(config_resource)s directory" msgid "Please add the resource %(resource)s to %(config_resource)s directory"
msgstr "" msgstr ""
"Kérem adja hozzá a(z) %(resource) erőforrást a %(config_resource) könyvtárhoz" "Kérem adja hozzá a(z) %(resource)s erőforrást a %(config_resource)s könyvtárhoz"
# Settings dialog # Settings dialog
msgid "New Break" msgid "New Break"

View File

@ -293,7 +293,7 @@ msgstr "Espressione cron non valida '%s'"
#, python-format #, python-format
msgid "Please add the resource %(resource)s to %(config_resource)s directory" msgid "Please add the resource %(resource)s to %(config_resource)s directory"
msgstr "" msgstr ""
"Aggiungi la/le risorse %(resource) nella/e cartella/e %(config_resource)" "Aggiungi la/le risorse %(resource)s nella/e cartella/e %(config_resource)s"
# Settings dialog # Settings dialog
msgid "New Break" msgid "New Break"

View File

@ -292,7 +292,7 @@ msgstr "A expressão cron '%s' é inválida"
# Settings dialog # Settings dialog
#, python-format #, python-format
msgid "Please add the resource %(resource)s to %(config_resource)s directory" msgid "Please add the resource %(resource)s to %(config_resource)s directory"
msgstr "Adicionar o recurso %(resource)s à pasta %(config_resources)s" msgstr "Adicionar o recurso %(resource)s à pasta %(config_resource)s"
# Settings dialog # Settings dialog
msgid "New Break" msgid "New Break"

View File

@ -294,7 +294,7 @@ msgstr "A expressão de cron '%s' é inválida"
msgid "Please add the resource %(resource)s to %(config_resource)s directory" msgid "Please add the resource %(resource)s to %(config_resource)s directory"
msgstr "" msgstr ""
"Por favor, adicione o recurso %(resource)s para a diretoria " "Por favor, adicione o recurso %(resource)s para a diretoria "
"%(config_resources)s" "%(config_resource)s"
# Settings dialog # Settings dialog
msgid "New Break" msgid "New Break"

View File

@ -290,7 +290,7 @@ msgstr "Недопустимое выражение cron '%s'"
# Settings dialog # Settings dialog
#, python-format #, python-format
msgid "Please add the resource %(resource)s to %(config_resource)s directory" msgid "Please add the resource %(resource)s to %(config_resource)s directory"
msgstr "Пожалуйста, добавьте источник %(resource)s в папку %(config_resource)" msgstr "Пожалуйста, добавьте источник %(resource)s в папку %(config_resource)s"
# Settings dialog # Settings dialog
msgid "New Break" msgid "New Break"

View File

@ -289,7 +289,7 @@ msgstr "Biểu thức cron '%s' không hợp lệ"
# Settings dialog # Settings dialog
#, python-format #, python-format
msgid "Please add the resource %(resource)s to %(config_resource)s directory" msgid "Please add the resource %(resource)s to %(config_resource)s directory"
msgstr "Vui lòng thêm tài nguyên %(resource) vào thư mục %(config_resource)" msgstr "Vui lòng thêm tài nguyên %(resource)s vào thư mục %(config_resource)s"
# Settings dialog # Settings dialog
msgid "New Break" msgid "New Break"

View File

@ -287,7 +287,7 @@ msgstr "无效的 cron 正则表达式 '%s'"
# Settings dialog # Settings dialog
#, python-format #, python-format
msgid "Please add the resource %(resource)s to %(config_resource)s directory" msgid "Please add the resource %(resource)s to %(config_resource)s directory"
msgstr "请将资源 %(resource) 添加到 %(config_resource) 目录" msgstr "请将资源 %(resource)s 添加到 %(config_resource)s 目录"
# Settings dialog # Settings dialog
msgid "New Break" msgid "New Break"

View File

@ -284,7 +284,7 @@ msgstr "無效的 cron 表述式 '%s'"
# Settings dialog # Settings dialog
#, python-format #, python-format
msgid "Please add the resource %(resource)s to %(config_resource)s directory" msgid "Please add the resource %(resource)s to %(config_resource)s directory"
msgstr "請將資源 %(resource)s 加入至 %(config_resource) 的路徑" msgstr "請將資源 %(resource)s 加入至 %(config_resource)s 的路徑"
# Settings dialog # Settings dialog
msgid "New Break" msgid "New Break"

View File

@ -23,24 +23,84 @@ import polib
import re import re
import sys import sys
def validate_placeholders(message: str) -> bool:
pos = 0
success = True
count_placeholders = 0
count_unnamed = 0
while True:
index = message.find("%", pos)
if index == -1:
break
pos = index + 1
nextchar = message[pos : pos + 1]
name = None
if nextchar == "(":
index = message.find(")", pos)
if index == -1:
success = False
print(f"Unclosed parenthetical in '{message}'")
break
name = message[pos + 1 : index]
pos = index + 1
nextchar = message[pos : pos + 1]
if nextchar not in ["%", "s", "d", "i", "f", "F"]:
success = False
print(f"Invalid format modifier in '{message}'")
break
if nextchar != "%":
count_placeholders += 1
if name is None:
count_unnamed += 1
pos += 1
continue
if count_unnamed > 1:
success = False
print(f"Multiple unnamed placeholders in '{message}'")
if count_unnamed > 0 and count_placeholders > count_unnamed:
success = False
print(f"Mixing named and unnamed placeholders in '{message}'")
return success
def has_equal_placeholders(left: str, right: str) -> bool: def has_equal_placeholders(left: str, right: str) -> bool:
percents = re.finditer(r'%(?P<name>\(\w+\))?(?P<format>[a-z])', left) def _get_placeholders(message: str) -> tuple:
percents = re.finditer(r"%(?P<name>\(\w+\))?(?P<format>[a-z])", message)
unnamed = defaultdict(int) unnamed = defaultdict(int)
named = [] named = set()
for percent in percents: for percent in percents:
if percent.group('name'): if percent.group("name"):
named.append(f"%({percent.group('name')}){percent.group('format')}") named.add(f"%({percent.group('name')}){percent.group('format')}")
else: else:
match = f"%{percent.group('format')}" match = f"%{percent.group('format')}"
unnamed[match] += 1 unnamed[match] += 1
return (unnamed, named)
(left_unnamed, left_named) = _get_placeholders(left)
(right_unnamed, right_named) = _get_placeholders(right)
# count unnamed cases (eg. %s, %d) # count unnamed cases (eg. %s, %d)
for match, count in unnamed.items(): for match, count in left_unnamed.items():
if right.count(match) != count: if right_unnamed.get(match, 0) != count:
return False return False
# no need to count named cases - they are optional # named cases are optional - but ensure that translation does not add new ones
if not right_named.issubset(left_named):
return False
return True return True
@ -48,13 +108,19 @@ def validate_po(locale: str, path: str) -> bool:
success = True success = True
po = polib.pofile(path) po = polib.pofile(path)
for entry in po: for entry in po:
if entry.msgstr and not has_equal_placeholders(entry.msgid, entry.msgstr): if entry.msgstr:
if not validate_placeholders(entry.msgstr):
success = False
if not has_equal_placeholders(entry.msgid, entry.msgstr):
print("Number of variables mismatched in " + locale) print("Number of variables mismatched in " + locale)
print(entry.msgid + " -> " + entry.msgstr) print(entry.msgid + " -> " + entry.msgstr)
print() print()
success = False success = False
for plural in entry.msgstr_plural.values(): for plural in entry.msgstr_plural.values():
if plural and not has_equal_placeholders(entry.msgid, plural): if plural:
if not validate_placeholders(plural):
success = False
if not has_equal_placeholders(entry.msgid, plural):
print("Number of variables mismatched in " + locale) print("Number of variables mismatched in " + locale)
print(entry.msgid + " -> " + plural) print(entry.msgid + " -> " + plural)
print() print()