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
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
msgides el mismo, se reutiliza el mismomsgstr
# 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:
<!-- Evita este enfoque -->
{% 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
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)
{% load i18n %}
<button>{% translate "Polish" %}</button>
<span>{% translate "Polish" %}</span>
En el .po ambas entradas quedan como msgid "Polish", así que solo puede existir una traducción.
2) Código mejorado (con contexto)
{% load i18n %}
<button>
{% translate "Polish" context "verb: to refine UI" %}
</button>
<span>
{% translate "Polish" context "language name" %}
</span>
Puntos clave:
- El texto de
contextno 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 %}
En frases más largas, context también funciona con {% blocktranslate %}:
{% 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
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
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
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
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?
Tras extraer mensajes, el .po incluirá msgctxt para las cadenas con contexto:
python manage.py makemessages -l <locale>
# 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
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
Suele ser buena idea usar context/pgettext cuando:
- La cadena es corta (1–2 palabras).
- Se reutiliza con significados distintos en la UI.
- Quien traduce no ve la interfaz (plataformas de traducción o entrega de
.po).
Resumen

- 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
.poincorporamsgctxt, 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
No hay comentarios.