---
title: "Formats"
description: "Tous les formats de fichier i18n pris en charge par verbatra, comment chacun lit et écrit ses fichiers et ce qu'une écriture préserve pour des diffs propres."
type: "reference"
---
# Formats

Tous les formats de fichier i18n pris en charge par verbatra, comment chacun lit et écrit ses fichiers et ce qu'une écriture préserve pour des diffs propres.

Un adaptateur de format lit un fichier de locale vers une forme neutre et le réécrit dans la forme propre au fichier. Règle le champ `format` de ta [configuration](https://verbatra.kreitz-webdev.de/fr/docs/config-file.md) sur l'un de ces identifiants :

| `format` | Fichiers | Placeholders | Pluriels | Fichier cible manquant | [Syntaxe de placeholder native](https://verbatra.kreitz-webdev.de/fr/docs/error-codes.md#foreign_placeholder_changed) <AvailableFrom version="0.12.0" /> |
| --- | --- | --- | --- | --- | --- |
| `i18next-json` | JSON i18next (`.json`) | `{{name}}`, `$t(...)` | suffixe CLDR sur la clé | créé | `{{name}}` |
| `vue-i18n-json` | JSON vue-i18n (`.json`) | `{name}`, `{0}` | pipe dans la valeur | créé | `{name}`, `%{name}`, `${name}` |
| `next-intl-json` | JSON next-intl (`.json`) | arguments et balises ICU | `plural`, `selectordinal` ICU | créé | `{name}`, `%{name}`, `${name}` |
| `ngx-translate-json` | JSON ngx-translate (`.json`) | `{{name}}` | aucun | créé | `{{name}}` |
| `xliff` | XLIFF 1.2 et 2.0 (`.xlf`, `.xliff`) | éléments en ligne, `{...}` | aucun | refusé | `{{name}}`, `{name}`, `%{name}`, `${name}` |
| `yaml` | YAML (`.yml`, `.yaml`) | `{{name}}` | aucun | créé | `{{name}}` |
| `arb` | ARB Flutter (`.arb`) | arguments et balises ICU | `plural`, `selectordinal` ICU | créé | `{name}`, `%{name}`, `${name}` |
| `properties` | properties Java/Spring (`.properties`) | MessageFormat `{0}`, `{count}` | aucun | créé | `{name}`, `%{name}`, `${name}` |
| `apple-strings` | `.strings` d'Apple, avec son `.stringsdict` | printf `%@`, `%d` | catégories `.stringsdict` | créé | printf `%s` |
| `apple-xcstrings` | Xcode String Catalogs (`.xcstrings`) | printf `%@`, `%lld` | `variations.plural` | refusé | printf `%s` |
| `android-xml` | `strings.xml` d'Android | printf `%s`, `%1$s` | quantités `<plurals>` | créé | printf `%s` |
| `gettext-po` | gettext (`.po`, `.pot`) | printf, `%(name)s` | `msgstr[n]` | créé | printf `%s`, `%(name)s` |
| `ini` | INI (`.ini`) | `{name}`, `{0}` | aucun | créé | `{name}`, `%{name}`, `${name}` |
| `resx` | ressources .NET (`.resx`) | composite `{0}`, `{1:C}`, nommé `{name}` | aucun | créé | `{{name}}`, `{name}`, `%{name}`, `${name}` |

Fais correspondre le format à ta bibliothèque i18n : il décide quels placeholders compare le [contrôle d'intégrité](https://verbatra.kreitz-webdev.de/fr/docs/translation-safety.md).

## Les quatre formats JSON [#les-quatre-formats-json]

Les quatre lisent des objets JSON imbriqués dont les feuilles sont des chaînes.

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

Interpolation `{{double-brace}}` ; pluriel d'après le suffixe CLDR sur la clé (`_zero`, `_one`, `_two`, `_few`, `_many`, `_other`). Les références d'imbrication `$t(common.foo)` et `$t(common.foo, { options })` sont protégées comme placeholders ; seul le préfixe par défaut `$t(` compte, sans parenthèses imbriquées dans les options.

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

Jetons à accolade simple `{name}` et `{0}`, noms dans n'importe quelle écriture (`{número}`). Pluriel d'après un pipe dans la valeur.

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

Les valeurs sont de l'ICU MessageFormat : les noms d'arguments et de balises de texte enrichi sont les placeholders, et le corps ICU est conservé tel quel, sauf que chaque `plural` et `selectordinal` reçoit les [branches CLDR de la langue cible](https://verbatra.kreitz-webdev.de/fr/docs/translation-safety.md#branches-plurielles-par-langue-cible). Une valeur qui ne s'analyse pas comme de l'ICU est signalée comme invalide et sautée.

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

Interpolation `{{double-brace}}`, sans références d'imbrication, pluriels ni ICU. Un fichier peut être plat (clés à points) ou imbriqué, et chacun garde son style à l'écriture ; une nouvelle cible est écrite imbriquée. Un fichier qui mélange les deux échoue avec `MIXED_STRUCTURE`.

### La barrière anti-fabrication à accolade simple [#la-barrière-anti-fabrication-à-accolade-simple]

<AvailableFrom version="0.9.0" />

Sous `i18next-json`, `ngx-translate-json` et `yaml`, un jeton `{name}` est du texte littéral qu'une traduction peut reformuler, mais un jeton qui n'a jamais figuré dans la source est rejeté comme une fabrication. Seul ce sens est protégé : avec des `interpolation.prefix` et `suffix` personnalisés à accolade simple, un placeholder perdu n'est pas détecté.

## Dispositions avec espaces de noms [#dispositions-avec-espaces-de-noms]

[`files.pattern`](https://verbatra.kreitz-webdev.de/fr/docs/config-file.md#locales-et-fichiers) est un chemin littéral, jamais un glob : `public/locales/{locale}/*.json` cherche un fichier nommé `*.json` et échoue avec `SOURCE_UNREADABLE`. Un seul espace de noms fonctionne tel qu'il est configuré :

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

### Une configuration par espace de noms [#une-configuration-par-espace-de-noms]

Pour plusieurs espaces de noms, écris une configuration pour chacun et lance-les l'un après l'autre :

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

<Callout type="warn" title="Seul l'espace de noms exécuté en dernier conserve la détection des changements">
Toutes les configurations d'un même répertoire partagent `verbatra.lock.json`, et chaque exécution remplace les références de la locale par ses propres clés : une chaîne modifiée d'un espace de noms antérieur apparaît donc comme synchronisée et n'est pas retraduite. Pour la retraduire, supprime la clé (ou le fichier cible) pour qu'elle compte comme manquante.
</Callout>

## XLIFF [#xliff]

XLIFF 1.2 (`file/body/trans-unit`) et 2.0 (`file/unit/segment`), une liste plate d'unités.

- **Clés.** L'`id` de l'unité, puis `resname`, puis une position qui se décale quand les unités bougent : donne donc un `id` à chaque unité. Une unité 2.0 à plusieurs segments reçoit des clés `<id>#<n>`. Une clé en double échoue avec `INVALID_STRUCTURE`.
- **Source ou traduction.** Un document est la source quand la locale correspond à son `source-language` (`srcLang` en 2.0 ; une langue sans région correspond à ses formes régionales) et qu'il ne déclare pas cette locale comme sa cible ; le reste est une traduction, sauf un document qui ne déclare aucune des deux langues : c'est la source s'il ne contient aucun `<target>`. Une cible qui partage la langue de la source (`en` vers `en-GB`) doit déclarer `target-language` (`trgLang`).
- **Valeurs manquantes.** Un `<target>` absent, vide ou, en 1.2, marqué `state="new"` ou `"needs-translation"` est manquant, tout comme une cible 2.0 égale à sa source dans un segment marqué `state="initial"`. Le texte de `<source>` n'est jamais copié à sa place.
- **Écritures en place.** Chaque valeur va dans son `<target>`, créé après `<source>` s'il est absent, et un état 1.2 `new` ou `needs-translation` ou 2.0 `initial` (avec son `subState`) devient `translated`. `<source>`, les attributs et `<note>` restent intacts.
- **Les unités manquantes sont ajoutées** comme copie de l'unité source (sans ses cibles ni son `<alt-trans>` 1.2) dans le `<file>` correspondant. Une valeur sans unité où aller fait échouer l'écriture avec `INVALID_STRUCTURE` en nommant la clé ; une source illisible la fait échouer sans clé. `verbatra extract` ajoute des unités source avec seulement un `<source>` ; en 2.0, la clé doit être un NMTOKEN valide (`u1#0` est refusé).
- **Balisage en ligne.** Les `g`, `x`, `bx`, `ex`, `bpt`, `ept`, `ph`, `it`, `mrk` de 1.2, les `ph`, `pc`, `sc`, `ec`, `mrk`, `sm`, `em`, `cp` de 2.0 et toute suite `{...}` sont tous des placeholders protégés dans les deux versions. À l'écriture, seuls ceux-ci (plus un `<sub>` 1.2 à l'intérieur de `bpt`, `ept`, `ph`, `it`) restent du balisage ; tout le reste, comme `<b>`, devient du texte.
- **Échappement.** Le texte est lu comme du texte (`A &amp; B` est `A & B`) et échappé une fois à l'écriture ; un texte qui correspond exactement à un élément autorisé, comme `&lt;x id="1"/&gt;`, est écrit comme cet élément, et `<!DOCTYPE` et `<!ENTITY` comme du texte.
- **Les notes** (les `<notes>` de l'unité en 2.0) sont du [contexte](https://verbatra.kreitz-webdev.de/fr/docs/manual-translation.md) en lecture seule.
- **Entrée rejetée.** Un document avec une déclaration DTD ou d'entité échoue avec `INVALID_XML`.

## YAML [#yaml]

Un arbre imbriqué comme les formats JSON, avec l'interpolation `{{double-brace}}` d'i18next et la [barrière anti-fabrication](#la-barrière-anti-fabrication-à-accolade-simple), détecté par extension. Les commentaires ne sont pas conservés. Les clés scalaires qui ne sont pas des chaînes deviennent des chaînes (`1:` se lit comme `"1"`), une map ou une séquence utilisée comme clé échoue avec `INVALID_STRUCTURE`, une syntaxe malformée échoue avec `INVALID_YAML`, et l'expansion des alias est bornée.

## ARB [#arb]

Le `.arb` de Flutter : un objet plat de messages avec des métadonnées `@key` et des globales `@@`.

- **Les messages sont de l'ICU**, gérés exactement comme `next-intl-json`.
- **Les métadonnées** ne sont jamais traduites et sont refusionnées à leur place ; une cible existante corrompue fait échouer l'écriture. `@key.description` est du contexte en lecture seule.
- **Une écriture contient exactement ses messages.** Un message laissé de côté (élagué, ou rejeté en relecture) est supprimé avec son bloc `@key` ; les globales `@@` restent.
- **`@@locale` passe en premier.** Une cible créée commence par lui, écrit à la manière de `gen-l10n` (`pt_BR`) ; un `@@locale` existant garde sa valeur et remonte en tête, et il n'est jamais ajouté. Corrige à la main une valeur qui ne concorde pas avec le nom de fichier, sinon `flutter gen-l10n` échoue.

## Properties [#properties]

Les `.properties` de Java et Spring : des lignes clé/valeur plates, clés conservées telles quelles.

- **La syntaxe** suit `java.util.Properties` (séparateurs, commentaires, lignes de continuation, échappements). Les fichiers sont lus en UTF-8 et le non-ASCII est écrit en `\uXXXX`.
- **Les écritures conservent** l'ordre des clés, les commentaires et les lignes vides, en réécrivant chaque ligne existante en place et en ajoutant les nouvelles clés dans l'ordre de la source. Une clé en double garde sa première position et sa dernière valeur.
- **Les fins de ligne suivent le fichier** : le moindre CRLF signifie tout en CRLF, uniquement CR reste en CR, sinon LF.
- **Les placeholders sont du `java.text.MessageFormat`** : `{0}`, `{0,number}`, `{count}` (noms dans n'importe quelle écriture), et les arguments `plural`, `select`, `selectordinal` et `choice`. Les guillemets simples ne sont pas interprétés, donc `'{0}'` compte toujours comme un placeholder.

## Apple .strings [#apple-strings]

<AvailableFrom version="0.10.0" />

Des instructions `"key" = "value";`, avec les pluriels dans le `.stringsdict` voisin (même répertoire, même nom de base), lus et écrits ensemble.

- **UTF-8 uniquement.** Un fichier UTF-16 échoue avec `INVALID_STRUCTURE` ; réenregistre-le en UTF-8.
- **Commentaires.** Un commentaire `/* ... */` placé avant une entrée est du contexte en lecture seule ; les commentaires `//` sont conservés. Les échappements `\"`, `\\`, `\n`, `\t` et `\U` sont décodés ; à l'écriture, seuls le guillemet, la barre oblique inversée, le saut de ligne et la tabulation sont échappés.
- **Les écritures conservent** l'ordre, les commentaires et les lignes vides ; une clé retirée part avec son propre commentaire. Une cible manquante est créée, répertoire `.lproj` et `.stringsdict` compris.
- **Les placeholders sont de style printf** : `%@`, `%d`, `%1$@`, `%%`, avec les indicateurs, la largeur et la longueur comme décoration ; une réorganisation positionnelle est acceptée. Un `%` isolé (`"50% off"`) n'en est pas un.
- **Pluriels.** Chaque catégorie du `.stringsdict` devient `<key>_<category>` (`photo_count_one`), en faisant l'aller-retour uniquement avec les catégories qu'a une locale. Les erreurs nomment le fichier et la clé ; la clé de format, le nom de variable et le type de valeur survivent à une réécriture.
- **Disposition.** `{locale}.lproj/Localizable.strings` n'a besoin d'aucun style de locale particulier.

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

<AvailableFrom version="0.10.0" />

Un seul catalogue JSON contenant toutes les locales, le remplaçant de `.strings` introduit dans Xcode 15.

- **Un fichier pour toutes les locales.** `files.pattern` a toujours besoin de `{locale}`, mais `{locale}` se développe en chaîne vide, donc chaque locale se résout vers le même fichier (`{locale}Localizable.xcstrings` donne `Localizable.xcstrings`). Chaque écriture y est sérialisée, donc les locales s'exécutent une par une même avec `--concurrency`.
- **Clés.** Une clé sans localization de `sourceLanguage` utilise le texte de la clé comme source ; une localization cible manquante compte comme non traduite.
- **Les pluriels** sous `variations.plural` deviennent `<key>_<category>`, uniquement pour les catégories présentes.
- **Les placeholders sont de style printf**, comme dans `.strings` ; `%lld` équivaut à `%d`.
- **Les clés `shouldTranslate: false`** ne sont jamais envoyées ni modifiées.
- **Les écritures retouchent le document.** Seules les localizations touchées changent, une nouvelle valeur reçoit `state: "translated"`, et le reste reste identique sur le plan sémantique, même si le fichier est re-sérialisé en JSON indenté de 2 espaces. Crée d'abord le catalogue dans Xcode.
- **Les erreurs** nomment le fichier et, à l'intérieur d'une entrée, la clé et la locale.

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

<AvailableFrom version="0.10.0" />

`res/values/strings.xml` pour la source et `res/values-<qualifier>/strings.xml` pour chaque cible ; règle `files.localeStyle: "android"` ([configuration](https://verbatra.kreitz-webdev.de/fr/docs/config-file.md#locales-et-fichiers)).

- **Entrées.** Un `<string name>` est une entrée ; `<plurals>` devient `key[quantity]` par `<item>`, uniquement pour les quantités présentes.
- **Sautés.** Les ressources `translatable="false"`, `<string-array>` et un `<string>` avec des éléments en ligne (`<b>`, `<xliff:g>`, CDATA) sont conservés tels quels ; `formatted="false"` est traduit normalement.
- **Échappement.** `\'`, `\"`, `\n`, `\t` et un `\@` ou `\?` en tête sont décodés, puis ré-échappés à l'écriture.
- **Les placeholders sont de style printf** : `%s`, `%d`, `%1$s`, `%%` ; un `%` isolé n'en est pas un.
- **Les écritures** retouchent le fichier, suppriment les clés retirées et créent un fichier manquant avec son répertoire.
- **Limites.** Une clé traduite qui devient ensuite non traduisible dans la source est retirée de la cible. `--prune` ne tient pas compte des quantités plurielles, donc il peut supprimer des quantités présentes uniquement dans la cible.
- **Les erreurs** signalent une racine autre que `<resources>`, un nom ou une quantité invalide, ou une clé en double ; une DTD ou une entité est rejetée.

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

<AvailableFrom version="0.10.0" />

Des entrées `msgid`/`msgstr` indexées par `msgid`.

- **`msgctxt`** se combine avec son `msgid` en une seule clé avec U+E000 ; une source contenant ce caractère est rejetée.
- **Les pluriels** sont indexés par position, `key[n]`, pas par catégorie CLDR. `Plural-Forms` est conservé mot pour mot et seul son `nplurals` borne les index ; les entrées plurielles sans lui sont rejetées, comme le fait `msgfmt --check`.
- **Marqueurs et commentaires.** Une valeur `#, fuzzy` est lue comme actuelle et le marqueur conservé. Les commentaires `#.` sont du contexte en lecture seule ; `#:` et les autres marqueurs sont conservés, les blocs `#~` restent inertes, et l'en-tête n'est jamais touché. Un `.pot` aux `msgstr` vides se lit sans accroc, mais dans un catalogue source chaque `msgstr` est le texte à traduire, donc un `msgstr` vide est ignoré avec [`SOURCE_VALUE_EMPTY`](https://verbatra.kreitz-webdev.de/fr/docs/error-codes.md#source_value_empty).
- **Une cible manquante** est créée avec un en-tête minimal et, pour les pluriels, un `Plural-Forms` dimensionné à l'index le plus élevé (amorce depuis un vrai modèle pour la grammaire exacte).
- **Les placeholders sont de style printf**, `%(name)s` compris.
- **Les erreurs** nomment le `msgid` et la ligne. `--prune` ne tient pas compte des index pluriels, donc il peut supprimer des formes présentes uniquement dans la cible.

## INI [#ini]

<AvailableFrom version="0.11.0" />

Un niveau d'en-têtes `[section]` au-dessus de lignes `key=value`.

- **Clés.** Une clé dans `[home]` est `home.title` ; les clés placées avant la première section gardent leur nom nu, et les points littéraux sont échappés.
- **Les écritures** ne changent que les valeurs, en gardant orthographe, espacement, commentaires et lignes vides. Une nouvelle clé va à la fin de sa section, une nouvelle section à la fin du fichier.
- **Valeurs.** Les valeurs entre guillemets doubles sont débarrassées de leurs guillemets, avec `\\`, `\"`, `\n`, `\r`, `\t` décodés, et une valeur qui en a besoin est écrite entre guillemets. Un `;` en milieu de ligne fait partie de la valeur.
- **Les placeholders** sont `{name}` et `{0}`, avec des noms dans n'importe quelle écriture ; pas de pluriels.
- **Erreurs.** Un en-tête malformé lève une `AdapterError` ; une ligne sans `=` est conservée mot pour mot.

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

<AvailableFrom version="0.11.0" />

Des éléments `<data name><value>` sous `<root>`, revendiqués uniquement pour `.resx`.

- **Les écritures ne retouchent que les valeurs** : tout le reste revient inchangé, avec le terminateur de ligne du fichier ; une marque d'ordre des octets est retirée, et un nouvel élément `<data>`, ou un dont la valeur a des espaces en bordure, reçoit `xml:space="preserve"`.
- **Non traduit.** Un `<data>` avec `type` ou `mimetype`, et les noms commençant par `>>` ou `$`, sont conservés tels quels. Un `<comment>` est du contexte en lecture seule.
- **Les placeholders** sont les éléments de format composite et les trous nommés pris en entier (`{0}`, `{1,-10}`, `{2:C}`, `{when:d}`), donc un trou renommé est refusé ; `{{`, `}}` et les accolades isolées sont aussi protégés. Pas de contrôle ICU ni de pluriels.
- **Une cible manquante** est créée avec les en-têtes standard.
- **Clés refusées.** Une valeur que la cible contient comme entrée typée, du concepteur ou avec du balisage, ou une clé qui ne pourrait pas être relue, fait échouer l'écriture avec une `AdapterError` qui la nomme.
- **Erreurs.** Un XML invalide, une racine autre que `<root>`, et une DTD ou une entité sont rejetés.

## Ordre des clés du document [#ordre-des-clés-du-document]

Les formats JSON, YAML et ARB conservent l'ordre du document : les clés de type entier comme `"404"` ne sont pas remontées, les nouvelles clés s'ajoutent dans l'ordre de la source, et les métadonnées ARB gardent leur position. XLIFF met à jour les cibles en place, donc son ordre ne change jamais.

## Clés à points et collisions [#clés-à-points-et-collisions]

Dans `i18next-json`, `vue-i18n-json` et `next-intl-json`, une feuille littérale à points comme `"foo.bar"` fait l'aller-retour telle qu'elle est écrite ; `ngx-translate-json` la lit comme un chemin imbriqué. Dans les autres formats, un même chemin écrit des deux façons (`"foo.bar"` à côté de `"foo": { "bar": ... }`) échoue avec `INVALID_STRUCTURE` ; `ngx-translate-json` refuse les clés plates à points à côté d'objets imbriqués et les clés à points dans des objets imbriqués avec `MIXED_STRUCTURE`.

## Comment les fichiers sont lus et écrits [#comment-les-fichiers-sont-lus-et-écrits]

- **Limites.** La taille d'entrée et la profondeur d'imbrication sont plafonnées (`INPUT_TOO_LARGE`, `MAX_DEPTH_EXCEEDED`).
- **Écritures atomiques.** Un fichier temporaire est renommé en place, sans jamais laisser un fichier à moitié écrit.
- **Mise en forme.** Les formats XML conservent la fin de ligne et le saut de ligne final, et indentent les nouveaux éléments comme leurs voisins. Une nouvelle entrée gettext suit une ligne vide ; un `.po` créé nomme sa locale dans `Language` (`pt_BR`).
- **Feuilles qui ne sont pas des chaînes.** Dans les formats JSON, YAML et ARB, une feuille nombre, booléen ou null est acceptée mais jamais traduite, et une réécriture l'abandonne ; un tableau échoue avec `INVALID_STRUCTURE`.
- **Les erreurs** sont structurées et sans secret ; voir les [codes d'erreur des adaptateurs](https://verbatra.kreitz-webdev.de/fr/docs/error-codes.md#adapter-error-codes).

## Formats extérieurs à verbatra [#formats-extérieurs-à-verbatra]

<AvailableFrom version="0.11.0" />

Un autre format peut venir de ton propre adaptateur avec un id `custom:` comme `custom:toml`, exécuté via le SDK comme du code de confiance : voir [Écrire un adaptateur de format personnalisé](https://verbatra.kreitz-webdev.de/fr/docs/custom-format-adapters.md).

## Balisage en ligne dans les valeurs [#balisage-en-ligne-dans-les-valeurs]

<AvailableFrom version="0.11.0" />

La [vérification de parité du balisage](https://verbatra.kreitz-webdev.de/fr/docs/translation-safety.md#parité-du-balisage-en-ligne) s'applique dans tous les formats, balise par balise :

- `xliff` protège ses propres éléments en ligne comme placeholders ; tout autre HTML comme `<b>` est comparé comme du balisage.
- `next-intl-json` et `arb` traitent un `<b>...</b>` apparié comme un placeholder et comparent un `<br/>` auto-fermant comme du balisage ; une balise portant des attributs est de l'ICU invalide et refusée.
- `android-xml` compare les valeurs décodées, donc `&lt;b&gt;` est `<b>`.
- Tout autre format n'a pas de notion de balisage, donc seule cette vérification le voit.
