2023-11-14 16:48:40 +01:00
|
|
|
import json
|
|
|
|
from collections import OrderedDict
|
|
|
|
from pathlib import Path
|
|
|
|
from typing import Any
|
|
|
|
|
|
|
|
# FIXME use Path
|
|
|
|
ROOT_FOLDER = Path(__file__).parent.parent
|
|
|
|
LOCALES_FOLDER = ROOT_FOLDER / "locales"
|
|
|
|
LOCALES_LIST_FILE = ROOT_FOLDER / "maintenance" / "locales.json"
|
|
|
|
LOCALE_FILES = {path.stem: path for path in LOCALES_FOLDER.glob("*.json")}
|
|
|
|
ALL_LOCALES = set(LOCALE_FILES.keys())
|
|
|
|
|
|
|
|
|
|
|
|
def get_json(path: Path) -> Any:
|
|
|
|
return json.loads(path.read_text(), object_pairs_hook=OrderedDict)
|
|
|
|
|
|
|
|
|
|
|
|
def save_json(path: Path, data: Any, sort: bool = False) -> None:
|
|
|
|
with path.open("w", encoding="utf-8") as fp:
|
|
|
|
json.dump(data, fp, ensure_ascii=False, sort_keys=sort, indent=4)
|
|
|
|
fp.write("\n")
|
|
|
|
|
|
|
|
|
|
|
|
def get_flatten_keys(d, parent_key=""):
|
|
|
|
items = set()
|
|
|
|
|
|
|
|
for k, v in d.items():
|
|
|
|
key = f"{parent_key}.{k}" if parent_key else k
|
|
|
|
items.add(key)
|
|
|
|
|
|
|
|
if isinstance(v, dict):
|
|
|
|
items |= get_flatten_keys(v, key)
|
|
|
|
|
|
|
|
return items
|
|
|
|
|
|
|
|
|
|
|
|
def filter_data(data, stale_keys):
|
|
|
|
filtered = OrderedDict()
|
|
|
|
|
|
|
|
for k, v in data.items():
|
|
|
|
if k not in stale_keys:
|
|
|
|
value = v
|
|
|
|
|
|
|
|
if isinstance(v, OrderedDict):
|
|
|
|
nested_stale_keys = [
|
|
|
|
key.replace(f"{k}.", "")
|
|
|
|
for key in stale_keys
|
|
|
|
if key.startswith(f"{k}.")
|
|
|
|
]
|
|
|
|
if nested_stale_keys:
|
|
|
|
value = filter_data(v, nested_stale_keys)
|
|
|
|
|
|
|
|
filtered[k] = value
|
|
|
|
|
|
|
|
return filtered
|
|
|
|
|
|
|
|
|
|
|
|
def remove_stale_translations(ref="en", locales=None):
|
|
|
|
locales = set(locales) if locales else set(ALL_LOCALES)
|
|
|
|
locales.discard(ref)
|
|
|
|
|
|
|
|
ref_path = LOCALE_FILES[ref]
|
|
|
|
ref_data = get_json(ref_path)
|
|
|
|
ref_keys = get_flatten_keys(ref_data)
|
|
|
|
|
|
|
|
for locale in locales:
|
|
|
|
path = LOCALE_FILES[locale]
|
|
|
|
data = get_json(path)
|
|
|
|
keys = get_flatten_keys(data)
|
|
|
|
stale_keys = sorted(keys - ref_keys)
|
|
|
|
filtered_data = filter_data(data, stale_keys)
|
|
|
|
save_json(path, filtered_data)
|
|
|
|
|
|
|
|
# Resave ref file to sort and indent
|
|
|
|
save_json(ref_path, ref_data, sort=True)
|
|
|
|
|
|
|
|
|
2023-11-14 17:05:16 +01:00
|
|
|
def generate_locales_list():
|
|
|
|
locales = []
|
|
|
|
|
2023-11-18 14:36:55 +01:00
|
|
|
for locale in sorted(ALL_LOCALES):
|
2023-11-14 17:05:16 +01:00
|
|
|
path = LOCALE_FILES[locale]
|
|
|
|
data = get_json(path).get("_language", {})
|
|
|
|
code = data.get("code", locale)
|
2023-11-18 14:36:55 +01:00
|
|
|
fallback = [
|
|
|
|
code.strip() for code in data.get("fallback", "").strip().split(",")
|
|
|
|
]
|
|
|
|
# Remove "en" from fallback locales since there's already a last resort fallback to "en"
|
|
|
|
fallback = [code for code in fallback if code not in ("en", "")]
|
|
|
|
|
2023-11-14 17:05:16 +01:00
|
|
|
if locale != code:
|
|
|
|
# Warn that translators have changed the language code
|
|
|
|
print(
|
|
|
|
f"Language's code for file '{locale}.json' is '{code}': file"
|
|
|
|
" should probably be renamed to '{code}.json'"
|
|
|
|
)
|
2023-11-18 14:36:55 +01:00
|
|
|
|
2023-11-14 17:05:16 +01:00
|
|
|
locales.append(
|
|
|
|
{
|
|
|
|
"code": code,
|
|
|
|
"iso": code,
|
|
|
|
"name": data.get("name", code),
|
|
|
|
"dir": data.get("dir", "ltr"),
|
2023-11-18 14:36:55 +01:00
|
|
|
"fallback": fallback or None,
|
2023-11-14 17:05:16 +01:00
|
|
|
"file": f"{locale}.json",
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
save_json(LOCALES_LIST_FILE, locales, sort=True)
|
|
|
|
|
|
|
|
|
2023-11-14 16:48:40 +01:00
|
|
|
if __name__ == "__main__":
|
|
|
|
remove_stale_translations()
|
2023-11-14 17:05:16 +01:00
|
|
|
generate_locales_list()
|