docs/docs/cn/plugin-development/client/component/i18n.md
NocoBase 插件通过 src/locale/ 目录管理多语言文件。翻译文件写好后,在 Plugin 里用 this.t()、在组件里用 useT() hook、在 FlowModel 定义里用 tExpr() 就能拿到翻译文本。
在插件的 src/locale/ 下按语言创建 JSON 文件,key 是英文原文,value 是对应语言的翻译:
my-plugin/
└── src/
└── locale/
├── zh-CN.json
└── en-US.json
// src/locale/zh-CN.json
{
"Hello": "你好",
"Save": "保存",
"Your name is {{name}}": "你的名字是 {{name}}",
"{{count}} February items": "{{count}} 条记录"
}
// src/locale/en-US.json
{
"Hello": "Hello",
"Save": "Save",
"Your name is {{name}}": "Your name is {{name}}",
"{{count}} February items": "{{count}} items"
}
几个注意事项:
{{name}},跟 i18next 的语法一致在 Plugin 类里,this.t() 会自动注入当前插件的包名作为 namespace,不需要手动传 ns:
// src/client-v2/plugin.tsx
import { Plugin, Application } from '@nocobase/client-v2';
export class MyPlugin extends Plugin<any, Application> {
async load() {
// 自动使用当前插件的包名作为 ns
console.log(this.t('Hello')); // -> "你好"
// 注册设置页时用 this.t() 翻译标题
this.pluginSettingsManager.addMenuItem({
key: 'my-settings',
title: this.t('My Settings'),
icon: 'SettingOutlined',
});
this.pluginSettingsManager.addPageTabItem({
menuKey: 'my-settings',
key: 'index',
title: this.t('My Settings'),
componentLoader: () => import('./pages/MySettingsPage'),
});
}
}
在 React 组件里不能直接用 this.t()。插件脚手架会自动生成一个 locale.ts 文件,里面提供了 useT() hook:
// src/client-v2/locale.ts(脚手架自动生成)
import { tExpr as _tExpr, useFlowEngine } from '@nocobase/flow-engine';
// @ts-ignore
import pkg from './../../package.json';
export function useT() {
const engine = useFlowEngine();
return (str: string) => engine.context.t(str, { ns: [pkg.name, 'client'] });
}
export function tExpr(key: string) {
return _tExpr(key, { ns: [pkg.name, 'client'] });
}
在组件里这样用:
// src/client-v2/pages/MySettingsPage.tsx
import React from 'react';
import { Button } from 'antd';
import { useT } from '../locale';
export default function MySettingsPage() {
const t = useT();
return (
<div>
<h2>{t('Hello')}</h2>
<p>{t('Your name is {{name}}', { name: 'NocoBase' })}</p>
<Button>{t('Save')}</Button>
</div>
);
}
useT() 返回的 t 函数已经绑定了插件的 namespace,直接传 key 就行。
FlowModel.define() 和 registerFlow() 是在模块加载时执行的,此时 i18n 还没初始化,不能直接调用 t()。这种场景用 tExpr()——它生成一个延迟翻译的表达式字符串,运行时再解析:
// src/client-v2/models/MyBlockModel.tsx
import { BlockModel } from '@nocobase/client-v2';
import { tExpr } from '../locale';
export class MyBlockModel extends BlockModel {
renderComponent() {
return <div>My custom block</div>;
}
}
// tExpr 生成类似 '{{t("My block")}}' 的字符串,运行时翻译
MyBlockModel.define({
label: tExpr('My block'),
});
MyBlockModel.registerFlow({
key: 'flow1',
title: tExpr('My Block Settings'),
on: 'beforeRender',
steps: {
editTitle: {
title: tExpr('Edit Title'),
uiSchema: {
title: {
type: 'string',
title: tExpr('Title'),
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
handler(ctx, params) {
ctx.model.props.title = params.title;
},
},
},
});
简单来说:this.t() 和 useT() 用于运行时翻译,tExpr() 用于静态定义时的延迟翻译。
:::tip 提示
tExpr 有两个来源:插件自动生成的 locale.ts 和 @nocobase/flow-engine。区别在于 locale.ts 里的 tExpr 已经绑定了插件的包名作为 namespace,而从 @nocobase/flow-engine 直接导入的 tExpr 没有 namespace 绑定。在插件代码里,始终用 locale.ts 导出的 tExpr,这样翻译才能正确匹配到插件自己的语言文件。
:::
| 场景 | 用法 | 来源 |
|---|---|---|
Plugin load() 里 | this.t('key') | Plugin 基类自带 |
| React 组件里 | const t = useT(); t('key') | locale.ts |
| FlowModel 静态定义 | tExpr('key') | locale.ts |