## Неприятная ситуация: gettext_lazy Django в качестве ключа JSON {#sec-66ae94ff1145} > Краткий вывод: **ключ JSON всегда должен быть «настоящей» строкой**, но `gettext_lazy` Django возвращает не строку, а объект `__proxy__`. ## 1. Почему внезапно перестал работать код, который отлично функционировал? {#sec-4a4b34e6a73c} Использование `gettext_lazy` для обработки многоязычности в процессе разработки Django — обычное дело. Однако иногда, когда вы используете его как обычно, внезапно может возникнуть ошибка сериализации в процессе ответа JSON (JsonResponse). Обычно в логах ошибок отображаются непонятные сообщения вроде `__proxy__` и т.п., что сбивает с толку. Но если разобраться в причине, то виновник окажется на удивление простым. ![Инфографика о проблеме gettext_lazy и ключа JSON](/media/whitedec/blog_img/gettext_lazy_json_error.webp) ## 2. Виновник был в "ключе" {#sec-361c46af305d} Сразу к делу: объект `gettext_lazy` без проблем проходит, когда используется в качестве **Value** словаря, но вызывает проблемы, как только попадает в **Key**. | Категория | При использовании в качестве Value | При использовании в качестве Key | | :--- | :--- | :--- | | **Работает ли** | Да (автоматически преобразуется в str) | **Возникает ошибка** | | **Причина** | Поддерживается автоматическое преобразование типов при сериализации JSON | Ключ JSON всегда должен быть 'настоящей' строкой | ## 3. Почему возникает такая разница? {#sec-80787a3802c2} `gettext_lazy` возвращает не настоящую строку, а объект `__proxy__`. Как следует из названия, это просто обещание: "Я переведу это, когда понадобится". `json.dumps()` в Python или `JsonResponse` в Django просматривают значения словаря и самостоятельно разрешают это 'обещание' в строку (Evaluation). Но с **ключом словаря** дело обстоит иначе. Согласно стандарту JSON, ключ должен быть строкой. В этом процессе объект `__proxy__` не преобразуется автоматически и вызывает ошибку. ```python # ✅ Без проблем: Value автоматически преобразуется в str. json.dumps({'language': _('Korean')}) # ❌ Ошибка: Key не может быть преобразован автоматически и отклоняется. json.dumps({_('Korean'): 'language'}) ``` ## 4. Как решить проблему? {#sec-87b95d49caed} ### Способ 1: Просто использовать `gettext` (самый чистый) {#sec-d736d0465485} Если ленивый подход не является строго необходимым, то гораздо проще использовать `gettext`, который возвращает строку немедленно при вызове. ```python from django.utils.translation import gettext as _ LANGUAGE_MAP = { "en": _("English"), "ko": _("Korean"), } ``` * **Примечание:** Однако этот метод определяет перевод в момент загрузки приложения, поэтому требуется осторожность, если важна динамическая среда перевода. ### Способ 2: Вставить `str()` непосредственно перед сериализацией {#sec-fc5762f0f995} Если вы уже широко используете ленивые объекты, принудительно преобразуйте их в строку непосредственно перед передачей в JSON. ```python # Перед использованием в качестве ключа оберните его в str(), чтобы получить 'настоящую строку'. lang_key = str(LANGUAGE_MAP.get(code)) ``` ### Способ 3: Передать ответственность за перевод клиенту {#sec-f61be7ca95b2} Если вы используете отдельный клиентский фронтенд, а не шаблоны Django (возможно, это сейчас тренд), то сервер Django (или DRF) просто отправляет коды, такие как `en` или `ko`, а фактический текст для отображения на экране обрабатывается библиотеками i18n на стороне фронтенда (React, Vue и т.д.). Преимущество этого подхода в том, что логика сервера становится легче. --- ## В заключение {#sec-0d2251a657d4} Если вы столкнулись с ситуацией, когда при использовании `json.dumps()` с `gettext_lazy()` вы задаетесь вопросом: "Вчера работало, а сегодня почему-то нет?", то, скорее всего, вчера вам повезло, и вы использовали его только в качестве Value, а сегодня — в качестве Key. `gettext_lazy` удобен, но всегда следует помнить, что это не "настоящая строка". Особенно при работе с JSON. Надеюсь, эта статья поможет тем, кто столкнулся с похожими ошибками.