Cómo traducir un archivo de idioma JSON sin romper claves ni marcadores de posición
Descubre qué valores de un archivo en.json se traducen, qué reescribe un traductor de archivos (comentarios, orden de claves, ID largos) y cómo comprobar marcadores de posición, plurales ICU y claves con un script gratuito y un archivo de prueba.

Para traducir un archivo de idioma JSON como en.json, entrega el archivo completo a un traductor que entienda JSON. Pegar el texto sin formato en una ventana de chat o en un traductor web es la forma arriesgada de hacerlo. Un traductor que entiende JSON lee el archivo, traduce solo los valores de texto y vuelve a escribir las mismas claves, de modo que tu aplicación aún pueda cargar el archivo. Copiar y pegar puede traducir una clave, cambiar una comilla o eliminar una llave. Cualquiera de esas cosas rompe el archivo.
Mantener las claves es la parte fácil. La mayoría de los problemas vienen después: un marcador de posición {name} que vuelve como {nom}, un plural ICU que pierde su caso other, una URL enviada a traducir como cualquier otro texto y algunos cambios que el traductor hace al propio archivo. Eso es lo que cubre esta guía.
Creamos un archivo de prueba con cada uno de esos casos y escribimos un verificador gratuito que compara un archivo de origen con su traducción. También pasamos el archivo de prueba por una copia local del código de análisis y escritura de JSON que usa BookTranslator, con un marcador en lugar de un modelo, para que puedas ver exactamente qué valores se envían y cómo vuelve el archivo.
Respuesta rápida: qué le pasa a cada parte del archivo
| Parte del archivo | Ejemplo de nuestra prueba | Qué hace BookTranslator | Qué comprobar |
|---|---|---|---|
| Claves | "greeting", "error" | Se mantienen tal cual | Nada |
| Valores de texto sin formato | "Reading list" | Traducidos | Significado, longitud en pantalla |
| Valores con marcadores de posición | "Welcome back, {name}!", "Saved %s of %d chapters" | Traducidos, con los marcadores de posición incluidos en el texto enviado | Nombres y recuentos de marcadores de posición (lo hace el verificador) |
| Mensajes plurales ICU | "{count, plural, one {# book} other {# books}}" | Enviados como una sola cadena | El caso other y las categorías de plural para el idioma de destino |
| HTML en línea | "accept the <b>Terms</b>." | Traducido; el recuento de etiquetas se comprueba después de la traducción | Nombres y recuentos de etiquetas (lo hace el verificador) |
| URL almacenadas como texto | "https://example.com/help" | Se envían para traducir | Que volvieron sin cambios |
| Valores sin letras | "2.4.1", "—", "" | Se dejaron como estaban | Nada |
Números, true/false, null | 20, true | Se dejaron como estaban | IDs muy grandes (ver más abajo) |
| Matrices de cadenas | ["Open", "Save", "Export as EPUB"] | Se traducen en el archivo traducido; se dejan sin traducir en el archivo bilingüe | El archivo bilingüe, si lo usas |
| Comentarios (JSONC) | // Shown on the home screen | Se eliminan. Si un valor también contiene //, el archivo puede no cargarse | Elimina los comentarios antes de subir el archivo |
El resto de esta guía muestra de dónde viene cada fila y qué hacer al respecto.
Paso 1: Revisa el archivo de origen antes de subirlo
Ejecuta el verificador en el archivo que planeas traducir. Solo necesita Python 3 y nada más:
python3 check_i18n_json.py en.json
En nuestro archivo de prueba, imprimió:
WARN limits.projectId: 9007199254740993 is above 2^53 and can change in a JavaScript round trip; store IDs as strings
WARN error.network: a flat key with dots and a nested key resolve to the same dot path; tools that address strings by dot path can mix them up
INFO (root): 3 keys look like integers; JavaScript sorts those first and in numeric order when it rewrites the object
INFO source: 17 strings, 2 numbers
Las dos advertencias y la nota sobre las claves similares a enteros coinciden cada una con un cambio que vimos cuando nuestro archivo de prueba pasó por el traductor. Si tu archivo no genera ninguna de ellas, puedes saltar al Paso 3.
El verificador también rechaza un archivo que no sea JSON estricto, como uno con comentarios o comas finales, porque ese es el siguiente problema.
Lo que volvió de nuestro archivo de prueba
La prueba se ejecutó el 2 de noviembre de 2026 con el manejo actual de JSON en el backend de BookTranslator. En lugar de llamar a un modelo, la prueba reemplazó cada valor que se enviaría para traducir con [ZH] más el texto original. Eso mantiene el resultado predecible: todo lo que tiene [ZH] delante se envió, todo lo que no lo tiene no se envió. Ambas salidas están publicadas: el archivo traducido y el archivo bilingüe.
El archivo traducido
Todas las claves volvieron sin cambios y el archivo seguía siendo válido. Se enviaron los valores con letras, incluidos {name}, %s, el mensaje ICU, la etiqueta <b> y la URL de ayuda. La cadena de versión "2.4.1", el guion y la cadena vacía no se enviaron, porque no contienen letras. Los números y los booleanos se dejaron intactos.
Otras cuatro cosas cambiaron:
- Una cadena se colocó en la clave equivocada. En nuestra prueba, nuestro archivo tenía una clave plana
"error.network"con el texto "Check your connection." y también una clave anidada"error": { "network": "Network error" }. El traductor identifica cada cadena por su ruta con puntos entre las partes, así que ambas cadenas tenían la misma dirección,error.network. En la salida, ambas claves contenían la traducción de "Network error". "Check your connection." había desaparecido. Esto requiere ambos estilos en el mismo archivo, lo cual es poco común, pero falla silenciosamente. El verificador advierte sobre esto antes de que subas el archivo. - Las claves con forma de entero se reordenaron.
"steps": { "10": ..., "2": ..., "1": ... }volvió como"1","2","10". JavaScript siempre lista primero las claves con forma de entero, en orden numérico, cuando reconstruye un objeto. Las claves y los valores siguen coincidiendo, así que tu aplicación está bien, pero un diff contra el archivo fuente será ruidoso. - Un ID grande perdió precisión.
9007199254740993volvió como9007199254740992. JavaScript almacena los números como punto flotante de 64 bits, y los enteros por encima de 2^53 no pueden representarse todos exactamente (MDN sobreNumber.MAX_SAFE_INTEGER). Guarda los ID largos como cadenas o mantenlos fuera del archivo de idioma. - El formato se reescribió. La salida usa sangría de dos espacios y termina con un salto de línea. Si tu fuente usaba tabulaciones o cuatro espacios, espera cambios de espacios en blanco en el diff.
Nuestro archivo de prueba no tenía claves duplicadas, pero vale la pena conocerlas: el analizador JSON de JavaScript solo conserva el último valor de una clave repetida, así que el texto anterior nunca se traduce. Tu aplicación cargaría el mismo último valor, así que nada visible se rompe, pero el verificador lista las claves duplicadas para que puedas limpiarlas.
El archivo bilingüe
El archivo bilingüe conserva cada valor original y añade una clave hermana con _translated al final:
"greeting": "Welcome back, {name}!",
"greeting_translated": "[ZH] Welcome back, {name}!",
Eso funciona bien para revisar, porque el original y la traducción quedan uno al lado del otro. No es un archivo que tu aplicación pueda cargar, porque ahora cada clave tiene un gemelo. Usa el archivo traducido en tu aplicación.
Una laguna: las cadenas dentro de arrays no reciben una clave hermana _translated, porque los elementos de un array no tienen clave a la que añadirla. En nuestra prueba, "menu": ["Open", "Save", "Export as EPUB"] volvió en inglés en el archivo bilingüe, pero traducido en el archivo traducido. Si revisas desde el archivo bilingüe, comprueba los arrays en el archivo traducido.
Archivos con comentarios
Muchos proyectos mantienen comentarios en sus archivos de idioma (JSONC), lo cual el JSON estricto no permite. El traductor primero intenta con JSON estricto. Si eso falla, elimina los comentarios y las comas finales y vuelve a intentarlo. Eliminar comentarios significa borrar todo lo que sigue a // en una línea, y eso incluye el // en https://.
Nuestro archivo con un comentario y una URL no cargó en absoluto; el análisis falló en el primer comentario. Un archivo con comentarios pero sin // dentro de ningún valor cargó bien, y los comentarios se eliminaron de la salida.
Así que: si tu archivo tiene comentarios, elimínalos antes de subirlo, o guarda una copia con los comentarios y acepta que la traducción no los tendrá.
Paso 2: Prepara el archivo
Puedes solucionar la mayoría de estos problemas antes de traducir:
- Haz que sea JSON estricto. Elimina los comentarios y las comas finales. Si tu herramienta de compilación lee JSONC, mantén el archivo con comentarios como fuente y exporta una copia limpia para traducir.
- Usa un solo estilo de claves. O anida las claves o aplánalas con puntos, no ambas cosas en un mismo archivo. Si usas i18next, su opción
keySeparatorcontrola cómo se leen los puntos en las claves, así que revisa esa configuración antes de aplanar nada. - Saca los datos que no son texto. Los ID, las URL, los indicadores de funcionalidad y los límites pertenecen a la configuración, no al archivo de idioma. Si una URL debe quedarse, mantenla, pero espera revisarla después de la traducción. Nuestra guía de qué no traducir cubre cómo decidir qué más debe quedarse en el idioma original.
- Escribe los marcadores de posición de la misma forma en todas partes. Un solo estilo, como
{name}, es más fácil de verificar que una mezcla de{name},{{name}}y%s.
Paso 3: Traduce el archivo completo
Sube el archivo limpio al traductor de documentos de BookTranslator, que incluye .json entre los demás formatos de localización y texto estructurado que acepta. El precio se muestra antes de pagar. Cuando termine la tarea, tu página de historial ofrece el archivo traducido y el archivo bilingüe como descargas separadas.
Dos cosas sobre cómo se traduce el texto afectan al resultado:
- El modelo no ve tus claves. Cada valor se envía como un segmento numerado, sin el nombre de la clave. Si
"button.open"y"status.open"dicen ambos "Open", el modelo no tiene forma de saber que uno es un verbo y el otro un adjetivo. Las cadenas cortas y ambiguas son las que conviene revisar a mano. - Cada valor se traduce por separado. Nada vincula dos claves entre sí, así que la misma palabra en dos lugares puede volver de dos formas distintas, y una palabra que necesita dos traducciones diferentes puede volver igual. El glosario automático opcional de BookTranslator recopila nombres, lugares y términos especializados y los mantiene consistentes, pero no puedes darle traducciones fijas propias. Para los términos que deben traducirse de una manera exacta, mantén una lista de términos y revisa esas cadenas después de la traducción; nuestra guía de glosarios explica cómo planificar uno.
Las etiquetas HTML en línea se cuentan después de la traducción, y un segmento cuyas etiquetas no coinciden se reintenta. Los marcadores de posición entre llaves, los marcadores de posición estilo %s y la sintaxis ICU se envían como parte del texto, pero no se comprueban de la misma manera. Ejecuta el paso 4 en cada traducción.
Si prefieres traducir con un modelo de chat o un modelo local, envía solo los valores, nunca el archivo sin procesar, y vuelve a colocarlos por clave con un script. Las comprobaciones siguientes se aplican en ambos casos.
Paso 4: Comprobar el archivo traducido
Compara la traducción con el original:
python3 check_i18n_json.py en.json fr.json
Para mostrar lo que detecta, creamos un archivo en francés con errores introducidos a propósito. Está hecho a mano, no es salida de un modelo. Tras las mismas advertencias del original que en el paso 1, el comprobador imprimió:
ERROR app.greeting: placeholders differ; lost ['name'] extra ['nom']
ERROR app.help_url: urls differ; lost ['https://example.com/help'] extra ['https://example.com/aide']
ERROR app.items: ICU message has no 'other' case
ERROR app.legacy: placeholders differ; lost ['%d'] extra []
ERROR limits.maxFiles: type changed from int to str
ERROR limits.projectId: non-text value changed: 9007199254740993 -> 9007199254740992
INFO menu[1]: identical to source: 'Save'
INFO compared 20 source values, 1 unchanged strings
Cada uno de esos aparecería como un error en la aplicación: un nombre vacío, un enlace roto, un fallo o un mensaje sin procesar al renderizar el plural, un recuento de capítulos ausente, un límite comparado como texto. La línea INFO enumera las cadenas que volvieron sin cambios. Algunas están bien, como los nombres de marcas; "Save" en un menú francés no lo está.
El comprobador compara claves, tipos de valor, valores que no son texto, marcadores de posición ({name}, {{name}}, %s, %1$s, %@), nombres de argumentos ICU, etiquetas HTML y URL. No juzga la traducción en sí.
Se supone que los casos plurales cambian
Un plural ICU enumera un mensaje por categoría de plural. El inglés tiene dos categorías, one y other, pero muchos idiomas tienen un conjunto diferente. Una buena traducción al ruso de nuestro mensaje items normalmente usaría one, few, many y other, y una al japonés solo other. Por eso, una discrepancia aquí no siempre es un error.
Las categorías provienen de las reglas de plural del CLDR de Unicode, y other es la que tiene todos los idiomas. Estas son las categorías cardinales que informa Intl.PluralRules en Node.js con CLDR 48:
| Idioma | Categorías de plural |
|---|---|
| Inglés, alemán | one, other |
| Francés, español, portugués | one, many, other |
| Ruso, polaco | one, few, many, other |
| Árabe | zero, one, two, few, many, other |
| Japonés, chino, coreano | otro |
El francés, el español y el portugués usan many solo para números redondos muy grandes, como un millón, así que un mensaje para recuentos pequeños normalmente puede prescindir de él. El verificador informa de un other ausente como error, y de cualquier otro cambio en las categorías como advertencia para compararla con esta tabla. Puedes imprimir la lista de cualquier idioma tú mismo:
node -e 'console.log(new Intl.PluralRules("ru").resolvedOptions().pluralCategories)'
Al traductor se le dan los casos en inglés, así que comprueba lo que devolvió. Si un mensaje en ruso o árabe sigue teniendo solo one y other, añade los casos que faltan a mano, o pídeselo a un traductor.
Luego cárgalo en la aplicación
El verificador demuestra que el archivo es seguro de cargar, no que se lea bien. Cambia la aplicación al nuevo idioma y revisa las pantallas: palabras largas en alemán o finés que desbordan un botón, texto de derecha a izquierda en árabe o hebreo, y fechas o números que tu código formatea por separado del archivo de idioma. Nuestra guía sobre traducción frente a localización cubre qué más cambia entre mercados además de las palabras.
Problemas habituales
El archivo traducido no se carga en mi aplicación. Pásale el verificador. Si el verificador no puede analizarlo, el archivo se editó después de la traducción o no era válido desde el principio. Si lo analiza, busca las líneas de ERROR sobre claves o tipos.
Una cadena aparece como un mensaje ICU en bruto, como {count, plural, ...}. La sintaxis ICU se rompió en la traducción, normalmente por una llave faltante o un caso other ausente. Copia la estructura del original y traduce solo las palabras dentro de cada caso.
Mi archivo es YAML, un archivo .po o Markdown. BookTranslator también los acepta, pero cada formato tiene sus propias reglas. Para documentación, consulta nuestra guía de traducción de Markdown; para páginas web, la guía de archivos HTML.
¿Puedo traducir solo las claves nuevas? No dentro de una sola carga; se traduce el archivo completo. Pon las claves nuevas en un archivo aparte, tradúcelo y combina el resultado por clave. El verificador confirmará que el archivo combinado tiene todas las claves.
¿Debo traducir las claves? No. Las claves son identificadores que busca tu código. Solo los valores son texto para que lo lean las personas.
Publicaciones relacionadas





