# General Translation Knowledge Base: Comment traduire des fichiers PO et POT ?
URL: https://generaltranslation.com/fr/kb/translate-po-files.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: Remplissez chaque msgstr, y compris chaque forme du pluriel exigée par l’en-tête Plural-Forms, et laissez msgid, msgctxt et les espaces réservés inchangés. Exécutez msgfmt --check pour contrôler les en-têtes, le nombre de formes du pluriel et les espaces réservés des entrées dotées d’une option de format, puis vérifiez séparément qu’aucun msgstr n’est vide. La CLI de General Translation génère un fichier PO traduit pour chaque langue à partir de votre modèle POT.
Category: Traduction
Last updated: 2026-10-28
Authors: General Translation Team
Published: 2026-10-28

Un fichier POT (Portable Object Template) contient les chaînes source extraites de votre code, et chaque fichier PO (Portable Object) associe ces chaînes à leurs traductions dans une langue donnée. Traduire consiste à remplir chaque `msgstr`, à raison d’un par forme du pluriel utilisée dans la langue, tandis que `msgid`, `msgctxt` et les espaces réservés restent exactement tels qu’ils ont été extraits. Vous compilez ensuite chaque fichier PO en un catalogue binaire `.mo`, que votre programme charge. Le format est simple, mais ses erreurs sont silencieuses : gettext affiche le texte source pour tout ce qu’il ne parvient pas à utiliser.

## D&#39;où viennent les fichiers POT et PO

Toute stack basée sur gettext suit les mêmes quatre étapes, sous des noms de commandes différents :

| Étape                                                                | GNU gettext | Babel (Flask, FastAPI) |
| -------------------------------------------------------------------- | ----------- | ---------------------- |
| Extraire les chaînes source dans un modèle                           | `xgettext`  | `pybabel extract`      |
| Créer un fichier PO pour une nouvelle langue                         | `msginit`   | `pybabel init`         |
| Fusionner les modifications du modèle dans les fichiers PO existants | `msgmerge`  | `pybabel update`       |
| Compiler un fichier PO en `.mo`                                      | `msgfmt`    | `pybabel compile`      |

Django regroupe les trois premières étapes dans `makemessages`, qui écrit directement les fichiers PO, et compile avec `compilemessages`. La traduction a lieu entre la fusion et la compilation : le modèle n&#39;est associé à aucune langue, si bien que seuls les fichiers PO contiennent des traductions. (Consultez [General Translation vs. GNU gettext](/kb/general-translation-vs-gettext) pour le cycle gettext complet, notamment le comportement de `msgmerge` lorsqu&#39;une chaîne source est modifiée.)

## Contenu d&#39;une entrée PO

Voici le fichier PO polonais que la CLI de General Translation a généré à partir d&#39;un modèle extrait par `xgettext` d&#39;un court script Python (en-tête abrégé) :

```text title="po/pl.po"
msgid ""
msgstr ""
"Language: pl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;\n"

#: inbox.py:8
#. TRANSLATORS: Heading of the inbox page.
msgid "Inbox"
msgstr "Skrzynka odbiorcza"

#: inbox.py:9
#, python-format
msgid "You have %(num)d unread message."
msgid_plural "You have %(num)d unread messages."
msgstr[0] "Masz %(num)d nieprzeczytaną wiadomość."
msgstr[1] "Masz %(num)d nieprzeczytane wiadomości."
msgstr[2] "Masz %(num)d nieprzeczytanych wiadomości."

#: inbox.py:10
msgctxt "email action"
msgid "Archive"
msgstr "Archiwizuj"
```

Le [manuel GNU gettext](https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html) définit chaque partie :

* **`msgid` et `msgid_plural`** constituent le texte source, et `msgid` sert également de clé de recherche. Ne les modifiez jamais dans un fichier PO : un `msgid` modifié ne correspond plus à rien dans le code, et les utilisateurs voient alors le texte source.
* **`msgstr`** contient la traduction, et c&#39;est la seule ligne que vous rédigez. Une entrée plurielle comporte des lignes allant de `msgstr[0]` jusqu&#39;au nombre de formes de la langue moins un.
* **`msgctxt`** distingue des chaînes sources identiques qui ont des sens différents, par exemple « Archiver » (l&#39;action sur un e-mail) et un dossier d&#39;archives. Il provient de `pgettext` ; chaque contexte reçoit sa propre traduction, et il doit rester tel qu&#39;il a été extrait.
* **Les commentaires extraits `#.`** sont des notes laissées par les développeurs (`xgettext --add-comments` les récupère dans le code), **les références `#:`** renvoient aux lignes sources, et **les commentaires du traducteur `# `** sont réservés à la personne qui traduit.
* **Les options `#,`** sont de deux types. `fuzzy` est une option de workflow qui indique que la traduction est peut-être erronée et doit être vérifiée par une personne. Les options de format telles que `c-format` et `python-format` sont définies par `xgettext` et indiquent à `msgfmt` de comparer les espaces réservés entre la source et la traduction.
* **L&#39;en-tête** est l&#39;entrée dont le `msgid` est vide. Ses champs `Language`, charset de `Content-Type` et `Plural-Forms` s&#39;appliquent à l&#39;ensemble du fichier.

## Comment Plural-Forms détermine le nombre de traductions

`Plural-Forms` indique le nombre de formes (`nplurals`) ainsi qu&#39;une expression de type C qui associe un nombre `n` à un indice de forme ([manuel de GNU gettext : formes du pluriel](https://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html)). Avec GNU gettext 1.0, `msginit` générait `nplurals=1; plural=0;` pour le japonais, `nplurals=2; plural=(n != 1);` pour l&#39;allemand, `nplurals=2; plural=(n > 1);` pour le français (où zéro est au singulier), et trois formes pour le polonais et le russe. L&#39;arabe en requiert six. Pour l&#39;arabe, ce même `msginit` laissait l&#39;espace réservé `nplurals=INTEGER; plural=EXPRESSION;` dans l&#39;en-tête : vérifiez donc l&#39;en-tête avant que la traduction ne commence.

Une fois le fichier polonais compilé, la fonction `ngettext` de Python sélectionne un emplacement différent selon le nombre :

```text title="Output"
count  slot        output
1      msgstr[0]   Masz 1 nieprzeczytaną wiadomość.
3      msgstr[1]   Masz 3 nieprzeczytane wiadomości.
5      msgstr[2]   Masz 5 nieprzeczytanych wiadomości.
22     msgstr[1]   Masz 22 nieprzeczytane wiadomości.
```

Une source anglaise ne comporte que deux formes : si on la traduit entrée par entrée en deux chaînes polonaises, `msgstr[2]` n&#39;a donc aucune forme correcte à afficher pour 5 ou 12.

## Ce qui casse lorsque vous traduisez un fichier PO

Aucune de ces erreurs ne bloque la compilation ni ne provoque de plantage à l&#39;exécution, c&#39;est pourquoi elles finissent en production :

* **Les entrées fuzzy n&#39;atteignent jamais les utilisateurs.** `msgfmt` les exclut du fichier `.mo`, sauf si vous passez `--use-fuzzy`, ce que le manuel qualifie de « usually wrong ». Après que l&#39;entrée plurielle a été marquée fuzzy, la compilation polonaise affichait « You have 5 unread messages. » en anglais. Relisez chaque entrée fuzzy, puis retirez l&#39;option.
* **Une forme plurielle vide peut ne rien afficher.** `msgfmt` a compilé une entrée dont le `msgstr[2]` était vide en la comptant comme traduite, et Python a affiché une ligne vide pour un nombre égal à 5. `--check` ne la rejette que si l&#39;entrée porte une option de format, car l&#39;emplacement vide ne contient alors pas l&#39;espace réservé ; sans option, l&#39;entrée passe.
* **Les espaces réservés disparaissent ou sont renommés.** Une traduction sans `%(num)d` perd le nombre. `msgfmt --check` ne le détecte que sur les entrées dotées d&#39;une option de format.
* **Les guillemets et les échappements suivent la syntaxe C.** Un `"` dans un `msgstr` doit s&#39;écrire `\"` ; un guillemet non échappé constitue une erreur de syntaxe, et un saut de ligne dans une chaîne de caractères s&#39;écrit `\n`.
* **Dans les modèles, le jeu de caractères est un espace réservé.** `xgettext` écrit `charset=CHARSET` dans l&#39;en-tête du POT, et `msgfmt --check` refuse tout fichier PO qui contient encore cette valeur. `msginit` et `pybabel` écrivent UTF-8 ; si un outil traduit directement une copie du modèle, exécutez d&#39;abord `msgconv --to-code=UTF-8` sur le modèle.

`msgfmt --check` signale les erreurs qu&#39;il est en mesure de détecter, et `--statistics` comptabilise ce qui a été intégré à la compilation :

```text title="msgfmt --check"
$ msgfmt --check -o /dev/null missing-placeholder.po
missing-placeholder.po:30: a format specification for argument 'num' doesn't exist in 'msgstr[2]'
msgfmt: found 1 fatal error

$ msgfmt --check -o /dev/null two-forms.po
two-forms.po:7: nplurals = 3...
two-forms.po:30: ...but some messages have only 2 plural forms
msgfmt: found 1 fatal error

$ msgfmt --check --statistics -o inbox.mo fuzzy.po
2 translated messages, 1 fuzzy translation.
```

Exécutez-la en CI sur chaque fichier PO, et considérez toute entrée fuzzy comme un travail inachevé.

Aucun outil gettext ne signale une entrée plurielle dont une seule forme est vide : `msgfmt --check` la laisse passer si l’entrée ne porte pas d’option de format, et `msgattrib --untranslated` l’ignore. Un court script utilisant [Babel](https://babel.pocoo.org/) (`pip install babel`) fait échouer la compilation dès qu’une traduction est vide :

```python title="check_po.py"
import sys

from babel.messages.pofile import read_po

failed = False
for path in sys.argv[1:]:
    with open(path, "rb") as f:
        catalog = read_po(f)
    for message in catalog:
        if not message.id or message.fuzzy:
            continue  # l'en-tête, ainsi que les entrées fuzzy, déjà ignorées par msgfmt
        forms = message.string if isinstance(message.string, tuple) else (message.string,)
        if not all(forms):
            print(f"{path}:{message.lineno}: empty translation for {message.id!r}")
            failed = True
sys.exit(1 if failed else 0)
```

```text title="Output"
$ python check_po.py po/pl.po
po/pl.po:17: empty translation for ('You have %(num)d unread message.', 'You have %(num)d unread messages.')
```

## Méthodes pour traduire des fichiers PO

| Approche                                                                                                                 | Qui traduit                                                     | Formes plurielles                                                            | Contexte fourni au traducteur                                                                              | Quand le modèle change                                                                                                                      |
| ------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| Édition manuelle ou dans [Poedit](https://poedit.com/features/)                                                          | Vous ou un traducteur                                           | Poedit signale les formes du pluriel incorrectes et les jetons manquants     | Commentaires et références présents dans le fichier                                                        | Exécutez `msgmerge`, puis relisez les entrées fuzzy                                                                                         |
| [Weblate](https://docs.weblate.org/en/latest/formats/gettext.html) ou un autre système de gestion de la traduction (TMS) | Des traducteurs, avec des suggestions automatiques facultatives | Prises en charge ; fuzzy correspond à « Needs editing »                      | Commentaires, références, ainsi que les captures d’écran ou le glossaire du TMS                            | Son module msgmerge ou la synchronisation du dépôt met à jour les fichiers PO                                                               |
| `msgpre` de GNU gettext                                                                                                  | Un LLM local via ollama, sous forme de brouillon                | Prérédigées pour relecture par un traducteur                                 | Le contenu du fichier PO                                                                                   | Relancez sur les nouvelles entrées ([prétraduction gettext 1.0](https://www.gnu.org/software/gettext/manual/html_node/Pretranslating.html)) |
| Un script s’appuyant sur une API de traduction automatique ou un LLM                                                     | Le modèle                                                       | Votre script doit produire chaque forme exigée par l’en-tête                 | Tout ce que votre script envoie                                                                            | Relancez et validez vous-même le résultat                                                                                                   |
| [La CLI de General Translation](/docs/cli/reference/formats/po-pot-files)                                                | Le service de traduction de General Translation                 | Toutes les formes de la langue cible, avec vérification des espaces réservés | `msgctxt`, commentaires, références, options, ainsi que votre glossaire et vos instructions personnalisées | Réutilise les traductions inchangées et traduit les entrées nouvelles ou modifiées                                                          |

La prétraduction automatique de Poedit est une fonctionnalité Pro ; l’édition, la mémoire de traduction et les vérifications sont disponibles dans la version gratuite. (Consultez [General Translation vs. Weblate](/kb/general-translation-vs-weblate) pour une comparaison avec un aller-retour via un TMS.)

Utilisez Poedit si vous traduisez vous-même une ou deux langues. Optez pour Weblate ou un autre [TMS](/kb/what-is-a-translation-management-system) lorsque des traducteurs professionnels gèrent les catalogues et ont besoin d’attributions de tâches et de files de relecture. N’utilisez un script ou `msgpre` que si vous assumez vous-même la validation. Choisissez General Translation lorsque les fichiers PO doivent être traduits dans toutes les langues à chaque compilation, avec une terminologie partagée par vos autres produits.

## Traduire des fichiers PO avec General Translation

La CLI de General Translation, `gt`, lit un fichier POT (ou un fichier PO dans la langue source) et génère un fichier PO traduit pour chaque paramètre régional. Ajoutez une entrée `pot` dans `gt.config.json`. Cette configuration a été vérifiée avec `gt` 2.26.11, et la résolution des chemins propre à la CLI fait correspondre `po/messages.pot` à `po/de.po`, `po/pl.po` et `po/pt_BR.po` :

```json title="gt.config.json"
{
  "defaultLocale": "en",
  "locales": ["de", "pl", "pt_BR"],
  "customMapping": { "pt_BR": { "code": "pt-BR" } },
  "files": {
    "pot": {
      "include": ["po/messages.pot"],
      "transform": { "match": "^po/messages\\.pot$", "replace": "po/{locale}.po" },
      "transformationFormat": "PO"
    }
  }
}
```

* **`pot`** lit aussi bien les fichiers PO que les fichiers POT (`POT` fonctionne également). La prise en charge des formats PO et POT est disponible depuis `gt` 2.14.22.
* **`"transformationFormat": "PO"`** est obligatoire. Sans cette option, la sortie reste un modèle non traduit.
* **[`transform`](/docs/cli/reference/config#locale-placeholders)** définit le nom de chaque fichier de sortie ; `{locale}` correspond au paramètre régional tel qu&#39;il est écrit dans `locales`, et [`customMapping`](/docs/cli/reference/config#custom-mapping) fait correspondre le `pt_BR` de gettext au code standard `pt-BR`.
* **Les arborescences Babel et Django** se passent de `transform` : faites pointer `include` vers le catalogue anglais en plaçant un espace réservé `[locale]` dans le nom de son dossier, par exemple `[locale]/LC_MESSAGES/*.po` sous `translations/`, et la CLI écrit le catalogue de chaque langue à côté, par exemple `pl/LC_MESSAGES/messages.po`.

Exécutez ensuite la CLI entre l&#39;extraction et la compilation :

```bash
xgettext --language=Python --keyword=pgettext:1c,2 --add-comments=TRANSLATORS -o po/messages.pot app.py
msgconv --to-code=UTF-8 -o po/messages.pot po/messages.pot
npx gt translate
msgfmt --check -o locale/pl/LC_MESSAGES/messages.mo po/pl.po
```

Configurez d’abord vos informations d’authentification avec [`npx gt login`](/docs/cli/reference/commands/login) et un identifiant de projet, ou avec une clé API de projet en CI ([informations d’authentification de la CLI](/docs/cli/guides/configuring#credentials)). Si vos fichiers PO contiennent déjà des traductions relues, importez-les avec [`npx gt upload`](/docs/cli/reference/commands/upload) avant de lancer le premier [`npx gt translate`](/docs/cli/reference/commands/translate).

Pour chaque entrée, General Translation traduit le texte du `msgid` en transmettant comme contexte son `msgctxt`, ses commentaires `#.`, ses références `#:` et ses options. Il renseigne l’en-tête `Language` du fichier généré et, lorsque le fichier contient des entrées au pluriel, écrit la règle `Plural-Forms` de la langue cible et remplit chaque `msgstr[n]` exigé par cette règle : trois pour le polonais, six pour l’arabe. Chaque traduction au pluriel est contrôlée pour détecter les formes vides et les espaces réservés tels que `%(num)d`, `%s` ou `{name}` manquants dans l’une des formes. Les commentaires, références, options et `msgctxt` sont repris du modèle, et les entrées inchangées conservent leurs traductions précédentes lors de l’exécution suivante.

Comme la source est le `msgid`, cette approche est plus efficace lorsque votre code utilise le texte source comme clé, ce qui est le comportement par défaut de gettext. Les relecteurs peuvent [modifier les entrées dans le tableau de bord](/docs/platform/dashboard/guides/reviewing-translations), et un glossaire associé à des instructions personnalisées dans un [groupe de contexte](/docs/platform/dashboard/guides/defining-context-for-translations) garantit qu’un terme comme « Archive » reste cohérent avec votre application web et votre documentation. Si quelqu’un modifie un fichier PO en local, synchronisez-le avec [`npx gt save-local`](/docs/cli/reference/commands/save-local) avant l’exécution suivante. Pour les configurations propres à chaque framework, consultez [General Translation vs. le framework de traduction de Django](/kb/general-translation-vs-django-i18n), [General Translation vs. Babel et Flask-Babel](/kb/general-translation-vs-flask-babel) et [Comment traduire une application Flask ou FastAPI ?](/kb/translate-python-app).

## Questions fréquentes

### Quelle est la différence entre un fichier PO et un fichier POT ?

Un fichier POT sert de modèle : chaque `msgstr` est vide et l&#39;en-tête ne précise aucune langue. Un fichier PO est une copie propre à une langue, qui contient un en-tête `Language`, la règle `Plural-Forms` de cette langue et les traductions.

### Ai-je encore besoin de msgmerge si General Translation génère les fichiers PO ?

Pas pour les langues cibles, lorsque le modèle POT sert de source. Chaque exécution régénère le fichier PO de chaque langue à partir du modèle actuel : les entrées supprimées du code disparaissent et les nouvelles arrivent déjà traduites, sans entrées marquées fuzzy à nettoyer. Avec une arborescence Babel ou Django, le catalogue anglais doit toujours être mis à jour avec `pybabel update` ou `makemessages`.

### Pourquoi mon application s&#39;affiche-t-elle toujours en anglais alors que j&#39;ai traduit le fichier PO ?

Il y a généralement trois causes possibles : l&#39;entrée est toujours marquée `fuzzy`, le fichier `.mo` n&#39;a pas été recompilé ou redéployé, ou le `msgid` du fichier PO ne correspond plus à la chaîne de caractères du code. `msgfmt --statistics` indique le nombre d&#39;entrées traduites et d&#39;entrées marquées `fuzzy`.

## Points clés

* Ne traduisez que `msgstr` : `msgid`, `msgid_plural` et `msgctxt` sont des clés de recherche et restent tels qu&#39;ils ont été extraits.
* Rédigez autant d&#39;emplacements `msgstr[n]` que l&#39;indique la valeur `nplurals` de l&#39;en-tête : un pour le japonais, deux pour l&#39;allemand, trois pour le polonais, six pour l&#39;arabe.
* `msgfmt` ignore les entrées fuzzy et compile une forme plurielle vide, qui n&#39;affichera rien. `--check` ne détecte ce problème que sur les entrées dotées d&#39;une option de format : exécutez donc `msgfmt --check --statistics`, ainsi qu&#39;une vérification distincte des traductions vides, sur chaque fichier PO.
* La CLI de General Translation traduit un modèle POT en un fichier PO par paramètre régional grâce à `"pot"`, `"transformationFormat": "PO"` et un `transform`, et remplit chaque forme plurielle requise par la langue cible.

## Related questions

- [General Translation ou GNU gettext : avez-vous besoin des deux ?](https://generaltranslation.com/fr/kb/general-translation-vs-gettext.mdx): Conservez GNU gettext pour l’extraction et l’affichage, et ajoutez General Translation pour générer et maintenir les fichiers PO. General Translation tient compte des notes aux traducteurs et du contexte des messages lors de la génération, applique une terminologie partagée et met à la disposition des relecteurs un éditeur dans le tableau de bord. Vos appels gettext existants et vos catalogues compilés restent en place.
- [General Translation ou le framework de traduction de Django : avez-vous besoin des deux ?](https://generaltranslation.com/fr/kb/general-translation-vs-django-i18n.mdx): Utilisez conjointement le framework de traduction de Django et General Translation. Django gère l’extraction des messages, la sélection de la langue et l’affichage. General Translation remplit ses fichiers PO, formes du pluriel comprises, avec la terminologie de votre produit et une relecture dans le tableau de bord. Vos modèles et vos motifs d’URL restent inchangés, tandis que les traductions évoluent au rythme de votre code.
- [General Translation ou Babel et Flask-Babel : lequel choisir ?](https://generaltranslation.com/fr/kb/general-translation-vs-flask-babel.mdx): Babel formate les dates, les nombres et les devises pour n’importe quel paramètre régional et gère les catalogues gettext, tandis que Flask-Babel l’intègre à Flask, mais aucun des deux ne traduit vos chaînes. La CLI de General Translation remplit les fichiers PO de Babel pour chaque langue, formes du pluriel comprises : vous pouvez ainsi conserver Flask-Babel et ne plus traduire vos catalogues à la main.
- [Comment traduire une application Flask ou FastAPI ?](https://generaltranslation.com/fr/kb/translate-python-app.mdx): Marquez les chaînes visibles par l’utilisateur, déterminez le paramètre régional de chaque requête (généralement à partir de l’en-tête Accept-Language), puis chargez les traductions correspondantes. Les applications Flask s’appuient généralement sur Flask-Babel et des catalogues gettext, tandis que les applications FastAPI intègrent Babel dans un middleware. La CLI de General Translation remplit ces catalogues, et ses SDK expérimentaux gt-flask et gt-fastapi traduisent les chaînes t() intégrées au code, sans catalogue.
- [General Translation ou Weblate : lequel choisir ?](https://generaltranslation.com/fr/kb/general-translation-vs-weblate.mdx): Weblate est une plateforme de localisation auto-hébergeable basée sur Git, conçue pour traduire les fichiers d’un dépôt avec une communauté ou une équipe. General Translation intègre la génération des traductions au développement et aux livraisons, en s’appuyant sur du JSX balisé et un contexte produit partagé. Conservez vos fichiers gettext, ou adoptez la rédaction directement dans le code pour vous affranchir aussi de la maintenance du catalogue source.

## Previous and next questions

- Previous question: [Comment traduire des fichiers de paramètres régionaux YAML ?](https://generaltranslation.com/fr/kb/translate-yaml-files.mdx)
- Next question: [Comment traduire du contenu dans Payload CMS ?](https://generaltranslation.com/fr/kb/translate-payload-content.mdx)

## Sitemap

See the [Knowledge Base sitemap](https://generaltranslation.com/fr/kb/sitemap.md) for every article.
