import os import json import glob import copy APP_PATH = r'c:\Dockers\dockersyscms\app' MODULES_PATH = os.path.join(APP_PATH, 'modules') # Simple dictionary for common terms to attempt basic translation COMMON_TRANSLATIONS = { 'pt_BR': { 'save': 'Salvar', 'cancel': 'Cancelar', 'edit': 'Editar', 'delete': 'Excluir', 'name': 'Nome', 'description': 'Descrição', 'title': 'Título', 'actions': 'Ações', 'create': 'Criar', 'update': 'Atualizar', 'back': 'Voltar', 'view': 'Visualizar', 'home': 'Início', 'dashboard': 'Painel', 'login': 'Entrar', 'register': 'Cadastrar', 'email': 'E-mail', 'password': 'Senha', 'logout': 'Sair', 'profile': 'Perfil', 'search': 'Buscar', 'filter': 'Filtrar', 'reset': 'Limpar', 'status': 'Status', 'active': 'Ativo', 'inactive': 'Inativo', 'yes': 'Sim', 'no': 'Não', 'success': 'Sucesso', 'error': 'Erro', 'warning': 'Aviso', 'info': 'Info', 'confirm': 'Confirmar', 'close': 'Fechar', 'image': 'Imagem', 'file': 'Arquivo', 'date': 'Data', 'author': 'Autor', 'category': 'Categoria', 'categories': 'Categorias', 'post': 'Post', 'posts': 'Posts', 'comment': 'Comentário', 'comments': 'Comentários', 'user': 'Usuário', 'users': 'Usuários', 'role': 'Função', 'roles': 'Funções', 'permission': 'Permissão', 'permissions': 'Permissões', 'settings': 'Configurações' }, 'en_US': { 'save': 'Save', 'cancel': 'Cancel', 'edit': 'Edit', 'delete': 'Delete', 'name': 'Name', 'description': 'Description', 'title': 'Title', 'actions': 'Actions', 'create': 'Create', 'update': 'Update', 'back': 'Back', 'view': 'View', 'home': 'Home', 'dashboard': 'Dashboard', 'login': 'Login', 'register': 'Register', 'email': 'Email', 'password': 'Password', 'logout': 'Logout', 'profile': 'Profile', 'search': 'Search', 'filter': 'Filter', 'reset': 'Reset', 'status': 'Status', 'active': 'Active', 'inactive': 'Inactive', 'yes': 'Yes', 'no': 'No', 'success': 'Success', 'error': 'Error', 'warning': 'Warning', 'info': 'Info', 'confirm': 'Confirm', 'close': 'Close', 'image': 'Image', 'file': 'File', 'date': 'Date', 'author': 'Author', 'category': 'Category', 'categories': 'Categories', 'post': 'Post', 'posts': 'Posts', 'comment': 'Comment', 'comments': 'Comments', 'user': 'User', 'users': 'Users', 'role': 'Role', 'roles': 'Roles', 'permission': 'Permission', 'permissions': 'Permissions', 'settings': 'Settings' }, 'es_ES': { 'save': 'Guardar', 'cancel': 'Cancelar', 'edit': 'Editar', 'delete': 'Eliminar', 'name': 'Nombre', 'description': 'Descripción', 'title': 'Título', 'actions': 'Acciones', 'create': 'Crear', 'update': 'Actualizar', 'back': 'Volver', 'view': 'Ver', 'home': 'Inicio', 'dashboard': 'Tablero', 'login': 'Iniciar sesión', 'register': 'Registrarse', 'email': 'Correo', 'password': 'Contraseña', 'logout': 'Cerrar sesión', 'profile': 'Perfil', 'search': 'Buscar', 'filter': 'Filtrar', 'reset': 'Restablecer', 'status': 'Estado', 'active': 'Activo', 'inactive': 'Inactivo', 'yes': 'Sí', 'no': 'No', 'success': 'Éxito', 'error': 'Error', 'warning': 'Advertencia', 'info': 'Info', 'confirm': 'Confirmar', 'close': 'Cerrar', 'image': 'Imagen', 'file': 'Archivo', 'date': 'Fecha', 'author': 'Autor', 'category': 'Categoría', 'categories': 'Categorías', 'post': 'Publicación', 'posts': 'Publicaciones', 'comment': 'Comentario', 'comments': 'Comentarios', 'user': 'Usuario', 'users': 'Usuarios', 'role': 'Rol', 'roles': 'Roles', 'permission': 'Permiso', 'permissions': 'Permisos', 'settings': 'Configuraciones' }, 'de_DE': { 'save': 'Speichern', 'cancel': 'Abbrechen', 'edit': 'Bearbeiten', 'delete': 'Löschen', 'name': 'Name', 'description': 'Beschreibung', 'title': 'Titel', 'actions': 'Aktionen', 'create': 'Erstellen', 'update': 'Aktualisieren', 'back': 'Zurück', 'view': 'Ansehen', 'home': 'Startseite', 'dashboard': 'Dashboard', 'login': 'Anmelden', 'register': 'Registrieren', 'email': 'E-Mail', 'password': 'Passwort', 'logout': 'Abmelden', 'profile': 'Profil', 'search': 'Suchen', 'filter': 'Filtern', 'reset': 'Zurücksetzen', 'status': 'Status', 'active': 'Aktiv', 'inactive': 'Inaktiv', 'yes': 'Ja', 'no': 'Nein', 'success': 'Erfolg', 'error': 'Fehler', 'warning': 'Warnung', 'info': 'Info', 'confirm': 'Bestätigen', 'close': 'Schließen', 'image': 'Bild', 'file': 'Datei', 'date': 'Datum', 'author': 'Autor', 'category': 'Kategorie', 'categories': 'Kategorien', 'post': 'Beitrag', 'posts': 'Beiträge', 'comment': 'Kommentar', 'comments': 'Kommentare', 'user': 'Benutzer', 'users': 'Benutzer', 'role': 'Rolle', 'roles': 'Rollen', 'permission': 'Berechtigung', 'permissions': 'Berechtigungen', 'settings': 'Einstellungen' }, 'fr_FR': { 'save': 'Enregistrer', 'cancel': 'Annuler', 'edit': 'Modifier', 'delete': 'Supprimer', 'name': 'Nom', 'description': 'Description', 'title': 'Titre', 'actions': 'Actions', 'create': 'Créer', 'update': 'Mettre à jour', 'back': 'Retour', 'view': 'Voir', 'home': 'Accueil', 'dashboard': 'Tableau de bord', 'login': 'Connexion', 'register': 'S\'inscrire', 'email': 'E-mail', 'password': 'Mot de passe', 'logout': 'Déconnexion', 'profile': 'Profil', 'search': 'Rechercher', 'filter': 'Filtrer', 'reset': 'Réinitialiser', 'status': 'Statut', 'active': 'Actif', 'inactive': 'Inactif', 'yes': 'Oui', 'no': 'Non', 'success': 'Succès', 'error': 'Erreur', 'warning': 'Attention', 'info': 'Info', 'confirm': 'Confirmer', 'close': 'Fermer', 'image': 'Image', 'file': 'Fichier', 'date': 'Date', 'author': 'Auteur', 'category': 'Catégorie', 'categories': 'Catégories', 'post': 'Article', 'posts': 'Articles', 'comment': 'Commentaire', 'comments': 'Commentaires', 'user': 'Utilisateur', 'users': 'Utilisateurs', 'role': 'Rôle', 'roles': 'Rôles', 'permission': 'Permission', 'permissions': 'Permissions', 'settings': 'Paramètres' } } def get_module_locales(): locales = {} pattern = os.path.join(MODULES_PATH, '*', 'locales', '*', '*.json') files = glob.glob(pattern) for file_path in files: parts = file_path.split(os.sep) try: locales_index = parts.index('locales') module = parts[locales_index - 1] lang = parts[locales_index + 1] filename = parts[locales_index + 2] key = f"{module}/{filename}" if key not in locales: locales[key] = {} locales[key][lang] = file_path except ValueError: continue return locales def load_json(path): try: with open(path, 'r', encoding='utf-8') as f: return json.load(f) except Exception as e: print(f"Error reading {path}: {e}") return {} def save_json(path, data): try: with open(path, 'w', encoding='utf-8') as f: json.dump(data, f, indent=4, ensure_ascii=False) print(f"Updated: {path}") except Exception as e: print(f"Error writing {path}: {e}") def flatten_keys(data, prefix=""): keys = set() if isinstance(data, dict): for k, v in data.items(): full_key = f"{prefix}.{k}" if prefix else k keys.add(full_key) if isinstance(v, dict): keys.update(flatten_keys(v, full_key)) return keys def get_value_by_key(data, key): parts = key.split('.') curr = data for part in parts: if isinstance(curr, dict) and part in curr: curr = curr[part] else: return None return curr def set_value_by_key(data, key, value): parts = key.split('.') curr = data for i, part in enumerate(parts[:-1]): if part not in curr: curr[part] = {} elif not isinstance(curr[part], dict): # Conflict: trying to use a string as a dict container # Convert to dict? No, this structure is invalid if keys conflict. # But for filling, we assume structure consistency. # If conflict, we skip or overwrite. Here we skip. print(f"Warning: Structure conflict for key {key} at {part}") return curr = curr[part] last_part = parts[-1] curr[last_part] = value def try_translate_word(word, target_lang): word_lower = word.lower().replace('_', ' ').strip() # Check direct match if word_lower in COMMON_TRANSLATIONS.get('en_US', {}): # It's an english key? # Find the key in en_US for k, v in COMMON_TRANSLATIONS['en_US'].items(): if k == word_lower: return COMMON_TRANSLATIONS[target_lang].get(k) # Check if word is in COMMON_TRANSLATIONS for target lang directly (as key) if word_lower in COMMON_TRANSLATIONS[target_lang]: return COMMON_TRANSLATIONS[target_lang][word_lower] return None def fill_translations(): groups = get_module_locales() for group_key, langs in groups.items(): print(f"Processing {group_key}...") # Load all data data_by_lang = {lang: load_json(path) for lang, path in langs.items()} # Collect all unique keys all_keys = set() for data in data_by_lang.values(): all_keys.update(flatten_keys(data)) # Ensure all 5 langs exist in file list (create if missing) target_langs = ['en_US', 'pt_BR', 'es_ES', 'de_DE', 'fr_FR'] module_path = os.path.dirname(os.path.dirname(list(langs.values())[0])) # .../locales for t_lang in target_langs: if t_lang not in data_by_lang: # Create empty file f_path = os.path.join(module_path, t_lang, os.path.basename(list(langs.values())[0])) os.makedirs(os.path.dirname(f_path), exist_ok=True) data_by_lang[t_lang] = {} langs[t_lang] = f_path # Fill missing for lang in target_langs: data = data_by_lang[lang] modified = False for key in all_keys: if get_value_by_key(data, key) is None: # Find a fallback fallback_val = None fallback_lang_source = None # 1. Try en_US if 'en_US' in data_by_lang: val = get_value_by_key(data_by_lang['en_US'], key) if val is not None: fallback_val = val fallback_lang_source = 'en_US' # 2. Try pt_BR if fallback_val is None and 'pt_BR' in data_by_lang: val = get_value_by_key(data_by_lang['pt_BR'], key) if val is not None: fallback_val = val fallback_lang_source = 'pt_BR' # 3. Any other if fallback_val is None: for other in data_by_lang: val = get_value_by_key(data_by_lang[other], key) if val is not None: fallback_val = val fallback_lang_source = other break if fallback_val is not None: # Try to smart translate simple keys # Extract the last part of the key as a "word" guess last_part = key.split('.')[-1] translated = try_translate_word(last_part, lang) final_val = translated if translated else fallback_val # If it's a fallback string and we didn't translate it, maybe prepend [LANG] ? # User said "complete translations", usually means "don't leave empty". # Using English/Portuguese as fallback is standard. set_value_by_key(data, key, final_val) modified = True if modified: save_json(langs[lang], data) if __name__ == "__main__": fill_translations()