Quand gettext_lazy de Django devient un casse-tête en tant que clé JSON
Pour résumer en une phrase : Une clé JSON doit impérativement être une 'vraie' chaîne de caractères, or,
gettext_lazyde Django ne renvoie pas une chaîne, mais un objet__proxy__.
1. Pourquoi mon code, qui fonctionnait parfaitement, s'est-il soudainement mis à planter ?
En développement Django, l'utilisation de gettext_lazy pour la gestion du multilinguisme est monnaie courante. On l'utilise sans problème jusqu'au jour où, soudainement, une erreur de sérialisation survient lors d'une réponse JSON (JsonResponse). Généralement, les logs d'erreur affichent des messages énigmatiques comme __proxy__, ce qui est déroutant. Pourtant, en y regardant de plus près, la cause est étonnamment simple.

2. Le coupable ? La "clé" elle-même.
Pour faire court, un objet gettext_lazy passe inaperçu lorsqu'il est utilisé comme valeur dans un dictionnaire, mais pose problème dès qu'il est employé comme clé.
| Distinction | Utilisation en tant que valeur | Utilisation en tant que clé |
|---|---|---|
| Fonctionnement | Normal (conversion automatique en str) | Erreur |
| Raison | Conversion de type automatique supportée lors de la sérialisation JSON | Une clé JSON doit impérativement être une 'vraie' chaîne de caractères |
3. Pourquoi une telle différence ?
Ce que gettext_lazy retourne n'est pas une vraie chaîne de caractères, mais un objet __proxy__. Comme son nom l'indique, c'est une promesse : « je te donnerai la traduction quand tu en auras besoin ». Les fonctions json.dumps() de Python ou JsonResponse de Django parcourent les valeurs d'un dictionnaire et transforment automatiquement cette 'promesse' en chaîne de caractères (évaluation). Mais pour les clés d'un dictionnaire, c'est une autre histoire. Selon la norme JSON, une clé doit impérativement être une chaîne de caractères. Dans ce contexte, l'objet __proxy__ n'est pas automatiquement converti et provoque une erreur.
# ✅ Pas de problème : la valeur est automatiquement convertie en str.
json.dumps({'language': _('Korean')})
# ❌ Erreur : la clé n'est pas convertie et est rejetée.
json.dumps({_('Korean'): 'language'})
4. Comment résoudre ce problème ?
Méthode 1 : Utiliser simplement gettext (la solution la plus simple)
Si une approche "lazy" n'est pas strictement nécessaire, la solution la plus simple est d'utiliser gettext, qui renvoie une chaîne de caractères immédiatement lors de l'appel.
from django.utils.translation import gettext as _
LANGUAGE_MAP = {
"en": _("English"),
"ko": _("Korean"),
}
- Note : Gardez à l'esprit que cette méthode détermine la traduction au moment du chargement de l'application. Soyez vigilant si un environnement de traduction dynamique est crucial.
Méthode 2 : Forcer la conversion avec str() juste avant la sérialisation
Si vous utilisez déjà des objets "lazy" de manière extensive, forcez leur conversion en chaîne de caractères juste avant de les passer au format JSON.
# Enveloppez la clé avec str() avant de l'utiliser comme clé pour en faire une 'vraie chaîne de caractères'.
lang_key = str(LANGUAGE_MAP.get(code))
Méthode 3 : Déléguer la traduction au client
Si vous utilisez un client frontal distinct sans les templates de Django (ce qui semble être la tendance actuelle), le serveur Django (ou DRF) ne renvoie que des codes comme en ou ko. La traduction et l'affichage du texte à l'écran sont alors gérés par une bibliothèque i18n côté frontend (React, Vue, etc.). Cela a l'avantage d'alléger la logique du serveur.
En guise de conclusion
Si, en utilisant json.dumps() et gettext_lazy(), vous vous êtes retrouvé dans une situation où « ça marchait hier, mais plus aujourd'hui », il est fort probable qu'hier vous ayez eu la chance de l'utiliser uniquement comme valeur, et aujourd'hui comme clé. gettext_lazy est pratique, mais il faut toujours garder à l'esprit qu'il ne s'agit pas d'une 'vraie chaîne de caractères'. C'est particulièrement vrai lorsque vous manipulez du JSON. J'espère que cet article sera utile à ceux qui rencontrent des problèmes similaires.