NocoBase RunJS 国际化翻译指南:精通 ctx.t() 的多语言文案方案
【免费下载链接】nocobaseNocoBase is an open-source AI + no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase
ctx.t()是 NocoBase RunJS 执行环境提供的 i18n 快捷翻译函数,用于在 JS 区块、JS 字段、JS 操作、事件流等场景中实现按钮、标题、提示等内联文案的国际化。本文以 RunJS 上下文 API 为基础,结合 flow-engine 源码实现,完整讲解ctx.t()的类型定义、参数语义、命名空间机制、插值用法,以及与本地化插件、ctx.i18n的协作方式,帮助你在多语言业务系统中写出可直接落地的翻译代码。
RunJS 与 ctx.t() 的定位
RunJS 是 NocoBase 中用于JS 区块(JSBlock)、JS 字段(JSField)、JS 操作等场景的 JavaScript 执行环境,代码运行在受限沙箱中,可安全访问ctx上下文 API,并支持顶层await、导入外部模块、容器内渲染与全局变量(见 RunJS 概述)。
ctx.t()正是该上下文中负责“翻译文案”的入口。从源码结构看,RunJS 上下文的t方法由引擎在创建执行环境时统一注入(见 flowContext.ts):
runCtx.defineMethod('t', (key: string, options?: any) => { return this.t(key, { ns: 'runjs', ...options }); });这段实现揭示了两个关键事实:
ctx.t()底层委托给引擎的翻译方法,并自动注入默认命名空间runjs;- 你传入的
options会覆盖默认值,因此可通过ns显式指定其他命名空间。
在 RunJS 上下文的元数据声明中,t被描述为“国际化函数,用于翻译文案”(见 runjs-context/contexts/base.ts),其补全示例即ctx.t("你好 {{name}}", { name: "世界" })。
适用场景
所有 RunJS 执行环境均可使用ctx.t(),包括但不限于:
- JS 区块(JSBlock):整块自定义渲染内容中的文案;
- JS 字段 / 可编辑字段(JSField / JSEditableField):字段展示与编辑界面的文案;
- JS 项 / JS 列(JSItem / JSColumn):列表项与列头文案;
- JS 操作(JSCollectionAction / JSRecordAction):按钮、确认提示等操作文案;
- 事件流、联动规则:流程节点与联动逻辑中的动态文案。
上述各场景分别对应 runjs-context/contexts 目录下的JSBlockRunJSContext.ts、JSFieldRunJSContext.ts、JSColumnRunJSContext.ts、JSItemRunJSContext.ts、JSCollectionActionRunJSContext.ts、JSRecordActionRunJSContext.ts等上下文实现,它们统一通过createJSRunner获得t方法(见 flowContext.ts)。
类型定义
t(key: string, options?: Record<string, any>): string参数说明
| 参数 | 类型 | 说明 |
|---|---|---|
key | string | 翻译 key 或带占位符的模板(如Hello {{name}}、{{count}} rows) |
options | object | 可选。插值变量(如{ name: '张三', count: 5 }),或 i18n 选项(如defaultValue、ns) |
参数语义的源码印证
options中的ns(命名空间)与插值变量是同时传递的。在 flowI18n.ts 的translateKey实现中,翻译最终委托给 i18next 风格的实例:
private translateKey(key: string, options?: any): string { if (this.context?.i18n?.t) { const translated = this.context.i18n.t(key, options); return translated == null || translated === '' ? key : translated; } // 如果没有翻译函数,返回原始键值 return key; }也就是说:只要翻译实例存在,key与options会被原样透传给 i18next;若翻译结果为空或翻译函数缺失,则回退返回 key 本身。这决定了下方“返回值”的行为。
返回值
- 返回翻译后的字符串;
- 若 key 无对应翻译且未提供
defaultValue,可能返回 key 本身或经插值后的字符串; - 若翻译函数缺失(例如本地化能力未就绪),直接返回 key 原样(源码见 flowI18n.ts)。
命名空间(ns)
RunJS 环境的默认命名空间为runjs。在不指定ns时,ctx.t(key)会从runjs命名空间查找 key。这正对应上文createJSRunner注入时自动拼接的{ ns: 'runjs', ...options }。
// 默认从 runjs 命名空间取 key ctx.t('Submit'); // 等价于 ctx.t('Submit', { ns: 'runjs' }) // 从指定命名空间取 key ctx.t('Submit', { ns: 'myModule' }); // 从多个命名空间依次查找(先 runjs,再 common) ctx.t('Save', { ns: ['runjs', 'common'] });说明:
ns支持字符串或字符串数组。传数组时按顺序依次查找,适合“业务模块优先、公共词条兜底”的常见场景。默认命名空间runjs意味着 RunJS 相关文案应统一维护在runjs命名空间下,便于本地化管理与复用。
示例
简单 key
ctx.t('Submit'); ctx.t('No data');带插值变量
i18next 风格插值:在 key 中使用{{变量名}},在options中传入同名变量即可替换。options中的值既可以是普通字符串/数字,也可以是动态计算的结果。
const text = ctx.t('Hello {{name}}', { name: ctx.user?.nickname || 'Guest' }); ctx.render(`<div>${text}</div>`);ctx.message.success(ctx.t('Processed {{count}} rows', { count: rows.length }));上例结合ctx.message.success(Ant Design 全局消息 API,见 runjs-context/contexts/base.ts 的上下文元数据)与ctx.render(容器内渲染,见 RunJS 概述 的“容器内渲染”小节),可构造带数量的操作反馈。
相对时间等动态文案
if (minutes < 60) return ctx.t('{{count}} minutes ago', { count: minutes }); if (hours < 24) return ctx.t('{{count}} hours ago', { count: hours });这类“复数+相对时间”文案同样走{{count}}插值,翻译词条在目标语言中可按语言习惯组织,例如中文可维护为“{{count}} 分钟前”“{{count}} 小时前”。
指定命名空间
ctx.t('Hello {{name}}', { name: 'Guest', ns: 'myModule' });指定ns后,本次翻译从myModule命名空间查找Hello {{name}},同时仍可携带插值变量,二者互不干扰。
渲染 JSX 中的翻译
结合 RunJS 的 JSX 渲染能力(RunJS 概述),ctx.t()也常直接嵌入组件:
ctx.render(<button>{ctx.t('Submit')}</button>);与 ctx.i18n 协作:读取与切换语言
翻译的语言由当前上下文决定(如ctx.i18n.language、用户 locale)。RunJS 上下文同时暴露ctx.i18n实例,用于读取或切换语言;官方约定翻译文案统一使用ctx.t(),不要使用ctx.i18n.t(见 ctx.i18n)。
ctx.i18n的类型定义(见 ctx.i18n):
interface i18n: { language: string; changeLanguage(lng: string): Promise<any>; }常用组合用法:
// 读取当前语言 const lang = ctx.i18n.language; // 'zh-CN' | 'en-US' | ... if (lang.startsWith('zh')) { ctx.render(ctx.t('中文界面')); } else { ctx.render(ctx.t('English UI')); }// 切换语言 await ctx.i18n.changeLanguage('en-US'); await ctx.i18n.changeLanguage('zh-CN');一个完整的语言切换按钮示例(复用ctx.libs.antd与 JSX 渲染):
const { Button } = ctx.libs.antd; const isZh = ctx.i18n.language.startsWith('zh'); ctx.render( <Button onClick={async () => { await ctx.i18n.changeLanguage(isZh ? 'en-US' : 'zh-CN'); }}> {ctx.t(isZh ? 'Switch to English' : '切换到中文')} </Button>, );引擎在定义locale属性时同样遵循该语言优先级:api?.auth?.locale || i18n?.language(见 flowContext.ts),这与文档中“语言由当前上下文(如ctx.i18n.language、用户 locale)决定”的描述一致。
引擎层的翻译机制:模板编译与兜底插值
除了简单 key,引擎的FlowI18n还支持模板编译:若 key 本身包含{{ t('...') }}形式的表达式,会先编译再翻译(见 flowI18n.ts):
flowEngine.t('Hello {name}', { name: 'John' }); // 简单翻译 flowEngine.t("{{ t('User Name', { ns: 'fields' }) }}"); // 模板编译 + 指定命名空间 flowEngine.t("前缀 {{ t('User Name') }} 后缀"); // 混合文本对应的单元测试(flowI18n.test.ts)验证了以下行为:
- 普通 key 与
{{ t('Hello') }}模板均能正确翻译('Hello' -> '你好'); - key 内嵌不同类型引号(如含
"Post-action event"的 key)不会被错误截断; - 畸形 options(如
{{ t('X', oops) }})会被安全降级为无 options 翻译,并输出警告日志。
此外,当翻译函数不可用时(如警告/降级提示路径),引擎会执行轻量级兜底插值,将{{var}}替换为options中的字符串或数字值(见 flowContext.ts):
// lightweight interpolation for fallback strings (i18next-style: {{var}}) return fallback.replace(/\{\{\s*([a-zA-Z0-9_]+)\s*\}\}/g, (_m, k) => { const v = options?.[k]; return typeof v === 'string' || typeof v === 'number' ? String(v) : ''; });这意味着即便在部分无法访问完整 i18next 实例的降级路径上,{{变量}}插值仍能尽力工作。
注意事项
- 本地化插件:如需翻译文案,需先激活本地化插件。缺失翻译的词条会自动提取到本地化管理列表,便于统一维护和翻译。仓库中对应插件位于 plugin-localization,其服务端动作(如 actions/localizationTexts.ts)负责词条的提取与维护。
- 插值语法:支持 i18next 风格插值,在 key 中使用
{{变量名}},在options中传入同名变量即可替换。 - 语言来源:语言由当前上下文(如
ctx.i18n.language、用户 locale)决定,切换语言后再次调用ctx.t()即返回新语言文案。 - 统一入口:翻译文案一律使用
ctx.t(),不要使用ctx.i18n.t;ctx.i18n仅用于读取语言与切换语言(见 ctx.i18n)。 - 默认命名空间:
ctx.t(key)默认从runjs命名空间取 key;跨模块词条可通过ns显式指定。 - 空结果回退:当翻译结果为空字符串或 null 时,引擎返回原始 key(见 flowI18n.ts),避免界面出现空白文案。
相关
- ctx.i18n:读取或切换语言
- RunJS 概述:RunJS 执行环境、渲染与模块导入能力
- flowContext.ts:
ctx.t()的注入实现 - flowI18n.ts:引擎翻译与模板编译核心
- flowI18n.test.ts:翻译行为单元测试
【免费下载链接】nocobaseNocoBase is an open-source AI + no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考