---
title: "Formatos"
description: "Todos los formatos de archivo i18n que admite verbatra, cómo lee y escribe sus archivos cada uno y qué conserva una escritura para que los diffs queden limpios."
type: "reference"
---
# Formatos

Todos los formatos de archivo i18n que admite verbatra, cómo lee y escribe sus archivos cada uno y qué conserva una escritura para que los diffs queden limpios.

Un adaptador de formato lee un archivo de locale en una forma neutral y lo escribe de vuelta con la forma propia del archivo. Pon en la clave `format` de tu [configuración](https://verbatra.kreitz-webdev.de/es/docs/config-file.md) uno de estos ids:

| `format` | Archivos | Placeholders | Plurales | Archivo de destino ausente | [Sintaxis de placeholder nativa](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#foreign_placeholder_changed) <AvailableFrom version="0.12.0" /> |
| --- | --- | --- | --- | --- | --- |
| `i18next-json` | JSON de i18next (`.json`) | `{{name}}`, `$t(...)` | sufijo CLDR en la clave | se crea | `{{name}}` |
| `vue-i18n-json` | JSON de vue-i18n (`.json`) | `{name}`, `{0}` | pipe en el valor | se crea | `{name}`, `%{name}`, `${name}` |
| `next-intl-json` | JSON de next-intl (`.json`) | argumentos y etiquetas ICU | `plural`, `selectordinal` de ICU | se crea | `{name}`, `%{name}`, `${name}` |
| `ngx-translate-json` | JSON de ngx-translate (`.json`) | `{{name}}` | ninguno | se crea | `{{name}}` |
| `xliff` | XLIFF 1.2 y 2.0 (`.xlf`, `.xliff`) | elementos en línea, `{...}` | ninguno | se rechaza | `{{name}}`, `{name}`, `%{name}`, `${name}` |
| `yaml` | YAML (`.yml`, `.yaml`) | `{{name}}` | ninguno | se crea | `{{name}}` |
| `arb` | ARB de Flutter (`.arb`) | argumentos y etiquetas ICU | `plural`, `selectordinal` de ICU | se crea | `{name}`, `%{name}`, `${name}` |
| `properties` | properties de Java/Spring (`.properties`) | MessageFormat `{0}`, `{count}` | ninguno | se crea | `{name}`, `%{name}`, `${name}` |
| `apple-strings` | `.strings` de Apple, con su `.stringsdict` | printf `%@`, `%d` | categorías de `.stringsdict` | se crea | printf `%s` |
| `apple-xcstrings` | Xcode String Catalogs (`.xcstrings`) | printf `%@`, `%lld` | `variations.plural` | se rechaza | printf `%s` |
| `android-xml` | `strings.xml` de Android | printf `%s`, `%1$s` | cantidades de `<plurals>` | se crea | printf `%s` |
| `gettext-po` | gettext (`.po`, `.pot`) | printf, `%(name)s` | `msgstr[n]` | se crea | printf `%s`, `%(name)s` |
| `ini` | INI (`.ini`) | `{name}`, `{0}` | ninguno | se crea | `{name}`, `%{name}`, `${name}` |
| `resx` | recursos de .NET (`.resx`) | compuesto `{0}`, `{1:C}`, con nombre `{name}` | ninguno | se crea | `{{name}}`, `{name}`, `%{name}`, `${name}` |

Haz coincidir el formato con tu librería de i18n: decide qué placeholders compara la [comprobación de integridad](https://verbatra.kreitz-webdev.de/es/docs/translation-safety.md).

## Los cuatro formatos JSON [#los-cuatro-formatos-json]

Los cuatro leen objetos JSON anidados con hojas de cadena.

### i18next-json [#i18next-json]

Interpolación `{{double-brace}}`; el plural sale del sufijo CLDR de la clave (`_zero`, `_one`, `_two`, `_few`, `_many`, `_other`). Las referencias de anidamiento `$t(common.foo)` y `$t(common.foo, { options })` se protegen como placeholders; solo cuenta el prefijo predeterminado `$t(`, sin paréntesis anidados en las opciones.

### vue-i18n-json [#vue-i18n-json]

Tokens de llave simple `{name}` y `{0}`, con nombres en cualquier escritura (`{número}`). El plural sale de un pipe en el valor.

### next-intl-json [#next-intl-json]

Los valores son ICU MessageFormat: los nombres de argumentos y de etiquetas de texto enriquecido son los placeholders, y el cuerpo ICU se mantiene textual salvo que cada `plural` y `selectordinal` recibe las [ramas CLDR del idioma de destino](https://verbatra.kreitz-webdev.de/es/docs/translation-safety.md#ramas-de-plural-por-idioma-de-destino). Un valor que no parsea como ICU se notifica como inválido y se omite.

### ngx-translate-json [#ngx-translate-json]

Interpolación `{{double-brace}}`, sin referencias de anidamiento, plurales ni ICU. Un archivo puede ser plano (claves con puntos) o anidado, y cada uno conserva su estilo al escribir; un destino nuevo se escribe anidado. Un archivo que mezcla ambos falla con `MIXED_STRUCTURE`.

### La salvaguarda contra fabricaciones de llave simple [#la-salvaguarda-contra-fabricaciones-de-llave-simple]

<AvailableFrom version="0.9.0" />

Bajo `i18next-json`, `ngx-translate-json` y `yaml`, un token `{name}` es texto literal que una traducción puede reformular, pero uno que nunca apareció en el origen se rechaza como fabricación. Solo se protege esa dirección: con `interpolation.prefix` y `suffix` propios de llave simple, un placeholder perdido no se detecta.

## Diseños con namespaces [#diseños-con-namespaces]

[`files.pattern`](https://verbatra.kreitz-webdev.de/es/docs/config-file.md#locales-y-archivos) es una ruta literal, nunca un glob: `public/locales/{locale}/*.json` busca un archivo llamado `*.json` y falla con `SOURCE_UNREADABLE`. Un solo namespace funciona tal cual se configura:

```ts title="verbatra.config.ts"
files: {
  pattern: "public/locales/{locale}/common.json",
},
```

### Una configuración por namespace [#una-configuración-por-namespace]

Para varios namespaces, escribe una configuración por cada uno y ejecútalas una tras otra:

```bash
verbatra translate --config verbatra.common.config.ts
verbatra translate --config verbatra.auth.config.ts
```

<Callout type="warn" title="Solo el namespace que se ejecutó en último lugar conserva la detección de cambios">
Todas las configuraciones de un mismo directorio comparten `verbatra.lock.json`, y cada ejecución reemplaza las líneas base del locale con sus propias claves, así que una cadena editada de un namespace anterior se lee como sincronizada y no se vuelve a traducir. Para volver a traducirla, borra la clave (o el archivo de destino) para que cuente como faltante.
</Callout>

## XLIFF [#xliff]

XLIFF 1.2 (`file/body/trans-unit`) y 2.0 (`file/unit/segment`), una lista plana de unidades.

- **Claves.** El `id` de la unidad, luego `resname`, luego una posición que se desplaza cuando las unidades se mueven, así que dale a cada unidad un `id`. Una unidad 2.0 con varios segmentos recibe claves `<id>#<n>`. Una clave duplicada falla con `INVALID_STRUCTURE`.
- **Origen o traducción.** Un documento es el origen cuando el locale coincide con su `source-language` (`srcLang` en 2.0; un idioma sin región coincide con sus formas regionales) y no declara ese locale como su destino; lo demás es una traducción, salvo un documento que no declara ninguno de los dos idiomas: es el origen si no contiene ningún `<target>`. Un destino que comparte idioma con el origen (`en` a `en-GB`) debe declarar `target-language` (`trgLang`).
- **Valores que faltan.** Un `<target>` ausente, vacío o, en 1.2, marcado con `state="new"` o `"needs-translation"` falta, y también un target de 2.0 igual a su origen en un segmento marcado con `state="initial"`. El texto de `<source>` nunca se copia en su lugar.
- **Escrituras en su sitio.** Cada valor va a su `<target>`, que se crea tras `<source>` si no existe, y un estado `new` o `needs-translation` de 1.2 o `initial` de 2.0 (con su `subState`) pasa a `translated`. `<source>`, los atributos y `<note>` quedan intactos.
- **Las unidades que faltan se añaden** como copia de la unidad de origen (sin sus targets ni el `<alt-trans>` de 1.2) en el `<file>` correspondiente. Un valor sin unidad a la que ir hace fallar la escritura con `INVALID_STRUCTURE` nombrando la clave; un origen ilegible la hace fallar sin nombrar ninguna. `verbatra extract` añade unidades de origen con solo un `<source>`; en 2.0 la clave debe ser un NMTOKEN válido (`u1#0` se rechaza).
- **Marcado en línea.** `g`, `x`, `bx`, `ex`, `bpt`, `ept`, `ph`, `it`, `mrk` de 1.2, `ph`, `pc`, `sc`, `ec`, `mrk`, `sm`, `em`, `cp` de 2.0, y cualquier secuencia `{...}` son todos placeholders protegidos en cualquiera de las dos versiones. Al escribir, solo estos (más un `<sub>` de 1.2 dentro de `bpt`, `ept`, `ph`, `it`) siguen siendo marcado; cualquier otra cosa, como `<b>`, se convierte en texto.
- **Escapado.** El texto se lee como texto (`A &amp; B` es `A & B`) y se escapa una vez al escribir; el texto que coincide exactamente con un elemento permitido, como `&lt;x id="1"/&gt;`, se escribe como ese elemento, y `<!DOCTYPE` y `<!ENTITY` como texto.
- **Las notas** (las `<notes>` de la unidad en 2.0) son [contexto](https://verbatra.kreitz-webdev.de/es/docs/manual-translation.md) de solo lectura.
- **Entrada rechazada.** Un documento con una declaración DTD o de entidad falla con `INVALID_XML`.

## YAML [#yaml]

Un árbol anidado como los formatos JSON, con interpolación `{{double-brace}}` de i18next y la [salvaguarda contra fabricaciones](#la-salvaguarda-contra-fabricaciones-de-llave-simple), detectado por extensión. Los comentarios no se conservan. Las claves escalares no string pasan a ser cadenas (`1:` se lee como `"1"`), un mapa o una secuencia usados como clave fallan con `INVALID_STRUCTURE`, la sintaxis malformada falla con `INVALID_YAML`, y la expansión de alias está acotada.

## ARB [#arb]

El `.arb` de Flutter: un objeto plano de mensajes con metadatos `@key` y globales `@@`.

- **Los mensajes son ICU**, manejados exactamente igual que `next-intl-json`.
- **Los metadatos** nunca se traducen y se fusionan de vuelta en su sitio; un destino existente corrupto hace fallar la escritura. `@key.description` es contexto de solo lectura.
- **Una escritura contiene exactamente sus mensajes.** Un mensaje que queda fuera (eliminado con prune, o rechazado en la revisión) se elimina junto con su bloque `@key`; los globales `@@` se quedan.
- **`@@locale` va primero.** Un destino creado empieza con él, escrito como `gen-l10n` (`pt_BR`); uno existente conserva su valor y se mueve arriba, y nunca se añade uno. Corrige a mano un valor que no coincide con el nombre de archivo, o `flutter gen-l10n` falla.

## Properties [#properties]

Los `.properties` de Java y Spring: líneas planas de clave/valor, con las claves conservadas literalmente.

- **La sintaxis** sigue a `java.util.Properties` (separadores, comentarios, líneas de continuación, escapes). Los archivos se leen como UTF-8 y lo que no es ASCII se escribe como `\uXXXX`.
- **Las escrituras conservan** el orden de claves, los comentarios y las líneas en blanco, reescribiendo cada línea existente en su sitio y añadiendo las claves nuevas en el orden del origen. Una clave duplicada mantiene su primera posición y su último valor.
- **Los finales de línea siguen al archivo**: cualquier CRLF significa todo CRLF, un archivo solo con CR sigue con CR, y si no, LF.
- **Los placeholders son `java.text.MessageFormat`**: `{0}`, `{0,number}`, `{count}` (nombres en cualquier escritura) y los argumentos `plural`, `select`, `selectordinal` y `choice`. El entrecomillado con comilla simple no se interpreta, así que `'{0}'` sigue contando como placeholder.

## Apple .strings [#apple-strings]

<AvailableFrom version="0.10.0" />

Sentencias `"key" = "value";`, con los plurales en el `.stringsdict` hermano (mismo directorio y mismo nombre base), que se leen y escriben juntos.

- **Solo UTF-8.** Un archivo UTF-16 falla con `INVALID_STRUCTURE`; vuelve a guardarlo como UTF-8.
- **Comentarios.** Un comentario `/* ... */` antes de una entrada es contexto de solo lectura; los comentarios `//` se conservan. Los escapes `\"`, `\\`, `\n`, `\t` y `\U` se decodifican; al escribir solo se escapan la comilla, la barra invertida, el salto de línea y el tabulador.
- **Las escrituras conservan** el orden, los comentarios y las líneas en blanco; una clave eliminada se va con su propio comentario. Un destino ausente se crea, directorio `.lproj` y `.stringsdict` incluidos.
- **Los placeholders son printf**: `%@`, `%d`, `%1$@`, `%%`, con banderas, ancho y longitud como decoración; un reordenamiento posicional se acepta. Un `%` suelto (`"50% off"`) no lo es.
- **Plurales.** Cada categoría del `.stringsdict` pasa a ser `<key>_<category>` (`photo_count_one`), y solo hacen el ciclo completo las categorías que tiene un locale. Los errores nombran el archivo y la clave; la clave de formato, el nombre de la variable y el tipo de valor sobreviven a una reescritura.
- **Diseño.** `{locale}.lproj/Localizable.strings` no necesita ningún estilo de locale especial.

## Xcode String Catalogs (.xcstrings) [#xcode-string-catalogs-xcstrings]

<AvailableFrom version="0.10.0" />

Un único catálogo JSON con todos los locales, el sustituto de `.strings` desde Xcode 15.

- **Un archivo para todos los locales.** `files.pattern` sigue necesitando `{locale}`, pero `{locale}` se expande a una cadena vacía, así que todos los locales se resuelven al mismo archivo (`{locale}Localizable.xcstrings` da `Localizable.xcstrings`). Toda escritura se serializa sobre él, así que los locales se ejecutan de uno en uno incluso con `--concurrency`.
- **Claves.** Una clave sin localization de `sourceLanguage` usa el texto de la clave como origen; una localization de destino ausente cuenta como sin traducir.
- **Los plurales** bajo `variations.plural` pasan a ser `<key>_<category>`, solo para las categorías presentes.
- **Los placeholders son printf**, como en `.strings`; `%lld` equivale a `%d`.
- **Las claves con `shouldTranslate: false`** nunca se envían ni se modifican.
- **Las escrituras parchean el documento.** Solo cambian las localizations tocadas, un valor nuevo recibe `state: "translated"` y el resto queda idéntico en lo semántico, aunque el archivo se vuelve a serializar como JSON con 2 espacios. Crea el catálogo primero en Xcode.
- **Los errores** nombran el archivo y, dentro de una entrada, la clave y el locale.

## Android strings.xml [#android-stringsxml]

<AvailableFrom version="0.10.0" />

`res/values/strings.xml` para el origen y `res/values-<qualifier>/strings.xml` para cada destino; pon `files.localeStyle: "android"` ([configuración](https://verbatra.kreitz-webdev.de/es/docs/config-file.md#locales-y-archivos)).

- **Entradas.** `<string name>` es una entrada; `<plurals>` pasa a ser `key[quantity]` por cada `<item>`, solo para las cantidades presentes.
- **Se omiten.** Los recursos con `translatable="false"`, `<string-array>` y un `<string>` con elementos en línea (`<b>`, `<xliff:g>`, CDATA) se conservan tal cual; `formatted="false"` se traduce con normalidad.
- **Escapado.** `\'`, `\"`, `\n`, `\t` y un `\@` o `\?` inicial se decodifican y se vuelven a escapar al escribir.
- **Los placeholders son printf**: `%s`, `%d`, `%1$s`, `%%`; un `%` suelto no lo es.
- **Las escrituras** parchean el archivo, borran las claves eliminadas y crean un archivo ausente con su directorio.
- **Limitaciones.** Una clave traducida que después pasa a ser de paso directo en el origen se elimina del destino. `--prune` no sabe nada de cantidades plurales, así que puede borrar cantidades que solo existen en el destino.
- **Los errores** nombran una raíz distinta de `<resources>`, un nombre o una cantidad no válidos, o una clave duplicada; una DTD o una entidad se rechaza.

## gettext .po/.pot [#gettext-popot]

<AvailableFrom version="0.10.0" />

Entradas `msgid`/`msgstr` con clave por `msgid`.

- **`msgctxt`** se une a su `msgid` en una sola clave con U+E000; un origen que contenga ese carácter se rechaza.
- **Los plurales** tienen clave por índice, `key[n]`, no por categoría CLDR. `Plural-Forms` se conserva literalmente y solo su `nplurals` acota los índices; las entradas plurales sin él se rechazan, como hace `msgfmt --check`.
- **Marcas y comentarios.** Un valor `#, fuzzy` se lee como actual y la marca se conserva. Los comentarios `#.` son contexto de solo lectura; `#:` y las demás marcas se conservan, los bloques `#~` quedan inertes y la cabecera nunca se toca. Un `.pot` con `msgstr` vacío se lee sin problemas, pero en un catálogo de origen cada `msgstr` es el texto que se traduce, así que uno vacío se omite con [`SOURCE_VALUE_EMPTY`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#source_value_empty).
- **Un destino ausente** se crea con una cabecera mínima y, para plurales, un `Plural-Forms` dimensionado al índice más alto (siembra desde una plantilla real para la gramática exacta).
- **Los placeholders son printf**, `%(name)s` incluido.
- **Los errores** nombran el `msgid` y la línea. `--prune` no sabe nada de índices plurales, así que puede borrar formas que solo existen en el destino.

## INI [#ini]

<AvailableFrom version="0.11.0" />

Un nivel de cabeceras `[section]` sobre líneas `key=value`.

- **Claves.** Una clave en `[home]` es `home.title`; las claves por encima de la primera sección conservan su nombre a secas, y los puntos literales se escapan.
- **Las escrituras** solo cambian valores, conservando grafía, espacios, comentarios y líneas en blanco. Una clave nueva va al final de su sección, una sección nueva al final del archivo.
- **Valores.** Los valores entre comillas dobles se desentrecomillan decodificando `\\`, `\"`, `\n`, `\r`, `\t`, y un valor que lo necesita se escribe entrecomillado. Un `;` en medio de la línea forma parte del valor.
- **Los placeholders** son `{name}` y `{0}`, con nombres en cualquier escritura; sin plurales.
- **Errores.** Una cabecera mal formada lanza un `AdapterError`; una línea sin `=` se conserva literalmente.

## .NET .resx [#net-resx]

<AvailableFrom version="0.11.0" />

Elementos `<data name><value>` bajo `<root>`, reclamados solo por `.resx`.

- **Las escrituras solo parchean valores**: todo lo demás vuelve sin cambios, con el salto de línea del archivo; una marca de orden de bytes se descarta, y un elemento `<data>` nuevo, o uno cuyo valor tiene espacios en los extremos, recibe `xml:space="preserve"`.
- **No se traducen.** Los `<data>` con `type` o `mimetype`, y los nombres que empiezan por `>>` o `$`, se conservan tal cual. Un `<comment>` es contexto de solo lectura.
- **Los placeholders** son elementos de formato compuesto y huecos con nombre tomados enteros (`{0}`, `{1,-10}`, `{2:C}`, `{when:d}`), así que se rechaza uno renombrado; `{{`, `}}` y las llaves sueltas también se protegen. Sin comprobación ICU ni plurales.
- **Un destino ausente** se crea con las cabeceras estándar.
- **Claves rechazadas.** Un valor que el destino tiene como entrada tipada, de diseñador o con marcado, o una clave que no se podría volver a leer, hace fallar la escritura con un `AdapterError` que la nombra.
- **Errores.** Se rechazan el XML no válido, una raíz distinta de `<root>` y una DTD o una entidad.

## Orden de claves del documento [#orden-de-claves-del-documento]

Los formatos JSON, YAML y ARB conservan el orden del documento: las claves con forma de entero como `"404"` no se suben al principio, las claves nuevas se añaden en el orden del origen, y los metadatos ARB conservan su posición. XLIFF actualiza los targets en su sitio, así que su orden nunca cambia.

## Claves con puntos y colisiones [#claves-con-puntos-y-colisiones]

En `i18next-json`, `vue-i18n-json` y `next-intl-json`, una hoja literal con puntos como `"foo.bar"` hace el viaje de ida y vuelta tal como está escrita; `ngx-translate-json` la lee como una ruta anidada. En los demás formatos, una misma ruta escrita de las dos formas (`"foo.bar"` junto a `"foo": { "bar": ... }`) falla con `INVALID_STRUCTURE`; `ngx-translate-json` rechaza claves planas con puntos junto a objetos anidados y claves con puntos dentro de objetos anidados con `MIXED_STRUCTURE`.

## Cómo se leen y escriben los archivos [#cómo-se-leen-y-escriben-los-archivos]

- **Límites.** El tamaño de entrada y la profundidad de anidamiento están acotados (`INPUT_TOO_LARGE`, `MAX_DEPTH_EXCEEDED`).
- **Escrituras atómicas.** Un archivo temporal se renombra a su sitio, sin dejar nunca medio archivo.
- **Formato.** Los formatos XML conservan el fin de línea y el salto de línea final y sangran los elementos nuevos como sus vecinos. Una entrada nueva de gettext va tras una línea en blanco; un `.po` creado nombra su locale en `Language` (`pt_BR`).
- **Hojas que no son cadenas.** En los formatos JSON, YAML y ARB, una hoja numérica, booleana o null se acepta pero nunca se traduce, y una reescritura la descarta; un array falla con `INVALID_STRUCTURE`.
- **Los errores** son estructurados y sin secretos; consulta los [códigos de error de adaptador](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#adapter-error-codes).

## Formatos externos a verbatra [#formatos-externos-a-verbatra]

<AvailableFrom version="0.11.0" />

Otro formato puede venir de tu propio adaptador con un id `custom:` como `custom:toml`, ejecutado a través del SDK como código de confianza: consulta [Escribe un adaptador de formato propio](https://verbatra.kreitz-webdev.de/es/docs/custom-format-adapters.md).

## Marcado en línea en los valores [#marcado-en-línea-en-los-valores]

<AvailableFrom version="0.11.0" />

La [comprobación de paridad del marcado](https://verbatra.kreitz-webdev.de/es/docs/translation-safety.md#paridad-del-marcado-en-línea) se aplica en todos los formatos, etiqueta por etiqueta:

- `xliff` protege sus propios elementos en línea como placeholders; el resto del HTML, como `<b>`, se compara como marcado.
- `next-intl-json` y `arb` tratan un `<b>...</b>` emparejado como placeholder y comparan un `<br/>` autocerrado como marcado; una etiqueta con atributos es ICU no válido y se rechaza.
- `android-xml` compara los valores decodificados, así que `&lt;b&gt;` es `<b>`.
- Cualquier otro formato no tiene concepto de marcado, así que solo esta comprobación lo ve.
