i18n automático: traduce solo lo nuevo, preserva lo revisado
El problema
La interfaz estaba traducida a 11 idiomas, con las cadenas en ficheros JSON por idioma. Cada vez que se añadía texto nuevo en español, alguien tenía que abrir los 11 ficheros y añadir la traducción a mano — con riesgo de pisar traducciones ya ajustadas o desincronizar ficheros entre sí.
La decisión técnica
El script tenía que resolver dos cosas que parecen contradictorias:
- Traducir automáticamente todo lo que sea nuevo
- No tocar lo que ya existe
La lógica central es un diff por claves: para cada entrada del JSON en español, comprueba si ya existe en el fichero destino. Si existe, la preserva. Si no, la traduce.
javascriptasync function sincronizar(fuente, destino, idioma) { const resultado = {}; for (const clave in fuente) { if (typeof fuente[clave] === "object") { // Recursivo para objetos anidados resultado[clave] = await sincronizar( fuente[clave], destino[clave] ?? {}, idioma ); } else { // Clave ya existe → preservar; clave nueva → traducir resultado[clave] = clave in destino ? destino[clave] : await traducir(fuente[clave], idioma); } } return resultado; }
Esto hace que el script sea seguro de ejecutar en cualquier momento — no rompe nada, solo rellena huecos.
Un detalle: los códigos ISO no se traducen
Los ficheros contenían códigos de idioma como valores ("ES", "EN", "FR"...).
Sin protección, la API los traduciría. La solución: una lista blanca que el script
identifica y devuelve sin tocar.
Resultado
| Antes | Después |
|---|---|
| Actualización manual de 11 ficheros | Un comando actualiza todos |
| Horas por cada texto nuevo | Minutos |
| Riesgo de desincronización | JSON en español como única fuente de verdad |
| Traducciones manuales en riesgo | Claves existentes siempre preservadas |
Lo que aprendí
El patrón clave in destino convierte los ficheros de idioma en una caché persistente:
solo se llama a la API para lo nuevo. Si tuviese que rehacerlo, añadiría un modo
--dry-run para ver qué claves se van a traducir antes de ejecutar.