Aperçu de la situation

infographique sur le problème gettext_lazy et clé JSON

En développant avec Django, il est fréquent d'utiliser gettext_lazy pour prendre en charge plusieurs langues. Cependant, il arrive que gettext_lazy, qui fonctionne habituellement bien, génère des erreurs lors de la création d'une réponse JSON.

Dans cet article, nous allons expliquer pourquoi gettext_lazy pose problème lors de la sérialisation JSON et comment le résoudre.

Fonctionnement normal vs erreur : comparaison des situations

Cas Fonctionnement normal Cas d'erreur
Position de la valeur renvoyée par LANGUAGE_MAP.get(...) Valeur du dictionnaire Clé du dictionnaire
Capacité de sérialisation possible (aucun problème) impossible (__proxy__ ne peut pas être converti en JSON)

L'essence du problème : pourquoi cette différence existe-t-elle ?

Les objets renvoyés par gettext_lazy sont de type __proxy__. Ces objets ont l'apparence de chaînes, mais ne sont en réalité pas des vraies chaînes.

Les JsonResponse de Django et json.dumps() de Python suivent cette règle :

# ✅ possible : on peut utiliser des objets paresseux comme valeur (conversion str automatique)
json.dumps({'language': _('English')})

# ❌ échec : on ne peut pas utiliser d'objets paresseux comme clé
json.dumps({_('English'): 'language'})

En d'autres termes, pour être utilisés comme clés, ils doivent être de vraies chaînes. Un objet __proxy__ ne se convertit pas automatiquement lorsqu'il est utilisé comme clé, ce qui entraîne une erreur.

Pourquoi n'y a-t-il pas de problème avec les valeurs mais des problèmes avec les clés ?

Les objets gettext_lazy ne posent pas de problème lorsqu'ils sont utilisés comme valeurs dans un dictionnaire, car Django ou Python les convertissent automatiquement en chaînes lors du processus de sérialisation JSON.

En revanche, lorsqu'ils sont utilisés comme clés, la norme JSON impose qu'une clé doive être une chaîne. Dans ce cas, les objets gettext_lazy ne se convertissent pas automatiquement et provoquent une erreur de sérialisation.

Conclusion :

  • Lorsqu'ils sont utilisés comme valeurs, ils passent sans problème grâce à une conversion interne en str.
  • Lorsqu'ils sont utilisés comme clés, la conversion ne se fait pas automatiquement, entraînant une erreur.

Solutions

Méthode 1 : Passer à gettext (ne pas utiliser de lazy)

C'est la méthode la plus sûre. N'utilisez pas lazy et utilisez directement les valeurs converties en chaînes.

from django.utils.translation import gettext as _  # pas de lazy!

LANGUAGE_MAP = {
    "en": _("English"),
    "ko": _("Korean"),
    "ja": _("Japanese"),
}
  • Avantage : pas de traitement supplémentaire nécessaire
  • Inconvénient : la traduction est déterminée au moment de l'importation initiale, ce qui peut différer de certains besoins de traduction dynamique

Méthode 2 : Appliquer str() juste avant la sérialisation JSON

Si vous souhaitez continuer à utiliser des objets lazy, convertissez la clé en str() avant de l'insérer dans le JSON.

lang = str(LANGUAGE_MAP.get(lang_code, lang_code.upper()))
  • Avantage : peu ou pas de modifications requises dans le code existant
  • Inconvénient : besoin de faire attention à chaque conversion str

Divers : traiter la traduction côté client

Il est également possible de ne pas effectuer de traduction multilingue côté serveur et de gérer cela entièrement côté frontend.

  • Le serveur envoie uniquement le code de langue
  • Le JavaScript client gère le tableau de traductions.

Cette méthode peut être choisie selon le projet.

Pour résumer

  • Ne jamais utiliser gettext_lazy comme clé de dictionnaire
  • S'assurer que la conversion en chaîne se fait avant la sérialisation JSON
  • La méthode la plus sûre est d'utiliser gettext pour une conversion immédiate en chaîne
  • Si la préservation du code existant est nécessaire, n'oubliez pas de faire str(...)

Commentaires de Jesse

Ce problème est une erreur courante qui se produit à la frontière subtile entre le traitement multilingue dans Django et la gestion des réponses JSON.

Pensez désormais à "Pourquoi ça fonctionnait auparavant ?" plutôt qu'à "Pourquoi ça ne fonctionne pas maintenant ?" et revérifiez votre code.

Le moment où les compétences d'un développeur s'améliorent, c'est lorsqu'il comprend ces 'subtilités'. En comprenant ce principe, vous pouvez rendre la gestion multilingue de Django et les réponses JSON beaucoup plus robustes !