# 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 {#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 "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:
```html
{% 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` {#sec-e28151952ba2}
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) {#sec-40afc992a96b}
```html
{% load i18n %}
{% translate "Polish" %}
```
Im `.po`‑File sind beide `msgid "Polish"`, daher kann nur eine Übersetzung existieren.
### 2) Verbesserter Code (mit Kontext) {#sec-4fa05f04b9d3}
```html
{% load i18n %}
{% translate "Polish" context "language name" %}
```
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 {#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):
# 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 {#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 ko`:
```po
# 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 {#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**: Der Kontext soll immer im Originaltext bleiben.
---
## 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`.
* Homonyme wie „Polish“, „May“, „Book“ führen häufig zu Konflikten.
* Statt den Quelltext zu ä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, ist der Einsatz von 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/)