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}

* 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/)