Django: het verschil tussen gettext en gettext_lazy afklaring (evaluatiepunt begrijpen)

Wanneer je Django i18n gebruikt, schommelt je vaak tussen gettext() en gettext_lazy() door. De meeste verwarring ontstaat doordat je probeert de "verschillen" als termen te onthouden.

Het kernpunt is simpel.

  • gettext vertaalt nu (direct evalueren, eager)
  • gettext_lazy vertaalt later (lazy evaluatie, lazy)

Met dit ene evaluatiepunt kun je bijna alle gevallen ordenen.


Waarom bepaalt het "wanneer" vertalen belangrijk?



Django verandert meestal de taal per verzoek (request).

Vergelijking van vertaalmomenten in Django

  • Middleware kijkt naar het verzoek en activeert activate("ko") / activate("en") om de huidige thread/context-taal te activeren.
  • Bij het renderen van templates, formulieren of admin-schermen moet de vertaling in die taal plaatsvinden.

Met andere woorden, het moment waarop je een string vertaalt bepaalt het resultaat.


gettext() : "nu" vertalen en een string teruggeven

from django.utils.translation import gettext as _

def view(request):
    message = _("Welcome")   # vertaald op het moment dat deze regel wordt uitgevoerd
    return HttpResponse(message)
  • Wordt aangeroepen binnen een functie/view (runtime) werkt het meestal zoals verwacht.
  • Wordt echter aangeroepen op module-importtijd, kan het problemen veroorzaken.

Veelgemaakte valkuil: gettext() gebruiken in module-constanten

# app/constants.py
from django.utils.translation import gettext as _

WELCOME = _("Welcome")  # ❌ kan vast blijven op de taal van de serverstart/import

In dit geval blijft WELCOME een vaste vertaalde string, zelfs als de taal later verandert (vooral in omgevingen waar import slechts één keer gebeurt).


gettext_lazy() : een proxy die later vertaald wordt



from django.utils.translation import gettext_lazy as _

WELCOME = _("Welcome")  # ✅ een object dat pas vertaald wordt wanneer nodig

gettext_lazy() levert meestal een "lazy object (proxy)".

  • Bij het renderen van formulieren/templates/admin wordt het vertaald.
  • Het gebeurt op het moment dat de string als tekst wordt gerenderd.

Samenvatting: In plekken waar de taal bij het renderen wordt bepaald, is lazy vaak de juiste keuze.


Praktische regels: waar wat gebruiken?

1) "Maak nu een scherm/antwoord" → gettext

  • In views of service-logica die direct een string maken voor een antwoord of log.
from django.utils.translation import gettext as _

def signup_done(request):
    return JsonResponse({"message": _("Signup completed.")})

2) "Definitie die bij import geëvalueerd kan worden" → gettext_lazy

  • verbose_name, help_text van modellen
  • label, help_text van formuliervelden
  • label van DRF serializer velden (vergelijkbare reden)
  • Beschrijvingen in admin list_display (definitie vooraf, render later)
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) "Module‑niveau constant/choices" → meestal gettext_lazy

  • Herbruikbare constanten zijn het veiligst als lazy.
from django.utils.translation import gettext_lazy as _

STATUS_CHOICES = [
    ("draft", _("Draft")),
    ("published", _("Published")),
]

4) "String naar externe systemen (log, derde‑partij API, header, etc.)" → gettext of force-evaluatie van lazy

  • Een lazy object rechtstreeks doorgeven kan onverwachte types/serialisatieproblemen veroorzaken.
from django.utils.translation import gettext_lazy as _
from django.utils.encoding import force_str

msg = _("Welcome")
logger.info(force_str(msg))  # ✅ converteert naar echte string

5) "String samenvoegen/formatteren" → overweeg speciale lazy‑hulpmiddelen

  • Het mengen van lazy berichten met f‑strings of str.format() kan het evaluatiepunt verwarren. Django biedt format_lazy voor dit doel.
from django.utils.translation import gettext_lazy as _
from django.utils.text import format_lazy

title = format_lazy("{}: {}", _("Error"), _("Invalid token"))

Of gebruik de oude %‑formattering voor eenvoudiger beheer.

from django.utils.translation import gettext as _

message = _("Hello, %(name)s!") % {"name": user.username}

De drie meest voorkomende fouten

Fout 1) gettext() gebruiken bij module‑import → vaste vertaling

  • Bij constanten/choices overweeg altijd lazy.

Fout 2) Lazy object rechtstreeks in JSON/ log plaatsen

  • Converteer eerst met force_str().

Fout 3) f‑string gebruiken om een vertaalde string te bouwen

# ❌ niet aanbevolen
_("Hello") + f" {user.username}"
  • De vertaalde eenheid wordt opgesplitst (taal‑specifieke woordvolgorde) en het evaluatiepunt wordt complexer.
  • Gebruik variabele vervanging binnen de vertaalde string.
# ✅ aanbevolen
_("Hello, %(name)s!") % {"name": user.username}

Tips om verwarring te verminderen

De meest effectieve manier om verwarring te verminderen is om de betekenis van _ te uniformeren per bestandstype.

  • In models.py, forms.py, admin.py (definitie eerst): from django.utils.translation import gettext_lazy as _
  • In views.py, services.py (uitvoering eerst): from django.utils.translation import gettext as _

Op deze manier ontstaat een basisregel: "Hier is lazy de standaard", waardoor fouten afnemen.


Samenvatting

  • Directe evaluatie veilig bij runtime‑logica: gettext
  • Later gerenderd (definitie/metadata/constanten/labels): gettext_lazy
  • Extern naar buiten: gebruik force_str indien nodig
  • Lazy + format: format_lazy of variabele vervanging binnen de vertaalde string

Met deze richtlijnen kun je de verwarring rond gettext en gettext_lazy minimaliseren en automatisch de juiste keuze maken.


Gerelateerde artikelen
- Problemen met het gebruik van gettext_lazy in JSON-sleutels en oplossingen