diff --git a/tools/validate_translations.py b/tools/validate_translations.py index 0136f83ab..dd4545566 100755 --- a/tools/validate_translations.py +++ b/tools/validate_translations.py @@ -6,17 +6,52 @@ from glob import glob import polib +def extractFmtFields(text): + """Parse fmt-style replacement fields, mirroring fmt's own escaping rules + ("{{" and "}}" are literal braces). Returns None if a brace is unmatched + or unescaped, which fmt::format would reject at runtime with a + format_error (crash).""" + fields = [] + i = 0 + n = len(text) + while i < n: + c = text[i] + if c == '{': + if i + 1 < n and text[i + 1] == '{': + i += 2 + continue + end = text.find('}', i) + if end == -1: + return None + fields.append(text[i:end + 1]) + i = end + 1 + elif c == '}': + if i + 1 < n and text[i + 1] == '}': + i += 2 + continue + return None + else: + i += 1 + return fields + + def validateEntry(original, translation): if translation == '': return True # Find fmt arguments in source message - src_arguments = re.findall(r"{.*?}", original) + src_arguments = extractFmtFields(original) + if src_arguments is None: + print(f"\033[31mMalformed format string in source message: {original}\033[0m") + return False if not src_arguments: return True # Find fmt arguments in translation - translated_arguments = re.findall(r"{.*?}", translation) + translated_arguments = extractFmtFields(translation) + if translated_arguments is None: + print(f"\033[36m{original}\033[0m has an unmatched/unescaped brace in \033[31m{translation}\033[0m") + return False # If paramteres are untyped with order, sort so that they still appear equal if reordered # Note: This does no hadle cases where the translator reordered arguments where not expected