i18n internacionalização

Os plugins do NocoBase gerenciam arquivos multilíngues através do diretório src/locale/. Após criar os arquivos de tradução, você pode obter os textos traduzidos usando this.t() no Plugin, o hook useT() em componentes e tExpr() nas definições de FlowModel.

Arquivos de tradução

Em src/locale/ do plugin, crie arquivos JSON por idioma. As chaves são os textos originais em inglês e os valores, as traduções no idioma correspondente:

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"
}

Algumas observações:

  • Use o texto original em inglês como chave, assim, mesmo que a tradução esteja faltando, há fallback para o inglês
  • Variáveis usam chaves duplas {{name}}, com a mesma sintaxe do i18next
  • Adicionar um novo arquivo de idioma pela primeira vez requer reiniciar a aplicação para ter efeito; depois disso, as alterações de conteúdo terão hot reload
  • O NocoBase usa automaticamente o nome do pacote do plugin como namespace de tradução, evitando que traduções de plugins diferentes entrem em conflito

Uso no Plugin: this.t()

Na classe Plugin, this.t() injeta automaticamente o nome do pacote do plugin atual como namespace, sem precisar passar ns manualmente:

// 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'),
    });
  }
}

Uso em componentes: useT()

Em componentes React, você não pode usar this.t() diretamente. O scaffold de plugins gera automaticamente um arquivo locale.ts que fornece o hook useT():

// 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'] });
}

Use assim no componente:

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

A função t retornada por useT() já tem o namespace do plugin vinculado; basta passar a chave.

Uso em FlowModel: tExpr()

FlowModel.define() e registerFlow() são executados no momento de carregamento do módulo, quando o i18n ainda não foi inicializado, então t() não pode ser chamado diretamente. Para esses cenários, use tExpr() — ele gera uma string de expressão de tradução adiada, resolvida em tempo de execução:

// 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;
      },
    },
  },
});

Em resumo: this.t() e useT() são para tradução em tempo de execução; tExpr() é para tradução adiada em definições estáticas.

Dica

tExpr tem duas origens: o locale.ts gerado automaticamente pelo plugin e @nocobase/flow-engine. A diferença é que o tExpr em locale.ts já tem o nome do pacote do plugin vinculado como namespace, enquanto o tExpr importado diretamente de @nocobase/flow-engine não tem namespace vinculado. No código do plugin, sempre use o tExpr exportado por locale.ts, para que a tradução seja corretamente correlacionada com os arquivos de idioma do próprio plugin.

Resumo dos três usos

CenárioUsoOrigem
Dentro do load() do Pluginthis.t('key')Embutido na classe base Plugin
Em componentes Reactconst t = useT(); t('key')locale.ts
Definição estática de FlowModeltExpr('key')locale.ts