""" Split frontend/src/lib/i18n.tsx into per-locale, per-namespace JSON files. Generates: frontend/src/lib/i18n/messages//.json frontend/src/lib/i18n/messages/index.json (manifest) """ import json import re from pathlib import Path from collections import defaultdict ROOT = Path(__file__).parent.parent SOURCE = ROOT / "frontend" / "src" / "lib" / "i18n.tsx" OUT_DIR = ROOT / "frontend" / "src" / "lib" / "i18n" / "messages" content = SOURCE.read_text(encoding="utf-8") def find_locale_blocks(text: str) -> list[tuple[str, str]]: """Find each locale block using brace matching.""" blocks = [] pattern = re.compile(r'\n\s*([a-z]{2}):\s*\{') for match in pattern.finditer(text): locale = match.group(1) start = match.end() - 1 # position of the opening '{' brace_count = 0 in_string = False escape = False i = start while i < len(text): ch = text[i] if escape: escape = False elif ch == "\\": escape = True elif ch == '"': in_string = not in_string elif not in_string: if ch == "{": brace_count += 1 elif ch == "}": brace_count -= 1 if brace_count == 0: blocks.append((locale, text[start + 1 : i])) break i += 1 return blocks def parse_block(block: str) -> dict[str, str]: """Parse key: value pairs from a locale block. Values may be concatenated strings.""" messages = {} # Match "key": value, where value is a string literal possibly followed by + "..." entry_pattern = re.compile( r'"([a-zA-Z0-9_\-\.]+)":\s*((?:"(?:[^"\\]|\\.)*"\s*(?:\+\s*)?)+)', re.DOTALL, ) for match in entry_pattern.finditer(block): key = match.group(1) raw = match.group(2) parts = re.findall(r'"((?:[^"\\]|\\.)*)"', raw) value = "".join(parts) messages[key] = value return messages OUT_DIR.mkdir(parents=True, exist_ok=True) manifest: dict[str, list[str]] = defaultdict(list) all_namespaces: set[str] = set() for locale, block in find_locale_blocks(content): messages = parse_block(block) by_namespace: dict[str, dict[str, str]] = defaultdict(dict) for key, value in messages.items(): namespace = key.split(".")[0] by_namespace[namespace][key] = value all_namespaces.add(namespace) locale_dir = OUT_DIR / locale locale_dir.mkdir(parents=True, exist_ok=True) for namespace, msgs in by_namespace.items(): (locale_dir / f"{namespace}.json").write_text( json.dumps(msgs, indent=2, ensure_ascii=False) + "\n", encoding="utf-8", ) if namespace not in manifest[locale]: manifest[locale].append(namespace) # Write manifest manifest = {loc: sorted(manifest[loc]) for loc in sorted(manifest)} (OUT_DIR / "index.json").write_text( json.dumps( { "locales": list(manifest.keys()), "namespaces": sorted(all_namespaces), "manifest": manifest, }, indent=2, ensure_ascii=False, ) + "\n", encoding="utf-8", ) print(f"Wrote namespace files for {len(manifest)} locales") print(f"Namespaces: {len(all_namespaces)}") print(f"Total keys (en): {sum(len(manifest[loc]) for loc in manifest)}")