在 Django 中將 gettext_lazy 用作 JSON 鍵時遇到的惱人狀況
結論簡而言之:JSON 鍵必須是「真正的」字串,但 Django 的
gettext_lazy返回的實際上並非字串,而是一個__proxy__物件。
1. 運作良好的程式碼為何突然出錯?
在 Django 開發中,為了處理多語言,使用 gettext_lazy 是家常便飯。然而,某天當我們像往常一樣使用它時,卻在 JSON 回應 (JsonResponse) 的序列化過程中突然爆出錯誤。
通常,錯誤日誌只會顯示諸如 __proxy__ 之類的未知訊息,讓人摸不著頭緒。但深入探究原因,會發現問題的根源其實出乎意料地簡單。

2. 禍根在於「鍵(Key)」
開門見山地說,gettext_lazy 物件作為字典的 值(Value) 時,通常能順利通過,但一旦作為 鍵(Key),就會引發問題。
| 分類 | 用作值(Value)時 | 用作鍵(Key)時 |
|---|---|---|
| 運作狀況 | 正常 (會自動轉換為字串) | 發生錯誤 |
| 原因 | JSON 序列化時支援自動型別轉換 | JSON 鍵必須是「真正的」字串 |
3. 為何會產生這種差異?
gettext_lazy 返回的並非真正的字串,而是一個名為 __proxy__ 的物件。顧名思義,它只是一個「稍後需要時再進行翻譯」的承諾。
Python 的 json.dumps() 或 Django 的 JsonResponse 在遍歷字典的值時,會自動將這個「承諾」解析為字串(Evaluation)。然而,對於 字典的鍵(Key) 來說,情況就不同了。根據 JSON 標準,鍵必須是字串,而在這個過程中,__proxy__ 物件不會自動轉換,直接導致衝突並產生錯誤。
# ✅ 沒有問題:值(Value)會自動轉換為字串。
json.dumps({'language': _('Korean')})
# ❌ 錯誤:鍵(Key)無法自行轉換,因此被拒絕。
json.dumps({_('Korean'): 'language'})
4. 如何解決?
方法 1:直接使用 gettext (最簡潔)
如果並非絕對需要延遲(Lazy)方式,那麼使用呼叫時立即返回字串的 gettext 會是最省心的方法。
from django.utils.translation import gettext as _
LANGUAGE_MAP = {
"en": _("English"),
"ko": _("Korean"),
}
- 注意: 然而,此方式的翻譯會在應用程式載入時決定,因此若需要動態翻譯環境,則需特別留意。
方法 2:在序列化之前插入 str()
如果您已經廣泛使用延遲(Lazy)物件,那麼在將其傳遞給 JSON 之前,強制進行字串轉換。
# 在將其用作鍵(Key)之前,用 str() 包裹一次,使其成為「真正的字串」。
lang_key = str(LANGUAGE_MAP.get(code))
方法 3:將翻譯主體轉移到客戶端
如果前端不使用 Django 的模板,而是採用獨立的前端客戶端(雖然我不知道這是否是目前的趨勢),那麼 Django(或 DRF)伺服器只需傳遞諸如 en、ko 之類的代碼(Code),實際顯示在螢幕上的文字則由前端(例如 React、Vue 等)的 i18n 函式庫處理。這種方式的優點是能減輕伺服器邏輯的負擔。
總結
在使用 json.dumps() 和 gettext_lazy() 時,如果遇到「昨天還好好的,今天怎麼就不行了?」的情況,那很可能是因為昨天您很幸運地只將其用作值(Value),而今天則用作了鍵(Key)。
gettext_lazy 雖然方便,但我們必須時刻記住它終究不是「真正的字串」。尤其是在處理 JSON 時更是如此。希望這篇文章能幫助到那些正為類似錯誤而苦惱的朋友們。
目前沒有評論。