import json import os import sys from pathlib import Path LOCALES_DIR = Path("src/locales") USER_MASTER = LOCALES_DIR / "translations.user.json" ADMIN_MASTER = LOCALES_DIR / "translations.admin.json" LANGUAGES = ["en", "me", "ru", "ua"] def get_nested_keys(data, prefix=""): keys = {} for k, v in data.items(): new_prefix = f"{prefix}.{k}" if prefix else k if isinstance(v, dict): if any(lang in v for lang in LANGUAGES): keys[new_prefix] = v else: keys.update(get_nested_keys(v, new_prefix)) else: keys[new_prefix] = v return keys def set_nested_key(data, key_path, value): parts = key_path.split('.') for part in parts[:-1]: data = data.setdefault(part, {}) data[parts[-1]] = value def deep_merge(dict1, dict2): """Recursive merge of two dictionaries.""" for key, value in dict2.items(): if key in dict1 and isinstance(dict1[key], dict) and isinstance(value, dict): # If both are dicts and it's NOT a leaf node with languages if not any(lang in value for lang in LANGUAGES): deep_merge(dict1[key], value) else: dict1[key] = value else: dict1[key] = value def assemble_master(fragment_dir, master_file, root_key=None): """Merge all fragments in a directory into one master file""" if not fragment_dir.exists(): print(f"Warning: {fragment_dir} not found.") return combined_data = {} # Sort files to ensure deterministic merging for item in sorted(fragment_dir.glob("*.json")): with open(item, "r", encoding="utf-8") as f: try: data = json.load(f) deep_merge(combined_data, data) except Exception as e: print(f"Error reading {item}: {e}") # Wrap in root key if needed (e.g. "admin": {...}) final_output = combined_data if root_key: final_output = {root_key: combined_data} with open(master_file, "w", encoding="utf-8") as f: json.dump(final_output, f, ensure_ascii=False, indent=2) print(f"Assembled fragments from {fragment_dir} into {master_file}") def _merge_files(master_file, suffix): """Generic merge from {lang}{suffix} into master_file""" master_data = {} all_keys = set() locale_data = {} for lang in LANGUAGES: path = LOCALES_DIR / f"{lang}{suffix}" if path.exists(): with open(path, "r", encoding="utf-8") as f: data = json.load(f) flat = {} def flatten(d, prefix=""): for k, v in d.items(): new_prefix = f"{prefix}.{k}" if prefix else k if isinstance(v, dict): flatten(v, new_prefix) else: flat[new_prefix] = v flatten(data) locale_data[lang] = flat all_keys.update(flat.keys()) for key in sorted(all_keys): translations = {} for lang in LANGUAGES: translations[lang] = locale_data.get(lang, {}).get(key, "") set_nested_key(master_data, key, translations) with open(master_file, "w", encoding="utf-8") as f: json.dump(master_data, f, ensure_ascii=False, indent=2) print(f"Merged {suffix} files into {master_file}") def _split_master(master_file, suffix): """Generic split from master_file into {lang}{suffix}""" if not master_file.exists(): print(f"Warning: {master_file} not found, skipping split.") return with open(master_file, "r", encoding="utf-8") as f: master_data = json.load(f) for lang in LANGUAGES: lang_data = {} def unflatten(d, target, current_lang): for k, v in d.items(): if isinstance(v, dict): if any(l in v for l in LANGUAGES): target[k] = v.get(current_lang, "") else: target[k] = {} unflatten(v, target[k], current_lang) else: target[k] = v unflatten(master_data, lang_data, lang) output_path = LOCALES_DIR / f"{lang}{suffix}" with open(output_path, "w", encoding="utf-8") as f: json.dump(lang_data, f, ensure_ascii=False, indent=2) print(f"Generated {output_path}") def assemble(): """Assemble fragments into masters""" assemble_master(LOCALES_DIR / "master_user", USER_MASTER) assemble_master(LOCALES_DIR / "master_admin", ADMIN_MASTER, root_key="admin") def merge(): """Merge individual JSON files into respective master files""" _merge_files(USER_MASTER, ".json") _merge_files(ADMIN_MASTER, ".admin.json") def split(): """Assemble and then split master files""" assemble() _split_master(USER_MASTER, ".json") _split_master(ADMIN_MASTER, ".admin.json") def list_missing(): """List all keys with missing translations in both master files""" for name, path in [("User", USER_MASTER), ("Admin", ADMIN_MASTER)]: if not path.exists(): continue print(f"\n--- Missing in {name} ---") with open(path, "r", encoding="utf-8") as f: data = json.load(f) flat_keys = get_nested_keys(data) missing_found = False for key, trans in sorted(flat_keys.items()): if isinstance(trans, dict): missing = [l for l in LANGUAGES if not trans.get(l)] if missing: print(f"Key: {key} - Missing: {', '.join(missing)}") missing_found = True if not missing_found: print("Everything translated!") if __name__ == "__main__": if len(sys.argv) < 2: print("Usage: python manage_locales.py [assemble|merge|split|missing]") sys.exit(1) cmd = sys.argv[1].lower() if cmd == "assemble": assemble() elif cmd == "merge": merge() elif cmd == "split": split() elif cmd == "missing": list_missing() else: print(f"Unknown command: {cmd}")