Internacionalización (i18n)

Los plugins de NocoBase gestionan los archivos multilingües en el directorio src/locale/. Una vez escritos los archivos de traducción, puede obtener los textos con this.t() en el Plugin, con el hook useT() en los componentes y con tExpr() en las definiciones de FlowModel.

Archivos de traducción

Cree archivos JSON por idioma bajo src/locale/ del plugin. La clave es el texto original en inglés y el valor es la traducción correspondiente:

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

Algunas consideraciones:

  • Use el texto original en inglés como clave, así, aunque falte una traducción, se mostrará el inglés.
  • Use dobles llaves para las variables, {{name}}, igual que en la sintaxis de i18next.
  • La primera vez que añada un archivo de idioma debe reiniciar la aplicación para que surta efecto; los cambios posteriores se aplican con hot reload.
  • NocoBase usa automáticamente el nombre del paquete del plugin como namespace de las traducciones, por lo que las traducciones de plugins distintos no chocan.

Uso en el Plugin: this.t()

Dentro de la clase Plugin, this.t() inyecta automáticamente el nombre del paquete del plugin actual como namespace, sin necesidad de pasar ns manualmente:

// src/client-v2/plugin.tsx
import { Plugin, Application } from '@nocobase/client-v2';

export class MyPlugin extends Plugin<any, Application> {
  async load() {
    // Usa automáticamente el nombre del paquete actual como ns
    console.log(this.t('Hello')); // -> "你好"

    // Al registrar la página de configuración, traduzca el título con 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 en componentes: useT()

En los componentes React no se puede usar this.t() directamente. El scaffold del plugin genera automáticamente un archivo locale.ts que expone el hook useT():

// src/client-v2/locale.ts (generado automáticamente por el scaffold)
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'] });
}

En el componente se utiliza así:

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

La función t que devuelve useT() ya tiene asociado el namespace del plugin; basta con pasarle la clave.

Uso en FlowModel: tExpr()

FlowModel.define() y registerFlow() se ejecutan al cargar el módulo, momento en el que i18n aún no se ha inicializado, por lo que no se puede llamar a t() directamente. En este escenario se utiliza tExpr(), que genera una expresión de traducción diferida que se resuelve en tiempo de ejecución:

// 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 genera una cadena similar a '{{t("My block")}}' y la traduce en tiempo de ejecución
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;
      },
    },
  },
});

En resumen: this.t() y useT() son para traducción en tiempo de ejecución; tExpr() es para traducción diferida en definiciones estáticas.

Consejo

tExpr proviene de dos sitios: el locale.ts autogenerado del plugin y @nocobase/flow-engine. La diferencia es que el tExpr de locale.ts ya tiene asociado el nombre del paquete del plugin como namespace, mientras que el importado directamente desde @nocobase/flow-engine no tiene ningún namespace asociado. En el código del plugin, utilice siempre el tExpr exportado por locale.ts para que las traducciones se asocien correctamente con los archivos de idioma propios del plugin.

Tabla rápida de los tres usos

EscenarioUsoOrigen
Dentro de load() del Pluginthis.t('key')Incluido en la clase base Plugin
Dentro de un componente Reactconst t = useT(); t('key')locale.ts
Definición estática de FlowModeltExpr('key')locale.ts

Enlaces relacionados