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 :
- Marquer les chaînes avec
gettextou{% trans %}dans votre code et vos templates - Lancer
makemessagespour les extraire dans des fichiers.po - Traduire les entrées
msgstrdans chaque fichier.po - Lancer
compilemessagespour produire les fichiers binaires.moque 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_pluralet les entrées multiplesmsgstr[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 %}. Lancezmakemessageset vérifiez s'il manque des chaînes. - Votre paramètre
LOCALE_PATHSest correct et la structure de votre répertoirelocale/est en place. compilemessagess'exécute sans erreurs sur vos fichiers.poexistants.- 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
makemessagesavec--no-obsoletepour garder les fichiers.popropres. Les entrées mortes perturbent aussi bien les relecteurs humains que les outils automatisés. - Utilisez
msgctxtquand 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.mdavec 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
.poet.moensemble. Si vous compilez les messages en CI, assurez-vous que les fichiers.mosont 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.