Files
orion/scripts/migrate_js_i18n.py
Samir Boulahtit f20266167d
Some checks failed
CI / ruff (push) Failing after 7s
CI / pytest (push) Failing after 1s
CI / architecture (push) Failing after 9s
CI / dependency-scanning (push) Successful in 27s
CI / audit (push) Successful in 8s
CI / docs (push) Has been skipped
fix(lint): auto-fix ruff violations and tune lint rules
- Auto-fixed 4,496 lint issues (import sorting, modern syntax, etc.)
- Added ignore rules for patterns intentional in this codebase:
  E402 (late imports), E712 (SQLAlchemy filters), B904 (raise from),
  SIM108/SIM105/SIM117 (readability preferences)
- Added per-file ignores for tests and scripts
- Excluded broken scripts/rename_terminology.py (has curly quotes)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 23:10:42 +01:00

197 lines
6.5 KiB
Python

#!/usr/bin/env python3
"""
Migrate hardcoded toast messages in JS files to use I18n.t().
Extracts messages, creates translation keys, and updates both JS and locale files.
"""
import json
import re
from collections import defaultdict
from pathlib import Path
PROJECT_ROOT = Path(__file__).parent.parent
MODULES_DIR = PROJECT_ROOT / "app" / "modules"
# Languages to update
LANGUAGES = ["en", "fr", "de", "lb"]
# Pattern to match Utils.showToast('message', 'type') or Utils.showToast("message", "type")
TOAST_PATTERN = re.compile(r"Utils\.showToast\(['\"]([^'\"]+)['\"],\s*['\"](\w+)['\"]\)")
# Map module names to their message namespace
MODULE_MESSAGE_NS = {
"catalog": "catalog",
"orders": "orders",
"customers": "customers",
"inventory": "inventory",
"marketplace": "marketplace",
"tenancy": "tenancy",
"core": "core",
"messaging": "messaging",
"billing": "billing",
"cms": "cms",
"checkout": "checkout",
"cart": "cart",
"dev_tools": "dev_tools",
"monitoring": "monitoring",
"analytics": "analytics",
}
def message_to_key(message: str) -> str:
"""Convert a message string to a translation key."""
# Remove special characters and convert to snake_case
key = message.lower()
key = re.sub(r"[^\w\s]", "", key)
key = re.sub(r"\s+", "_", key)
# Truncate if too long
if len(key) > 40:
key = key[:40].rstrip("_")
return key
def find_js_files_with_toasts() -> dict[str, list[Path]]:
"""Find all JS files with toast messages, grouped by module."""
files_by_module = defaultdict(list)
for module_dir in sorted(MODULES_DIR.iterdir()):
if not module_dir.is_dir():
continue
module_name = module_dir.name
# Find all JS files in this module
for js_file in module_dir.rglob("*.js"):
content = js_file.read_text(encoding="utf-8")
if "Utils.showToast(" in content:
files_by_module[module_name].append(js_file)
return dict(files_by_module)
def extract_messages_from_file(js_file: Path) -> list[tuple[str, str]]:
"""Extract all toast messages from a JS file."""
content = js_file.read_text(encoding="utf-8")
return TOAST_PATTERN.findall(content)
def load_locale_file(module_path: Path, lang: str) -> dict:
"""Load a module's locale file."""
locale_file = module_path / "locales" / f"{lang}.json"
if locale_file.exists():
with open(locale_file, encoding="utf-8") as f:
return json.load(f)
return {}
def save_locale_file(module_path: Path, lang: str, data: dict):
"""Save a module's locale file."""
locale_file = module_path / "locales" / f"{lang}.json"
locale_file.parent.mkdir(parents=True, exist_ok=True)
with open(locale_file, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
f.write("\n")
def update_js_file(js_file: Path, module_name: str, message_keys: dict[str, str]):
"""Update a JS file to use I18n.t() for toast messages."""
content = js_file.read_text(encoding="utf-8")
original = content
for message, key in message_keys.items():
# Replace both single and double quoted versions
full_key = f"{module_name}.messages.{key}"
# Pattern to match the exact message in Utils.showToast
for quote in ["'", '"']:
old = f"Utils.showToast({quote}{message}{quote},"
new = f"Utils.showToast(I18n.t('{full_key}'),"
content = content.replace(old, new)
if content != original:
js_file.write_text(content, encoding="utf-8")
return True
return False
def process_module(module_name: str, js_files: list[Path]) -> dict[str, str]:
"""Process all JS files for a module and return message->key mapping."""
all_messages = {}
# Extract all unique messages
for js_file in js_files:
messages = extract_messages_from_file(js_file)
for message, _msg_type in messages:
if message not in all_messages:
all_messages[message] = message_to_key(message)
return all_messages
def main():
print("=" * 70)
print("JS i18n Migration Script")
print("=" * 70)
# Find all JS files with toasts
files_by_module = find_js_files_with_toasts()
total_files = sum(len(files) for files in files_by_module.values())
print(f"Found {total_files} JS files with toast messages across {len(files_by_module)} modules")
print()
for module_name, js_files in sorted(files_by_module.items()):
print(f"\n{'='*70}")
print(f"Module: {module_name} ({len(js_files)} files)")
print("=" * 70)
module_path = MODULES_DIR / module_name
# Process all JS files and get message mappings
message_keys = process_module(module_name, js_files)
if not message_keys:
print(" No messages found")
continue
print(f" Found {len(message_keys)} unique messages:")
for msg, key in sorted(message_keys.items(), key=lambda x: x[1]):
print(f" {key}: {msg[:50]}{'...' if len(msg) > 50 else ''}")
# Update locale files for all languages
print("\n Updating locale files...")
for lang in LANGUAGES:
locale_data = load_locale_file(module_path, lang)
# Add messages section if not exists
if "messages" not in locale_data:
locale_data["messages"] = {}
# Add each message (only add if not already present)
for message, key in message_keys.items():
if key not in locale_data["messages"]:
# For English, use the original message
# For other languages, we'll use the English as placeholder
locale_data["messages"][key] = message
save_locale_file(module_path, lang, locale_data)
print(f" Updated: {lang}.json")
# Update JS files
print("\n Updating JS files...")
for js_file in js_files:
if update_js_file(js_file, module_name, message_keys):
rel_path = js_file.relative_to(PROJECT_ROOT)
print(f" Updated: {rel_path}")
print("\n" + "=" * 70)
print("Migration complete!")
print("\nNext steps:")
print("1. Review the generated message keys in locale files")
print("2. Translate non-English messages (currently using English as placeholder)")
print("3. Test the application to verify toast messages work")
print("=" * 70)
if __name__ == "__main__":
main()