nocobase/docs/en-US/development/server/i18n.md
chenos b8d0ad8fbc
feat: update docs (#996)
* feat: update docs

* feat: update docs

* fix: update docs

* Add files via upload

* Add files via upload

* Update the-first-app.md

* Update the-first-app.md

* Update v08-changelog.md

* feat: update docs

Co-authored-by: Zhou <zhou.working@gmail.com>
2022-10-31 22:41:24 +08:00

3.0 KiB
Raw Blame History

国际化

NocoBase 国际化基于 i18next 实现。

如何注册多语言包?

export class MyPlugin extends Plugin {
  load() {
    this.app.i18n.addResources('zh-CN', 'test', {
      Hello: '你好',
      World: '世界',
    });
    this.app.i18n.addResources('en-US', 'test', {
      Hello: 'Hello',
      World: 'World',
    });
  }
}

两个 i18n 实例

app.i18n

全局 i18n 实例,一般用于 CLI 中国。

app.i18n.t('World') // “世界”或“World”

ctx.i18n

全局 i18n 的 cloneInstance每个请求的 context 完全独立,通常用于根据客户端语言响应多语言信息。

app.use(async (ctx, next) => {
  ctx.body = `${ctx.i18n.t('Hello')} ${ctx.i18n.t('World')}`;
  await next();
});

客户端请求参数可以放在 query string 里

GET /?locale=en-US HTTP/1.1
Host: localhost:13000

或放在 request headers 里

GET / HTTP/1.1
Host: localhost:13000
X-Locale: en-US

建议配置

以英文文案为 key翻译为 value这样的好处即使多语言缺失也会以英文显示不会造成阅读障碍

i18n.addResources('zh-CN', 'your-namespace', {
  'Show dialog': '显示对话框',
  'Hide dialog': '隐藏对话框'
});

为了更方便管理多语言文件,推荐在插件中创建一个 locale 文件夹,并把对应语言文件都放置在其中方便管理:

|- /my-plugin
  |- /src
    |- /server
      |- locale     # 多语言文件夹
        |- zh-CN.ts
        |- en-US.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.values.productId
          });

          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();
        }
      }
    });
  }
}