Понимание разницы между gettext и gettext_lazy в Django (по моменту оценки)

При работе с i18n в Django часто возникает сомнение: какой из методов использовать – gettext() или gettext_lazy()? Большая часть путаницы возникает из‑за попытки запомнить «разницу» как термин.

Ключевой момент прост: один и тот же.

  • gettext переводит сейчас (непосредственная оценка, eager)
  • gettext_lazy переводит позже (ленивая оценка, lazy)

Эта разница по моменту оценки позволяет решить почти все случаи.


Почему важно, когда определяется перевод?



В Django язык меняется с каждым запросом.

Сравнение моментов оценки перевода в Django

  • Middleware смотрит на запрос и вызывает activate("ko") / activate("en"), активируя язык для текущего потока/контекста.
  • При рендеринге шаблонов, форм и админки перевод должен быть выполнен именно в этот момент.

То есть когда вы переводите строку, определяет результат.


gettext() : «переводим сейчас» и возвращаем строку

from django.utils.translation import gettext as _

def view(request):
    message = _("Welcome")   # переводится в момент выполнения этой строки
    return HttpResponse(message)
  • При вызове внутри функции/вью, во время обработки запроса (runtime), обычно всё работает как ожидается.
  • Но при вызове во время импорта модуля возникают проблемы.

Типичная ловушка: использование gettext() в константах модуля

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

WELCOME = _("Welcome")  # ❌ может быть «запечатан» на языке, который был активен при старте сервера

В таком случае WELCOME фиксируется как строка, переведённая в момент импорта, и не меняется при смене языка.


gettext_lazy() : возвращает «ленивый» объект, который переводится позже



from django.utils.translation import gettext_lazy as _

WELCOME = _("Welcome")  # ✅ это не строка, а объект, который будет переведен при необходимости

gettext_lazy() обычно возвращает «ленивый объект».

  • При рендеринге шаблонов/форм/админки, когда объект преобразуется в строку,
  • перевод выполняется в активный язык в тот момент.

Кратко: в местах, где язык определяется во время рендеринга, lazy – правильный выбор.


Практические правила использования

1) «Сейчас создаём ответ/экран» → gettext

  • В логике вью/сервиса, когда сразу формируется строка для ответа или логирования.
from django.utils.translation import gettext as _

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

2) «Определения, которые могут быть оценены при импорте» → gettext_lazy

  • verbose_name, help_text в моделях
  • label, help_text в формах
  • label в DRF сериализаторах
  • Описание в list_display админки
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) «Константы/choices, которые переиспользуются» → обычно gettext_lazy

from django.utils.translation import gettext_lazy as _

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

4) «Строки, отправляемые наружу (логи, сторонние API, заголовки)» → gettext или принудительная оценка lazy

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

msg = _("Welcome")
logger.info(force_str(msg))  # ✅ преобразуем в реальную строку

5) «Строки с форматированием» → используйте format_lazy или форматирование внутри строки

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

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

Или используйте %‑форматирование, которое удобно для перевода:

from django.utils.translation import gettext as _

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

Три самые частые ошибки

Ошибка 1) Создание «запечатанного» перевода при импорте модуля

  • Если есть константы/choices, сначала подумайте о gettext_lazy.

Ошибка 2) Передача lazy‑объекта в JSON/логи без преобразования

  • Преобразуйте через force_str().

Ошибка 3) Сборка строки через f‑строку

# ❌ Не рекомендуется
_("Hello") + f" {user.username}"
  • Перевод разбивается, порядок слов меняется, момент оценки усложняется.
  • Вместо этого используйте переменные внутри строки:
# ✅ Рекомендовано
_("Hello, %(name)s!") % {"name": user.username}

Советы, чтобы уменьшить путаницу

Самый эффективный способ – унифицировать смысл _ в зависимости от типа файла.

  • В 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 _

Такой подход создаёт правило «здесь по умолчанию lazy», что снижает вероятность ошибок.


Быстрый обзор

  • Непосредственная оценка (runtime): gettext
  • Ленивая оценка (при рендеринге): gettext_lazy
  • Выходящие строки: при необходимости force_str
  • Ленивая + форматирование: format_lazy или переменные внутри строки

С этими правилами вы быстро выберете правильный инструмент и избежите типичных ошибок.


Ссылки на связанные статьи
- Проблемы и решения при использовании gettext_lazy с ключами JSON