# Понимание разницы между `gettext` и `gettext_lazy` в Django (по моменту оценки) При работе с i18n в Django часто возникает сомнение: какой из методов использовать – `gettext()` или `gettext_lazy()`? Большая часть путаницы возникает из‑за попытки запомнить «разницу» как термин. Ключевой момент прост: один и тот же. * **`gettext` переводит сейчас (непосредственная оценка, eager)** * **`gettext_lazy` переводит позже (ленивая оценка, lazy)** Эта разница по моменту оценки позволяет решить почти все случаи. --- ## Почему важно, когда определяется перевод? {#sec-cd5e4068943f} В Django язык меняется с каждым запросом. ![Сравнение моментов оценки перевода в Django](/media/editor_temp/6/29f9dbe8-44b7-47ed-bef6-4cc0cc62bb79.png) * Middleware смотрит на запрос и вызывает `activate("ko")` / `activate("en")`, активируя язык для текущего потока/контекста. * При рендеринге шаблонов, форм и админки перевод должен быть выполнен именно в этот момент. То есть **когда** вы переводите строку, определяет результат. --- ## `gettext()` : «переводим сейчас» и возвращаем строку {#sec-9ff53b7dc3ad} ```python from django.utils.translation import gettext as _ def view(request): message = _("Welcome") # переводится в момент выполнения этой строки return HttpResponse(message) ``` * При вызове внутри функции/вью, во время обработки запроса (runtime), обычно всё работает как ожидается. * Но при вызове во время импорта модуля возникают проблемы. ### Типичная ловушка: использование `gettext()` в константах модуля {#sec-7c331db329fb} ```python # app/constants.py from django.utils.translation import gettext as _ WELCOME = _("Welcome") # ❌ может быть «запечатан» на языке, который был активен при старте сервера ``` В таком случае `WELCOME` фиксируется как строка, переведённая в момент импорта, и не меняется при смене языка. --- ## `gettext_lazy()` : возвращает «ленивый» объект, который переводится позже {#sec-a90df9111315} ```python from django.utils.translation import gettext_lazy as _ WELCOME = _("Welcome") # ✅ это не строка, а объект, который будет переведен при необходимости ``` `gettext_lazy()` обычно возвращает «ленивый объект». * При рендеринге шаблонов/форм/админки, когда объект преобразуется в строку, * перевод выполняется в активный язык в тот момент. > Кратко: **в местах, где язык определяется во время рендеринга, lazy – правильный выбор.** --- ## Практические правила использования {#sec-4a8a3cbed930} ### 1) «Сейчас создаём ответ/экран» → `gettext` {#sec-197c23c83765} * В логике вью/сервиса, когда сразу формируется строка для ответа или логирования. ```python from django.utils.translation import gettext as _ def signup_done(request): return JsonResponse({"message": _("Signup completed.")}) ``` ### 2) «Определения, которые могут быть оценены при импорте» → `gettext_lazy` {#sec-b545464ef5b7} * `verbose_name`, `help_text` в моделях * `label`, `help_text` в формах * `label` в DRF сериализаторах * Описание в `list_display` админки ```python 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` {#sec-9ea28af08fa3} ```python from django.utils.translation import gettext_lazy as _ STATUS_CHOICES = [ ("draft", _("Draft")), ("published", _("Published")), ] ``` ### 4) «Строки, отправляемые наружу (логи, сторонние API, заголовки)» → `gettext` или принудительная оценка lazy {#sec-f04525bb1d99} ```python 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` или форматирование внутри строки {#sec-67d42a88924b} ```python from django.utils.translation import gettext_lazy as _ from django.utils.text import format_lazy title = format_lazy("{}: {}", _("Error"), _("Invalid token")) ``` Или используйте `%`‑форматирование, которое удобно для перевода: ```python from django.utils.translation import gettext as _ message = _("Hello, %(name)s!") % {"name": user.username} ``` --- ## Три самые частые ошибки {#sec-273d53d27224} ### Ошибка 1) Создание «запечатанного» перевода при импорте модуля {#sec-7f4b5a25d9d0} * Если есть константы/choices, сначала подумайте о `gettext_lazy`. ### Ошибка 2) Передача lazy‑объекта в JSON/логи без преобразования {#sec-7a741de5c1d8} * Преобразуйте через `force_str()`. ### Ошибка 3) Сборка строки через f‑строку {#sec-76316b53a754} ```python # ❌ Не рекомендуется _("Hello") + f" {user.username}" ``` * Перевод разбивается, порядок слов меняется, момент оценки усложняется. * Вместо этого используйте переменные внутри строки: ```python # ✅ Рекомендовано _("Hello, %(name)s!") % {"name": user.username} ``` --- ## Советы, чтобы уменьшить путаницу {#sec-79205bb8a9a7} Самый эффективный способ – унифицировать смысл `_` в зависимости от типа файла. * В `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», что снижает вероятность ошибок. --- ## Быстрый обзор {#sec-3aa43e207283} * **Непосредственная оценка (runtime)**: `gettext` * **Ленивая оценка (при рендеринге)**: `gettext_lazy` * **Выходящие строки**: при необходимости `force_str` * **Ленивая + форматирование**: `format_lazy` или переменные внутри строки С этими правилами вы быстро выберете правильный инструмент и избежите типичных ошибок. --- **Ссылки на связанные статьи** - [Проблемы при использовании gettext_lazy в ключах JSON и их решение](/ko/whitedec/2025/4/26/gettext-lazy-json-key-problem-solution/)