情境概述

有關 gettext_lazy 和 JSON 鍵問題的資訊圖

在進行 Django 開發時,我們經常使用 gettext_lazy 來支援多語言。 然而,我們平時使用的 gettext_lazy 在某些時候製作 JSON 响應時可能會出現錯誤。

在這篇文章中,我將詳細解釋為什麼 gettext_lazy 會在 JSON 序列化中引發問題 以及 如何解決這個問題

正常運作 vs 錯誤發生:情境比較

案例 正常運作的情況 發生錯誤的情況
LANGUAGE_MAP.get(...) 回傳值位置 字典的 value 字典的 key
序列化可行性 可以 (沒有問題) 無法 (__proxy__ 鍵無法 JSON 轉換)

問題的核心:為什麼會產生這種差異?

gettext_lazy 回傳的物件是 __proxy__ 類型。這個物件看起來像字串,但實際上不是真實的字串

Django 的 JsonResponse 或 Python 的 json.dumps() 遵循以下規則:

# ✅ 可行:可以使用 lazy 物件作為 value(自動 str 轉換)
json.dumps({'language': _('English')})

# ❌ 失敗:無法將 lazy 物件作為 key
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 响應方面,都能變得更加穩健!