Files
2026-09-01 10:46:29 -03:00

262 lines
13 KiB
Python
Executable File

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()