Обзор ситуации
Когда вы разрабатываете на Django, вы, возможно, часто используете gettext_lazy
для поддержки многоязычности. Однако в какой-то момент gettext_lazy
, который вы привыкли использовать, может начать вызывать ошибку при создании ответа JSON.
В этой статье я подробно объясню почему gettext_lazy
вызывает проблемы при сериализации JSON и как это можно исправить.
Нормальная работа vs ошибка: сравнение ситуаций
Кейс | Случай с нормальной работой | Случай с ошибкой |
---|---|---|
LANGUAGE_MAP.get(...) возвращаемое значение |
значение словаря | ключ словаря |
Сериализуемость | возможно (без проблем) | невозможно (ключ __proxy__ не может быть преобразован в JSON) |
Суть проблемы: почему возникает такая разница?
Объект, возвращаемый gettext_lazy
, имеет тип __proxy__
. Этот объект выглядит как строка, но на самом деле не является настоящей строкой.
Django's JsonResponse
или json.dumps()
Python следуют следующим правилам:
# ✅ Возможно: можно использовать lazy объект в качестве value (автоматическое преобразование в str)
json.dumps({'language': _('English')})
# ❌ Не удалось: нельзя использовать lazy объект в качестве key
json.dumps({_('English'): 'language'})
То есть, для использования в качестве ключа словаря значение должно быть настоящей строкой. Объект __proxy__
не автоматически преобразуется в строку, когда используется как ключ, что и вызывает ошибку.
Почему нет проблем с использованием в качестве value, а с ключом проблемы возникают?
Объект gettext_lazy
не вызывает проблем, когда используется в качестве value словаря. Это связано с тем, что Django или Python автоматически преобразует value в строку в процессе сериализации JSON.
Однако в случае использования в качестве key стандарт JSON требует, чтобы ключ был обязательно строкой. В этом случае объект gettext_lazy
не преобразуется автоматически, что и приводит к ошибке сериализации.
В заключение:
- При использовании в качестве value происходит внутреннее преобразование в str, поэтому проблем нет.
- При использовании в качестве key автоматическое преобразование не происходит, что приводит к ошибкам.
Способы решения
Способ 1: Переключение на gettext
(без использования Lazy)
Это самый надежный способ. Используйте значение, которое уже преобразовано в строку, без использования lazy.
from django.utils.translation import gettext as _ # не lazy!
LANGUAGE_MAP = {
"en": _("English"),
"ko": _("Korean"),
"ja": _("Japanese"),
}
- Плюсы: дополнительная обработка не требуется
- Минусы: перевод определяется на момент первого импорта, поэтому может отличаться от некоторых динамических требований к переводу
Способ 2: Принудительное применение str() сразу перед сериализацией JSON
Если вы хотите продолжать использовать lazy объекты, преобразуйте ключ в str()
перед вставкой в JSON.
lang = str(LANGUAGE_MAP.get(lang_code, lang_code.upper()))
- Плюсы: почти не нужно менять существующий код
- Минусы: нужно помнить о каждом преобразовании str
Дополнительно: Обработка перевода на стороне клиента
Можно также обойтись без выполнения многоязычного перевода на сервере и обрабатывать его на фронтенде.
- Сервер отправляет только код языка
- Клиентский JavaScript управляет таблицей переводов.
Этот метод можно выбрать в зависимости от проекта.
Итак, в заключение
gettext_lazy
никогда не следует использовать в качестве ключа словаря- Обязательно преобразуйте в строку перед сериализацией JSON
- Самый безопасный способ - сразу преобразовать в строку с помощью
gettext
- Если необходимо сохранить существующий код, не забудьте про
str(...)
Комментарий Джесси
Эта проблема возникает из-за тонкой границы между многоязычной обработкой в Django и обработкой ответов JSON.
В будущем вместо "почему это не работает, хотя раньше работало?" подумайте, что "раньше вам просто повезло" и проверьте код.
Момент, когда уровень разработчика действительно повышается, - это когда вы начинаете понимать такие 'тонкости'. Поняв этот принцип, вы сможете значительно укрепить как многоязычную обработку Django, так и ответы JSON!
댓글이 없습니다.