# 国际化 ## 基础概念 多语言国际化支持根据服务端和客户端分为两大部分,各自有相应的实现。 语言配置均为普通的 JSON 对象键值对,如果没有翻译文件(或省略编写)的话会直接输出键名的字符串。 ### 服务端 服务端的多语言国际化基于 npm 包 [i18next](https://npmjs.com/package/i18next) 实现,在服务端应用初始化时会创建一个 i18next 实例,同时也会将此实例注入到请求上下文(`context`)中,以供在各处方便的使用。 在创建服务端 Application 实例时可以传入配置对应的初始化参数: ```ts import { Application } from '@nocobase/server'; const app = new Application({ i18n: { defaultNS: 'test', resources: { 'en-US': { test: { hello: 'Hello', }, }, 'zh-CN': { test: { hello: '你好', } } } } }) ``` 或者在插件中对已存在的 app 实例添加语言数据至对应命名空间: ```ts app.i18n.addResources('zh-CN', 'test', { Hello: '你好', World: '世界', }); app.i18n.addResources('en-US', 'test', { Hello: 'Hello', World: 'World', }); ``` 基于应用: ```ts app.i18n.t('World') // “世界”或“World” ``` 基于请求: ```ts app.resource({ name: 'test', actions: { async get(ctx, next) { ctx.body = `${ctx.t('Hello')} ${ctx.t('World')}`; await next(); } } }); ``` 通常服务端的多语言处理主要用于错误信息的输出。 ### 客户端 客户端的多语言国际化基于 npm 包 [react-i18next](https://npmjs.com/package/react-i18next) 实现,在应用顶层提供了 `` 组件的包装,可以在任意位置直接使用相关的方法。 在组件中调用翻译函数: ```tsx | pure import React from 'react'; import { useTranslation } from 'react-i18next'; export default function MyComponent() { const { t } = useTranslation(); return (

{t('World')}

); } ``` 其中 `compile()` 方法是专门针对 SchemaComponent 中纯 JSON 配置提供的编译方法,执行后也可以得到对应的翻译结果。 在 Schema 组件中使用: ```tsx import React from 'react'; import { useTranslation } from 'react-i18next'; import { SchemaComponent } from '@nocobase/client'; export default function MySchemaComponent() { const { t } = useTranslation(); return ( ); } ``` 在 SchemaComponent 组件中使用时,可以在 schema 配置里直接使用 JSON 的可编译字符串模板表达要翻译的语言片段键名。 ## 示例 ### 服务端错误提示 例如用户在店铺对某个商品下单时,如果商品的库存不够,或者未上架,那么下单接口被调用时,应该返回相应的错误。 ```ts const namespace = 'shop'; export default class ShopPlugin extends Plugin { async load() { this.app.i18n.addResources('zh-CN', namespace, { 'No such product': '商品不存在', 'Product not on sale': '商品已下架', 'Out of stock': '库存不足', }); this.app.resource({ name: 'orders', actions: { async create(ctx, next) { const productRepo = ctx.db.getRepository('products'); const product = await productRepo.findOne({ filterByTk: ctx.action.params.filterByTk }); if (!product) { return ctx.throw(404, ctx.t('No such product')); } if (!product.enabled) { return ctx.throw(400, ctx.t('Product not on sale')); } if (!product.inventory) { return ctx.throw(400, ctx.t('Out of stock')); } const orderRepo = ctx.db.getRepository('orders'); ctx.body = await orderRepo.create({ values: { productId: product.id, quantity: 1, totalPrice: product.price, userId: ctx.state.currentUser.id } }); next(); } } }); } } ``` ### 客户端组件多语言 例如订单状态的组件,根据不同值有不同的文本显示: ```tsx import React from 'react'; import { Select } from 'antd'; import i18next from 'i18next'; import { I18nextProvider, initReactI18next, useTranslation } from 'react-i18next'; const i18n = i18next.createInstance(); i18n.use(initReactI18next).init({ lng: 'zh-CN', defaultNS: 'client', resources: { 'zh-CN': { client: { Pending: '已下单', Paid: '已支付', Delivered: '已发货', Received: '已签收' } } } }); const ORDER_STATUS_LIST = [ { value: -1, label: 'Canceled (untranslated)' }, { value: 0, label: 'Pending' }, { value: 1, label: 'Paid' }, { value: 2, label: 'Delivered' }, { value: 3, label: 'Received' }, ] function OrderStatusSelect() { const { t } = useTranslation(); return ( ); } export default function () { return ( ); } ```