#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" python3 - "$ROOT_DIR" <<'PY' import json import pathlib import re import sys root = pathlib.Path(sys.argv[1]) l10n_swift = root / "Apptag" / "L10n.swift" localization_dir = root / "Apptag" / "Localization" english_path = localization_dir / "en.json" EXPECTED_LANGUAGE_COUNT = 29 ALLOWED_ENGLISH_COPY_KEYS = { "pro.card.badge", } REQUIRED_PLACEHOLDERS = { "pro.notes.remaining": {"%limit%", "%remaining%"}, "pro.theme.status.previewCountdown": {"%time%"}, } def fail(message: str) -> None: print(f"FAIL: {message}", file=sys.stderr) sys.exit(1) def load_json(path: pathlib.Path) -> dict[str, str]: try: data = json.loads(path.read_text(encoding="utf-8")) except Exception as error: fail(f"{path} is not valid JSON: {error}") if not isinstance(data, dict): fail(f"{path} must contain a JSON object") return data def extract_language_codes() -> list[str]: text = l10n_swift.read_text(encoding="utf-8") match = re.search( r"static\s+let\s+supported\s*:\s*\[\(code:\s*String,\s*name:\s*String\)\]\s*=\s*\[(.*?)\n\s*\]", text, re.S, ) if not match: fail("could not find L10n.supported language list") codes = re.findall(r'\("([^"]+)",\s*"[^"]+"\)', match.group(1)) if len(codes) != EXPECTED_LANGUAGE_COUNT: fail(f"L10n.supported has {len(codes)} languages, expected {EXPECTED_LANGUAGE_COUNT}") if len(set(codes)) != len(codes): fail("L10n.supported contains duplicate language codes") return codes language_codes = extract_language_codes() english = load_json(english_path) pro_keys = [key for key in english if key.startswith("pro.")] if not pro_keys: fail("en.json has no pro.* keys") for code in language_codes: path = localization_dir / f"{code}.json" if not path.is_file(): fail(f"missing localization file: {path}") data = load_json(path) missing = [ key for key in pro_keys if not isinstance(data.get(key), str) or not data.get(key, "").strip() ] if missing: fail(f"{code}.json is missing pro translations: {', '.join(missing)}") badge = data["pro.card.badge"] if data.get("pro.card.title.free") == badge: fail(f"{code}.json uses the badge text as the upgrade title for pro.card.title.free") dirty_keys = [key for key in pro_keys if "__" in data[key] or "###FILE" in data[key]] if dirty_keys: fail(f"{code}.json still contains placeholder artifacts: {', '.join(dirty_keys)}") placeholder_errors = [] for key, placeholders in REQUIRED_PLACEHOLDERS.items(): value = data.get(key, "") missing_placeholders = sorted(placeholder for placeholder in placeholders if placeholder not in value) if missing_placeholders: placeholder_errors.append(f"{key} missing {', '.join(missing_placeholders)}") if placeholder_errors: fail(f"{code}.json has broken Pro placeholders: {'; '.join(placeholder_errors)}") if code != "en": copied_english = [ key for key in pro_keys if key not in ALLOWED_ENGLISH_COPY_KEYS and data.get(key) == english.get(key) ] if copied_english: fail(f"{code}.json copies English Pro copy: {', '.join(copied_english)}") print( "PASS Pro localization QA: " f"{len(language_codes)} languages, {len(pro_keys)} keys, " "no placeholder artifacts or English fallback copies" ) PY