# Internacionalización en Django: evitar colisiones de significado con marcadores contextuales
Cuando se implementa bien el soporte multilingüe (i18n), es habitual encontrarse con situaciones como estas:
* Un botón con **“Polish”** (en el sentido de “pulir” o “refinar”) acaba traducido como **“polaco”**.
* En un selector de fechas, **“May”** puede traducirse como un **nombre propio** en algunos idiomas.
* El ítem de menú **“Book”** se traduce solo como **“libro”** y no como **“reservar”**.
La causa es sencilla:
> Las computadoras no entienden el *contexto*.
> Si dos cadenas son idénticas, se tratan como la misma.
En este artículo veremos cómo usar los **marcadores contextuales** de Django para mantener el texto fuente intacto y, aun así, asignar traducciones distintas a una misma cadena.
---
## ¿Por qué ocurre esto? Comportamiento básico de gettext {#sec-aff43a618865}
El sistema de traducción de Django se apoya en **GNU gettext**, que funciona con reglas muy simples:
* Cadena fuente → `msgid`
* Traducción → `msgstr`
* **Si `msgid` es el mismo, se reutiliza el mismo `msgstr`**
```po
# django.po
msgid "Polish"
msgstr "polaco"
```
Una vez creada esa asociación en el `.po`, cualquier aparición de `"Polish"` usará la misma traducción, aunque el sentido sea distinto.
Por eso no se distinguen estos dos usos:
* `"Polish"` = verbo (pulir / refinar)
* `"Polish"` = nombre de idioma o gentilicio
Para evitarlo, a veces se modifica el texto fuente:
```html
{% trans "Polish (verb)" %}
{% trans "Polish (language)" %}
```
Aunque “funcione”, el texto visible se vuelve artificial y se pierde reutilización. Lo que queremos es:
* Mantener la cadena fuente **"Polish"**
* Añadir una pista externa sobre el significado en ese punto
Ahí entran los **marcadores contextuales**.
---
## Solución en plantillas: `{% translate %}` + `context` {#sec-e28151952ba2}
En plantillas de Django, el tag `{% translate %}` (o el antiguo `{% trans %}`) admite la opción `context` para diferenciar cadenas idénticas según su uso.
### 1) Código original (colisión) {#sec-40afc992a96b}
```html
{% load i18n %}
{% translate "Polish" %}
```
En el `.po` ambas entradas quedan como `msgid "Polish"`, así que solo puede existir una traducción.
### 2) Código mejorado (con contexto) {#sec-4fa05f04b9d3}
```html
{% load i18n %}
{% translate "Polish" context "language name" %}
```
Puntos clave:
* El texto de `context` **no se muestra al usuario**.
* Es metainformación para el sistema y para quienes traducen.
* Conviene que sea breve y específico (por ejemplo: `"button text"`, `"menu item"`, `"language name"`).
### 3) También con `{% blocktranslate %}` {#sec-c2a9f27dea01}
En frases más largas, `context` también funciona con `{% blocktranslate %}`:
```html
{% load i18n %}
{% blocktranslate context "greeting message" with username=user.username %}
Hello {{ username }}
{% endblocktranslate %}
```
Así puedes reutilizar estructuras de frase sin perder el significado por falta de contexto.
---
## Solución en código Python: `pgettext` {#sec-3a9a008d3f99}
En código Python (vistas, modelos, formularios), en lugar de `gettext` se usa la familia `pgettext`:
* `pgettext(context, message)`
* `pgettext_lazy(context, message)` – evaluación diferida (por ejemplo, en modelos)
* `npgettext(context, singular, plural, number)` – plural + contexto
### 1) Ejemplo básico {#sec-27f89a09cea2}
```python
from django.utils.translation import pgettext
def my_view(request):
month = pgettext("month name", "May")
person = pgettext("person name", "May")
verb = pgettext("auxiliary verb", "may")
```
Aunque el `msgid` sea el mismo, el `context` permite traducciones distintas.
### 2) `pgettext_lazy` en modelos {#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)
```
Con esto, una misma cadena puede traducirse de forma distinta según sea “estado” o “acción”.
### 3) Plurales con `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}
```
---
## ¿Cómo se ve en el archivo `.po`? {#sec-fb610023c81a}
Tras extraer mensajes, el `.po` incluirá `msgctxt` para las cadenas con contexto:
```bash
python manage.py makemessages -l
```
```po
# django.po
msgctxt "verb: to refine UI"
msgid "Polish"
msgstr "pulir"
msgctxt "language name"
msgid "Polish"
msgstr "polaco"
```
Aunque `msgid` sea idéntico, `msgctxt` separa las entradas y las herramientas de traducción las gestionan por separado.
---
## Consejos para escribir buenos contextos {#sec-df37b35ab408}
El contexto no se ve en pantalla, pero suele ser la pista más útil para traducir bien.
* **Describe el rol**: `"button label"`, `"menu item"`, `"tooltip"`, `"error message"`, `"form field label"`.
* **Aclara el concepto** cuando haga falta: `"File"` → `"file menu item"` / `"uploaded file object"`.
* **No metas la traducción en el contexto**: debe ser una descripción breve en el idioma fuente (normalmente inglés).
---
## Cuándo usar marcadores contextuales {#sec-58e84d2a2b4c}
Suele ser buena idea usar `context`/`pgettext` cuando:
1. La cadena es corta (1–2 palabras).
2. Se reutiliza con significados distintos en la UI.
3. Quien traduce no ve la interfaz (plataformas de traducción o entrega de `.po`).
---
## Resumen {#sec-8cc5759d0812}

* Django asocia por defecto una única traducción a cada `msgid`.
* Palabras ambiguas como `"Polish"`, `"May"` o `"Book"` generan colisiones.
* En vez de cambiar el texto fuente, añade contexto:
* Templates: `{% translate "…" context "…" %}`
* Python: `pgettext`, `pgettext_lazy`, `npgettext`
* El `.po` incorpora `msgctxt`, lo que permite traducciones distintas y mejora la mantenibilidad.
En proyectos multilingües, los marcadores contextuales son una herramienta clave para mantener el código limpio y las traducciones coherentes.
---
**Artículos relacionados**
- [Django: Diferencia entre gettext y gettext_lazy](/whitedec/2026/1/5/django-gettext-vs-gettext-lazy/)
- [Problemas al usar gettext_lazy como clave JSON y cómo solucionarlos](/whitedec/2025/4/26/gettext-lazy-json-key-problem-solution/)