diff --git a/safeeyes/config/locale/ar/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/ar/LC_MESSAGES/safeeyes.po index a6e7981..2ca905c 100644 --- a/safeeyes/config/locale/ar/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/ar/LC_MESSAGES/safeeyes.po @@ -286,7 +286,7 @@ msgstr "تعبير الوظيفة المجدولة غير صحيح '%s'" # Settings dialog #, python-format 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 msgid "New Break" diff --git a/safeeyes/config/locale/da/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/da/LC_MESSAGES/safeeyes.po index 5045138..7106d00 100644 --- a/safeeyes/config/locale/da/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/da/LC_MESSAGES/safeeyes.po @@ -287,7 +287,7 @@ msgstr "Ugyldigt cron-udtryk '%s'" # Settings dialog #, python-format 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 msgid "New Break" diff --git a/safeeyes/config/locale/hu/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/hu/LC_MESSAGES/safeeyes.po index ce79f1d..45c02dd 100644 --- a/safeeyes/config/locale/hu/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/hu/LC_MESSAGES/safeeyes.po @@ -285,7 +285,7 @@ msgstr "" #, python-format msgid "Please add the resource %(resource)s to %(config_resource)s directory" 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 msgid "New Break" diff --git a/safeeyes/config/locale/it/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/it/LC_MESSAGES/safeeyes.po index cb52454..22dbca1 100644 --- a/safeeyes/config/locale/it/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/it/LC_MESSAGES/safeeyes.po @@ -293,7 +293,7 @@ msgstr "Espressione cron non valida '%s'" #, python-format msgid "Please add the resource %(resource)s to %(config_resource)s directory" 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 msgid "New Break" diff --git a/safeeyes/config/locale/pt/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/pt/LC_MESSAGES/safeeyes.po index b9baf34..bc5504e 100644 --- a/safeeyes/config/locale/pt/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/pt/LC_MESSAGES/safeeyes.po @@ -292,7 +292,7 @@ msgstr "A expressão cron '%s' é inválida" # Settings dialog #, python-format 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 msgid "New Break" diff --git a/safeeyes/config/locale/pt_BR/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/pt_BR/LC_MESSAGES/safeeyes.po index 5d79ac4..d528c42 100644 --- a/safeeyes/config/locale/pt_BR/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/pt_BR/LC_MESSAGES/safeeyes.po @@ -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" msgstr "" "Por favor, adicione o recurso %(resource)s para a diretoria " -"%(config_resources)s" +"%(config_resource)s" # Settings dialog msgid "New Break" diff --git a/safeeyes/config/locale/ru/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/ru/LC_MESSAGES/safeeyes.po index 1423ea4..e078fd6 100644 --- a/safeeyes/config/locale/ru/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/ru/LC_MESSAGES/safeeyes.po @@ -290,7 +290,7 @@ msgstr "Недопустимое выражение cron '%s'" # Settings dialog #, python-format 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 msgid "New Break" diff --git a/safeeyes/config/locale/vi/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/vi/LC_MESSAGES/safeeyes.po index eb79d94..fe2a14f 100644 --- a/safeeyes/config/locale/vi/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/vi/LC_MESSAGES/safeeyes.po @@ -289,7 +289,7 @@ msgstr "Biểu thức cron '%s' không hợp lệ" # Settings dialog #, python-format 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 msgid "New Break" diff --git a/safeeyes/config/locale/zh_CN/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/zh_CN/LC_MESSAGES/safeeyes.po index d1a2747..01c69ef 100644 --- a/safeeyes/config/locale/zh_CN/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/zh_CN/LC_MESSAGES/safeeyes.po @@ -287,7 +287,7 @@ msgstr "无效的 cron 正则表达式 '%s'" # Settings dialog #, python-format msgid "Please add the resource %(resource)s to %(config_resource)s directory" -msgstr "请将资源 %(resource) 添加到 %(config_resource) 目录" +msgstr "请将资源 %(resource)s 添加到 %(config_resource)s 目录" # Settings dialog msgid "New Break" diff --git a/safeeyes/config/locale/zh_TW/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/zh_TW/LC_MESSAGES/safeeyes.po index 7b18e46..65970b3 100644 --- a/safeeyes/config/locale/zh_TW/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/zh_TW/LC_MESSAGES/safeeyes.po @@ -284,7 +284,7 @@ msgstr "無效的 cron 表述式 '%s'" # Settings dialog #, python-format 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 msgid "New Break" diff --git a/validate_po.py b/validate_po.py index 6e64dda..8447cae 100644 --- a/validate_po.py +++ b/validate_po.py @@ -23,24 +23,84 @@ import polib import re import sys -def has_equal_placeholders(left: str, right: str) -> bool: - percents = re.finditer(r'%(?P\(\w+\))?(?P[a-z])', left) +def validate_placeholders(message: str) -> bool: + pos = 0 - unnamed = defaultdict(int) - named = [] - for percent in percents: - if percent.group('name'): - named.append(f"%({percent.group('name')}){percent.group('format')}") - else: - match = f"%{percent.group('format')}" - unnamed[match] += 1 + 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 _get_placeholders(message: str) -> tuple: + percents = re.finditer(r"%(?P\(\w+\))?(?P[a-z])", message) + + unnamed = defaultdict(int) + named = set() + for percent in percents: + if percent.group("name"): + named.add(f"%({percent.group('name')}){percent.group('format')}") + else: + match = f"%{percent.group('format')}" + 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) - for match, count in unnamed.items(): - if right.count(match) != count: + for match, count in left_unnamed.items(): + if right_unnamed.get(match, 0) != count: 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 @@ -48,17 +108,23 @@ def validate_po(locale: str, path: str) -> bool: success = True po = polib.pofile(path) for entry in po: - if entry.msgstr and not has_equal_placeholders(entry.msgid, entry.msgstr): - print("Number of variables mismatched in " + locale) - print(entry.msgid + " -> " + entry.msgstr) - print() - success = False - for plural in entry.msgstr_plural.values(): - if plural and not has_equal_placeholders(entry.msgid, plural): + 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(entry.msgid + " -> " + plural) + print(entry.msgid + " -> " + entry.msgstr) print() success = False + for plural in entry.msgstr_plural.values(): + 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(entry.msgid + " -> " + plural) + print() + success = False return success success = True