#!/usr/bin/env python3 """Detect corrupted ICU placeholders in ARB localization files. Guards against the class of bug where a translation string value gets a literal '?' glued next to an ICU placeholder brace (e.g. "{hours}?? {minutes}?"), silently destroying the translated text while staying valid JSON/ICU. This does not require Flutter tooling, so it can run early and fast in CI. """ from __future__ import annotations import glob import json import re import sys from pathlib import Path # A literal '?' sitting immediately adjacent (0-1 whitespace chars) to a # placeholder brace. Narrow on purpose: a normal sentence ending in a real # '?' does not sit glued to a '{' or '}', so it won't match. CORRUPTION_PATTERN = re.compile(r"\}\s?\?|\?\s?\{") ARB_GLOB = "lib/l10n/app_*.arb" def find_repo_root(start: Path) -> Path: """Walk up from `start` until a directory containing lib/l10n is found.""" current = start.resolve() for candidate in [current, *current.parents]: if (candidate / "lib" / "l10n").is_dir(): return candidate return current def check_file(path: Path) -> list[str]: """Return a list of human-readable findings for a single ARB file.""" findings: list[str] = [] try: raw = path.read_text(encoding="utf-8") except UnicodeDecodeError as exc: return [f"{path}: FAILED TO DECODE AS UTF-8 -> {exc}"] try: data = json.loads(raw) except json.JSONDecodeError as exc: return [f"{path}: FAILED TO PARSE JSON -> {exc}"] for key, value in data.items(): if key.startswith("@"): continue meta = data.get(f"@{key}") if not isinstance(meta, dict) or "placeholders" not in meta: continue if not isinstance(value, str): continue if CORRUPTION_PATTERN.search(value): findings.append(f'{path}:{key} -> "{value}"') return findings def main() -> int: repo_root = find_repo_root(Path(__file__).parent) arb_files = sorted(glob.glob(str(repo_root / ARB_GLOB))) if not arb_files: print(f"No ARB files found matching {ARB_GLOB} under {repo_root}") return 1 all_findings: list[str] = [] for arb_path in arb_files: all_findings.extend(check_file(Path(arb_path))) if all_findings: print("Placeholder corruption detected in ARB files:") for finding in all_findings: print(f" {finding}") print(f"\n{len(all_findings)} finding(s) across {len(arb_files)} file(s).") return 1 print(f"OK: checked {len(arb_files)} ARB file(s), no placeholder corruption found.") return 0 if __name__ == "__main__": sys.exit(main())