Django 中的 gettext 與 gettext_lazy 混淆終結(從評估時點理解差異)
在使用 Django i18n 時,常常會為該使用 gettext() 或 gettext_lazy() 而猶豫不決。大多數混亂源於「兩者差異」的術語記憶。
核心只有一點。
gettext立即翻譯(即時評估,eager)gettext_lazy延遲翻譯(lazy 評估)
只要掌握這一「評估時點」,大多數情況都能輕鬆決定。
為什麼「翻譯何時」如此重要?{#sec-cd5e4068943f}
Django 通常在每一次請求時切換語言。

- 中介軟體根據請求執行
activate("ko")/activate("en"),啟用「當前執行緒/上下文」的語言。 - 在模板渲染、表單渲染、admin 界面渲染時,必須以該語言進行翻譯。
換句話說,「何時翻譯」決定了最終顯示的文字。
gettext():立即翻譯並返回字串{#sec-9ff53b7dc3ad}
from django.utils.translation import gettext as _
def view(request):
message = _("Welcome") # 這行執行時即以當前語言翻譯
return HttpResponse(message)
- 在函式/視圖內部(即請求處理期間)調用,通常如預期。
- 但若在模組匯入時調用,則會在啟動時就固定為當時語言,後續語言切換不會影響。
常見陷阱:在模組常數中使用 gettext()
# app/constants.py
from django.utils.translation import gettext as _
WELCOME = _("Welcome") # ❌ 會在啟動時以當前語言固定
此時 WELCOME 會在模組載入時就被翻譯,之後語言改變也不會更新。
gettext_lazy():返回「稍後翻譯」的代理物件{#sec-a90df9111315}
from django.utils.translation import gettext_lazy as _
WELCOME = _("Welcome") # ✅ 不是實際字串,而是需要時才翻譯的物件
gettext_lazy() 產生的是「lazy object(代理)」。
- 在表單/模板/管理員渲染時,才會以當前語言翻譯。
一句話總結:「渲染時決定語言」的地方,lazy 通常是正確選擇。
實戰規則:何時使用哪一個{#sec-4a8a3cbed930}
1) 「立即產生畫面/回應」 → gettext
- 在視圖/服務邏輯中立即產生字串並回傳或記錄時。
from django.utils.translation import gettext as _
def signup_done(request):
return JsonResponse({"message": _("Signup completed.")})
2) 「在匯入時就會評估的屬性/元資料」 → gettext_lazy
- 模型的
verbose_name、help_text - 表單欄位的
label、help_text - DRF 序列化器欄位的
label - admin 的
list_display說明等「先定義、後渲染」的地方
from django.db import models
from django.utils.translation import gettext_lazy as _
class Article(models.Model):
title = models.CharField(_("title"), max_length=100)
status = models.CharField(
_("status"),
max_length=20,
choices=[
("draft", _("Draft")),
("published", _("Published")),
],
help_text=_("Visibility of the article."),
)
3) 「模組層常數/choices 等可重複使用的值」 → 通常 gettext_lazy
from django.utils.translation import gettext_lazy as _
STATUS_CHOICES = [
("draft", _("Draft")),
("published", _("Published")),
]
4) 「傳遞給外部系統的字串(日誌/第三方 API/標頭等)」 → gettext 或強制評估 lazy{#sec-f04525bb1d99}
from django.utils.translation import gettext_lazy as _
from django.utils.encoding import force_str
msg = _("Welcome")
logger.info(force_str(msg)) # ✅ 先轉成實際字串再使用
5) 「包含字串拼接/格式化」 → 考慮使用 lazy 專用工具{#sec-67d42a88924b}
- 直接將 lazy 物件與 f-string 或
str.format()混用會導致評估時點混亂。 - Django 提供
format_lazy來解決此問題。
from django.utils.translation import gettext_lazy as _
from django.utils.text import format_lazy
title = format_lazy("{}: {}", _("Error"), _("Invalid token"))
或使用舊式 % 格式化,便於管理翻譯字串。
from django.utils.translation import gettext as _
message = _("Hello, %(name)s!") % {"name": user.username}
最常見的三個錯誤{#sec-273d53d27224}
錯誤 1:在模組匯入時使用 gettext() 產生「固定翻譯」{#sec-7f4b5a25d9d0}
- 若有「常數/choices」,先考慮使用 lazy。
錯誤 2:將 lazy 物件直接放入 JSON 序列化/日誌{#sec-7a741de5c1d8}
- 先用
force_str()轉成字串。
錯誤 3:用 f-string 拼接翻譯字串{#sec-76316b53a754}
# ❌ 不建議
_("Hello") + f" {user.username}"
- 會破壞翻譯單位,且評估時點複雜。
- 建議在翻譯字串內使用變數佔位符。
# ✅ 建議
_("Hello, %(name)s!") % {"name": user.username}
減少混淆的技巧{#sec-79205bb8a9a7}
最有效的做法是根據檔案性質統一 _ 的意義。
models.py、forms.py、admin.py等「先定義」檔案:from django.utils.translation import gettext_lazy as _views.py、services.py等「先執行」檔案:from django.utils.translation import gettext as _
這樣就能在「這裡使用 lazy」的規則下減少錯誤。
簡易總結{#sec-3aa43e207283}
- 即時評估安全的地方(執行時邏輯):
gettext - 稍後渲染的地方(定義/元資料/常數/choices/標籤):
gettext_lazy - 傳遞給外部的字串:必要時使用
force_str - lazy + 格式化:使用
format_lazy或在翻譯字串內置換
只要掌握這些規則,就能在「概念不清」的狀態下自動選擇正確的函式。
相關文章連結 - gettext_lazy 在 JSON 鍵中使用時的問題與解決方法