Django 自带的国际化体系在后端模板中工作得很好,但一旦按钮文本、确认弹窗这类内容由前端 JavaScript 动态生成,{% trans %} 标签就无能为力了。要让 JS 里的字符串也能跟随用户语言变化,需要借助 Django 提供的 JavaScript 国际化目录机制。本文将完整讲解这套方案的配置与使用方法。

一、为什么后端 i18n 管不到 JavaScript
先理解问题的根源。Django 的翻译机制基于 GNU gettext,工作流程是:在模板中标记待翻译字符串,通过 makemessages 提取到 po 文件,翻译后编译成 mo 文件,请求到达时根据 LANGUAGE_CODE 或会话中的语言设置返回对应译文。这一切都发生在服务端渲染阶段。
而 JavaScript 是在浏览器中执行的。如果按钮文本写成 document.getElementById('btn').textContent = '确定',浏览器拿到的就是硬编码字符串,服务端根本不知道这个字符串需要翻译。即使你在模板里做了语言判断,JS 代码一旦缓存或复用,语言信息也会丢失。
Django 的解决方案是提供一个专门的视图,把 gettext 函数和当前语言对应的翻译字典打包成一份 JavaScript 文件,让前端代码可以像在 Python 中一样调用翻译函数。这就是所谓的 JavaScript Catalog(JS 翻译目录)。
二、配置 JavaScript 国际化目录
第一步是在项目的 urls.py 中挂载 i18n 视图。Django 提供了一个现成的 JavaScriptCatalog 视图,配置非常简单:
from django.urls import path
from django.views.i18n import JavaScriptCatalog
urlpatterns = [
# packages 参数指定从哪些应用中收集翻译字符串
path('jsi18n/', JavaScriptCatalog.as_view(packages=['myapp']), name='javascript-catalog'),
]
packages 参数非常重要,它告诉 Django 从哪些应用的 locale 目录中收集翻译条目。如果你希望全局可用,可以传入 settings 所在的包。需要注意的是,被列出的应用必须存在对应的 locale 目录,否则目录文件会是空的。
第二步是在基础模板中引入这份脚本,注意要放在你自己的 JS 代码之前:
<script src="{% url 'javascript-catalog' %}"></script>
<script src="{% static 'js/app.js' %}"></script>
同时确认 settings.py 中已完成国际化基础配置,包括 USE_I18N = True、LOCALE_PATHS 指向 locale 目录,以及 django.middleware.locale.LocaleMiddleware 已加入中间件列表且位置在 SessionMiddleware 之后。
三、在 JavaScript 中调用翻译函数
引入目录脚本后,前端全局就可以使用若干翻译函数。最常用的是 gettext,它接收一个字符串并返回译文:
// 简单翻译
document.getElementById('submitBtn').textContent = gettext('确定');
// 带参数插值,使用 interpolate
var msg = interpolate(gettext('欢迎你,%s'), ['张三']);
alert(msg);
// 处理单复数
var count = 3;
var text = ngettext('%s 个文件待删除', '%s 个文件们待删除', count);
alert(interpolate(text, [count]));
ngettext 对中文场景用处不大,但如果你的站点同时支持英文、俄文等有单复数区分的语言,它是必不可少的。对于命名参数插值,interpolate 还支持对象形式:
var fmt = gettext('用户 %(name)s 于 %(time)s 提交了订单');
var text = interpolate(fmt, {name: '李四', time: '10:30'}, true);
// 第三个参数为 true 时启用命名插值
这些函数的翻译数据全部来自第二步加载的目录文件,因此切换语言后需要重新加载页面或重新请求该脚本,才能拿到新语言的译文。
四、提取并翻译 JS 中的字符串
JS 文件里的字符串同样要进入 po 文件才能被翻译。在项目根目录执行:
django-admin makemessages -l zh_Hans -d djangojs
注意这里使用了 -d djangojs 域,JS 相关的翻译会生成在 djangojs.po 中,与后端的 django.po 互不干扰。Django 会扫描所有 JS 文件(默认扩展名包括 .js,也支持 .jsx 等,可通过 xgettext 的参数调整),把 gettext、ngettext 调用中的字符串提取出来。
翻译完成后执行编译命令:
django-admin compilemessages
编译生成的 djangojs.mo 会被 JavaScriptCatalog 视图读取并打包成 JS 数据。一个小技巧是:目录脚本带有缓存,调试时如果修改了翻译却看不到变化,可以在浏览器中强制刷新,或者在开发环境给 URL 加时间戳参数绕过缓存。
五、动态切换语言与常见问题排查
如果站点支持用户在前端切换语言,标准做法是使用 Django 提供的 set_language 重定向视图,通过一个 POST 表单提交语言参数,随后整页刷新,目录脚本也会以新语言重新生成。切莫只改前端某个变量而不刷新,目录文件中的翻译数据在加载时就固定了。
排查问题时可以按以下顺序检查:第一,直接在浏览器访问 /jsi18n/ 路径,确认返回的脚本中包含你期望的翻译条目;第二,检查 po 文件中该字符串是否已翻译并编译成功;第三,确认字符串完全匹配,包括空格和标点,gettext 匹配是精确匹配;第四,确认 packages 参数覆盖了字符串所在的应用。
还有一点容易踩坑:如果多个应用之间存在同名翻译字符串但译文不同,目录会按应用顺序覆盖。建议把前端公共字符串统一放到一个专门的应用中管理,避免冲突。按照这套流程做下来,前端按钮、弹窗、提示文本就能和后端模板保持完全一致的多语言体验了。
Django国际化JavaScript i18n前端翻译修改时间:2026-09-01 06:54:59