如果你使用过 Django 内置的国际化 (i18n) 框架,就会知道它在处理静态字符串方面表现出色。将文本包装在 gettext() 中或使用 {% trans %} 模板标签可以将字符串提取到 .po 文件中,然后由翻译人员填写。这个系统经过了实战检验,在代码和模板中运行良好。
但是,存储在数据库中的内容怎么办呢?
产品名称、文章标题、分类描述、常见问题解答、用户生成的内容。这些都不在你的源代码中。Django 的 makemessages 命令永远找不到它们,.po 文件也无法帮助你。如果你的应用程序需要以多种语言向用户提供动态内容,你需要一种不同的策略。
下面是解决方法:使用 django-modeltranslation 为模型添加可翻译字段,然后使用 TranslateBot 通过 AI 自动完成翻译。
Django 数据库翻译包
有几个第三方包可以解决数据库翻译问题,每个包的架构各不相同。
django-modeltranslation
直接在现有表中添加特定语言的列。一个 title 字段会变成 title_en、title_de、title_fr 等。查询速度很快,因为所有数据都在同一张表中。管理界面可以并排显示所有语言。
- 优点: 无需 JOIN 操作,查询速度快,通过字段描述符透明访问,生态系统成熟
- 缺点: 每增加一种新语言就需要修改表结构,表会变得更宽
django-parler
为每个模型创建一个单独的翻译表。原始表保持简洁,翻译存储在通过外键关联的相关表中。
- 优点: 表结构清晰,添加语言时无需迁移
- 缺点: 获取翻译内容需要 JOIN 操作,查询模式稍微复杂一些
django-translations
使用单个翻译表,通过通用外键指向任何模型。所有模型的所有翻译都存储在一张表中。
- 优点: 表结构变化最小,适用于任何模型
- 缺点: 通用外键查询可能较慢,API 透明度较低
手动 JSON 字段
你可以将翻译存储在 JSONField 中:
class Product(models.Model):
name_translations = models.JSONField(default=dict)
# {"en": "Running Shoes", "de": "Laufschuhe", "fr": "Chaussures de course"}
- 优点: 无需额外依赖,灵活性高
- 缺点: 无 ORM 集成,无管理后台支持,所有操作都需要手动完成
应该选择哪个?
对于大多数项目来说,django-modeltranslation 是最佳选择。它是最成熟的包,拥有最好的 Django 管理后台集成,并且将所有数据保存在同一张表中以实现快速查询。其权衡(更宽的表和每增加一种新语言需要一次迁移)对于绝大多数应用来说是可以接受的。本指南的其余部分将使用 django-modeltranslation。
逐步设置 django-modeltranslation
第 1 步:安装包
TranslateBot 将 django-modeltranslation 作为可选依赖捆绑提供。一次性安装两者:
pip install translatebot-django[modeltranslation]
或者如果你使用 uv:
uv add --dev translatebot-django[modeltranslation]
第 2 步:配置 Django 设置
在 settings.py 中有两件事很重要:应用顺序和语言列表。
# settings.py
INSTALLED_APPS = [
'modeltranslation', # Must be BEFORE django.contrib.admin
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Your apps
'shop',
'translatebot_django',
]
LANGUAGE_CODE = 'en'
LANGUAGES = [
('en', 'English'),
('de', 'German'),
('fr', 'French'),
('nl', 'Dutch'),
]
modeltranslation 应用必须在 django.contrib.admin 之前,这样它才能修补管理类以显示翻译字段。
第 3 步:注册需要翻译的模型
在每个包含可翻译模型的应用中创建一个 translation.py 文件。以电商应用为例:
# shop/translation.py
from modeltranslation.translator import register, TranslationOptions
from .models import Product, Category
@register(Product)
class ProductTranslationOptions(TranslationOptions):
fields = ('name', 'description', 'short_description')
@register(Category)
class CategoryTranslationOptions(TranslationOptions):
fields = ('name', 'description')
只包含包含人类可读文本的字段。不要注册 slug、price 或 sku 等字段。
第 4 步:创建并运行迁移
python manage.py makemigrations
python manage.py migrate
完成后,你的 shop_product 表将拥有新的列:
| 列名 | 类型 |
|---|---|
name |
varchar(200) |
name_en |
varchar(200) |
name_de |
varchar(200) |
name_fr |
varchar(200) |
name_nl |
varchar(200) |
description |
text |
description_en |
text |
description_de |
text |
description_fr |
text |
description_nl |
text |
short_description |
text |
short_description_en |
text |
short_description_de |
text |
short_description_fr |
text |
short_description_nl |
text |
你在 LANGUAGES 中定义的每种语言都会为每个注册字段获得自己的列。
问题:空的翻译字段
现在你已经有了表结构,但每个 _de、_fr 和 _nl 列都是空的。如果你有 500 个产品,每个产品有 3 个可翻译字段和 3 种目标语言,那就是 4,500 个等待填充的空字段。
手动翻译这些内容是不现实的。即使使用专业翻译服务,你也需要导出数据、发送出去、等待交付,然后将结果导入回来。对于小团队或独立开发者来说,这通常意味着该功能永远不会上线。
这就是 TranslateBot 发挥作用的地方。
使用 TranslateBot 自动化翻译
TranslateBot 的 translate 管理命令可以使用 AI 填充所有这些空字段。首先配置你的 API 密钥:
# settings.py
TRANSLATEBOT_API_KEY = os.getenv("OPENAI_API_KEY")
TRANSLATEBOT_MODEL = "gpt-4o-mini" # Fast and cost-effective
然后将所有注册的模型翻译为目标语言:
python manage.py translate --target-lang de --models
这一条命令就能找到所有在 django-modeltranslation 中注册的模型,识别目标语言中为空的字段,并用 AI 生成的翻译填充它们。
翻译特定模型
如果你只想翻译产品而不是分类:
python manage.py translate --target-lang de --models Product
或者翻译多个特定模型:
python manage.py translate --target-lang de --models Product Category
使用试运行预览
在写入数据库之前始终先预览:
python manage.py translate --target-lang de --models --dry-run
这会准确显示将要翻译的内容,而不会修改任何记录。
重新翻译现有内容
默认情况下,TranslateBot 会跳过已有翻译的字段。要覆盖现有翻译(例如,在改进 AI 模型或添加上下文之后):
python manage.py translate --target-lang de --models --overwrite
一次翻译所有语言
如果你省略 --target-lang 并且在设置中定义了 LANGUAGES,TranslateBot 会翻译为所有配置的语言:
python manage.py translate --models
翻译管道的工作原理
以下是运行翻译命令时发生的事情。
-
发现。 TranslateBot 查询 django-modeltranslation 的注册表,找到所有注册的模型及其可翻译字段。
-
源语言检测。 对于每条记录,它读取源内容。首先检查基础字段(例如
name),然后回退到第一个有内容的语言特定字段(例如name_en)。没有源内容的记录会被跳过。 -
批处理。 记录被分组为批次并发送到 AI 提供商。这使 API 调用保持高效并避免触及速率限制。
-
翻译。 每个批次使用配置的 AI 模型进行翻译。你可以使用 LiteLLM 支持的任何 LLM 提供商(OpenAI、Anthropic、Google、Azure 以及许多其他提供商)或 DeepL。
-
原子写入。 一次翻译运行的所有数据库更新都包装在一个事务中。如果出现任何问题,如 API 错误或数据库约束违反,不会保存任何部分数据。要么全部成功,要么全部失败。
完整工作流示例
以下是从模型定义到翻译内容的完整示例,使用电商 Product 模型。
定义模型
# shop/models.py
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=200)
description = models.TextField()
short_description = models.CharField(max_length=500, blank=True)
price = models.DecimalField(max_digits=10, decimal_places=2)
sku = models.CharField(max_length=50, unique=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
注册翻译
# shop/translation.py
from modeltranslation.translator import register, TranslationOptions
from .models import Product
@register(Product)
class ProductTranslationOptions(TranslationOptions):
fields = ('name', 'description', 'short_description')
运行迁移
python manage.py makemigrations shop
python manage.py migrate
添加翻译上下文(可选)
在项目根目录创建一个 TRANSLATING.md 文件,为 AI 提供产品领域的上下文:
# Translation Context
## About This Project
E-commerce store for outdoor sports equipment.
## Terminology
- "trail runners" refers to trail running shoes, not people
- Keep brand names (Nike, Salomon, Arc'teryx) untranslated
- "Gore-Tex" is a brand name, do not translate
## Tone
- Use informal "du" form in German
- Product descriptions should sound enthusiastic but not exaggerated
翻译
# Preview first
python manage.py translate --target-lang de --models Product --dry-run
# Apply translations
python manage.py translate --target-lang de --models Product
输出:
Translating Product model fields to German (de)...
Found 142 products with untranslated fields
Translating batch 1/15...
Translating batch 2/15...
...
Translating batch 15/15...
Successfully translated 142 products
在管理后台中验证
打开 Django 管理后台并进入任意产品。你将看到翻译字段已填充:
- Name [de]: Ultraleichte Trail-Laufschuhe
- Description [de]: Diese leichten Trail-Laufschuhe bieten hervorragenden Grip...
- Short description [de]: Leicht, schnell und griffig auf jedem Untergrund.
对每种目标语言重复此操作:
python manage.py translate --target-lang fr --models Product
python manage.py translate --target-lang nl --models Product
或一次翻译所有语言:
python manage.py translate --models Product
最佳实践
在生产环境中运行批量翻译之前,请备份数据库。TranslateBot 使用原子事务,因此失败的运行不会留下部分数据。但备份可以在翻译质量不符合预期时让你有回退的方法。
# PostgreSQL example
pg_dump mydb > backup_before_translation.sql
先使用试运行。在对新模型或新语言应用翻译之前,始终使用 --dry-run 运行。检查输出以确保源内容被正确检测,翻译结果看起来合理。
对于大型数据库,一次翻译一个模型。这样更容易审查结果,并在需要时重新运行特定模型。
python manage.py translate --target-lang de --models Product
python manage.py translate --target-lang de --models Category
python manage.py translate --target-lang de --models Article
添加翻译上下文。包含特定领域术语和语气指南的 TRANSLATING.md 文件可以显著提高翻译质量。这对于医学、法律或技术产品等专业领域尤为重要。
保持源语言字段填充。TranslateBot 从基础字段或源语言字段读取。确保你的数据录入工作流程始终填充源语言。空的源字段意味着空的翻译。
结合 PO 文件翻译以实现完全覆盖。同时翻译代码字符串和数据库内容:
# Static strings in code and templates
python manage.py makemessages -l de -l fr -l nl
python manage.py translate
python manage.py compilemessages
# Dynamic content in the database
python manage.py translate --models
这样你的用户看到的每个字符串,无论是来自模板还是数据库,都已被翻译。