状況の概要

gettext_lazyとJSONキー問題のインフォグラフィック

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レスポンスの両方をはるかに堅牢にすることができます!