Retour au blog

Comment automatiser votre workflow de traduction Django

2026-07-11 10 min de lecture

Si vous avez travaillé avec le système de traduction de Django pendant un certain temps, vous connaissez la routine. Vous marquez les chaînes avec _(), lancez makemessages, ouvrez un fichier .po, et vous retrouvez face à des centaines de champs msgstr vides. Puis vous recommencez pour chaque langue supportée par votre application.

Le processus fonctionne. Il consomme aussi des heures qui pourraient servir à déveloper des fonctionnalités. Un projet avec 200 chaînes traduisibles réparties sur cinq langues représente 1 000 traductions à gérer. Changez un libellé de bouton et vous devez mettre à jour les cinq fichiers .po avant le prochain déploiement, ou livrer des pages partiellement traduites.

Le workflow manuel a ses limites. Il existe des moyens concrets de l'automatiser sans perdre le contrôle sur la qualité.

Le workflow manuel

Le pipeline de traduction intégré à Django comporte quatre étapes :

  1. Marquer les chaînes avec gettext ou {% trans %} dans votre code et vos templates
  2. Lancer makemessages pour les extraire dans des fichiers .po
  3. Traduire les entrées msgstr dans chaque fichier .po
  4. Lancer compilemessages pour produire les fichiers binaires .mo que Django utilise

Les étapes 1 et 2 sont rapides. L'étape 4 se résume à une commande. L'étape 3 est celle où tout ralentit.

Ce que l'étape 3 donne en pratique

Ouvrez locale/de/LC_MESSAGES/django.po dans votre éditeur. Vous voyez quelque chose comme ceci :

#: myapp/views.py:42
msgid "Your subscription has been renewed."
msgstr ""

#: myapp/templates/dashboard.html:18
msgid "Welcome back, %(username)s"
msgstr ""

#: myapp/forms.py:15
msgid "This field is required."
msgstr ""

Pour chaque entrée, il faut une traduction correcte qui préserve les variables de substitution comme %(username)s ou {count}. Cassez un placeholder et Django lèvera une erreur de formatage à l'éxécution.

Avec cinq langues et une centaine de nouvelles chaînes, comptez un après-midi entier à copier-coller entre des services de traduction, corriger les formats de placeholders, et espérer n'avoir rien oublié. Et au sprint suivant, c'est reparti.

Où l'approche manuelle atteint ses limites

Les petits projets s'en sortent avec la traduction manuelle. Dès qu'un projet dépasse certains seuils, le workflow commence à poser problème.

Nombre croissant de chaînes

Une application Django avec une douzaine de templates, quelques formulaires et des notifications par email peut facilement atteindre 300 chaînes traduisbles. Multipliez par le nombre de langues supportées et le total grimpe vite. À un moment donné, personne ne veut être celui qui ouvre ce fichier .po.

Traductions obsolètes

Quand les développeurs modifient des chaînes existantes, makemessages marque les anciennes traductions comme fuzzy. Ces entrées fuzzy nécesitent une revue manuelle, et elles ont tendance à s'accumuler parce que personne ne les remarque jusqu'à ce qu'un utilisateur signale voir de l'anglais sur une page en allemand. Le backlog de fuzzy grossit silencieusement entre les releases.

Erreurs de placeholders

Le format .po de Django utilise plusieurs styles de placeholders : %(name)s pour le formatage % de Python, {name} pour .format() et les f-strings, et {{ name }} pour les variables de template. Les traducteurs (humains ou machines) les déforment parfois, et les erreurs ne se manifestent qu'à l'exécution. Un %(username)s mal formé dans une traduction allemande peut faire planter une vue qui fonctionne parfaitement en anglais.

Déploiements bloqués

Si les traductions sont une étape manuelle dans votre pipeline de déploiement, elles deviennent un goulot d'étranglement. Soit vous attendez les traductions avant de déployer (lent), soit vous déployez avec des traductions manquantes (mauvaise expérience utilisateur). Aucune des deux options n'est satisfaisante quand vous livrez chaque semaine.

Ce que l'automatisation implique

Automatiser les traductions Django signifie remplacer l'étape 3 (la traduction manuelle) par un processus programatique tout en gardant le reste identique. Votre workflow makemessages et compilemessages ne change pas. Vous arrêtez simplement de traduire les fichiers .po à la main.

Option 1 : écrire votre propre script

Vous pouvez écrire un script qui lit un fichier .po, envoie les chaînes non traduites à une API de traduction, et réécrit les résultats. La version de base est étonnamment courte :

import polib
from openai import OpenAI

client = OpenAI()
po = polib.POFile("locale/de/LC_MESSAGES/django.po")

for entry in po.untranslated_entries():
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": "Translate to German. Preserve all "
             "placeholder variables like %(name)s exactly as they appear."},
            {"role": "user", "content": entry.msgid},
        ],
    )
    entry.msgstr = response.choices[0].message.content.strip()

po.save()

Cela vous amène à 80% du chemin. Les 20% restants sont là où ça se complique :

  • Les formes plurielles (msgid_plural et les entrées multiples msgstr[n]) nécessitent un traitement spécial, car chaque langue a ses propres règles de pluriel
  • Les chaînes de contexte (msgctxt) doivent être transmises à l'API pour que la traduction corresponde à l'usage
  • Il faut valider que les placeholders ont survécu à la traduction intacts
  • Les limites de débit et les erreurs d'API nécessitent une logique de retry
  • Le script doit gérer correctement l'encodage des fichiers .po (UTF-8 avec des champs d'en-tête spécifiques)
  • Les entrées fuzzy nécessitent un traitement différent de celui des entrées non traduites

Construire tout cela vous-même est faisable, mais cela demande quelques jours de travail et une maintenace continue. Chaque fois qu'une API de traduction change son comportement ou ses tarifs, vous devez mettre à jour votre script.

Option 2 : utiliser une plateforme de gestion des traductions

Des services comme Crowdin, Transifex et Lokalise s'intègrent au contrôle de version et fournissent une interface web pour les traducteurs. Ils gèrent nativement les fichiers .po, supportent les formes plurielles, et permettent de configurer des workflows automatisés avec la traduction automatique comme première passe.

Le compromis se situe entre le coût et la complexité. Ces plateformes sont conçues pour des équipes avec des traducteurs dédiés, et la tarification reflète cela. Pour un développeur solo ou une petite équipe qui a simplement besoin de traduire des fichiers .po, elles ajoutent plus d'infrastucture que nécessaire.

Option 3 : outils spécialisés

Les outils conçus spécifiquement pour le workflow des fichiers .po de Django peuvent automatiser la traduction tout en respectant les particularités du format. Ils gèrent les formes plurielles, préservent les placeholders, traitent les entrées fuzzy, et s'intègrent à votre pipeline makemessages/compilemessages existant.

L'avantage est qu'ils sont conçus pour exactement ce cas d'usage, ce qui réduit le code de liaison à écrire et maintenir. L'inconvénient est que vous ajoutez une dépendace à votre workflow de traduction.

Configurer les traductions automatisées en CI/CD

Quel que soit l'outil choisi, l'intégration avec votre pipeline de déploiement suit le même schéma :

# .github/workflows/translate.yml
name: Update translations

on:
  push:
    branches: [main]

jobs:
  translate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Install dependencies
        run: pip install django

      - name: Extract new strings
        run: python manage.py makemessages -a --no-obsolete

      - name: Translate .po files
        run: python scripts/translate.py  # your translation script

      - name: Compile messages
        run: python manage.py compilemessages

      - name: Commit and push
        run: |
          git config user.name "github-actions"
          git config user.email "actions@github.com"
          git add locale/
          git diff --staged --quiet || git commit -m "chore: update translations"
          git push

Le workflow extrait les nouvelles chaînes, les traduit, compile les résultats et commit les fichiers mis à jour. La vérification git diff --staged --quiet évite de créer des commits vides quand rien n'a changé.

Vous pouvez aussi exécuter cela comme un hook pre-commit ou une tâche planifiée. L'idée centrale reste la même : la traduction se fait automatiquement dans le cadre de votre processus de développement normal, pas comme une étape manuelle séparée.

Valider les traductions

Les traductions automatisées ont besoin de validation. Même les meilleurs modèles d'IA cassent parfois les formats de placeholders ou produisent des traductions inadaptées au contexte de l'interface.

Validation des placeholders

Écrivez une vérification qui compare les placeholders entre msgid et msgstr :

import re
import polib

PLACEHOLDER_RE = re.compile(r"%\(\w+\)[sd]|\{\w+\}|\{\{.*?\}\}")

def validate_po(path):
    po = polib.POFile(path)
    errors = []
    for entry in po.translated_entries():
        src = set(PLACEHOLDER_RE.findall(entry.msgid))
        dst = set(PLACEHOLDER_RE.findall(entry.msgstr))
        if src != dst:
            errors.append(f"Mismatch: {entry.msgid!r}")
    return errors

Lancez cela en CI et faites échouer le build si des placeholders sont cassés. Cela prend quelques secondes et attrape la catégorie d'erreurs de traduction la plus dangereuse avant qu'elles n'atteignent la production.

Vérification de la longueur

Certaines traductions sont significativement plus longues que la chaîne source. Le texte allemand est typiquement 20 à 30% plus long que l'anglais, et cela peut casser des éléments d'interface à largeur fixe. Une simple vérification du ratio de longueur peut signaler les traductions susceptibles de causer des problèmes de mise en page.

Audit des entrées fuzzy

Après la traduction automatisée, vérifiez qu'aucune entrée n'est restée fuzzy. Les entrées fuzzy signifient que Django n'utilisera pas la traduction, se rabattant sur l'anglais à la place. Votre pipeline CI devrait traiter les entrées fuzzy comme une condition d'échec, de la même manière qu'un test qui échoue.

Garder les traductions synchronisées

La partie la plus difficile de la maintenance des traductions n'est pas la traduction initiale. C'est de garder les traductions à jour au fur et à mesure que votre codebase évolue.

Modification de chaînes

Quand vous modifiez une chaîne traduisible existante, makemessages fait quelque chose de subtil : il cherche une chaîne similaire dans le fichier .po et crée une correspondance fuzzy. L'ancienne traduction est conservée mais marquée comme nécessitant une revue. C'est utile quand un traducteur humain révise les changements, mais cela signifie que les workflows automatisés doivent gérer les entrées fuzzy de manière spécifique.

Une approche pratique : supprimez le flag fuzzy et retraduisez l'entrée à partir de zéro. Le fuzzy matching fait gagner du temps aux traducteurs humains, mais un outil automatisé peut simplement traduire la nouvelle chaîne directement. Le résultat est généralement meilleur que d'essayer d'adapter une ancienne traduction à une nouvelle chaîne source.

Nouvelles chaînes

Les nouvelles chaînes apparaissent comme des entrées non traduites après l'exécution de makemessages. Votre workflow automatisé devrait les traduire et commiter les résultats. Si vous exécutez makemessages en CI, les nouvelles chaînes sont traduites automatiquement lors du merge.

Chaînes supprimées

Lancer makemessages --no-obsolete supprime les entrées pour les chaînes qui n'existent plus dans votre code. Cela garde les fichiers .po propres et évite d'accumuler des traductions mortes au fil du temps.

Ce qu'il faut vérifier avant d'automatiser

Avant de mettre en place la traduction automatisée, vérifiez quelques points :

  • Toutes les chaînes traduisibles sont correctement marquées avec _() ou {% trans %}. Lancez makemessages et vérifiez s'il manque des chaînes.
  • Votre paramètre LOCALE_PATHS est correct et la structure de votre répertoire locale/ est en place.
  • compilemessages s'exécute sans erreurs sur vos fichiers .po existants.
  • Vous avez un moyen de relire les traductions automatisées avant leur mise en production, même si c'est juste un environement de staging où quelqu'un peut parcourir l'application dans chaque langue.

L'automatisation des traductions fonctionne mieux quand le reste de votre configuration i18n est solide. Automatiser l'étape 3 ne servira à rien si les étapes 1 et 2 sont cassées.

Conseils pratiques

Quelques leçons tirées du travail avec les traductions Django automatisées :

  • Lancez makemessages avec --no-obsolete pour garder les fichiers .po propres. Les entrées mortes perturbent aussi bien les relecteurs humains que les outils automatisés.
  • Utilisez msgctxt quand la même chaîne anglaise nécessite des traductions différentes selon le contexte. "Post" peut signifier un article de blog ou l'action de publier, et sans contexte le traducteur (humain ou automatisé) doit deviner.
  • Testez avec des langues structurellement différentes de l'anglais. Les mots plus longs de l'allemand et le jeu de caractères du japonais révèlent des bugs de mise en page que les langues européennes ne détectent pas.
  • Maintenez un fichier TRANSLATING.md avec la terminologie spécifique au projet et les consignes de ton. Si votre projet appelle un "workspace" un "projet" dans l'interface, documentez-le. Certains outils de traduction automatisée peuvent lire ce fichier et appliquer vos préférences de manière cohérente.
  • Commitez les fichiers .po et .mo ensemble. Si vous compilez les messages en CI, assurez-vous que les fichiers .mo sont inclus dans votre artefact de déploiement. Django lit les fichiers .mo, pas les fichiers .po.

Comparaison des approches

Pour un développeur solo ou une petite équipe avec un projet Django supportant 3 à 5 langues :

Un script personnalisé fonctionne si vous aimez le maintenir et que votre nombre de chaînes est inférieur à 200. Au-delà, les cas limites autour des formes plurielles, des placeholders et des entrées fuzzy commencent à grignoter le temps que vous avez économisé.

Les plateformes de gestion de traductions sont pertinantes si vous avez une équipe de traducteurs travaillant sur plusieurs projets. Pour une seule application Django, elles sont généralement surdimensionnées.

Les outils spécialisés occupent le terrain intermédiaire : ils gèrent les particularités des fichiers .po sans vous obliger à construire et maintenir l'intégration vous-même. Le coût mensuel est généralement inférieur au temps d'ingénierie consacré à une solution sur mesure.

Choisissez l'approche qui correspond à la taille de votre équipe et à votre volonté de maintenir l'infrastructure de traduction. L'essentiel est que les traductions cessent d'être un goulot d'étranglement manuel et s'intègrent à votre cycle de développement normal.

Arrêtez d'éditer les fichiers .po manuellement

TranslateBot automatise les traductions Django avec l'IA. Une commande, toutes vos langues, des centimes par traduction.