如果你曾经发布过多语言的 Django 应用,你一定知道这个流程。你用 gettext() 包装字符串,运行 makemessages,打开一个包含数百条记录的 .po 文件,然后逐行翻译。对于两种语言和五十个字符串,这还可以忍受。但如果是六种语言和五百个字符串,那就是一整天再也找不回来的工作时间。
本指南从头到尾介绍 Django 的国际化(i18n)流程,解释它在哪些环节会出问题,并展示如何使用 AI 驱动的翻译来自动化最痛苦的部分。
标准的 Django i18n 工作流程
Django 内置的 i18n 系统设计得很好。核心流程如下:
步骤 1:在代码中标记需要翻译的字符串,包括 Python 代码和模板:
from django.utils.translation import gettext as _
def dashboard(request):
welcome = _("Welcome back, %(name)s!") % {"name": request.user.first_name}
return render(request, "dashboard.html", {"welcome": welcome})
{% load i18n %}
<h1>{% trans "Account Settings" %}</h1>
<p>{% blocktrans %}You have {{ count }} unread messages.{% endblocktrans %}</p>
步骤 2:将字符串提取到 .po 文件中:
python manage.py makemessages -l de -l fr -l nl
这会扫描整个代码库,为每种语言生成一个 .po 文件,包含每个可翻译的字符串:
#: myapp/views.py:4
msgid "Welcome back, %(name)s!"
msgstr ""
#: templates/dashboard.html:2
msgid "Account Settings"
msgstr ""
步骤 3:手动翻译每个空的 msgstr。
步骤 4:编译完成的 .po 文件为二进制 .mo 文件:
python manage.py compilemessages
步骤 1、2 和 4 很快。步骤 3 是流程崩溃的地方。
为什么手动翻译无法扩展
一个典型的 Django 应用大约有 200 到 2,000 个可翻译的字符串。乘以目标语言的数量,你面对的就是一项巨大的时间投入。
这不是空谈。在一个知名的 Django 论坛帖子中,一位开发者报告称每个 .po 文件的手动翻译花费了 8 个小时以上。一位 Django 核心贡献者描述了花费 10 多个小时将社区提交的翻译整合到一个版本中,主要是审查、格式修正和修复损坏的占位符。
问题会随着时间不断累积:
- 占位符会损坏。 把像
Welcome, %(name)s!这样的字符串复制到 Google 翻译中,你经常会得到Willkommen, %(Name)s!或Bienvenue, %(nom)s!。这种微妙的损坏会导致运行时崩溃。 - 一致性会漂移。 没有术语表,"dashboard" 在一个文件中被翻译为 "Instrumententafel",在另一个文件中被翻译为 "Dashboard"。三个月后,没人记得哪个是故意的。
- 迭代会增加字符串。 每个功能分支都会添加新的可翻译字符串。即使你上个季度付费做了一次完整的翻译,现在你的
.po文件中散布着 40 个未翻译的条目。 - AI 助手只能帮一次。 你可以把
.po文件粘贴到 ChatGPT 或 Claude 中获得不错的结果。但下一个迭代,当出现 15 个新字符串时,你得从头开始提示,重新翻译整个文件,并希望它与已有的翻译保持一致。
根本原因是翻译被当作一次性事件,而不是一个增量的、可重复的过程。
使用 AI 自动化翻译
想法很简单:不再由人工打开每个 .po 文件填写 msgstr 值,而是由工具读取文件,将未翻译的字符串发送到 AI 模型或翻译 API,将结果写回,并保留其他所有内容(注释、文件结构、占位符、复数形式)。
TranslateBot Django 是一个开源包,正是做这件事的。它集成到 Django 的管理命令系统中,因此适合你已有的工作流程。
分步设置
1. 安装包
pip install translatebot-django
或者,如果你使用 uv(推荐):
uv add --dev translatebot-django
将其作为开发依赖安装是有意为之的。你只在生成翻译时需要 TranslateBot,生产环境运行时不需要。
2. 添加到 INSTALLED_APPS
# settings.py
INSTALLED_APPS = [
# ...
"translatebot_django",
]
3. 配置你的 AI 提供商
# settings.py
import os
TRANSLATEBOT_API_KEY = os.getenv("OPENAI_API_KEY")
TRANSLATEBOT_MODEL = "gpt-4o-mini"
TranslateBot 底层使用 LiteLLM,这意味着你可以通过更改一个字符串来切换 100 多个模型中的任何一个:
| 提供商 | TRANSLATEBOT_MODEL 值 |
|---|---|
| OpenAI | gpt-4o-mini, gpt-4o |
| Anthropic | claude-sonnet-4-5-20250929 |
gemini/gemini-2.5-flash |
|
| Azure OpenAI | azure/gpt-4o-mini |
| DeepL | 改用 TRANSLATEBOT_PROVIDER = "deepl" |
对于 DeepL,安装额外依赖:pip install translatebot-django[deepl]。DeepL 的免费套餐每月提供 500,000 个字符的免费额度,对大多数中小型项目来说足够了。
4. 定义你的语言
# settings.py
LANGUAGES = [
("en", "English"),
("de", "German"),
("fr", "French"),
("nl", "Dutch"),
("ja", "Japanese"),
]
5. 运行翻译
python manage.py translate
就这样。TranslateBot 扫描你的项目中的 .po 文件,识别未翻译的条目,以优化的批次将它们发送到配置的 AI 模型,并将结果写回。已有的翻译保持不变。
翻译单一语言:
python manage.py translate --target-lang nl
输出如下:
Translating to Dutch (nl)...
Found 42 strings to translate
Translating batch 1/2...
Translating batch 2/2...
Successfully translated 42 strings
6. 照常编译
python manage.py compilemessages
你的完整工作流程现在是:
python manage.py makemessages -l de -l fr -l nl -l ja
python manage.py translate
python manage.py compilemessages
三条命令。每种语言。每次迭代。
增量设计
对于可重复工作流来说,最重要的功能是增量翻译。TranslateBot 只翻译 msgstr 为空的条目。如果你有 500 个字符串,本次迭代新增了 15 个,只有这 15 个会被发送到 API。
这在实际中很重要:
- 成本。 你只为新字符串付费,而不是整个文件。
- 速度。 翻译 15 个字符串只需几秒,而不是几分钟。
- 稳定性。 你已经审查和批准的翻译永远不会被覆盖(除非你明确传递
--overwrite)。
占位符安全
Django 使用多种占位符格式:%(name)s、%s、%d、{0}、{name},以及内联 HTML 标签如 <strong> 或 <a href="...">。如果其中任何一个在翻译中被破坏,你就会遇到运行时错误或损坏的标记。
TranslateBot 指示 AI 模型保留所有占位符格式并验证输出。像这样的字符串:
Welcome to %(site_name)s! You have <strong>%(count)d</strong> new messages.
翻译成荷兰语为:
Welkom bij %(site_name)s! Je hebt <strong>%(count)d</strong> nieuwe berichten.
每个占位符都完好无损地保留下来。
使用 TRANSLATING.md 控制质量
AI 模型在理解上下文时翻译得更好。TranslateBot 会在你的项目根目录中查找 TRANSLATING.md 文件,并将其内容包含在每个翻译请求中。
# Translation Context
## About This Project
A B2B project management tool for construction companies.
## Terminology
- "project" means a construction project, not a software project
- "plan" means a building plan/blueprint, not a subscription plan
- Keep "Gantt chart" as-is in all languages
## Tone
- German: use formal "Sie" form (business context)
- French: use formal "vous" form
- Dutch: use informal "je" form
## Do Not Translate
- Brand name: "BuildFlow"
- Feature names: "SmartSchedule", "CostTracker"
这个文件与你的代码一起进行版本控制,因此整个团队共享相同的翻译上下文。你也可以为具有专业术语的应用放置各自的 TRANSLATING.md 文件。医疗记录模块和计费模块可以各有自己的术语表。
提交前预览
--dry-run 标志可以准确显示将要翻译的内容,而不会进行任何 API 调用或修改文件:
python manage.py translate --target-lang fr --dry-run
Found 15 untranslated entries
Dry run mode: skipping LLM translation
Would translate 'Welcome to our site'
Would translate 'Hello, %(name)s!'
...
Dry run complete: 15 entries would be translated
这在进行大规模翻译运行之前,或者在新团队成员想要在承担 API 费用之前了解命令的功能时非常有用。
CI/CD 集成
如果不加以强制,翻译变得过时是不可避免的。TranslateBot 包含一个专为 CI 流水线设计的 check_translations 管理命令:
# .github/workflows/ci.yml
jobs:
translations:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
with:
enable-cache: true
- run: uv python install
- run: uv sync --frozen
- name: Install gettext
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends gettext
- name: Check translations
run: uv run python manage.py check_translations --makemessages
--makemessages 标志会先运行 makemessages -a --no-obsolete,确保 .po 文件反映当前源代码后再进行检查。如果有任何未翻译或模糊的条目,命令将以退出码 1 退出并使构建失败:
locale/de/LC_MESSAGES/django.po: 2 untranslated, 0 fuzzy
locale/nl/LC_MESSAGES/django.po: 0 untranslated, 1 fuzzy
CommandError: Translation check failed
典型的开发者工作流程变为:
- 在功能分支中添加新的可翻译字符串。
- CI 因为这些字符串未翻译而失败。
- 在本地运行
python manage.py translate。 - 提交更新后的
.po文件。 - CI 通过。
翻译永远不会悄悄地失去同步。
翻译数据库内容
如果你的应用在数据库中存储可翻译的内容(产品名称、博客文章标题、分类标签),TranslateBot 也可以与 django-modeltranslation 集成:
pip install translatebot-django[modeltranslation]
# Translate all registered model fields
python manage.py translate --target-lang de --models
# Translate specific models only
python manage.py translate --target-lang de --models Product Category
同样的增量逻辑适用:只有目标语言值为空的字段才会被翻译。
成本比较
最常见的问题之一是 AI 翻译与其他方案相比是否具有成本效益。以下是一个拥有 500 个可翻译字符串、5 种语言的项目的粗略比较:
| 方案 | 预估成本 | 时间投入 |
|---|---|---|
| 手动(开发者时间) | 零直接费用,20-40+ 小时 | 非常高 |
| 专业翻译服务 | $500-2,000+ | 低(但周转慢) |
| SaaS 本地化平台 | $50-200/月 | 中等 |
| TranslateBot + GPT-4o-mini | ~$0.05(一次性) | 几分钟 |
| TranslateBot + DeepL Free | $0(每月最多 500k 字符) | 几分钟 |
| TranslateBot + Claude/GPT-4o | ~$0.30(一次性) | 几分钟 |
数字会随字符串数量和目标语言的不同而变化,但数量级的差异是一致的。对于持续维护(翻译每次迭代新增的 20-50 个字符串),AI 成本基本为零。
最佳实践
从 --dry-run 开始。在你第一次真正翻译运行之前,预览将会发生什么。这能建立信心并尽早发现配置问题。
翻译前提交 .po 文件。如果出了问题,git checkout 可以立即回到干净的状态。
从第一天就编写 TRANSLATING.md。即使是一个简短的文件,包含你的项目描述和一些术语规则,也能显著提高翻译质量。
将 check_translations 添加到 CI。这一个步骤就能防止最常见的 i18n 失败模式:标记为翻译但从未实际翻译的字符串。
使用 gpt-4o-mini 或 DeepL 以提高成本效率。将 GPT-4o 或 Claude 等高级模型留给精度要求更高的项目,如营销文案、法律文本或领域特定术语。
审查关键字符串。AI 翻译对大多数 UI 文本来说已经足够好,但对于任何具有法律约束力的、安全关键的或在高风险场景中面向客户的内容,请让母语人士进行审查。
从数小时到数秒
Django 的 i18n 框架在提取和编译翻译方面表现出色。差距一直在翻译步骤本身——跨多种语言填写数百个 msgstr 值这一繁琐且容易出错的工作。
TranslateBot 弥补了这个差距。安装它,指向一个 AI 提供商,运行一条命令。新字符串被翻译。已有字符串保持不变。占位符完好无损。CI 捕获任何遗漏。
你的 .po 文件不再是一件苦差事,而只是构建的另一个部分。
pip install translatebot-django
在 translatebot.dev 开始使用。