Django Mehrsprachigkeit: "Polish" wird nicht zu "Polnisch" (Contextual Markers)
Wenn man i18n richtig implementiert, kommt man irgendwann auf diese Probleme.
- Im Button steht “Polish” (UI verbessern), aber die Übersetzung lautet “Polnisch”
- Der Monat‑Picker zeigt “May”, das in manchen Sprachen wie ein Vorname übersetzt wird
- Das Menü‑Label “Book” wird nur als „Buch“ übersetzt, nicht als „reservieren“
Die Ursache ist simpel.
Computer verstehen keinen Kontext. Gleichnamige Zeichenketten werden immer gleich behandelt.
In diesem Beitrag zeige ich, wie man mit Django’s Contextual Markers dieselbe Zeichenkette in unterschiedlichen Bedeutungen übersetzen kann, ohne den Quellcode zu verändern.
Warum passiert das? Grundlegendes Verhalten von gettext
Django nutzt intern GNU gettext. Es arbeitet nach sehr einfachen Regeln:
- Originaltext →
msgid - Übersetzung →
msgstr - Gleiche
msgid→ immer dieselbemsgstr
# django.po
msgid "Polish"
msgstr "Polnisch"
Sobald eine solche Zuordnung existiert, wird überall, wo „Polish“ auftaucht, dieselbe Übersetzung verwendet.
Damit können die beiden UI‑Elemente nicht unterschieden werden:
- „Polish“ = Verb (UI verbessern)
- „Polish“ = Sprach‑/Länderkürzel
Eine Möglichkeit, das zu umgehen, wäre den Quellcode zu ändern:
<!-- zu vermeiden -->
{% trans "Polish (verb)" %}
{% trans "Polish (language)" %}
Das funktioniert zwar, führt aber zu seltsamen Texten für den Nutzer und erschwert die Wiederverwendung.
Wir wollen:
- Den Originaltext "Polish" beibehalten
- Und gleichzeitig angeben, welche Bedeutung gerade verwendet wird
Hier kommen Contextual Markers ins Spiel.
Lösung in Templates: {% translate %} + context
In Django‑Templates kann man dem {% translate %} (oder {% trans %}) Tag ein context‑Attribut hinzufügen, um die gleiche Zeichenkette kontextabhängig zu unterscheiden.
1) Aktueller Code (Kollision)
{% load i18n %}
<button>{% translate "Polish" %}</button>
<span>{% translate "Polish" %}</span>
Im .po‑File sind beide msgid "Polish", daher kann nur eine Übersetzung existieren.
2) Verbesserter Code (mit Kontext)
{% load i18n %}
<button>
{% translate "Polish" context "verb: to refine UI" %}
</button>
<span>
{% translate "Polish" context "language name" %}
</span>
Wichtig:
- Der Kontext‑Text ist für den Nutzer unsichtbar.
- Er dient ausschließlich dem Übersetzungssystem und den Übersetzern.
- Er sollte kurz und prägnant sein, z. B.:
"verb: to refine UI""language name""menu label""button text"
3) Auch bei {% blocktranslate %} möglich
{% load i18n %}
{% blocktranslate context "greeting message" with username=user.username %}
Hello {{ username }}
{% endblocktranslate %}
Damit kann dieselbe Zeichenkette in unterschiedlichen Kontexten mehrfach verwendet werden.
Lösung in Python‑Code: pgettext
In Views, Models, Forms usw. verwendet man statt gettext die pgettext‑Familie.
pgettext(context, message)pgettext_lazy(context, message)– lazy evaluationnpgettext(context, singular, plural, number)– Plural + Kontext
1) Grundbeispiel
from django.utils.translation import pgettext
def my_view(request):
# 1. Monat "May"
month = pgettext("month name", "May")
# 2. Vorname "May"
person = pgettext("person name", "May")
# 3. Modalverb "may" (~könnte)
verb = pgettext("auxiliary verb", "may")
2) pgettext_lazy in Modellen
from django.db import models
from django.utils.translation import pgettext_lazy
class Order(models.Model):
type = models.CharField(
verbose_name=pgettext_lazy("order model field", "Order type"),
max_length=20,
)
STATUS_CHOICES = [
("open", pgettext_lazy("order status", "Open")),
("opened", pgettext_lazy("log action", "Open")),
]
status = models.CharField(
max_length=20,
choices=STATUS_CHOICES,
)
3) Pluralformen mit npgettext
from django.utils.translation import npgettext
def get_notification(count):
return npgettext(
"user notification",
"You have %(count)d message",
"You have %(count)d messages",
count
) % {"count": count}
Wie sieht das in der .po‑Datei aus?
Nach dem Ausführen von python manage.py makemessages -l ko:
# django.po
# 1) "Polish" = Verb (UI verbessern)
msgctxt "verb: to refine UI"
msgid "Polish"
msgstr "anpassen"
# 2) "Polish" = Sprach‑/Länderkürzel
msgctxt "language name"
msgid "Polish"
msgstr "Polnisch"
msgid bleibt gleich, aber msgctxt unterscheidet die Einträge.
Tipps für gute Kontext‑Strings
- Rolle beschreiben:
"button label","menu item","tooltip","error message","form field label" - Konzept ergänzen:
"File"→"file menu item"/"uploaded file object" - Übersetzungen nicht im Kontext‑String: Der Kontext soll immer im Originaltext bleiben.
Wann sollte man Contextual Markers einsetzen?
- Kurze Zeichenketten (1–2 Wörter) wie Button‑Text, Tab‑Name, Menü‑Eintrag
- Wiederverwendete Zeichenketten mit unterschiedlichen Bedeutungen
- Szenarien, in denen Übersetzer keinen Zugriff auf die UI haben
Fazit

- Django bindet standardmäßig eine Übersetzung pro
msgid. - Homonyme wie „Polish“, „May“, „Book“ führen häufig zu Konflikten.
- Statt den Quelltext zu ändern, nutzt man:
{% translate "…" context "…" %}in Templatespgettext,pgettext_lazy,npgettextin Python- Dadurch erhält man in der
.po‑Dateimsgctxt, was unterschiedliche Übersetzungen ermöglicht.
Die Code‑Lesbarkeit bleibt erhalten, die Übersetzungsqualität und Wartbarkeit steigen deutlich.
In einer Welt, in der Mehrsprachigkeit immer wichtiger wird, ist der Einsatz von Contextual Markers ein unverzichtbares Werkzeug für Django‑Entwickler.
Weitere nützliche Artikel