88점
# Django meertalige verwerking: "Polish" wordt "Polen" vermijden (Contextual Markers)
Wanneer je een app met meerdere talen ondersteunt, kom je soms op dit probleem.
* Een knop bevat **“Polish”** (verbeteren van de UI), maar de vertaling wordt **“Polen”**
* De maand **“May”** wordt in sommige talen als een persoonsnaam vertaald
* Het menu-item **“Book”** vertaalt alleen naar “boek” en niet naar “reserveren”
De oorzaak is simpel.
> Computers kennen geen *context*.
> Als een string identiek is, behandelen ze deze als één en dezelfde.
In dit artikel leg ik uit hoe je met Django’s **Contextual Markers** dezelfde string in verschillende contexten kunt vertalen, zonder de broncode te veranderen.
---
## Waarom gebeurt dit? De basiswerking van gettext {#sec-aff43a618865}
Django’s vertaal-systeem gebruikt intern **GNU gettext**.
Gettext werkt met een heel eenvoudige regel:
* Originele string → `msgid`
* Vertaalde string → `msgstr`
* **Als `msgid` gelijk is, wordt altijd dezelfde `msgstr` gebruikt**
```po
# django.po
msgid "Polish"
msgstr "Polen"
```
Zodra een `.po`-bestand een mapping heeft, wordt overal waar “Polish” voorkomt dezelfde vertaling gebruikt.
Daarom kunnen de volgende twee UI-elementen niet onderscheiden worden:
* “Polish” = werkwoord (UI verbeteren)
* “Polish” = taal/land
Om dit te voorkomen, kun je de broncode aanpassen, maar dat leidt vaak tot onduidelijke strings of moeilijk herbruikbare code.
We willen:
* De bronstring nog steeds `"Polish"`
* En een extra uitleg over de betekenis in de context
Hier komt de **Contextual Marker** (contextuele informatie) om de hoek kijken.
---
## Oplossen in templates: `{% translate %}` + `context` {#sec-e28151952ba2}
In Django-templates kun je het `{% translate %}` (of de oudere `{% trans %}`) tag gebruiken met een `context`-optie om dezelfde string in verschillende contexten te onderscheiden.
### 1) Originele code (conflict) {#sec-40afc992a96b}
```html
{% load i18n %}
{% translate "Polish" %}
```
In het `.po`-bestand zijn beide `msgid "Polish"`, dus er is maar één vertaling.
### 2) Verbeterde code (met context) {#sec-4fa05f04b9d3}
```html
{% load i18n %}
{% translate "Polish" context "language name" %}
```
Belangrijk:
* De tekst na `context` is **niet zichtbaar voor de gebruiker**.
* Het is alleen meta-informatie voor het vertaalsysteem en vertalers.
* Houd het kort en duidelijk.
* `"verb: to refine UI"`
* `"language name"`
* `"menu label"`
* `"button text"`
### 3) Ook in `{% blocktranslate %}` {#sec-c2a9f27dea01}
Voor langere zinnen kun je `{% blocktranslate %}` gebruiken met een `context`.
```html
{% load i18n %}
{% blocktranslate context "greeting message" with username=user.username %}
Hello {{ username }}
{% endblocktranslate %}
```
Op deze manier kun je dezelfde `"Hello %(username)s"` in verschillende contexten gebruiken.
---
## Oplossen in Python-code: `pgettext` {#sec-3a9a008d3f99}
In views, models, forms, enz. gebruik je in plaats van `gettext` de `pgettext`-familie.
Voorbeelden:
* `pgettext(context, message)`
* `pgettext_lazy(context, message)` – voor lazy evaluation (model-velden, module-niveau)
* `npgettext(context, singular, plural, number)` – meervoud + context
### 1) Basisvoorbeeld {#sec-27f89a09cea2}
```python
from django.utils.translation import pgettext
def my_view(request):
# 1. Maand "May"
month = pgettext("month name", "May")
# 2. Persoonsnaam "May"
person = pgettext("person name", "May")
# 3. Hulpwerkwoord "may" (~mogelijk)
verb = pgettext("auxiliary verb", "may")
```
Hier zijn alle `msgid` gelijk, maar dankzij verschillende `context`-waarden krijgen ze verschillende vertalingen.
### 2) `pgettext_lazy` in een model {#sec-11832a96275e}
```python
from django.db import models
from django.utils.translation import pgettext_lazy
class Order(models.Model):
# "Order" = bestelling
type = models.CharField(
verbose_name=pgettext_lazy("order model field", "Order type"),
max_length=20,
)
STATUS_CHOICES = [
# "Open" = status
("open", pgettext_lazy("order status", "Open")),
# "Open" = actie (openen)
("opened", pgettext_lazy("log action", "Open")),
]
status = models.CharField(
max_length=20,
choices=STATUS_CHOICES,
)
```
### 3) Meervoud met `npgettext` {#sec-ed84f3cf8d15}
```python
from django.utils.translation import npgettext
def get_notification(count):
return npgettext(
"user notification", # context
"You have %(count)d message", # singular
"You have %(count)d messages", # plural
count
) % {"count": count}
```
---
## Hoe ziet het eruit in een `.po`-bestand? {#sec-fb610023c81a}
Na het toevoegen van context:
```bash
python manage.py makemessages -l ko
```
Wordt in het `.po`-bestand een `msgctxt`-veld toegevoegd.
```po
# django.po
# 1) "Polish" = UI verbeteren (werkwoord)
msgctxt "verb: to refine UI"
msgid "Polish"
msgstr "opp oetsen"
# 2) "Polish" = taal/land
msgctxt "language name"
msgid "Polish"
msgstr "Pools"
```
`msgid` blijft hetzelfde, maar `msgctxt` maakt het een aparte vertaling.
---
## Tips voor goede context {#sec-df37b35ab408}
Context-strings zijn niet zichtbaar voor de gebruiker, maar geven vertalers een unieke hint.
### 1) Beschrijf de *rol* {#sec-9dfe29490c86}
* `"button label"`
* `"menu item"`
* `"tooltip"`
* `"error message"`
* `"form field label"`
### 2) Voeg een extra concept toe {#sec-135dce4b280b}
* `"File"`
* `"file menu item"`
* `"uploaded file object"`
* `"Order"`
* `"e-commerce order"`
* `"sorting order"`
### 3) Plaats geen vertaalde tekst in de context {#sec-9197b1a38f27}
Context moet altijd in de originele taal (meestal Engels) en beschrijvend zijn.
---
## Wanneer gebruik je Contextual Markers? {#sec-58e84d2a2b4c}
Overweeg altijd een `context`/`pgettext` als:
1. **Korte strings (1–2 woorden)** – knoppen, tabs, menu-items
2. **Herbruikbare strings met verschillende betekenissen** – `"Open"`, `"Close"`, `"Book"`
3. **Vertalers werken zonder de UI te zien** – externe vertaal-platforms, `.po`-bestanden alleen
---
## Samenvatting {#sec-8cc5759d0812}

* Django koppelt standaard één `msgid` aan één vertaling.
* Homoniemen zoals `"Polish"`, `"May"`, `"Book"` veroorzaken vaak fouten.
* Gebruik in templates `{% translate "…" context "…" %}` en in Python `pgettext`/`pgettext_lazy`/`npgettext`.
* Hierdoor krijgt het `.po`-bestand een `msgctxt`-veld, waardoor dezelfde string verschillende vertalingen kan hebben.
Resultaat: leesbare code, betere vertaal-kwaliteit en onderhoudbaarheid.
---
**Aanbevolen artikelen**
* [Django: gettext vs gettext_lazy verduidelijkt](/whitedec/2026/1/5/django-gettext-vs-gettext-lazy/)
* [Problemen met gettext_lazy in JSON-sleutels](/whitedec/2025/4/26/gettext-lazy-json-key-problem-solution/)