#!/usr/bin/env python3 """ Add I18n.loadModule() calls to JS files that use I18n.t(). This ensures module translations are loaded before use. """ import re from pathlib import Path PROJECT_ROOT = Path(__file__).parent.parent MODULES_DIR = PROJECT_ROOT / "app" / "modules" # Pattern to find I18n.t('module.xxx') calls and extract module name I18N_PATTERN = re.compile(r"I18n\.t\(['\"](\w+)\.") def find_modules_used(content: str) -> set[str]: """Find all modules referenced in I18n.t() calls.""" return set(I18N_PATTERN.findall(content)) def add_module_loading(js_file: Path): """Add I18n.loadModule() calls to a JS file.""" content = js_file.read_text(encoding="utf-8") # Find modules used in this file modules = find_modules_used(content) if not modules: return False # Check if already has module loading if "I18n.loadModule(" in content: return False # Find the init() method and add loading there # Look for common patterns: # 1. init() { ... } # 2. async init() { ... } init_patterns = [ # Pattern for "init() {" or "async init() {" (r"((?:async\s+)?init\s*\(\s*\)\s*\{)", "init"), # Pattern for "mounted() {" (Vue style) (r"(mounted\s*\(\s*\)\s*\{)", "mounted"), ] for pattern, method_name in init_patterns: match = re.search(pattern, content) if match: # Generate loading code load_calls = "\n".join(f" await I18n.loadModule('{m}');" for m in sorted(modules)) # If init is not async, we need to make it async full_match = match.group(1) if "async" not in full_match: # Make init async new_init = full_match.replace(f"{method_name}()", f"async {method_name}()") content = content.replace(full_match, new_init) full_match = new_init # Add loading after the opening brace insert_code = f"\n // Load i18n translations\n{load_calls}\n" content = content.replace(full_match, full_match + insert_code) js_file.write_text(content, encoding="utf-8") return True # If no init found, add at file level (for simpler scripts) # This handles files that don't use Alpine components if "function " in content or "const " in content: # Find first function definition func_match = re.search(r"^(function\s+\w+\s*\([^)]*\)\s*\{)", content, re.MULTILINE) if func_match: load_calls = "\n".join(f" await I18n.loadModule('{m}');" for m in sorted(modules)) # Make function async if needed full_match = func_match.group(1) if "async" not in full_match: new_func = full_match.replace("function ", "async function ") content = content.replace(full_match, new_func) full_match = new_func insert_code = f"\n // Load i18n translations\n{load_calls}\n" content = content.replace(full_match, full_match + insert_code) js_file.write_text(content, encoding="utf-8") return True return False def main(): print("=" * 70) print("Adding I18n.loadModule() to JS files") print("=" * 70) updated = 0 skipped = 0 # Find all JS files with I18n.t() calls for js_file in MODULES_DIR.rglob("*.js"): content = js_file.read_text(encoding="utf-8") if "I18n.t(" not in content: continue modules = find_modules_used(content) if not modules: continue rel_path = js_file.relative_to(PROJECT_ROOT) if add_module_loading(js_file): print(f" Updated: {rel_path} (modules: {', '.join(sorted(modules))})") updated += 1 else: print(f" Skipped: {rel_path} (already has loading or no init method)") skipped += 1 print() print(f"Updated: {updated} files") print(f"Skipped: {skipped} files") print("=" * 70) if __name__ == "__main__": main()