Wer schon länger mit Djangos Übersetzungssytem gearbeitet hat, kennt den Ablauf. Sie markieren Strings mit _(), führen makemessages aus, öffnen eine .po-Datei und starren auf hunderte leere msgstr-Felder. Dann wiederholen Sie das Ganze für jede Sprache, die Ihre App unterstützt.
Der Prozess funktioniert. Er frisst aber auch Stunden, die in die Entwicklung neuer Features fließen könnten. Ein Projekt mit 200 übersetzbaren Strings in fünf Sprachen bedeutet 1.000 Übersetzungen, die verwaltet werden müssen. Ändern Sie ein Button-Label, müssen alle fünf .po-Dateien vor dem nächsten Deploy aktualisiert werden, sonst liefern Sie teilweise unübersetzte Seiten aus.
Der manuelle Workflow stößt irgendwann an seine Grenzen. Es gibt praktische Wege, ihn zu automatisieren, ohne die Kontrolle über die Qualität zu verlieren.
Der manuelle Workflow
Djangos eingebaute Übersetzungs-Pipeline besteht aus vier Schritten:
- Strings mit
gettextoder{% trans %}in Code und Templates markieren makemessagesausführen, um sie in.po-Dateien zu extrahieren- Die
msgstr-Einträge in jeder.po-Datei übersetzen compilemessagesausführen, um die binären.mo-Dateien zu erzeugen, die Django liest
Schritte 1 und 2 gehen schnell. Schritt 4 ist ein einziger Befehl. Schritt 3 ist der Punkt, an dem alles langsam wird.
Wie Schritt 3 in der Praxis aussieht
Öffnen Sie locale/de/LC_MESSAGES/django.po in Ihrem Editor. Sie sehen etwas wie:
#: 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 ""
Für jeden Eintrag brauchen Sie eine korrekte Übersetzung, die alle Platzhalter-Variablen wie %(username)s oder {count} beibehält. Ein kaputter Platzhalter führt zur Laufzeit zu einem Formatierungsfehler in Django.
Bei fünf Sprachen und hundert neuen Strings verbringen Sie einen ganzen Nachmittag damit, zwischen Übersetzungsdiensten hin und her zu kopieren, Platzhalterformate zu korrigieren und zu hoffen, dass Sie nichts übersehen haben. Und im nächsten Sprint geht das Ganze von vorne los.
Wo der manuele Ansatz an seine Grenzen stößt
Kleine Projekte kommen mit manueller Übersetzung zurecht. Sobald ein Projekt bestimmte Schwellenwerte überschreitet, wird der Workflow zum Problem.
Wachsende Anzahl von Strings
Eine Django-App mit einem Dutzend Templates, ein paar Formularen und einigen E-Mail-Benachrichtigungen erreicht schnell 300 übersetzbare Strings. Multipliziert mit der Anzahl unterstützter Sprachen steigt die Gesamtzahl rasant. Ab einem bestimmten Punkt will niemand mehr die Person sein, die diese .po-Datei öffnet.
Veraltete Übersetzungen
Wenn Entwickler bestehende Strings ändern, markiert makemessages die alten Übersetzungen als fuzzy. Diese Fuzzy-Einträge müssen manuell überprüft werden, und sie häufen sich, weil sie niemandem auffallen, bis ein Benutzer meldet, dass auf einer deutschen Seite englischer Text erscheint. Der Fuzzy-Rückstau wächst still zwischen den Releases.
Platzhalter-Fehler
Djangos .po-Format verwendet verscheidene Platzhalter-Stile: %(name)s für Pythons %-Formatierung, {name} für .format() und f-Strings, und {{ name }} für Template-Variablen. Übersetzer (menschliche oder maschinelle) beschädigen diese manchmal, und die Fehler treten erst zur Laufzeit auf. Ein fehlerhafter %(username)s in einer deutschen Übersetzung kann eine View zum Absturz bringen, die auf Englisch einwandfrei funktioniert.
Blockierte Deployments
Wenn Übersetzungen ein manueller Schritt in Ihrer Deployment-Pipeline sind, werden sie zum Flaschenhals. Entweder warten Sie mit dem Deploy auf die Übersetzungen (langsam), oder Sie deployen mit fehlenden Übersetzungen (schlechte Benutzererfahrung). Keine der beiden Optionen ist gut, wenn Sie wöchentlich deployen.
Wie Automatisierung aussieht
Django-Übersetzungen zu automatisieren bedeutet, Schritt 3 (den manuellen Übersetzungsteil) durch einen programmatischen Prozess zu ersetzen, während alles andere gleich bleibt. Ihr makemessages- und compilemessages-Workflow ändert sich nicht. Sie hören einfach auf, .po-Dateien von Hand zu übersetzen.
Option 1: selbst skripten
Sie können ein Skript schreiben, das eine .po-Datei liest, unübersetzte Strings an eine Übersetzungs-API sendet und die Ergebnisse zurückschreibt. Die Basisversion ist überraschend kurz:
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()
Damit kommen Sie 80% ans Ziel. Die restlichen 20% werden kompliziert:
- Pluralformen (
msgid_pluralund mehreremsgstr[n]-Einträge) brauchen Sonderbehandlung, weil verschiedene Sprachen unterschiedliche Pluralregeln haben - Kontext-Strings (
msgctxt) sollten an die API übergeben werden, damit die Übersetzung zum Verwendungszweck passt - Sie müssen validieren, dass Platzhalter die Übersetzung unbeschadet überstanden haben
- Rate-Limits und API-Fehler brauchen Retry-Logik
- Das Skript muss die
.po-Datei-Kodierung korrekt handhaben (UTF-8 mit bestimmten Header-Feldern) - Fuzzy-Einträge brauchen eine andere Behandlung als unübersetzte
All das selbst zu bauen ist machbar, kostet aber ein paar Tage Arbeit und laufende Wartung. Jedes Mal, wenn eine Übersetzungs-API ihr Verhalten oder ihre Preise ändert, müssen Sie Ihr Skript aktualiseren.
Option 2: eine Übersetzungsmanagement-Plattform nutzen
Dienste wie Crowdin, Transifex und Lokalise integrieren sich mit Versionskontrolle und bieten eine Web-Oberfläche für Übersetzer. Sie unterstützen .po-Dateien nativ, beherrschen Pluralformen und können automatisierte Workflows mit maschineller Übersetzung als ersten Durchgang einrichten.
Der Kompromiss ist Kosten und Komplexität. Diese Plattformen sind für Teams mit dedizierten Übersetzern gebaut, und die Preisgestaltung spiegelt das wider. Für einen Solo-Entwickler oder ein kleines Team, das einfach nur .po-Dateien übersetzt braucht, bringen sie mehr Infrastrucktur als nötig mit.
Option 3: spezialisierte Tools
Tools, die speziell für Djangos .po-Datei-Workflow gebaut sind, können die Übersetzung automatisieren und dabei die Eigenheiten des Formats berücksichtigen. Sie beherrschen Pluralformen, bewahren Platzhalter, verwalten Fuzzy-Einträge und integrieren sich in Ihre bestehende makemessages/compilemessages-Pipeline.
Der Vorteil ist, dass sie genau für diesen Anwendungsfall entwickelt wurden, sodass weniger Glue-Code geschrieben und gewartet werden muss. Der Nachteil ist, dass Sie Ihrem Übersetzungs-Workflow eine Abhängikeit hinzufügen.
Automatisierte Übersetzungen in CI/CD einrichten
Unabhängig davon, welches Tool Sie wählen, folgt die Integration in Ihre Deployment-Pipeline demselben Muster:
# .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
Der Workflow extrahiert neue Strings, übersetzt sie, kompiliert die Ergebnisse und committet die aktualisierten Dateien. Der git diff --staged --quiet-Check verhindert leere Commits, wenn sich nichts geändert hat.
Sie können das auch als Pre-Commit-Hook oder als geplanten Job laufen lassen. Die Kernidee bleibt gleich: Übersetzung passiert automtisch als Teil Ihres normalen Entwicklungsprozesses, nicht als separater manueller Schritt.
Übersetzungen validieren
Automatisierte Übersetzungen brauchen Valideirung. Selbst die besten KI-Modelle beschädigen gelegentlich Platzhalterformate oder erzeugen Übersetzungen, die nicht zum UI-Kontext passen.
Platzhalter-Validierung
Schreiben Sie einen Check, der Platzhalter zwischen msgid und msgstr vergleicht:
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
Führen Sie das in CI aus und lassen Sie den Build fehlschlagen, wenn Platzhalter defekt sind. Es dauert Sekunden und fängt die gefährlichste Klasse von Übersetzungsfehlern ab, bevor sie die Produktion erreichen.
Längenprüfungen
Manche Übersetzungen sind deutlich länger als der Quellstring. Deutscher Text ist typischerweise 20-30% länger als englischer, und das kann bei UI-Elementen mit fester Breite zu Problemen führen. Eine einfache Längenverhältnis-Prüfung kann Übersetzungen markieren, die möglicherweise Layout-Probleme verursachen.
Fuzzy-Einträge prüfen
Stellen Sie nach der automatisierten Übersetzung sicher, dass keine Einträge als fuzzy markiert bleiben. Fuzzy-Einträge bedeuten, dass Django die Übersetzung nicht verwenden wird und stattdessen auf Englisch zurückfällt. Ihre CI-Pipeline sollte Fuzzy-Einträge genauso als Fehlerbedingung behandeln wie einen fehlgeschlagnen Test.
Übersetzungen synchron halten
Der schwierigste Teil der Übersetzungswartung ist nicht die initiale Übersetzung. Es ist das Aktualisieren der Übersetzungen, während sich die Codebasis weiterentwickelt.
String-Änderungen
Wenn Sie einen bestehenden übersetzbaren String ändern, macht makemessages etwas Subtiles: Es sucht nach einem ähnlichen String in der .po-Datei und erstellt einen Fuzzy-Match. Die alte Übersetzung bleibt erhalten, wird aber als überprüfungsbedürftig markiert. Das ist nützlich, wenn ein menschlicher Übersetzer Änderungen durchsieht, bedeutet aber, dass automatisierte Workflows Fuzzy-Einträge gezielt behandeln müssen.
Ein praktischer Ansatz: Entfernen Sie das Fuzzy-Flag und übersetzen Sie den Eintrag komplett neu. Fuzzy-Matching spart menschlichen Übersetzern Arbeit, aber ein automatsiertes Tool kann den neuen String einfach direkt übersetzen. Das Ergebnis ist in der Regel besser, als zu versuchen, eine alte Übersetzung an einen neuen Quellstring anzupassen.
Neue Strings
Neue Strings erscheinen als unübersetzte Einträge nach dem Ausführen von makemessages. Ihr automatisierter Workflow sollte diese übersetzen und die Ergebnisse committen. Wenn Sie makemessages in CI ausführen, werden neue Strings beim Mergen automatisch übersetzt.
Entfernte Strings
makemessages --no-obsolete entfernt Einträge für Strings, die in Ihrem Code nicht mehr existieren. Das hält .po-Dateien sauber und verhindert, dass sich tote Übersetzungen über die Zeit ansammeln.
Was Sie vor der Automatisierung prüfen sollten
Bevor Sie automatisierte Übersetzung einrichten, überprüfen Sie ein paar Dinge:
- Alle übersetzbaren Strings sind korrekt mit
_()oder{% trans %}markiert. Führen Siemakemessagesaus und prüfen Sie, ob Sie Strings übersehen haben. - Ihre
LOCALE_PATHS-Einstellung ist korrekt und Ihrelocale/-Verzeichnisstruktur ist vorhanden. compilemessagesläuft fehlerfrei auf Ihren bestehenden.po-Dateien.- Sie haben eine Möglichkeit, automatisierte Übersetzungen vor dem Go-Live zu prüfen, auch wenn es nur eine Staging-Umgebung ist, in der jemand die App in jeder Sprache durchklicken kann.
Übersetzungsautomatisierung funktioniert am besten, wenn der Rest Ihres i18n-Setups solide ist. Schritt 3 zu automatisieren hilft nicht, wenn Schritt 1 und 2 fehlerhaft sind.
Praktische Tipps
Einige Dinge, die ich bei der Arbeit mit automatisierten Django-Übersetzungen gelernt habe:
- Führen Sie
makemessagesmit--no-obsoleteaus, um.po-Dateien sauber zu halten. Tote Einträge verwirren sowohl menschliche Reviewer als auch automatisierte Tools. - Verwenden Sie
msgctxt, wenn derselbe englische String in unterschiedlichen Kontexten verschiedene Übersetzungen braucht. "Post" kann entweder einen Blog-Beitrag oder die Aktion des Postens bedeuten, und ohne Kontext muss der Übersetzer (menschlich oder automatisiert) raten. - Testen Sie mit Sprachen, die sich strukturell vom Englischen unterscheiden. Die längeren Wörter des Deutschen und die Zeichensätze des Japanischen decken Layout-Fehler auf, die europäische Sprachen übersehen.
- Pflegen Sie eine
TRANSLATING.md-Datei mit projektspezifischer Terminologie und Tonalitätsrichtlinien. Wenn Ihr Projekt einen "Workspace" im UI als "Projekt" bezeichnet, dokumentieren Sie das. Einige automatisierte Übersetzungstools können diese Datei lesen und Ihre Präferenzen konsistent anwenden. - Committen Sie
.po- und.mo-Dateien zusammen. Wenn Sie Messages in CI kompilieren, stellen Sie sicher, dass die.mo-Dateien in Ihrem Deploy-Artefakt enthalten sind. Django liest.mo-Dateien, nicht.po-Dateien.
Ansätze im Vergleich
Für einen Solo-Entwickler oder ein kleines Team mit einem Django-Projekt, das 3-5 Sprachen unterstützt:
Ein eigenes Skript funktioniert, wenn Sie es gerne warten und Ihre String-Anzahl unter 200 liegt. Darüber hinaus fangen die Sonderfälle bei Pluralformen, Platzhaltern und Fuzzy-Einträgen an, die eingesparte Zeit wieder aufzufressen.
Übersetzungsmanagement-Plattformen sind sinnvoll, wenn Sie ein Team von Übersetzern haben, das an mehreren Projekten arbeitet. Für eine einzelne Django-App sind sie in der Regel überdimensioniert.
Spezialisierte Tools treffen die goldene Mitte: Sie beherrschen die Eigenheiten von .po-Dateien, ohne dass Sie die Integration selbst bauen und warten müssen. Die monatlichen Kosten sind in der Regel geringer als die Entwicklungszeit für eine eigene Lösung.
Wählen Sie den Ansatz, der zu Ihrer Teamgröße und Ihrer Bereitschaft passt, Übersetzungsinfrastruktur zu pflegen. Wichtig ist, dass Übersetzungen kein manueller Flaschenhals mehr sind, sondern als Teil Ihres normalen Entwicklungszyklus stattfinden.