# Django: Despejando la confusión entre gettext y gettext_lazy (entendiendo el momento de evaluación) Cuando trabajas con i18n en Django, a menudo te encuentras indeciso sobre cuándo usar `gettext()` y cuándo usar `gettext_lazy()`. La mayoría de la confusión surge al intentar memorizar la diferencia entre ambos términos. El núcleo de la cuestión es una sola idea. * **`gettext` traduce ahora (evaluación inmediata, eager)** * **`gettext_lazy` traduce después (evaluación perezosa, lazy)** Con este único concepto de *momento de evaluación*, la mayoría de los casos se resuelven. --- ## ¿Por qué es importante cuándo se decide la traducción? {#sec-cd5e4068943f} En Django, el idioma suele cambiar con cada solicitud (request). ![Flujo comparativo de los momentos de traducción en Django](/media/editor_temp/6/29f9dbe8-44b7-47ed-bef6-4cc0cc62bb79.png) * El middleware observa la solicitud y activa el idioma con `activate("ko")` / `activate("en")`, activando el idioma del *hilo/contexto* actual. * Cuando se renderizan plantillas, formularios o la interfaz de administración, la traducción debe ocurrir en ese idioma. En otras palabras, **el momento en que traduces una cadena determina el resultado**. --- ## `gettext()`: traduce *ahora* y devuelve la cadena {#sec-9ff53b7dc3ad} ```python from django.utils.translation import gettext as _ def view(request): message = _("Welcome") # Se traduce con el idioma activo en este instante return HttpResponse(message) ``` * Si se llama dentro de una vista o función (runtime), normalmente funciona como se espera. * Pero si se llama al momento de importar el módulo, surgen problemas. ### Trampa común: usar `gettext()` en constantes de módulo {#sec-7c331db329fb} ```python # app/constants.py from django.utils.translation import gettext as _ WELCOME = _("Welcome") # ❌ Se fija al idioma del momento de importación del servidor ``` En este caso, `WELCOME` queda *fija* con la traducción del idioma en el que se importó el módulo, incluso si el idioma cambia después (especialmente en entornos donde la importación ocurre una sola vez). --- ## `gettext_lazy()`: devuelve un proxy que se traduce *más tarde* {#sec-a90df9111315} ```python from django.utils.translation import gettext_lazy as _ WELCOME = _("Welcome") # ✅ No es la cadena real, sino un objeto que se traduce cuando sea necesario ``` `gettext_lazy()` devuelve típicamente un *objeto proxy perezoso*. * Cuando formularios, plantillas o la administración convierten el valor a cadena, * Se traduce con el idioma activo en ese momento. > Resumen de una línea: **En lugares donde el idioma se decide al momento de renderizar, el lazy suele ser la opción correcta.** --- ## ¿Dónde usar qué? Reglas prácticas {#sec-4a8a3cbed930} ### 1) “Estoy construyendo la respuesta ahora” → `gettext` {#sec-197c23c83765} * Cuando la lógica de la vista o servicio genera una cadena inmediatamente para la respuesta o el registro. ```python from django.utils.translation import gettext as _ def signup_done(request): return JsonResponse({"message": _("Signup completed.")}) ``` ### 2) “Definición que se evalúa al importar, renderización más tarde” → `gettext_lazy` {#sec-b545464ef5b7} * `verbose_name`, `help_text` de modelos. * `label`, `help_text` de campos de formularios. * `label` de campos de serializadores DRF. * Descripciones en `list_display` de la administración. ```python from django.db import models from django.utils.translation import gettext_lazy as _ class Article(models.Model): title = models.CharField(_("title"), max_length=100) status = models.CharField( _("status"), max_length=20, choices=[ ("draft", _("Draft")), ("published", _("Published")), ], help_text=_("Visibility of the article."), ) ``` ### 3) “Constantes reutilizables a nivel de módulo” → normalmente `gettext_lazy` {#sec-9ea28af08fa3} * Las constantes que se usan en varios lugares deben mantenerse perezosas. ```python from django.utils.translation import gettext_lazy as _ STATUS_CHOICES = [ ("draft", _("Draft")), ("published", _("Published")), ] ``` ### 4) “Enviando cadenas a sistemas externos (logs, APIs, cabeceras)” → `gettext` o fuerza la evaluación del lazy {#sec-f04525bb1d99} El objeto lazy puede causar tipos inesperados o problemas de serialización. ```python from django.utils.translation import gettext_lazy as _ from django.utils.encoding import force_str msg = _("Welcome") logger.info(force_str(msg)) # ✅ Se convierte a cadena real antes de usarla ``` ### 5) “Se incluye formateo o concatenación de cadenas” → considera herramientas específicas para lazy {#sec-67d42a88924b} * Mezclar f-strings o `str.format()` con mensajes lazy puede confundir el momento de evaluación. Django ofrece `format_lazy`. ```python from django.utils.translation import gettext_lazy as _ from django.utils.text import format_lazy title = format_lazy("{}: {}", _("Error"), _("Invalid token")) ``` O usar el formato `%` de Python para mantener la cadena traducida. ```python from django.utils.translation import gettext as _ message = _("Hello, %(name)s!") % {"name": user.username} ``` --- ## Los tres errores más comunes {#sec-273d53d27224} ### Error 1) Crear traducción fija con `gettext()` al importar el módulo {#sec-7f4b5a25d9d0} * Si tienes constantes o elecciones, sospecha primero de usar lazy. ### Error 2) Incluir objetos lazy directamente en JSON o logs {#sec-7a741de5c1d8} * Convierte a cadena con `force_str()` antes de usarlo. ### Error 3) Construir cadenas con f-strings y traducción {#sec-76316b53a754} ```python # ❌ No recomendado _("Hello") + f" {user.username}" ``` * La unidad de traducción se fragmenta y el orden de palabras varía por idioma. * **Reemplaza con sustitución dentro de la cadena traducida**. ```python # ✅ Recomendado _("Hello, %(name)s!") % {"name": user.username} ``` --- ## Tips para reducir la confusión {#sec-79205bb8a9a7} La forma más efectiva de evitar confusiones es **unificar el significado de `_` según el tipo de archivo**. * En `models.py`, `forms.py`, `admin.py` (definición primero): `from django.utils.translation import gettext_lazy as _` * En `views.py`, `services.py` (ejecución primero): `from django.utils.translation import gettext as _` Con esta convención, *lazy* se vuelve la opción por defecto en los archivos de definición, reduciendo errores. --- ## Resumen rápido {#sec-3aa43e207283} * **Evaluación inmediata segura (lógica de tiempo de ejecución)**: `gettext` * **Evaluación perezosa (definición/meta/constantes/etiquetas)**: `gettext_lazy` * **Cadenas que salen al exterior**: usa `force_str` si es necesario * **Lazy + formateo**: `format_lazy` o sustitución dentro de la cadena traducida Con estos criterios claros, la elección se vuelve automática y la confusión desaparece. --- **Enlaces relacionados** - [Problemas al usar gettext_lazy como clave JSON y cómo solucionarlos](/ko/whitedec/2025/4/26/gettext-lazy-json-key-problem-solution/)