news 2026/9/9 13:02:09

ToolJet 前端本地化(L10n)贡献指南:从零为产品新增一套界面语言翻译

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ToolJet 前端本地化(L10n)贡献指南:从零为产品新增一套界面语言翻译

ToolJet 前端本地化(L10n)贡献指南:从零为产品新增一套界面语言翻译

【免费下载链接】ToolJetOpen-source foundation of ToolJet AI - the enterprise app generation platform for internal tools, dashboards, business applications, workflows and AI agents. Build visually, from a prompt, or from Claude Code, Codex and Cursor over MCP 🚀项目地址: https://gitcode.com/GitHub_Trending/to/ToolJet

本文对应官方贡献指南 docs/docs/contributing-guide/l10n.md,带你完整走一遍 ToolJet 的本地化流程。作为面向全球用户的开源低代码平台,ToolJet 的前端界面文本全部由 JSON 翻译文件驱动,社区贡献者只需要“建文件、复制模板、逐条翻译、注册语言”四步,就能让整个产品界面呈现一门新语言。读完本文,你将掌握翻译文件的目录结构与 JSON 命名空间约定、languages.json 注册机制、i18next 的加载与回退原理,以及如何通过环境变量把某门语言设为实例默认语言。

ToolJet 的本地化机制概览

ToolJet 前端的国际化(i18n)并不是一个黑盒,而是基于成熟的i18next + react-i18next + i18next-http-backend组合实现的按需加载方案。三条核心链路在源码中可以逐一验证:

  1. 翻译资源:所有语言文案存放在 frontend/assets/translations 目录下,每门语言一个<语言码>.json文件,另有一个languages.json维护“支持语言清单”。
  2. 运行时加载:前端启动入口 frontend/src/index.jsx 中完成i18n初始化,通过loadPath拼接assets/translations/{{lng}}.json,让 i18next 在运行期按需 HTTP 拉取对应语言的 JSON。
  3. 界面切换:语言选择组件 frontend/src/_components/LanguageSelection.jsx 从languages.json读取清单、渲染语言弹窗,并在用户选中后调用i18n.changeLanguage(lang.code)实现全局即时切换。

对贡献者来说,你不需要改动任何 React 组件或构建逻辑——本地化的全部工作都集中在 JSON 资源文件上。这也是官方将其定位为“最容易上手的贡献方式”的原因。

frontend |-- assets | |-- translations | | |-- languages.json # 语言注册清单 | | |-- en.json # 英文(模板基准) | | |-- fr.json # 法语 | | |-- ...

docs/static/img/l10n/files.png直观展示了该目录的真实组织形态:

翻译文件命名与目录约定

本地化工作的第一步是“按规范命名新语言文件”,文件位置与命名规则如下:

  • 目录frontend/assets/translations(仓库根目录下frontend包内的静态资源目录);
  • 文件名:必须是语言代码.json,语言代码遵循ISO 639-1标准(两位小写字母);
  • 清单:同时维护languages.json,只有登记在清单中的语言才会出现在产品界面的语言选择器里。

当前仓库中已内置了 9 门语言的翻译文件(可实际查看 frontend/assets/translations):

语言文件语言文件
英语en.json乌克兰语uk.json
法语fr.json俄语ru.json
西班牙语es.json德语de.json
意大利语it.json中文zh.json
印度尼西亚语id.json

提示:官方文档示例以法语(fr,ISO 639-1 中 French 的代码)展开,本文后续步骤保持一致。

新增一门语言的四个步骤

第 1 步:创建<languagecode>.json翻译文件

进入frontend/assets/translations目录,新建一个以目标语言代码命名的 JSON 文件。以法语为例,创建fr.jsonfr即法语的语言代码):

frontend/assets/translations/fr.json # 新建

第 2 步:复制en.json作为翻译模板

打开现有的en.json,将全部内容原样复制到新文件中。en.json是 ToolJet 的“母本”语言文件,其中包含了全部界面文案的键(key),键层级多、条目多(当前仓库中该文件约 1000+ 行),因此永远不要把英文翻译文件当成可丢弃的临时文件——它是新增语言唯一的键来源。官方文档同样强调:新语言文件必须以en.json为起点逐条翻译,避免漏键。

docs/static/img/l10n/en.png展示了en.json的真实内容形态(顶部是globals命名空间中的通用按钮文案):

第 3 步:逐条翻译键值

复制完成后,在fr.json中把每个 key 右侧的英文值替换为对应的法语文本。文件采用嵌套命名空间结构组织,例如抽取自仓库真实en.json的开头部分:

{ "globals": { "readDocumentation": "Read documentation", "cancel": "Cancel", "save": "Save", "savechanges": "Save changes", "execute": "Execute", "edit": "Edit", "search": "Search", "add": "Add", "delete": "Delete" }, "errorBoundary": "Something went wrong.", "viewer": "Sorry!. This app is under maintenance", "app": { "updateAvailable": "Update available", "newVersionReleased": "A new version of ToolJet has been released.", "readReleaseNotes": "Read release notes & update", "skipVersion": "Skip this version" } }

真实文件共有 25 个顶级命名空间,覆盖全局按钮、错误页、登录注册、编辑器、头部导航、首页、工作流仪表盘、组件管理、日期选择器等全部界面区域:

globals · errorBoundary · viewer · app · stripe · openApi · slack · googleSheets zendesk · profile · verificationSuccessPage · loginSignupPage · editor · header homePage · workflowsDashboard · confirmationPage · onBoarding · redirectSso · oAuth2 widgetManager · widget · leftSidebar · datepicker · notifications

翻译时的三条纪律

  1. 只改值、不动键:key 是前端代码里t(...)查表的“身份证”,改名会导致文案丢失并回退到英文;
  2. 保留插值占位符:部分值包含{{xxx}}模板变量(例如 Slack 授权文案中的{{whiteLabelText}},见 en.json 的slack命名空间),翻译时必须原样保留,否则运行期无法注入动态内容;
  3. 尽量同步最新键:仓库里en.json是键最全的母本(25 个顶级命名空间),而部分旧语言文件缺少后来新增的命名空间(如法语/德语文件目前为 21 个)。得益于 i18next 的fallbackLng: 'en'配置(见 frontend/src/index.jsx),缺失的键会自动显示英文而非空白——但若要保证完整体验,翻译时应以最新en.json为基准补齐。

第 4 步:在languages.json中注册语言

翻译完成后,还需要把语言登记到 frontend/assets/translations/languages.json。官方要求为每种语言添加一个包含三个键值对的对象

字段含义示例(法语)
lang语言名称(英文表述,用于与语言列表展示)"French"
code语言代码(须与 JSON 文件名一致)"fr"
nativeLang该语言的母语名称(用于语言选择器中以母语展示)"Français"

languages.json整体结构是一个languageList数组。以“英语 + 法语”为例:

{ "languageList": [ { "lang": "English", "code": "en", "nativeLang": "English" }, { "lang": "French", "code": "fr", "nativeLang": "Français" } ] }

仓库当前真实的注册清单即为该结构的完整范例,读者可打开 languages.json 对照:

{ "languageList": [ { "lang": "English", "code": "en", "nativeLang": "English" }, { "lang": "French", "code": "fr", "nativeLang": "Français" }, { "lang": "Spanish", "code": "es", "nativeLang": "Español" }, { "lang": "Italian", "code": "it", "nativeLang": "Italiano" }, { "lang": "Indonesian", "code": "id", "nativeLang": "Bahasa Indonesia" }, { "lang": "Ukrainian", "code": "uk", "nativeLang": "Українська" }, { "lang": "Russian", "code": "ru", "nativeLang": "Русский" }, { "lang": "German", "code": "de", "nativeLang": "Deutsch" }, { "lang": "Chinese", "code": "zh", "nativeLang": "Chinese" } ] }

完成以上四步后,新语言即已可被 ToolJet 前端识别与加载。docs/static/img/l10n/list.png展示了语言选择器的实际交互形态——注意每条语言同时展示了lang(英文名)与nativeLang(母语名)两行文本,这与 LanguageSelection.jsx 的渲染逻辑一一对应:

源码视角:新语言如何被前端“看见”

为了让新加的翻译真正生效,需要理解前端三条调用链。以下均可直接在仓库中查阅源码验证。

① i18next 的初始化与加载路径

在 frontend/src/index.jsx 中,应用从服务端拉取的public_config读取LANGUAGE配置作为初始语言(缺省en),然后初始化 i18next:

const language = config.LANGUAGE || 'en'; const path = config?.SUB_PATH || '/'; i18n .use(Backend) .use(initReactI18next) .init({ load: 'languageOnly', fallbackLng: 'en', lng: language, backend: { loadPath: `${path}assets/translations/{{lng}}.json`, }, });

几个关键配置项的含义:

  • load: 'languageOnly':只按两位语言代码匹配,例如浏览器上报en-US也只会去加载en.json,不会尝试加载en-US.json
  • fallbackLng: 'en':当前语言缺失某条文案时自动回退英文,这正是“漏键不报错、只显示英文”的兜底机制;
  • loadPath:基于 HTTP 的后端按需加载模板,{{lng}}在运行期替换为语言代码(如fr),因此只要 JSON 文件放在该目录、命名正确,前端无需重新注册资源映射
  • pathSUB_PATH):当 ToolJet 部署在子路径(sub-path)下时,翻译文件同样走子路径前缀,保证静态资源可访问。

② 语言选择器的清单读取与切换

frontend/src/_components/LanguageSelection.jsx 是整个本地化 UI 的核心实现,包含三段逻辑:

  • 拉取清单:组件挂载时fetch('/assets/translations/languages.json'),将languageList存入引用;
  • 当前语言回显:以i18n.language(缺省en)在清单中查找当前语言,找不到则回退到英文条目;
  • 实时切换onLanguageSelection中调用i18n.changeLanguage(lang.code)完成全局语言切换,界面上所有经useTranslation()t(...)取值的文案会立即重渲染。

组件还提供了语言搜索能力:用户可按langnativeLangcode的前缀过滤语言列表(LanguageSelection.jsx),例如输入fr即可快速定位法语。

③ 页面中“取文案”的方式

业务组件通过react-i18nextuseTranslation()获取t函数来读取翻译,并支持“命名空间 key + 英文兜底默认值”的写法。例如语言选择器自身的标题:

const { t } = useTranslation(); // 读取 header.languageSelection.changeLanguage,缺省英文 "Change language" t('header.languageSelection.changeLanguage', 'Change language');

对应的 key 定义位于en.jsonheader.languageSelection命名空间:

"header": { "languageSelection": { "changeLanguage": "Change language", "searchLanguage": "Search language" } }

这解释了为什么新增语言的 JSON 文件必须保留与en.json完全一致的键结构——t()的路径就是 JSON 里的嵌套路径,任何一个层级写错都会导致取不到值而回退英文。

设置部署实例的默认语言(LANGUAGE 环境变量)

除了用户在界面手动切换外,ToolJet 还允许自托管实例的管理员通过环境变量设定全站默认语言。在官方部署环境变量文档 docs/docs/setup/env-vars.md 中给出了如下说明:

变量说明
LANGUAGE期望的默认语言代码(LANGUAGE_CODE

例如将实例默认界面语言设为法语,设置LANGUAGE=fr即可;运行时前端启动逻辑会读取该值作为i18n.initlng(对应 frontend/src/index.jsx 的config.LANGUAGE)。该变量与languages.json中的code必须一一对应,未登记的语言代码不会命中任何翻译文件。

注意:官方文档注明,云版本(cloud)上不开放自定义默认语言的选项,此项仅适用于可自行控制环境变量的自托管部署。

贡献前自检清单与常见陷阱

向官方仓库提交本地化改动前,建议逐项核对:

  1. 文件名:是否使用 ISO 639-1 两位语言代码,如fr.jsonzh.json
  2. 键完整性:新文件是否以当前最新en.json为基准整体复制,再逐条翻译;
  3. JSON 合法性:翻译文本中的引号、换行需要正确转义,文件必须是合法 JSON(可先用JSON.parse校验,避免拖拽符号破坏结构);
  4. 键未被改写t('header.languageSelection.changeLanguage', ...)这类调用路径对应的嵌套结构未被改动;
  5. 插值占位符保留{{whiteLabelText}}一类模板变量保持原样;
  6. 注册清单languages.jsonlanguageList中已添加含lang/code/nativeLang的对象,且code与文件名严格一致;
  7. 本地验证:本地启动frontend后打开语言选择器,确认新语言出现在列表中、切换后无大面积英文回退(个别回退属于“键未翻译”,应在语言文件中补齐)。

关于“浏览器语言自动检测”的现状说明

官方文档正文中有一段被注释掉(HTML 注释区块内)的描述:ToolJet 会自动检测浏览器默认语言并切换,若浏览器语言不可用则回退英文。在本文所依据的仓库版本中,该行为尚未作为正式文档承诺对外发布(相关章节仍处于注释状态),且前端初始语言实际由服务端下发的LANGUAGE配置决定。因此社区贡献者在本地开发时可先通过语言选择器手动切换验证,不必依赖浏览器自动检测。

小结

ToolJet 的本地化贡献流程可以被精确概括为一条命令链级别的操作闭环:

  1. 在 frontend/assets/translations 创建语言代码.json
  2. 复制 en.json 全部内容作为翻译母本;
  3. 按“只改值、不动键、保留插值占位符”的原则逐条翻译;
  4. 在 languages.json 中追加{ lang, code, nativeLang }注册项。

后端兜底由 frontend/src/index.jsx 的 i18next 初始化负责(缺省英文、HTTP 按需加载),界面展示由 LanguageSelection.jsx 负责(清单拉取、搜索、changeLanguage即时切换)。整个过程中无需触碰任何组件源码,风险低、易评审,是进入 ToolJet 社区贡献的最佳切入点之一。

【免费下载链接】ToolJetOpen-source foundation of ToolJet AI - the enterprise app generation platform for internal tools, dashboards, business applications, workflows and AI agents. Build visually, from a prompt, or from Claude Code, Codex and Cursor over MCP 🚀项目地址: https://gitcode.com/GitHub_Trending/to/ToolJet

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/9 12:59:39

方舟属性计算神器:ARKStatsExtractor截图识别与反推全攻略

简介&#xff1a;这是一款面向《方舟&#xff1a;生存进化》玩家的免费辅助工具ARKStatsExtractor&#xff0c;目标用户为热衷驯养、繁殖与优化属性的玩家。工具通过提取游戏内生物升级时的隐藏统计数据&#xff0c;实现繁殖数值整理、动物库管理、属性排序对比、血统书查看以及…

作者头像 李华
网站建设 2026/9/9 12:59:28

华为S系列交换机缺省账号密码速查与首次登录配置指南

1. S系列交换机的缺省帐号与密码速查 很多刚接触华为S系列交换机的朋友&#xff0c;第一台设备到手后做的第一件事往往是插上Console线、打开终端软件、敲回车&#xff0c;然后对着屏幕上冒出来的“Password”或者“Please configure the login password”发呆。这太正常了&…

作者头像 李华
网站建设 2026/9/9 12:57:43

讯飞声纹验证SDK接入实战:从原理到踩坑全解析

简介&#xff1a;这是科大讯飞推出的Android端声纹验证SDK&#xff0c;面向需要在移动应用中集成声纹识别身份验证的开发者。压缩包共104个文件&#xff0c;包体约10.33MB&#xff0c;包含Java源码、XML配置、SO动态库、JAR依赖库、WAV音频样本、语法文件及说明文档等&#xff…

作者头像 李华
网站建设 2026/9/9 12:56:48

ECC一词多义:从内存纠错到SAP年结与芯片MBIST

1. 从ECC说起&#xff1a;这个三个字母在IT圈到底指什么 搞技术的人对“ECC”这个词应该都不陌生&#xff0c;但你要真问一句“ECC是什么”&#xff0c;十个人能给你说出七八种答案。在数据库领域&#xff0c;SAP ECC是那套经典的ERP核心组件&#xff1b;在存储和内存领域&…

作者头像 李华