All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 45s
- Implemented `fill-de-extended-gaps.js` to fill missing billing/orders keys in de-extended from de. - Created `fill-i18n-deep.py` for deep translation of locale JSONs using deep-translator with fallback options. - Added `fill-i18n-locales.js` to translate locale JSONs and write overrides for untranslated keys. - Introduced `fix-en-leaks.py` to translate keys that still match the en-US merge, addressing English leaks. - Developed `patch-de-ch-swiss.js` to replace 'ß' with 'ss' in de-CH.json without deleting existing entries. - Created `patch-en-gb-au.js` to apply UK/AU spelling corrections in en-GB and en-AU locales. - Added shell scripts `run-fix-en-leaks.sh` and `run-i18n-deep-fill.sh` for sequential execution of translation tasks. - Implemented `update-i18n-todo-stats.js` to update statistics in the I18N_TODO.md file based on translation completeness.
121 lines
3.6 KiB
Python
121 lines
3.6 KiB
Python
#!/usr/bin/env python3
|
||
"""Schreibt Locale-Overrides aus Cache + EN-Fallback – ohne API-Aufrufe."""
|
||
|
||
from __future__ import annotations
|
||
|
||
import json
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
ROOT = Path(__file__).resolve().parents[1]
|
||
LOCALES_DIR = ROOT / "frontend" / "src" / "i18n" / "locales"
|
||
CACHE_FILE = Path(__file__).resolve().parent / ".i18n-translate-cache.json"
|
||
|
||
TARGETS = {
|
||
"fr": "fr",
|
||
"es": "es",
|
||
"it": "it",
|
||
"pl": "pl",
|
||
"ja": "ja",
|
||
"zh": "zh-CN",
|
||
"th": "th",
|
||
"tl": "tl",
|
||
"fil": "tl",
|
||
}
|
||
|
||
|
||
def deep_merge(base, override):
|
||
if not isinstance(base, dict) or isinstance(base, list):
|
||
return override if override is not None else base
|
||
result = dict(base)
|
||
for key, value in (override or {}).items():
|
||
if (
|
||
isinstance(value, dict)
|
||
and not isinstance(value, list)
|
||
and isinstance(result.get(key), dict)
|
||
and not isinstance(result.get(key), list)
|
||
):
|
||
result[key] = deep_merge(result[key], value)
|
||
else:
|
||
result[key] = value
|
||
return result
|
||
|
||
|
||
def flatten(obj, prefix=""):
|
||
out = {}
|
||
for key, value in (obj or {}).items():
|
||
next_key = f"{prefix}.{key}" if prefix else key
|
||
if isinstance(value, dict) and not isinstance(value, list):
|
||
out.update(flatten(value, next_key))
|
||
elif isinstance(value, str):
|
||
out[next_key] = value
|
||
return out
|
||
|
||
|
||
def set_by_path(obj, dot_path, value):
|
||
parts = dot_path.split(".")
|
||
cur = obj
|
||
for part in parts[:-1]:
|
||
if part not in cur or not isinstance(cur[part], dict):
|
||
cur[part] = {}
|
||
cur = cur[part]
|
||
cur[parts[-1]] = value
|
||
|
||
|
||
def build_overrides(de_flat, target_flat):
|
||
out = {}
|
||
for key, value in target_flat.items():
|
||
if value != de_flat.get(key):
|
||
set_by_path(out, key, value)
|
||
return out
|
||
|
||
|
||
def apply_locale(code: str, cache: dict, en_flat: dict, de_flat: dict) -> None:
|
||
target = TARGETS[code]
|
||
locale_path = LOCALES_DIR / f"{code}.json"
|
||
locale_json = json.loads(locale_path.read_text(encoding="utf-8"))
|
||
de = json.loads((LOCALES_DIR / "de.json").read_text(encoding="utf-8"))
|
||
merged = flatten(deep_merge(json.loads(json.dumps(de)), locale_json))
|
||
|
||
from_cache = from_en = still = 0
|
||
for key, de_val in de_flat.items():
|
||
if merged.get(key) != de_val:
|
||
continue
|
||
from_lang = "en" if en_flat.get(key) and en_flat[key] != de_val else "de"
|
||
text = en_flat[key] if from_lang == "en" else de_val
|
||
cache_key = f"{from_lang}|{target}|{text}"
|
||
if cache_key in cache:
|
||
merged[key] = cache[cache_key]
|
||
from_cache += 1
|
||
elif from_lang == "en":
|
||
merged[key] = text
|
||
from_en += 1
|
||
else:
|
||
still += 1
|
||
|
||
overrides = build_overrides(de_flat, merged)
|
||
locale_path.write_text(json.dumps(overrides, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||
print(
|
||
f"[{code}] cache={from_cache} enFallback={from_en} stillDe={still} overrides={len(flatten(overrides))}",
|
||
flush=True,
|
||
)
|
||
|
||
|
||
def main():
|
||
codes = sys.argv[1:] or list(TARGETS.keys())
|
||
cache = json.loads(CACHE_FILE.read_text(encoding="utf-8"))
|
||
de = json.loads((LOCALES_DIR / "de.json").read_text(encoding="utf-8"))
|
||
en_us = json.loads((LOCALES_DIR / "en-US.json").read_text(encoding="utf-8"))
|
||
de_flat = flatten(de)
|
||
en_flat = flatten(deep_merge(de, en_us))
|
||
|
||
for code in codes:
|
||
if code not in TARGETS:
|
||
print(f"skip unknown: {code}", file=sys.stderr)
|
||
continue
|
||
apply_locale(code, cache, en_flat, de_flat)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|