85점 # Django Mehrsprachigkeit: „banco / carta / cura“ sauber übersetzen mit Contextual Markers Wenn man i18n ernsthaft implementiert, stolpert man früher oder später über Mehrdeutigkeiten: * Ein Button zeigt **„banco“**, aber die Übersetzung lautet **„bank“**, obwohl eigentlich **„Sitzbank“** gemeint ist. * In einer Restaurant-Ansicht steht **„carta“**, wird aber als **„Brief“** übersetzt, obwohl hier **„Speisekarte“** gemeint ist. * In einem religiösen Kontext erscheint **„cura“**, wird jedoch als **„Heilung“** übersetzt statt als **„Priester“**. Die Ursache ist simpel. > Computer verstehen keinen Kontext. > Identische Zeichenketten werden immer gleich behandelt. In diesem Beitrag zeige ich, wie man mit Djangos **Contextual Markers** dieselbe Zeichenkette je nach Bedeutung unterschiedlich übersetzen kann, ohne den Quellcode künstlich zu verbiegen. --- ## Warum passiert das? Grundlegendes Verhalten von gettext {#sec-aff43a618865} Django nutzt intern **GNU gettext**. Es arbeitet nach sehr einfachen Regeln: * Originaltext → `msgid` * Übersetzung → `msgstr` * **Gleiche `msgid` → immer dieselbe `msgstr`** ```po # django.po msgid "banco" msgstr "bank" ``` Sobald eine solche Zuordnung existiert, wird überall, wo „banco“ auftaucht, dieselbe Übersetzung verwendet — auch dann, wenn im UI eigentlich „Sitzbank“ gemeint ist. Damit können diese beiden Verwendungen nicht unterschieden werden: * „banco“ = Sitzbank (Möbel) * „banco“ = Bank (Finanzinstitut) Eine Möglichkeit, das zu umgehen, wäre, den Quellcode zu ändern: ```html {% trans "banco (bench)" %} {% trans "banco (bank)" %} ``` Das funktioniert zwar, führt aber zu unnötig künstlichen Strings und erschwert die Wiederverwendung. Wir wollen: * Den Originaltext **"banco"** beibehalten * Und gleichzeitig angeben, welche Bedeutung gerade verwendet wird Hier kommen **Contextual Markers** ins Spiel. --- ## Lösung in Templates: `{% translate %}` + `context` {#sec-e28151952ba2} In Django-Templates kann man dem `{% translate %}`-Tag (oder `{% trans %}`) ein `context`-Attribut hinzufügen, um die gleiche Zeichenkette kontextabhängig zu unterscheiden. ### 1) Aktueller Code (Kollision) {#sec-40afc992a96b} ```html {% load i18n %} {% translate "banco" %} ``` Im `.po`-File sind beide `msgid "banco"`, daher kann nur eine Übersetzung existieren. ### 2) Verbesserter Code (mit Kontext) {#sec-4fa05f04b9d3} ```html {% load i18n %} {% translate "banco" context "financial institution" %} ``` 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.: * `"seat: bench"` * `"financial institution"` * `"restaurant menu"` * `"letter (mail)"` ### 3) Auch bei `{% blocktranslate %}` möglich {#sec-c2a9f27dea01} ```html {% 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` {#sec-3a9a008d3f99} In Views, Models, Forms usw. verwendet man statt `gettext` die `pgettext`-Familie. * `pgettext(context, message)` * `pgettext_lazy(context, message)` – lazy evaluation * `npgettext(context, singular, plural, number)` – Plural + Kontext ### 1) Grundbeispiel {#sec-27f89a09cea2} ```python from django.utils.translation import pgettext def my_view(request): bench = pgettext("seat: bench", "banco") bank = pgettext("financial institution", "banco") menu = pgettext("restaurant menu", "carta") letter = pgettext("letter (mail)", "carta") priest = pgettext("religion: priest", "cura") cure = pgettext("medical: cure", "cura") ``` ### 2) `pgettext_lazy` in Modellen {#sec-11832a96275e} ```python 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` {#sec-ed84f3cf8d15} ```python 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? {#sec-fb610023c81a} Nach dem Ausführen von `python manage.py makemessages -l `: ```po # django.po # 1) "banco" = Sitzbank msgctxt "seat: bench" msgid "banco" msgstr "bench" # 2) "banco" = Bank (Finanzinstitut) msgctxt "financial institution" msgid "banco" msgstr "bank" # 3) "carta" = Speisekarte msgctxt "restaurant menu" msgid "carta" msgstr "menu" # 4) "carta" = Brief msgctxt "letter (mail)" msgid "carta" msgstr "letter" # 5) "cura" = Priester msgctxt "religion: priest" msgid "cura" msgstr "priest" # 6) "cura" = Heilung msgctxt "medical: cure" msgid "cura" msgstr "cure" ``` `msgid` bleibt gleich, aber `msgctxt` unterscheidet die Einträge. --- ## Tipps für gute Kontext-Strings {#sec-df37b35ab408} * **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**: Kontext beschreibt Bedeutung/Rolle, nicht das Ergebnis. --- ## Wann sollte man Contextual Markers einsetzen? {#sec-58e84d2a2b4c} * 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 {#sec-8cc5759d0812} ![Entwickler mit verwirrenden Mehrdeutigkeiten](/media/editor_temp/6/14112b82-d049-4b32-9275-02429e9305c0.png) * Django bindet standardmäßig eine Übersetzung pro `msgid`. * Mehrdeutige Strings wie „banco“, „carta“, „cura“ führen häufig zu Konflikten. * Statt den Quelltext zu verändern, nutzt man: * `{% translate "…" context "…" %}` in Templates * `pgettext`, `pgettext_lazy`, `npgettext` in Python * Dadurch erhält man in der `.po`-Datei `msgctxt`, 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, sind Contextual Markers ein unverzichtbares Werkzeug für Django-Entwickler. --- **Weitere nützliche Artikel** - [Gettext vs Gettext_lazy in Django: Klarheit über Auswertungszeit](/ko/whitedec/2026/1/5/django-gettext-vs-gettext-lazy/) - [Probleme bei der Verwendung von gettext_lazy als JSON‑Schlüssel](/ko/whitedec/2025/4/26/gettext-lazy-json-key-problem-solution/)