diff --git a/static/i18n.js b/static/i18n.js index e1b2c986..ca94dc5e 100644 --- a/static/i18n.js +++ b/static/i18n.js @@ -5027,7 +5027,7 @@ const LOCALES = { profile_gateway_stopped: 'Gateway gestoppt', profile_active: 'Aktiv', profile_no_configuration: 'Keine Konfiguration', - profile_skill_count: '{count} Fähigkeiten', + profile_skill_count: (count) => `${count} Fähigkeit${count === 1 ? '' : 'en'}`, profile_use: 'Verwenden', profile_switch_title: 'Profil wechseln', profile_delete_title: 'Profil löschen', diff --git a/tests/test_issue1989_profile_skill_count.py b/tests/test_issue1989_profile_skill_count.py new file mode 100644 index 00000000..1b1641a8 --- /dev/null +++ b/tests/test_issue1989_profile_skill_count.py @@ -0,0 +1,42 @@ +from pathlib import Path +import re + +I18N_JS = (Path(__file__).resolve().parents[1] / "static" / "i18n.js").read_text(encoding="utf-8") + + +def _extract_locale_block(locale: str, src: str) -> str: + locale_key_re = re.compile( + rf"(?m)^[ \t]{{2}}(?:'{re.escape(locale)}'|\"{re.escape(locale)}\"|{re.escape(locale)})\s*:\s*\{{" + ) + start_match = locale_key_re.search(src) + assert start_match is not None, f"Locale {locale!r} not found in i18n.js" + + brace_start = start_match.end() - 1 + assert brace_start != -1, f"Locale {locale!r} block has no opening brace" + + next_locale_re = re.compile( + r"(?m)^[ \t]{2}(?:[A-Za-z]{2,3}(?:[-_][A-Za-z0-9_]+)?|'[A-Za-z]{2,3}(?:[-_][A-Za-z0-9_]+)?'|\"[A-Za-z]{2,3}(?:[-_][A-Za-z0-9_]+)?\")\s*:\s*\{" + ) + next_match = next_locale_re.search(src, pos=brace_start + 1) + end = next_match.start() if next_match else len(src) + + depth = 0 + for idx in range(brace_start, end): + char = src[idx] + if char == "{": + depth += 1 + elif char == "}": + depth -= 1 + if depth == 0: + return src[brace_start : idx + 1] + assert False, f"Locale {locale!r} block did not close cleanly" + + +def test_german_profile_skill_count_is_function(): + de_block = _extract_locale_block("de", I18N_JS) + # German locale should pass count as an interpolation arg, not expose {count} verbatim. + assert "profile_skill_count:" in de_block + assert "{count} Fähigkeiten" not in de_block + assert re.search(r"profile_skill_count:\s*\([^)]*\)\s*=>", de_block), ( + "profile_skill_count in de locale should be an arrow function, not a string template" + )