First Commit
This commit is contained in:
Executable
+101
@@ -0,0 +1,101 @@
|
||||
import os
|
||||
import json
|
||||
import glob
|
||||
|
||||
APP_PATH = r'c:\Dockers\dockersyscms\app'
|
||||
MODULES_PATH = os.path.join(APP_PATH, 'modules')
|
||||
|
||||
def get_module_locales():
|
||||
locales = {}
|
||||
# Find all json files in modules/*/locales/*/*.json
|
||||
pattern = os.path.join(MODULES_PATH, '*', 'locales', '*', '*.json')
|
||||
files = glob.glob(pattern)
|
||||
|
||||
for file_path in files:
|
||||
# Extract module, lang, filename
|
||||
parts = file_path.split(os.sep)
|
||||
# .../modules/{module}/locales/{lang}/{filename}.json
|
||||
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 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 analyze_locales():
|
||||
groups = get_module_locales()
|
||||
report = {}
|
||||
|
||||
for group_key, langs in groups.items():
|
||||
# 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))
|
||||
|
||||
# Check for missing
|
||||
group_report = {}
|
||||
for lang, data in data_by_lang.items():
|
||||
missing = []
|
||||
for key in all_keys:
|
||||
if get_value_by_key(data, key) is None:
|
||||
# Find a fallback value from another language
|
||||
fallback = None
|
||||
for other_lang in ['en_US', 'pt_BR', 'es_ES', 'de_DE', 'fr_FR']:
|
||||
if other_lang in data_by_lang:
|
||||
val = get_value_by_key(data_by_lang[other_lang], key)
|
||||
if val is not None:
|
||||
fallback = val
|
||||
break
|
||||
missing.append({'key': key, 'fallback': fallback})
|
||||
|
||||
if missing:
|
||||
group_report[lang] = missing
|
||||
|
||||
if group_report:
|
||||
report[group_key] = group_report
|
||||
|
||||
return report
|
||||
|
||||
if __name__ == "__main__":
|
||||
report = analyze_locales()
|
||||
print(json.dumps(report, indent=2, ensure_ascii=False))
|
||||
Executable
+261
@@ -0,0 +1,261 @@
|
||||
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()
|
||||
Executable
+55
@@ -0,0 +1,55 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
LOCK_FILE = 'composer.lock'
|
||||
OUTPUT_FILE = 'THIRD-PARTY-LICENSES.txt'
|
||||
|
||||
def main():
|
||||
if not os.path.exists(LOCK_FILE):
|
||||
print(f"Error: {LOCK_FILE} not found.")
|
||||
return
|
||||
|
||||
try:
|
||||
with open(LOCK_FILE, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
except Exception as e:
|
||||
print(f"Error reading {LOCK_FILE}: {e}")
|
||||
return
|
||||
|
||||
packages = data.get('packages', [])
|
||||
packages_dev = data.get('packages-dev', [])
|
||||
|
||||
# Combine all packages
|
||||
all_packages = packages + packages_dev
|
||||
|
||||
# Sort by name
|
||||
all_packages.sort(key=lambda x: x.get('name', '').lower())
|
||||
|
||||
with open(OUTPUT_FILE, 'w', encoding='utf-8') as f:
|
||||
f.write("THIRD-PARTY SOFTWARE NOTICES AND INFORMATION\n")
|
||||
f.write("============================================\n\n")
|
||||
f.write("This software includes the following third-party software components:\n\n")
|
||||
|
||||
for pkg in all_packages:
|
||||
name = pkg.get('name', 'Unknown')
|
||||
version = pkg.get('version', 'Unknown')
|
||||
licenses = pkg.get('license', [])
|
||||
license_str = ', '.join(licenses) if licenses else 'Unknown'
|
||||
|
||||
# Try to find a URL
|
||||
url = pkg.get('source', {}).get('url', '')
|
||||
if not url:
|
||||
url = pkg.get('dist', {}).get('url', '')
|
||||
|
||||
f.write(f"Package: {name}\n")
|
||||
f.write(f"Version: {version}\n")
|
||||
f.write(f"License: {license_str}\n")
|
||||
if url:
|
||||
f.write(f"URL: {url}\n")
|
||||
|
||||
f.write("-" * 80 + "\n")
|
||||
|
||||
print(f"Successfully generated {OUTPUT_FILE} with {len(all_packages)} packages.")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Executable
+110
@@ -0,0 +1,110 @@
|
||||
import os
|
||||
import json
|
||||
|
||||
APP_PATH = r'c:\Dockers\dockersyscms\app'
|
||||
LOCALES_PATH = os.path.join(APP_PATH, 'templates', 'locales')
|
||||
|
||||
LANGS = ['pt_BR', 'es_ES', 'de_DE', 'fr_FR']
|
||||
SOURCE_LANG = 'en_US'
|
||||
|
||||
TRANSLATIONS = {
|
||||
'pt_BR': {
|
||||
'about': 'Sobre', 'contact': 'Contato', 'services': 'Serviços', 'posts': 'Posts',
|
||||
'dashboard': 'Painel', 'logout': 'Sair', 'login': 'Entrar', 'register': 'Cadastrar',
|
||||
'profile': 'Perfil', 'settings': 'Configurações', 'pages': 'Páginas', 'faq': 'FAQ',
|
||||
'vitrine': 'Vitrine', 'search': {'placeholder': 'Buscar...'}
|
||||
},
|
||||
'es_ES': {
|
||||
'about': 'Sobre', 'contact': 'Contacto', 'services': 'Servicios', 'posts': 'Publicaciones',
|
||||
'dashboard': 'Tablero', 'logout': 'Cerrar sesión', 'login': 'Iniciar sesión', 'register': 'Registrarse',
|
||||
'profile': 'Perfil', 'settings': 'Configuraciones', 'pages': 'Páginas', 'faq': 'FAQ',
|
||||
'vitrine': 'Vitrina', 'search': {'placeholder': 'Buscar...'}
|
||||
},
|
||||
'de_DE': {
|
||||
'about': 'Über', 'contact': 'Kontakt', 'services': 'Dienstleistungen', 'posts': 'Beiträge',
|
||||
'dashboard': 'Dashboard', 'logout': 'Abmelden', 'login': 'Anmelden', 'register': 'Registrieren',
|
||||
'profile': 'Profil', 'settings': 'Einstellungen', 'pages': 'Seiten', 'faq': 'FAQ',
|
||||
'vitrine': 'Vitrine', 'search': {'placeholder': 'Suchen...'}
|
||||
},
|
||||
'fr_FR': {
|
||||
'about': 'À propos', 'contact': 'Contact', 'services': 'Services', 'posts': 'Articles',
|
||||
'dashboard': 'Tableau de bord', 'logout': 'Déconnexion', 'login': 'Connexion', 'register': 'S\'inscrire',
|
||||
'profile': 'Profil', 'settings': 'Paramètres', 'pages': 'Pages', 'faq': 'FAQ',
|
||||
'vitrine': 'Vitrine', 'search': {'placeholder': 'Rechercher...'}
|
||||
}
|
||||
}
|
||||
|
||||
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 update_recursive(source, target, lang_code, key_prefix=''):
|
||||
modified = False
|
||||
for k, v in source.items():
|
||||
if k not in target:
|
||||
# Key missing in target
|
||||
print(f"Missing key in {lang_code}: {key_prefix}{k}")
|
||||
if isinstance(v, dict):
|
||||
target[k] = {}
|
||||
update_recursive(v, target[k], lang_code, f"{key_prefix}{k}.")
|
||||
else:
|
||||
# Try to translate
|
||||
trans = None
|
||||
|
||||
# Check explicit translations map
|
||||
if lang_code in TRANSLATIONS:
|
||||
# Try direct key match
|
||||
if k in TRANSLATIONS[lang_code]:
|
||||
val = TRANSLATIONS[lang_code][k]
|
||||
if isinstance(val, str):
|
||||
trans = val
|
||||
|
||||
# Try nested map if v is simple string but we mapped a dict? No, v is string here.
|
||||
|
||||
if trans is None:
|
||||
# Use source value (fallback)
|
||||
trans = v
|
||||
|
||||
target[k] = trans
|
||||
modified = True
|
||||
elif isinstance(v, dict):
|
||||
if not isinstance(target[k], dict):
|
||||
# Conflict
|
||||
print(f"Conflict at {key_prefix}{k}")
|
||||
continue
|
||||
if update_recursive(v, target[k], lang_code, f"{key_prefix}{k}."):
|
||||
modified = True
|
||||
return modified
|
||||
|
||||
def sync_layout():
|
||||
source_path = os.path.join(LOCALES_PATH, SOURCE_LANG, 'layout.json')
|
||||
source_data = load_json(source_path)
|
||||
|
||||
if not source_data:
|
||||
print("Source file not found or empty.")
|
||||
return
|
||||
|
||||
for lang in LANGS:
|
||||
target_path = os.path.join(LOCALES_PATH, lang, 'layout.json')
|
||||
target_data = load_json(target_path)
|
||||
|
||||
# We need to preserve existing data but add missing keys from source
|
||||
if update_recursive(source_data, target_data, lang):
|
||||
save_json(target_path, target_data)
|
||||
else:
|
||||
print(f"No changes for {lang}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
sync_layout()
|
||||
Reference in New Issue
Block a user