- 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>
197 lines
6.5 KiB
Python
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()
|