Resumen de la situación
Al desarrollar en Django, a menudo usamos gettext_lazy
para soportar múltiples idiomas. Sin embargo, hay momentos en que el habitual gettext_lazy
empieza a generar errores al crear respuestas JSON.
En este artículo, explicaremos en detalle por qué gettext_lazy
causa problemas en la serialización JSON y cómo solucionarlos.
Funcionamiento normal vs. error: comparación de situaciones
Caso | Cuando funciona correctamente | Cuando ocurre un error |
---|---|---|
Ubicación del valor de retorno de LANGUAGE_MAP.get(...) |
Valor del diccionario | Clave del diccionario |
Posibilidad de serialización | Posible (sin problemas) | Imposible (la clave __proxy__ no puede ser convertida a JSON) |
El núcleo del problema: ¿por qué surge esta diferencia?
El objeto que devuelve gettext_lazy
es de tipo __proxy__
. Este objeto parece una cadena, pero en realidad no es una cadena real.
Los métodos JsonResponse
de Django o json.dumps()
de Python siguen las siguientes reglas:
# ✅ Posible: se puede usar un objeto lazy como valor (conversión automática a str)
json.dumps({'language': _('English')})
# ❌ Fallo: no se puede usar un objeto lazy como clave
json.dumps({_('English'): 'language'})
Es decir, para ser usado como clave del diccionario, debe ser necesariamente una cadena real. El objeto __proxy__
no se convierte automáticamente al ser usado como clave, lo que causa el error.
¿Por qué no hay problemas al usarlo como valor, pero sí como clave?
El objeto gettext_lazy
no presenta inconvenientes cuando se utiliza como valor en el diccionario, porque Django o Python lo convierten automáticamente a una cadena durante el proceso de serialización JSON.
Sin embargo, al usarse como clave, el estándar JSON exige que la clave sea necesariamente una cadena. En este caso, el objeto gettext_lazy
no se convierte automáticamente, generando un error de serialización.
Conclusión:
- Cuando se usa como valor, se realiza internamente una conversión a str, así que no hay problema.
- Cuando se usa como clave, la conversión no se realiza automáticamente y ocurre un error.
Métodos de solución
Método 1: Cambiar a gettext
(no usar lazy)
Este es el método más seguro. Se utiliza el valor convertido a cadena directamente sin lazy.
from django.utils.translation import gettext as _ # ¡no lazy!
LANGUAGE_MAP = {
"en": _("English"),
"ko": _("Korean"),
"ja": _("Japanese"),
}
- Ventaja: no se necesita procesamiento adicional
- Desventaja: como la traducción se decide en el momento de la importación, puede no coincidir con algunos requisitos de traducción dinámica
Método 2: Aplicar str() forzadamente justo antes de la serialización JSON
Si deseas seguir usando objetos lazy, convierte la clave a str()
antes de insertarla en JSON.
lang = str(LANGUAGE_MAP.get(lang_code, lang_code.upper()))
- Ventaja: no es necesario modificar mucho el código existente
- Desventaja: debes estar atento a la conversión str cada vez
Extra: Procesar traducciones en el cliente
También puedes optar por no realizar la traducción multilingüe en el servidor y manejarla en el frontend.
- El servidor envía solo el código del idioma
- El JavaScript del cliente gestiona la tabla de traducción.
Este método se puede elegir según el proyecto.
En resumen
- No usar
gettext_lazy
como clave en diccionarios. - Asegurarse de convertir a cadena antes de la serialización JSON.
- La manera más segura es convertir instantáneamente a cadena usando
gettext
. - Si es necesario mantener el código existente, no olvides usar
str(...)
.
Comentario de Jesse
Este problema es un error común que ocurre en la delicada frontera entre el manejo de múltiples idiomas en Django y las respuestas JSON.
En vez de pensar "antes funcionaba, ¿por qué no ahora?", piensa en que "antes tuvimos suerte" y revisa tu código.
El momento en que se perfecciona la habilidad de un desarrollador es cuando comienza a entender esta 'delicadeza'. Conocer este principio te permitirá hacer que tanto el manejo de múltiples idiomas en Django como las respuestas JSON sean mucho más robustas.
No hay comentarios.