#!/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 from collections import defaultdict 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()