docs: fix i18n dev sample (#910)

This commit is contained in:
Junyi 2022-10-16 17:20:53 +08:00 committed by GitHub
parent 7df5129cd7
commit faa01c9335
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 116 additions and 42 deletions

View File

@ -31,7 +31,7 @@ const app = new Application({
} }
} }
} }
}) });
``` ```
或者在插件中对已存在的 app 实例添加语言数据至对应命名空间: 或者在插件中对已存在的 app 实例添加语言数据至对应命名空间:
@ -74,6 +74,19 @@ app.resource({
客户端的多语言国际化基于 npm 包 [react-i18next](https://npmjs.com/package/react-i18next) 实现,在应用顶层提供了 `<I18nextProvider>` 组件的包装,可以在任意位置直接使用相关的方法。 客户端的多语言国际化基于 npm 包 [react-i18next](https://npmjs.com/package/react-i18next) 实现,在应用顶层提供了 `<I18nextProvider>` 组件的包装,可以在任意位置直接使用相关的方法。
添加语言包:
```tsx | pure
import { i18n } from '@nocobase/client';
i18n.addResources('zh-CN', 'test', {
Hello: '你好',
World: '世界',
});
```
注:这里第二个参数填写的 `'test'` 是语言的命名空间通常插件自己定义的语言资源都应该按自己插件包名创建特定的命名空间以避免和其他语音资源冲突。NocoBase 中默认的命名空间是 `'client'`,大部分常用和基础的语言翻译都放置在此命名空间,当没有提供所需语言时,可在插件自身的命名空间内进行扩展定义。
在组件中调用翻译函数: 在组件中调用翻译函数:
```tsx | pure ```tsx | pure
@ -81,7 +94,8 @@ import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
export default function MyComponent() { export default function MyComponent() {
const { t } = useTranslation(); // 使用之前定义的命名空间
const { t } = useTranslation('test');
return ( return (
<div> <div>
@ -91,35 +105,65 @@ export default function MyComponent() {
} }
``` ```
其中 `compile()` 方法是专门针对 SchemaComponent 中纯 JSON 配置提供的编译方法,执行后也可以得到对应的翻译结果。 在 SchemaComponent 组件中可以直接使用模板方法 `'{{t(<languageKey>)}}'`,模板中的翻译函数会自动被执行:
在 Schema 组件中使用: ```tsx | pure
```tsx
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next';
import { SchemaComponent } from '@nocobase/client'; import { SchemaComponent } from '@nocobase/client';
export default function MySchemaComponent() { export default function MySchemaComponent() {
const { t } = useTranslation();
return ( return (
<SchemaComponent <SchemaComponent
schema={{ schema={{
type: 'string', type: 'string',
'x-component': 'Input', 'x-component': 'Input',
'x-component-props': { 'x-component-props': {
value: '{{t("Hello")}}' value: '{{t("Hello", { ns: "test" })}}'
}, },
}} }}
scope={{
t
}}
/> />
); );
} }
``` ```
在 SchemaComponent 组件中使用时,可以在 schema 配置里直接使用 JSON 的可编译字符串模板表达要翻译的语言片段键名。 在某些特殊情况下也需要以模板的方式定义多语言时,可以使用 NocoBase 内置的 `compile()` 方法编译为多语言结果:
```tsx | pure
import React from 'react';
import { useCompile } from '@nocobase/client';
const title = '{{t("Hello", { ns: "test" })}}';
export default function MyComponent() {
const { compile } = useCompile();
return (
<div>{compile(title)}</div>
);
}
```
### 建议配置
当添加语言资源时,推荐在 JSON 配置模板中将语言串的键名设置为默认语言,更方便统一处理,且可以省去默认语言的翻译。例如以英语为默认语言:
```ts
i18n.addResources('zh-CN', 'your-namespace', {
'Show dialog': '显示对话框',
'Hide dialog': '隐藏对话框'
});
```
语言内容如果比较多,推荐在插件中创建一个 `locals` 目录,并把对应语言文件都放置在其中方便管理:
```
- server
| - locals
| | - zh-CN.ts
| | - en-US.ts
| | - ...
| - index.ts
```
## 示例 ## 示例
@ -144,7 +188,7 @@ export default class ShopPlugin extends Plugin {
async create(ctx, next) { async create(ctx, next) {
const productRepo = ctx.db.getRepository('products'); const productRepo = ctx.db.getRepository('products');
const product = await productRepo.findOne({ const product = await productRepo.findOne({
filterByTk: ctx.action.params.filterByTk filterByTk: ctx.action.params.values.productId
}); });
if (!product) { if (!product) {
@ -184,23 +228,14 @@ export default class ShopPlugin extends Plugin {
```tsx ```tsx
import React from 'react'; import React from 'react';
import { Select } from 'antd'; import { Select } from 'antd';
import i18next from 'i18next'; import { i18n } from '@nocobase/client';
import { I18nextProvider, initReactI18next, useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
const i18n = i18next.createInstance(); i18n.addResources('zh-CN', '@nocobase/plugin-sample-shop-i18n', {
i18n.use(initReactI18next).init({
lng: 'zh-CN',
defaultNS: 'client',
resources: {
'zh-CN': {
client: {
Pending: '已下单', Pending: '已下单',
Paid: '已支付', Paid: '已支付',
Delivered: '已发货', Delivered: '已发货',
Received: '已签收' Received: '已签收'
}
}
}
}); });
const ORDER_STATUS_LIST = [ const ORDER_STATUS_LIST = [
@ -212,7 +247,7 @@ const ORDER_STATUS_LIST = [
] ]
function OrderStatusSelect() { function OrderStatusSelect() {
const { t } = useTranslation(); const { t } = useTranslation('@nocobase/plugin-sample-shop-i18n');
return ( return (
<Select style={{ minWidth: '8em' }}> <Select style={{ minWidth: '8em' }}>
@ -225,9 +260,7 @@ function OrderStatusSelect() {
export default function () { export default function () {
return ( return (
<I18nextProvider i18n={i18n}>
<OrderStatusSelect /> <OrderStatusSelect />
</I18nextProvider>
); );
} }
``` ```

View File

@ -0,0 +1 @@
export { default } from '@nocobase/plugin-sample-shop-i18n/client';

View File

@ -1,5 +1,36 @@
import React from 'react'; import React from 'react';
import { Select } from 'antd';
import { i18n } from '@nocobase/client';
import { useTranslation } from 'react-i18next';
i18n.addResources('zh-CN', '@nocobase/plugin-sample-shop-i18n', {
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('@nocobase/plugin-sample-shop-i18n');
return (
<Select style={{ minWidth: '8em' }}>
{ORDER_STATUS_LIST.map(item => (
<Select.Option value={item.value}>{t(item.label)}</Select.Option>
))}
</Select>
);
}
export default React.memo((props) => { export default React.memo((props) => {
return <>{props.children}</>; return <OrderStatusSelect />;
}); });

View File

@ -2,6 +2,10 @@ import path from 'path';
import { InstallOptions, Plugin } from '@nocobase/server'; import { InstallOptions, Plugin } from '@nocobase/server';
import zhCN from './locales/zh-CN';
const ns = '@nocobase/plugin-sample-shop-i18n';
export class ShopPlugin extends Plugin { export class ShopPlugin extends Plugin {
getName(): string { getName(): string {
return this.getPackageName(__dirname); return this.getPackageName(__dirname);
@ -16,25 +20,25 @@ export class ShopPlugin extends Plugin {
directory: path.resolve(__dirname, 'collections'), directory: path.resolve(__dirname, 'collections'),
}); });
this.app.i18n.addResources('zh-CN', ns, zhCN);
this.app.resource({ this.app.resource({
name: 'order', name: 'orders',
actions: { actions: {
async create(ctx, next) { async create(ctx, next) {
const productRepo = ctx.db.getRepository('products'); const productRepo = ctx.db.getRepository('products');
const product = await productRepo.findOne({ const product = await productRepo.findById(ctx.action.params.values.productId);
filterByTk: ctx.action.params.filterByTk
});
if (!product) { if (!product) {
return ctx.throw(404, ctx.t('No such product')); return ctx.throw(404, ctx.t('No such product', { ns }));
} }
if (!product.enabled) { if (!product.enabled) {
return ctx.throw(400, ctx.t('Product not on sale')); return ctx.throw(400, ctx.t('Product not on sale', { ns }));
} }
if (!product.inventory) { if (!product.inventory) {
return ctx.throw(400, ctx.t('Out of stock')); return ctx.throw(400, ctx.t('Out of stock', { ns }));
} }
const orderRepo = ctx.db.getRepository('orders'); const orderRepo = ctx.db.getRepository('orders');

View File

@ -0,0 +1,5 @@
export default {
'No such product': '商品不存在',
'Product not on sale': '商品已下架',
'Out of stock': '库存不足',
};