状況の概要
Djangoの開発を行っていると、gettext_lazy
を使用して多言語対応を行うことがよくあります。
しかし、普段使い慣れたgettext_lazy
が、ある時JSONレスポンスを作成する際にエラーを引き起こすことがあります。
この記事では、gettext_lazy
がJSONシリアル化で問題を引き起こす理由と、どのように解決できるかを詳しく説明します。
正常動作 vs エラー発生: 状況比較
ケース | 正常動作した場合 | エラーが発生した場合 |
---|---|---|
LANGUAGE_MAP.get(...) の返り値位置 |
辞書のvalue | 辞書のkey |
シリアル化可能か | 可能 (問題なし) | 不可能 (__proxy__ keyはJSON変換不可) |
問題の核心: なぜこのような差が生まれたのか?
gettext_lazy
が返すオブジェクトは__proxy__
タイプです。このオブジェクトは、文字列のように見えますが、実際には本物の文字列ではありません。
DjangoのJsonResponse
やPythonのjson.dumps()
は次のルールに従います:
# ✅ 可能: valueとしてはlazyオブジェクトを使える (自動的にstr変換)
json.dumps({'language': _('English')})
# ❌ 失敗: keyとしてはlazyオブジェクトを使えない
json.dumps({_('English'): 'language'})
つまり、辞書のkeyとして使用するためには、必ず本物の文字列でなければなりません。
__proxy__
オブジェクトはkeyとして使われるときに自動変換されないため、エラーが発生します。
なぜvalueでは問題がなく、keyでは問題になるのか?
gettext_lazy
オブジェクトは辞書のvalueとして使用される場合は問題がありません。DjangoやPythonはJSONシリアル化の過程でvalueを文字列に自動変換してくれるからです。
しかしkeyとして使われる場合、JSON標準はkeyを必ず文字列にすべきだと規定しています。このとき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: JSONシリアル化直前に強制的にstr()を適用
lazyオブジェクトを引き続き使用したい場合、JSONに入れる前にkeyをstr()
で変換します。
lang = str(LANGUAGE_MAP.get(lang_code, lang_code.upper()))
- 利点: 既存のコードをほとんど変更せずに済む
- 欠点: 毎回str変換に気を付けなければならない
番外: クライアントで翻訳処理を行う
サーバーで全く多言語翻訳を行わず、フロントエンドで処理する方法もあります。
- サーバーは言語コードのみ送信し
- クライアントのJavaScriptで翻訳テーブルを管理します。
この方法はプロジェクトによって選択できます。
まとめると
gettext_lazy
は絶対に辞書のkeyとして使用しないこと- JSONシリアル化の前に必ず文字列に変換すること
- 最も安全な方法は
gettext
で即座に文字列変換すること - 既存のコードを維持する必要がある場合は
str(...)
を忘れないこと
Jesseのコメント
この問題はDjangoにおける多言語処理とJSONレスポンス処理の非常に微妙な境界で発生する一般的なミスです。
これからは「以前はできたけれど、なぜできないのだろう?」ではなく、「以前は運が良かったのだ」と考え、コードをチェックしてください。
開発者のスキルが高まる瞬間は、このような「微妙さ」を理解できるようになったときです。この原理を知っていれば、Djangoの多言語処理とJSONレスポンスの両方をはるかに堅牢にすることができます!
コメントはありません。