Situatieoverzicht

infographic about gettext_lazy and JSON key problem

Bij het ontwikkelen van Django komen we vaak situaties tegen waarin we gettext_lazy gebruiken voor meertalige ondersteuning. Echter, soms kan gettext_lazy een fout veroorzaken bij het maken van een JSON-respons.

In dit artikel zal ik uitgelegd worden waarom gettext_lazy problemen veroorzaakt bij JSON-serialisatie en hoe dit kan worden opgelost.

Normale werking vs. fout: situatievergelijking

Geval Normaal functionerend geval Foutgevend geval
LANGUAGE_MAP.get(...) returnwaarde locatie Waarde van het woordenboek Sleutel van het woordenboek
Serialisatie mogelijkheid Mogelijk (geen probleem) Onmogelijk (__proxy__ sleutel kan niet naar JSON worden omgezet)

De kern van het probleem: waarom is deze afwijking ontstaan?

Het object dat door gettext_lazy wordt geretourneerd, is van het type __proxy__. Dit object lijkt op een string, maar is eigenlijk geen echte string.

Django's JsonResponse of Python's json.dumps() volgen de volgende regels:

# ✅ Oké: je kunt lazy-objecten gebruiken voor waardes (automatische str-conversie)
json.dumps({'language': _('English')})

# ❌ Fout: je kunt geen lazy-objecten gebruiken voor sleutels
json.dumps({_('English'): 'language'})

Met andere woorden, de sleutel van een woordenboek moet altijd een echte string zijn. __proxy__ objecten worden niet automatisch omgezet wanneer ze als sleutel worden gebruikt, wat de fout veroorzaakt.

Waarom zijn er geen problemen met waardes, maar wel met sleutels?

De gettext_lazy objecten zijn geen probleem wanneer ze als waarde in een woordenboek worden gebruikt. Dit komt omdat Django of Python de waarde automatisch naar een string omzet tijdens het JSON-serialisatieproces.

Echter, als ze als sleutel worden gebruikt, vereist de JSON-standaard dat sleutels strengen zijn. In dat geval wordt het gettext_lazy object niet automatisch omgezet, wat resulteert in een serialisatiefout.

Conclusie:

  • Bij gebruik als waarde vindt er intern een str-conversie plaats, wat zonder problemen doorgaat.
  • Als sleutel vindt er echter geen automatische conversie plaats, wat tot fouten leidt.

Oplossingsmethoden

Methode 1: Overstappen op gettext (Geen lazy gebruiken)

Dit is de meest zekere methode. Gebruik onmiddellijk de waarden die naar strings zijn omgezet zonder lazy.

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

LANGUAGE_MAP = {
    "en": _("English"),
    "ko": _("Koreaans"),
    "ja": _("Japans"),
}
  • Voordeel: geen extra verwerking nodig
  • Nadeel: de vertaling wordt beslist bij het eerste importmoment, wat mogelijk niet overeenkomt met bepaalde dynamische vertaalvereisten

Methode 2: Forceer str() net voor JSON-serialisatie

Als je lazy-objecten wilt blijven gebruiken, zet dan de sleutel om in een string met str() voordat je deze in JSON gaat zetten.

lang = str(LANGUAGE_MAP.get(lang_code, lang_code.upper()))
  • Voordeel: je hoeft de bestaande code bijna niet aan te passen
  • Nadeel: je moet telkens op de str-conversie letten

Bonus: Vertaling aan de clientzijde verwerken

Er is ook een mogelijkheid om meertalige vertalingen helemaal niet op de server te verwerken en deze oplossingen aan de frontend over te laten.

  • De server stuurt alleen de taalkode
  • De client-side javascript beheert de vertalingen.

Deze methode kan gekozen worden, afhankelijk van het project.

Samenvattend

  • Gebruik gettext_lazy absoluut niet als woordenboeksleutel.
  • Zorg ervoor dat je altijd de string omzet voor JSON-serialisatie.
  • De veiligste methode is om direct naar strings om te zetten met gettext.
  • Vergeet niet om str(...) te gebruiken als je de bestaande code wilt behouden.

Jesse's opmerking

Dit probleem is een veelvoorkomende fout die voorkomt in het delicate evenwicht van meertalige verwerking en JSON-reactieverwerking in Django.

In de toekomst, beschouw het als "het werkte eerder, waarom nu niet?" meer als "het was eerder geluk" en controleer je code.

Het moment waarop de vaardigheid van de ontwikkelaar toeneemt, is wanneer men deze 'delicate' nuances begrijpt. Wanneer je dit principe begrijpt, kun je zowel de meertalige verwerkingen in Django als de JSON-reacties veel robuuster maken!