Plataforma SaaSNode.js · Google Cloud Translation API · JWT

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.

javascript
async 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

AntesDespués
Actualización manual de 11 ficherosUn comando actualiza todos
Horas por cada texto nuevoMinutos
Riesgo de desincronizaciónJSON en español como única fuente de verdad
Traducciones manuales en riesgoClaves 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.