
在构建支持多语言的django网站时,经常会遇到需要翻译模型中charfield或integerfield字段的choices选项值的情况。直接在模板中使用{% blocktranslate %}{{ variable }}{% endblocktranslate %}来翻译动态变量(如each_order.status)往往无法达到预期效果,因为blocktranslate期望的是静态字符串或已标记为可翻译的字符串。django为此提供了更为优雅和推荐的解决方案,即结合使用textchoices、gettext_lazy和get_foo_display方法。
核心问题分析
当模型字段定义了choices选项时,例如:
class Order_product(models.Model):
# ...
status = models.CharField(max_length = 255, choices = [('Pending', 'Pending'), ('Need-Delivery', 'Need-Delivery'), ...])在模板中直接显示{{ each_order.status }}会输出原始的英文值(如'Pending')。如果尝试用blocktranslate包裹它,Django的makemessages工具可能会生成一个包含%(each_order.status)s的msgid,这表明它将变量视为一个插值,而不是一个可以直接翻译的字符串,导致翻译失效。
解决方案:TextChoices 与 get_FOO_display
Django 3.0及更高版本推荐使用TextChoices(或IntegerChoices)来定义模型字段的选项。这种方式不仅使代码更具可读性,还与Django的国际化(i18n)功能更好地集成。
1. 定义可翻译的Choices
首先,我们需要在模型中定义可翻译的choices。这通过django.utils.translation.gettext_lazy(通常别名为_)来实现。gettext_lazy会延迟翻译字符串,直到它们被实际使用时,这对于模型定义非常重要,因为它避免了在应用启动时就加载所有翻译。
修改 models.py 如下:
from django.db import models
from django.utils.translation import gettext_lazy as _
class OrderStatus(models.TextChoices):
PENDING = "Pending", _("Pending")
NEED_DELIVERY = "Need-Delivery", _("Need-Delivery")
DELIVERY = "Delivery", _("Delivery")
SUCCESS = "Success", _("Success")
RETURN_ORDER = "Return-Order", _("Return-Order")
CANCEL = "Cancel", _("Cancel")
class Order_product(models.Model):
name = models.CharField(max_length=255)
note = models.TextField()
status = models.CharField(
max_length=255,
choices=OrderStatus.choices,
default=OrderStatus.PENDING # 可以设置默认值
)
def __str__(self):
return f"{self.name} - {self.get_status_display()}"在上述代码中:
- 我们创建了一个OrderStatus枚举类,它继承自models.TextChoices。
- 每个枚举成员的第二个元素都通过_()函数包裹,将其标记为可翻译字符串。
- Order_product模型中的status字段现在使用OrderStatus.choices作为其选项。
2. 在模板中显示翻译后的值
Django模型实例提供了一个便捷的方法来获取choices字段的“人类可读”表示,这个方法命名约定是get_FOO_display,其中FOO是字段的名称。例如,对于status字段,这个方法就是get_status_display()。这个方法会自动返回当前活动语言环境下的翻译值。
修改 test.html 如下:
{% load i18n %} {# 确保加载i18n标签 #}
{% for each_order in get_order %}
{{ each_order.get_status_display }}
{% endfor %}注意,这里不再需要{% blocktranslate %}标签,因为get_status_display方法本身就负责返回已翻译的字符串。
3. 视图层保持不变
views.py 中的逻辑无需改变,因为它只是简单地获取模型实例并传递给模板:
from django.shortcuts import render
from .models import Order_product # 导入具体的模型
def test(request):
get_order = Order_product.objects.all()
return render(request, 'test/test.html', {'get_order': get_order})4. 生成和编译翻译文件
完成上述修改后,需要更新Django的翻译文件:
-
生成翻译消息文件:
运行以下命令来扫描项目中的可翻译字符串,并更新.po文件:
python manage.py makemessages -l vi # 替换'vi'为你的目标语言代码
执行此命令后,在locale/vi/LC_MESSAGES/django.po文件中,你会找到类似这样的条目:
msgid "Pending" msgstr ""
-
添加翻译:
打开django.po文件,为msgid对应的字符串添加msgstr翻译。例如:
msgid "Pending" msgstr "待处理" # 越南语可能是 "Đang chờ" 或其他
msgid "Success" msgstr "成功" # 越南语可能是 "Thành công"
-
编译翻译文件:
保存django.po文件后,运行以下命令编译翻译文件,使其生效:
python manage.py compilemessages
5. 配置语言切换中间件
确保你的settings.py中包含了LocaleMiddleware,它是处理语言切换的关键:
# settings.py
MIDDLEWARE = [
# ...
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware', # 确保在 SessionMiddleware 之后,CommonMiddleware 之前
'django.middleware.common.CommonMiddleware',
# ...
]
# 定义支持的语言
LANGUAGES = [
('en', _('English')),
('vi', _('Vietnamese')),
# ... 其他语言
]
# 定义翻译文件查找路径
LOCALE_PATHS = [
BASE_DIR / 'locale',
]注意事项与总结
- TextChoices的优势: TextChoices不仅提供了更好的代码组织和可读性,还与Django的国际化系统无缝集成,是定义模型选项的推荐方式。
- gettext_lazy的重要性: 使用gettext_lazy而不是gettext(或_()的直接调用)来标记choices字符串,可以避免在应用启动时过早地进行翻译,这在某些情况下是必要的。
- get_FOO_display方法: 这是获取choices字段翻译值的标准方法,它会自动处理当前语言环境,无需手动在模板中进行翻译。
- 翻译流程: 记住makemessages -> 编辑.po -> compilemessages的完整流程,以确保所有翻译都已正确生成和加载。
- LocaleMiddleware: 它是Django处理HTTP请求中语言偏好的核心组件,必须正确配置。
通过采用TextChoices结合get_FOO_display的方法,Django开发者可以高效且优雅地实现模型choices字段的国际化,确保在多语言环境下用户能够看到正确翻译的选项值。这种方法避免了在模板中直接处理动态翻译的复杂性,使代码更加清晰和易于维护。










