ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

NocoBase 插件开发:客户端 I18n 国际化完整指南

NocoBase 插件开发:客户端 I18n 国际化完整指南 NocoBase 插件开发客户端 I18n 国际化完整指南【免费下载链接】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在 NocoBase 插件开发中国际化i18n是让插件同时服务中英文等多语言用户的基础能力。NocoBase 在前后端提供了统一的国际化机制后端通过ctx.i18n、ctx.t、plugin.t()完成服务端文案翻译前端则基于 i18next 生态提供useTranslation、tExpr等 API让插件作者只需维护一份 JSON 语言文件即可让界面、Schema 表达式和服务端返回的文案全部实现多语言。本文将基于客户端 i18n 文档的骨架结合nocobase/client、nocobase/server、nocobase/flow-engine的真实源码完整讲解语言文件的组织方式、JSON 词条格式、客户端与服务端翻译 API 的用法以及app:getLang接口背后的运行时资源下发机制帮助你写出的插件从第一天起就具备完整的多语言能力。插件多语言文件管理语言文件存放目录插件的多语言文件统一存放在插件包的src/locale目录下按语言locale命名文件例如|- /plugin-hello |- /src |- /locale |- en-US.json # 英文语言 |- zh-CN.json # 中文语言该约定与仓库内正式插件一致。以用户管理插件为例其语言文件就位于 packages/plugins/nocobase/plugin-users/src/locale 目录下包含en-US.json、zh-CN.json、de-DE.json、es-ES.json、fr-FR.json、ja-JP.json等多个语言文件说明该目录机制是 NocoBase 所有插件通用的标准结构。JSON 词条格式与插值每个语言文件导出一个 JSON 对象包含该语言的所有翻译词条。key 是翻译键value 是翻译结果例如zh-CN.json{ Hello: 你好, World: 世界, Enter your name: 请输入你的名字, Your name is {{name}}: 你的名字是 {{name}} }对应的en-US.json{ Hello: Hello, World: World, Enter your name: Enter your name, Your name is {{name}}: Your name is {{name}} }其中{{name}}是 i18next 标准的插值语法interpolation翻译函数在调用时会用传入的 options 替换占位符例如t(Your name is {{name}}, { name: NocoBase })会得到你的名字是 NocoBase。需要注意的是由于客户端的 i18n 实例在初始化时关闭了 key 分隔符详见下文客户端 i18n 实例的初始化翻译键中允许直接包含空格、等字符而不必担心被当作命名空间或层级分隔符解析。这一点在真实插件的语言文件中有大量体现例如 plugin-users 的 en-US.json 中就有Are you sure you want to delete selected users?、Users Permissions这样包含空格和的完整句子作为键名。新增语言文件后需要重启应用初次添加语言文件需要重启应用才能生效。可以通过接口校验翻译词条是否生效http://localhost:13000/api/app:getLang?localezh-CN之所以需要重启是因为服务端对语言资源做了缓存。在 packages/core/server/src/locale/locale.ts 中Locale管理器通过resourceCached集合记录已加载过的语言并使用wrapCache将每个语言的资源包缓存起来getCacheResources、loadResourcesByLang方法getBuiltInResources则遍历app.pm.getPlugins()中已加载的插件并读取其语言资源。因此新增加的语言文件在应用启动后才会被扫描并写入缓存重启应用或执行资源重载reload()是让新词条生效的最直接方式。客户端 i18n 相关 API客户端 i18n 实例的初始化在深入各 API 之前先看客户端 i18n 实例是如何创建的。packages/core/client/src/i18n/i18n.ts 中通过 i18next 创建了全局单例import i18next from i18next; import { initReactI18next } from react-i18next; import locale from ../locale; export const i18n i18next.createInstance(); const resources {}; Object.keys(locale).forEach((lang) { resources[lang] locale[lang].resources; }); i18n .use(initReactI18next) .init({ lng: en-US, defaultNS: client, resources: {}, keySeparator: false, nsSeparator: false, });几个关键点基于i18next.createInstance()创建独立实例避免污染全局 i18next接入initReactI18next使 React 组件可以通过useTranslation等 Hook 使用默认语言为en-US默认命名空间namespace为clientkeySeparator: false与nsSeparator: false表示翻译键不使用.和:作为层级/命名空间分隔符翻译键可以是任意完整句子。另外文件中导出的tval()函数已被标记为deprecated建议改用nocobase/utils/client中的tval见 packages/core/utils/src/i18n.ts而后者同样被标记为 deprecated建议改用nocobase/flow-engine的tExpr。下文会详细说明tExpr的用途。useTranslation(ns)useTranslation是客户端 React 组件内使用最频繁的翻译 Hook它直接来自react-i18next。用法import { useTranslation } from react-i18next; export const MyComponent () { const { t } useTranslation(); // 可传入命名空间如 useTranslation(plugin-hello) return div{t(Hello)}/div; };useTranslation接受可选的命名空间参数ns。不传时使用defaultNS: client如果插件使用了独立的命名空间通常与插件包名一致则应显式传入。仓库内的正式插件大量使用该模式。以 plugin-users 的 ChangePassword.tsx 为例import { useTranslation } from react-i18next; export const ChangePassword () { const { t } useTranslation(); // ... return div{t(Allow change password)}/div; };在 block-provider/hooks/index.ts 等核心模块中同样通过const { t } useTranslation()获取翻译函数后包装按钮文案、提示信息等。需要特别强调的是useTranslation返回的t函数在组件首次渲染时消费的词条来自客户端初始 resources当前默认是空的真正多语言资源是应用启动后由服务端通过app:getLang接口下发、再通过i18n.addResources()注入客户端 i18n 实例的详见下文app:getLang 与运行时资源下发。因此只要资源加载完成t()就能正确返回对应语言文案并随i18n.changeLanguage()的调用自动更新。tExpr(text)tExpr是 NocoBase 用于Schema 表达式翻译的函数它的作用不是立即翻译而是生成一段{{t(...)}}模板字符串交给 Schema 渲染引擎在渲染时延迟求值。其实现位于 packages/core/flow-engine/src/utils/translation.tsexport function tExpr(text: TFuncKey | TFuncKey[], options?: TOptions) { if (options) { return {{t(${JSON.stringify(text)}, ${JSON.stringify(options)})}}; } return {{t(${JSON.stringify(text)})}}; }例如import { tExpr } from nocobase/flow-engine; // 无 options tExpr(Hello); // {{t(\Hello\)}} // 带插值 options tExpr(Your name is {{name}}, { name: NocoBase }); // {{t(\Your name is {{name}}\, {\name\:\NocoBase\})}}返回的模板字符串可以直接写入 UI Schema如按钮标题、区块标题、字段 label 等当 Schema 被渲染时其中的{{t(...)}}表达式会由翻译上下文求值从而实现Schema 中的文案也支持多语言。同文件中还导出了两个辅助函数getT(model)从FlowModel实例获取翻译函数自动使用flow-engine命名空间ns: [flow-engine, client]nsMode: fallback在流程引擎相关场景中优先使用escapeT(text, options)已被标记为deprecated等价于tExpr。useT()文档中列出的useT()是客户端获取翻译函数的另一种方式当前仓库中部分模块仍在使用的快捷封装。如果你的插件需要统一入口获取t函数可以使用useT()代替直接解构useTranslation()它与useTranslation共享同一个 i18n 实例与命名空间配置行为一致可根据团队代码风格二选一。withTranslation(ns)withTranslation是react-i18next提供的高阶组件HOC形式适用于函数组件之外或需要把t注入 props 的场景import { withTranslation } from react-i18next; class LegacyComponent extends React.Component { render() { const { t } this.props; return div{t(Hello)}/div; } } export default withTranslation()(LegacyComponent);参数ns与useTranslation(ns)相同用于指定命名空间。在新代码中更推荐使用useTranslationHookHOC 主要用于兼容旧组件。服务端 i18n API配套虽然本文聚焦客户端国际化但客户端多语言资源恰恰由服务端下发因此服务端的三个 API 是完整国际化链路中不可或缺的一环。ctx.i18n 与 ctx.t(text, options)服务端中间件 packages/core/server/src/middlewares/i18n.ts 在每个请求进入时根据请求上下文确定语言并把翻译能力挂载到 Koa 的ctx上export async function i18n(ctx, next) { ctx.getCurrentLocale () { const lng ctx.get(X-Locale) || (ctx.request.query.locale as string) || ctx.app.i18n.language || ctx.acceptsLanguages().shift() || en-US; return lng; }; const lng ctx.getCurrentLocale(); const localeManager ctx.app.localeManager as Locale; const i18n await localeManager.getI18nInstance(lng); ctx.i18n i18n; ctx.t i18n.t.bind(i18n); if (lng ! * lng) { await i18n.changeLanguage(lng); await localeManager.loadResourcesByLang(lng); } await next(); }可以看到语言解析的优先级依次为请求头X-LocaleURL 查询参数locale应用默认语言app.i18n.languageAccept-Language请求头兜底en-US。在请求处理函数中即可使用// 某个 action 或中间件内 ctx.body { message: ctx.t(Hello), // 当前请求语言下的翻译 hello: ctx.i18n.t(Hello), // 等价写法 };plugin.t()插件类内部提供了实例方法plugin.t()自动把命名空间绑定为插件自身的packageName实现见 packages/core/server/src/plugin.tst(text: TFuncKey | TFuncKey[], options: TOptions {}) { return this.app.i18n.t(text, { ns: this.options[packageName], ...(options as any) }); }也就是说在插件的服务端代码中调用this.t(Hello)时会自动从该插件包名对应的命名空间查找词条无需手动传入ns。这要求插件的语言文件 key 与该插件包名命名空间对应。app:getLang 接口与运行时资源下发接口定义app:getLang是 NocoBase 提供的语言资源查询接口也是文档中校验翻译词条是否生效的入口。接口定义见 packages/core/server/src/swagger/app.ts请求方式GET /api/app:getLang查询参数locale可选请求的语言服务端会校验其是否为已启用语言ns可选逗号分隔的资源命名空间列表用于只返回指定命名空间的资源响应返回服务端当前语言及对应的多语言资源。文档给出的校验方式即http://localhost:13000/api/app:getLang?localezh-CN请求后返回的resources中应包含已加载插件含你新增词条的插件在该语言下的翻译资源。客户端如何消费接口返回的资源客户端在应用启动时会主动请求该接口把服务端聚合后的语言资源注入本地 i18n 实例。实现位于 packages/core/client/src/antd-config-provider/index.tsxexport function AntdConfigProvider(props) { const api useAPIClient(); const { i18n } useTranslation(); const { data, loading } useRequest({ url: app:getLang, params: { locale: api.auth.locale }, }, { onSuccess(data) { const locale api.auth.locale; if (data?.data?.lang !locale) { api.auth.setLocale(data?.data?.lang); i18n.changeLanguage(data?.data?.lang); } Object.keys(data?.data?.resources || {}).forEach((key) { i18n.addResources(data?.data?.lang, key, data?.data?.resources[key] || {}); }); loadConstrueLocale(data?.data); dayjs.locale(data?.data?.moment); window[cronLocale] data?.data?.cron; }, }); }该组件完成了几件关键工作以当前登录用户的auth.locale为参数请求app:getLang若用户尚未显式设置语言则把服务端返回的语言设为应用语言并调用i18n.changeLanguage()切换客户端语言遍历服务端返回的resources按命名空间组织逐个调用i18n.addResources(lang, ns, resources)注入客户端 i18n 实例——这就是你写的插件语言文件最终到达浏览器并可供useTranslation消费的完整链路同步设置 dayjs 与 cron 组件的本地化配置。语言切换的底层支持客户端 i18n 的语言切换能力来自 packages/core/client/src/i18n/i18n.ts 中创建的 i18next 实例而服务端负责聚合各插件资源的是 packages/core/server/src/locale/locale.ts 中的Locale管理器。它提供loadResourcesByLang(lang)按需加载某个语言的资源并写入缓存getBuiltInResources(lang)遍历已注册插件读取内置资源reload()/reset()清空缓存并广播localeManagerreload 事件用于语言文件变更后的资源刷新syncSources(ctx, types)合并各LocaleSource的同步资源。此外packages/core/client/src/locale/index.ts 维护了语言代码与 dayjs locale 的映射表dayjsLocale以及各语言的展示名如en-US: English、zh-CN: 简体中文供语言切换组件见 packages/core/client/src/i18n/SwitchLanguage.tsx与日期组件共用。最佳实践小结基于文档约定与源码实现在 NocoBase 插件中落地国际化的推荐做法如下语言文件统一放src/locale目录按en-US.json、zh-CN.json等语言代码命名键值对采用完整句子作 key的风格并利用{{name}}插值处理动态内容React 组件内使用useTranslation()获取t函数翻译界面文案旧代码可用withTranslation(ns)HOC 或useT()Schema 中的文案使用tExpr()生成{{t(...)}}模板字符串实现 Schema 级延迟翻译服务端文案使用ctx.t()请求上下文或this.t()插件类内自动绑定包名命名空间新增语言文件后重启应用生产环境再用GET /api/app:getLang?localezh-CN校验词条是否已被服务端聚合、能够下发到客户端熟悉 i18n.ts 中keySeparator: false、nsSeparator: false的配置含义避免在翻译键中使用会被误解析的分隔符。只要遵循以上目录约定与 API 用法你的插件即可无缝接入 NocoBase 的全局语言切换机制让界面、Schema 与服务端返回的文案在en-US、zh-CN等语言之间自由切换。【免费下载链接】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),仅供参考
返回列表