## 当在Django中将gettext_lazy用作JSON Key时令人头疼的情况 {#sec-66ae94ff1145} > 一句话总结: **JSON Key必须是“真正的”字符串**,但Django的`gettext_lazy`返回的实际上并不是字符串,而是一个名为`__proxy__`的对象。 ## 1. 运行良好的代码为何突然出错了? {#sec-4a4b34e6a73c} 在Django开发中,为了处理多语言,使用`gettext_lazy`是家常便饭。然而,当你像往常一样使用它时,某一天在JSON响应(`JsonResponse`)过程中可能会突然爆发序列化(Serialization)错误。 通常,查看错误日志时,只会看到`__proxy__`之类的莫名其妙的信息,让人感到困惑。但深入探究原因,你会发现“罪魁祸首”隐藏在一个意想不到的简单地方。 ![gettext_lazy和JSON键问题的信息图](/media/whitedec/blog_img/gettext_lazy_json_error.webp) ## 2. “罪魁祸首”在于“Key” {#sec-361c46af305d} 简而言之,当`gettext_lazy`对象作为字典的**Value**时,它会默默地通过;但一旦它作为**Key**,就会立即引发问题。 | 类别 | 用作Value时 | 用作Key时 | | :--- | :--- | :--- | | **运行状态** | 正常 (自动转换为str) | **发生错误** | | **原因** | JSON序列化时支持自动类型转换 | JSON Key必须是“真正的”字符串 | ## 3. 为何会出现这种差异? {#sec-80787a3802c2} `gettext_lazy`返回的并非真正的`String`,而是一个名为`__proxy__`的对象。顾名思义,它只是一个“稍后需要时再进行翻译”的承诺。 Python的`json.dumps()`或Django的`JsonResponse`在遍历字典的值时,会自动将这个“承诺”解析为字符串(Evaluation)。然而,对于**字典的Key**,情况就不同了。根据JSON标准,Key必须是字符串。在这个过程中,`__proxy__`对象无法自动转换,直接导致冲突并引发错误。 ```python # ✅ 没有问题:Value会自动转换为str。 json.dumps({'language': _('Korean')}) # ❌ 错误:Key无法自行转换,被拒绝。 json.dumps({_('Korean'): 'language'}) ``` ## 4. 如何解决? {#sec-87b95d49caed} ### 方法 1: 直接使用`gettext` (最简洁) {#sec-d736d0465485} 如果并非必须使用延迟(Lazy)方式,那么使用调用时即返回字符串的`gettext`会是更省心的选择。 ```python from django.utils.translation import gettext as _ LANGUAGE_MAP = { "en": _("English"), "ko": _("Korean"), } ``` * **注意:** 但这种方式的翻译是在应用程序加载时确定的,如果需要动态翻译环境,则需谨慎使用。 ### 方法 2: 在序列化前强制使用`str()`转换 {#sec-fc5762a0f995} 如果已经广泛使用了延迟对象,那么在将其传递给JSON之前,可以强制进行字符串转换。 ```python # 在用作Key之前,用str()包裹一次,使其成为“真正的字符串”。 lang_key = str(LANGUAGE_MAP.get(code)) ``` ### 方法 3: 将翻译职责转移到客户端 {#sec-f61be7ca95b2} 如果前端不使用Django模板,而是采用独立的客户端(这或许是当前的趋势),那么Django(或DRF)服务器只需传递`en`、`ko`等代码,而实际显示在屏幕上的文本则由前端(如React、Vue等)的i18n库来处理。这种方式的优点是能减轻服务器逻辑的负担。 --- ## 结语 {#sec-0d2251a657d4} 如果你在使用`json.dumps()`和`gettext_lazy()`时,遇到了“昨天还好好的,今天怎么就不行了?”的情况,那很可能昨天你侥幸只将其用作了Value,而今天却用作了Key。 `gettext_lazy`虽然方便,但我们始终需要牢记它并非“真正的字符串”。尤其是在处理JSON时,这一点更需注意。希望这篇分享能帮助到那些正在为类似错误而苦恼的朋友们。