Démystifier gettext vs gettext_lazy dans Django (Comprendre le moment d’évaluation)

Lorsque vous utilisez l’internationalisation de Django, vous vous demandez souvent quel est le bon choix entre gettext() et gettext_lazy(). La plupart des confusions proviennent de la tentative de mémoriser les différences terminologiques.

Le principe est simple :

  • gettext traduit immédiatement (évaluation immédiate, eager)
  • gettext_lazy attend pour traduire (évaluation paresseuse, lazy)

Un seul concept d’évaluation suffit pour clarifier la plupart des cas.


Pourquoi le moment de la traduction est-il important ?



Dans Django, la langue change généralement à chaque requête.

Comparaison des moments de traduction dans Django

  • Le middleware active la langue du thread/corps de la requête via activate("ko") ou activate("en").
  • La traduction doit se produire au moment du rendu du template, du formulaire ou de l’interface d’administration.

En d’autres termes, le moment où vous traduisez une chaîne détermine le résultat.


gettext() : traduire immédiatement et retourner la chaîne

from django.utils.translation import gettext as _

def view(request):
    message = _("Welcome")   # traduit selon la langue active au moment de l’exécution
    return HttpResponse(message)
  • Appelé dans une vue ou une fonction de service (runtime) → fonctionne comme prévu.
  • Appelé au moment de l’importation du module → problème.

Piège courant : utiliser gettext() dans les constantes de module

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

WELCOME = _("Welcome")  # ❌ peut être « figé » dans la langue du démarrage du serveur

Dans ce cas, WELCOME reste la traduction effectuée lors de l’importation, même si la langue change par la suite.


gettext_lazy() : retourner un proxy qui se traduit plus tard



from django.utils.translation import gettext_lazy as _

WELCOME = _("Welcome")  # ✅ un objet proxy, pas encore traduit

gettext_lazy() renvoie généralement un objet proxy paresseux.

  • Lors du rendu du formulaire/template/admin, l’objet est traduit avec la langue active.

Résumé en une phrase : Dans les endroits où la langue est déterminée au moment du rendu, le lazy est la solution.


Où utiliser quoi : règles pratiques

1) Vous créez immédiatement une réponse ou un message → gettext

from django.utils.translation import gettext as _

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

2) Vous définissez des attributs de classe, des métadonnées ou des champs de formulaire qui seront évalués lors de l’importation → gettext_lazy

  • verbose_name, help_text de modèles
  • label, help_text de champs de formulaire
  • label de sérialiseur DRF
  • list_display d’admin
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) Vous avez des constantes ou des choix réutilisables au niveau du module → généralement gettext_lazy

from django.utils.translation import gettext_lazy as _

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

4) Vous envoyez une chaîne vers un système externe (logs, API tierce, en-têtes) → gettext ou forcez l’évaluation du lazy

from django.utils.translation import gettext_lazy as _
from django.utils.encoding import force_str

msg = _("Welcome")
logger.info(force_str(msg))  # ✅ convertit en chaîne réelle

5) Vous combinez ou formatez des chaînes → utilisez format_lazy ou la substitution interne

from django.utils.translation import gettext_lazy as _
from django.utils.text import format_lazy

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

ou

from django.utils.translation import gettext as _

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

Trois erreurs fréquentes

Erreur 1 : créer une traduction figée au moment de l’importation avec gettext()

  • Si vous avez des constantes ou des choix, privilégiez le lazy.

Erreur 2 : insérer un objet lazy directement dans JSON ou les logs

  • Utilisez force_str() pour obtenir une chaîne.

Erreur 3 : assembler des chaînes traduites avec f‑string {#sec-76316b53a754}

# ❌ non recommandé
_("Hello") + f" {user.username}"
  • La traduction se fait en morceaux et l’ordre peut changer selon la langue.
  • Utilisez la substitution interne :
# ✅ recommandé
_("Hello, %(name)s!") % {"name": user.username}

Astuces pour réduire la confusion

Uniformisez l’usage de _ selon le type de fichier :

  • models.py, forms.py, admin.py : from django.utils.translation import gettext_lazy as _
  • views.py, services.py : from django.utils.translation import gettext as _

Cette convention crée un point de référence qui diminue les erreurs.


Résumé rapide

  • Évaluation immédiate (runtime) : gettext
  • Évaluation paresseuse (définition/metadata/constantes) : gettext_lazy
  • Chaînes sortantes : utilisez force_str si nécessaire
  • Lazy + formatage : format_lazy ou substitution interne

Avec ces règles, vous choisissez automatiquement la bonne fonction sans confusion.


Liens connexes - Problèmes et solutions lors de l'utilisation de gettext_lazy comme clé JSON