Formatos

Max PrilutskiyCEO y cofundadorUpdated hace 16 días · 7 min read

La CLI traduce dieciocho formatos de archivo. El formato se deduce a partir de la extensión del archivo; para sobrescribirlo, configura format en una entrada files[], algo imprescindible en los tres formatos que siempre lo requieren (yaml-openapi, yaml-root-key, android).

FormatoExtensionesValor de formatNotas
JSON.jsonjsonClave/valor. Compatible con controles de clave.
JSONC.jsoncjsoncJSON con comentarios. Los comentarios se conservan y además funcionan como notas para traductores.
YAML.yaml, .ymlyamlYAML genérico. Se traduce cada valor de texto; las claves y la estructura se conservan.
YAML de OpenAPI.yaml, .ymlyaml-openapiEspecificaciones OpenAPI. Define format explícitamente: un .yaml normal se detecta automáticamente como yaml.
Clave raíz YAML del idioma.yaml, .ymlyaml-root-keyEl idioma es la clave raíz de YAML (Rails config/locales). La clave raíz se reescribe al idioma de destino. Configura format explícitamente.
Markdown.mdmdSe traduce la prosa; el frontmatter es opcional.
MDX.mdxmdxMarkdown + JSX. Las props de los componentes son opcionales.
Markdoc.mdocmarkdocMarkdown + etiquetas. Frontmatter + atributos de etiquetas.
TypeScript.ts, .mts, .ctstypescriptMódulos de idioma (export default { … }). Se traducen los literales de texto; el código se conserva.
Gettext PO.popoSe traduce msgstr; msgid, los comentarios y las cabeceras se conservan.
Flutter ARB.arbflutterSe traducen los valores de texto; se conservan los metadatos @ y {placeholders}.
Android.xmlandroidstrings.xml. Define format explícitamente: .xml no se detecta automáticamente.
Cadenas de Xcode.stringsxcode-stringsSe traducen los valores; las claves se conservan.
Catálogo de cadenas de Xcode.xcstringsxcode-xcstringsUn solo archivo contiene todos los idiomas: los destinos se vuelven a escribir en ese mismo archivo (véase más abajo).
stringsdict de Xcode.stringsdictxcode-stringsdictSe traducen las cadenas plurales; las claves de control de formato se conservan.
XLIFF.xlf, .xliffxliffSe conserva <source> y se escribe <target> por unidad. Versiones 1.2 y 2.0.
SubRip.srtsrtSe traduce el texto de los subtítulos; los índices y los códigos de tiempo no se tocan.
PHP.phpphpArchivos de idioma de Laravel. Se traducen los valores de texto; las claves, los números y la estructura se conservan.

Verlo de principio a fin

El proyecto de demostración es un pequeño Sandbox que reúne los formatos de documentos y datos — JSON, JSONC, Markdown, MDX, Markdoc y OpenAPI YAML — cada uno con la configuración exacta que necesita. Clónalo con npx degit lingodotdev/lingo.dev/demo/new-cli my-lingo-demo y haz un push. Los formatos de app y framework no están incluidos; para esos casos, Example projects ofrece un repositorio completo por framework, y los fragmentos de abajo, organizados por formato, cubren la configuración.

bash
npx degit lingodotdev/lingo.dev/demo/new-cli my-lingo-demo
cd my-lingo-demo

JSON y JSONC#

Traducción sencilla de clave/valor. Se traduce cada valor de texto, salvo que un control de clave indique lo contrario.

json
{ "pattern": "content/en/app.json", "lockedKeys": ["meta.version"] }

JSONC, además, conserva los comentarios, que el motor interpreta como contexto. Consulta notas para traductores.

json
{ "pattern": "content/en/settings.jsonc", "preservedKeys": ["featureFlags"] }

Markdown, MDX y Markdoc#

La prosa del cuerpo se traduce por defecto. El frontmatter y los componentes incrustados no se traducen a menos que lo actives expresamente.

Frontmatter#

Indica con translateFrontmatterFields qué campos del frontmatter quieres traducir:

json
{
  "pattern": "content/en/guide.md",
  "translateFrontmatterFields": ["title", "description"]
}

Props de componentes MDX#

En MDX, puedes traducir props concretas de componentes concretos con translateComponentProps:

json
{
  "pattern": "content/en/landing.mdx",
  "translateFrontmatterFields": ["title"],
  "translateComponentProps": [{ "component": ["Hero", "Callout"], "props": ["title", "body"] }]
}

Esto traduce las props title y body de <Hero> y <Callout>, y deja intactas todas las demás.

Markdoc#

Markdoc funciona como Markdown, conservando el frontmatter y los atributos de las etiquetas:

json
{
  "pattern": "content/en/changelog.mdoc",
  "translateFrontmatterFields": ["title"]
}

YAML#

El YAML genérico se detecta automáticamente a partir de .yaml/.yml: se traduce cada valor de texto y se conservan las claves y la estructura:

json
{ "pattern": "content/en/strings.yaml" }

Las especificaciones OpenAPI son un caso especial: comparten la extensión .yaml, pero necesitan un format explícito para que el motor traduzca solo los campos visibles para las personas (resúmenes, descripciones) y deje intactas las claves del esquema, las rutas y los ID de operación:

json
{ "pattern": "content/en/api.yaml", "format": "yaml-openapi" }

Los archivos de idioma con formato Rails son el otro caso especial: el idioma es la clave raíz de YAML (en:) y el archivo de destino debe tener como raíz el idioma de destino (es:). En estos casos, configura también format explícitamente:

json
{ "pattern": "config/locales/en.yml", "format": "yaml-root-key" }

Formatos de apps y frameworks#

PO, Flutter ARB, Xcode .strings, Xcode .stringsdict, módulos de idioma de TypeScript, XLIFF, SubRip y PHP siguen la misma regla: se traduce el texto traducible y se conserva todo lo estructural (claves, ID, metadatos, marcadores de posición, códigos de tiempo, código, formato). Cada uno se detecta automáticamente por su extensión; solo tienes que apuntar un patrón al archivo fuente:

json
{ "pattern": "lib/l10n/app_en.arb" }
{ "pattern": "locales/en.po" }
{ "pattern": "Localizable.strings" }
{ "pattern": "l10n/en.xlf" }

Hay uno que necesita un format explícito porque su extensión es demasiado ambigua para detectarla automáticamente:

json
{ "pattern": "res/values/strings.xml", "format": "android" }

Catálogos de cadenas de Xcode#

Un archivo .xcstrings (String Catalog) contiene todos los idiomas en un solo archivo. No hay una ruta de salida por idioma: el CLI lee el idioma de origen y vuelve a escribir cada destino en ese mismo archivo:

json
{ "pattern": "Localizable.xcstrings" }

Las cadenas marcadas con "shouldTranslate": false en el catálogo se omiten: la CLI las deja sin traducir y sin tocar.

¿Qué formatos admiten un alcance por clave?#

lingo push --key vuelve a traducir las claves indicadas y nada más. Necesita claves con nombres estables y un archivo en el que una clave pueda simplemente no estar, lo que deja fuera los formatos de documento y los diccionarios de plurales:

Los alcances por clave funcionanLos alcances por clave no se admiten
json, jsonc, yaml, yaml-root-key, po, flutter, android, xcode-strings, xcode-xcstrings, xliff, php, typescriptmd, mdx, markdoc, html, srt, yaml-openapi, xcode-stringsdict

En md, mdx, markdoc, html, srt y yaml-openapi, una unidad se identifica por su posición en el documento —un ordinal de sección, una ruta de nodo o un número de cue de subtítulo—, así que su clave cambia en cuanto se edita algo por encima, y un alcance dirigido a una de ellas seleccionaría la cadena equivocada o ninguna. xcode-stringsdict se rechaza por el motivo contrario: sus claves son categorías de plural que el archivo necesita para seguir siendo un diccionario de plurales válido, así que nunca pueden omitirse.

lingo push excluye los archivos rechazados de una ejecución acotada por claves con una advertencia, en lugar de hacer que falle, así que un push aún puede mezclarlos con archivos de clave-valor; súbelos sin --key.

Los elementos posicionales dentro de los formatos compatibles se comportan igual y mantienen su texto fuente dentro de un alcance: los elementos de array, los elementos <string-array> de Android y las cantidades de <plurals>.

Rutas de salida#

El patrón da nombre a tu archivo de origen; a partir de él se derivan todas las rutas de destino. Se aplican cuatro reglas, en este orden:

  1. Un segmento completo de la ruta o un nombre de archivo que sea el idioma — el caso más habitual. content/en/app.jsoncontent/de/app.json, locales/en.jsonlocales/de.json.
  2. Un idioma al final de un segmento o de un nombre de archivo, en las estructuras que lo expresan así: Android, Xcode .strings/.stringsdict, Flutter y yaml-root-key. res/values-en/res/values-de/, app_en.arbapp_de.arb, devise.en.ymldevise.de.yml.
  3. El nombre reservado de una plataforma para el idioma predeterminado, sin que aparezca ningún idioma en la ruta. El res/values/ a secas de Android pasa a ser res/values-de/, y el Base.lproj de Xcode pasa a ser de.lproj.
  4. El String Catalog, donde la ruta de destino es la ruta de origen, porque un único archivo contiene todos los idiomas.

Android es la única plataforma cuyas rutas de destino no siguen BCP 47: la CLI escribe el calificador de recursos que Android realmente utiliza, así que pt-BR va a parar a values-pt-rBR/ y zh-Hans a values-b+zh+Hans/.

Si ninguna de las reglas encaja —el idioma de origen no aparece en ninguna parte de la ruta—, la CLI recurre a crear un directorio <locale>/ junto al archivo, y lingo push te avisa de que ha hecho esa suposición. Tómalo como una señal de que el patrón no es correcto. Consulta Configuration.

¿Vienes del CLI heredado?#

La mayoría de los formatos del CLI heredado ya están disponibles en el CLI actual (PO, XLIFF, cadenas de Android/Xcode, Flutter ARB y más; consulta la tabla de arriba). Algunos formatos menos habituales (por ejemplo, CSV, HTML, MJML, .properties) aún no están disponibles; hasta que llegue el tuyo, la documentación del CLI heredado te lo cubre.