mirror of
https://github.com/awizemann/scarf.git
synced 2026-05-10 10:36:35 +00:00
91 lines
2.7 KiB
Python
91 lines
2.7 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""
|
||
|
|
Merge per-locale translation JSON files into Localizable.xcstrings.
|
||
|
|
|
||
|
|
Each JSON under tools/translations/<locale>.json is a flat
|
||
|
|
{ "English source key": "Translation" } map. Keys absent from the JSON
|
||
|
|
fall through to English at runtime — that's the desired behavior for
|
||
|
|
proper nouns, format-only strings, and technical terminology.
|
||
|
|
|
||
|
|
Usage:
|
||
|
|
python3 tools/merge-translations.py
|
||
|
|
|
||
|
|
Re-runnable: rewrites the per-locale stringUnit entries each time, so
|
||
|
|
translators can iterate on a JSON and re-merge.
|
||
|
|
"""
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import json
|
||
|
|
import sys
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||
|
|
CATALOG = REPO_ROOT / "scarf" / "scarf" / "Localizable.xcstrings"
|
||
|
|
TRANSLATIONS_DIR = REPO_ROOT / "tools" / "translations"
|
||
|
|
|
||
|
|
LOCALES = ["zh-Hans", "de", "fr", "es", "ja", "pt-BR"]
|
||
|
|
|
||
|
|
|
||
|
|
def load_json(path: Path) -> dict:
|
||
|
|
with path.open("r", encoding="utf-8") as f:
|
||
|
|
return json.load(f)
|
||
|
|
|
||
|
|
|
||
|
|
def save_json(path: Path, data: dict) -> None:
|
||
|
|
with path.open("w", encoding="utf-8") as f:
|
||
|
|
json.dump(data, f, ensure_ascii=False, indent=2, sort_keys=True)
|
||
|
|
f.write("\n")
|
||
|
|
|
||
|
|
|
||
|
|
def main() -> int:
|
||
|
|
catalog = load_json(CATALOG)
|
||
|
|
source_keys = set(catalog.get("strings", {}).keys())
|
||
|
|
|
||
|
|
applied: dict[str, int] = {}
|
||
|
|
skipped_unknown: dict[str, list[str]] = {}
|
||
|
|
|
||
|
|
for locale in LOCALES:
|
||
|
|
path = TRANSLATIONS_DIR / f"{locale}.json"
|
||
|
|
if not path.exists():
|
||
|
|
print(f"[skip] {locale}: no file at {path}")
|
||
|
|
continue
|
||
|
|
|
||
|
|
translations = load_json(path)
|
||
|
|
applied[locale] = 0
|
||
|
|
skipped_unknown[locale] = []
|
||
|
|
|
||
|
|
for source, target in translations.items():
|
||
|
|
if source not in source_keys:
|
||
|
|
skipped_unknown[locale].append(source)
|
||
|
|
continue
|
||
|
|
entry = catalog["strings"].setdefault(source, {})
|
||
|
|
entry.setdefault("localizations", {})
|
||
|
|
entry["localizations"][locale] = {
|
||
|
|
"stringUnit": {
|
||
|
|
"state": "translated",
|
||
|
|
"value": target,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
applied[locale] += 1
|
||
|
|
|
||
|
|
save_json(CATALOG, catalog)
|
||
|
|
|
||
|
|
# Summary
|
||
|
|
print("Merge summary:")
|
||
|
|
for locale in LOCALES:
|
||
|
|
if locale in applied:
|
||
|
|
extras = len(skipped_unknown.get(locale, []))
|
||
|
|
print(f" {locale:8} applied={applied[locale]:4} unknown-keys-skipped={extras}")
|
||
|
|
any_unknown = any(skipped_unknown.values())
|
||
|
|
if any_unknown:
|
||
|
|
print("\nKeys present in translation files but missing from the catalog:")
|
||
|
|
for locale, unknowns in skipped_unknown.items():
|
||
|
|
for k in unknowns:
|
||
|
|
print(f" [{locale}] {k!r}")
|
||
|
|
return 1
|
||
|
|
return 0
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
sys.exit(main())
|