Internationalisation i18n

Les plugins NocoBase gèrent les fichiers multilingues via le répertoire src/locale/. Une fois les fichiers de traduction écrits, vous pouvez utiliser this.t() dans le Plugin, le hook useT() dans les composants et tExpr() dans les définitions FlowModel pour récupérer le texte traduit.

Fichiers de traduction

Sous src/locale/ du plugin, créez un fichier JSON par langue. La clé est le texte source en anglais, la valeur est la traduction dans la langue correspondante :

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

Quelques points à noter :

  • Utilisez le texte anglais comme clé, ainsi même si une traduction manque, le repli vers l'anglais reste possible
  • Les variables utilisent les doubles accolades {{name}}, conformément à la syntaxe d'i18next
  • L'ajout initial des fichiers de langue nécessite un redémarrage de l'application ; les modifications ultérieures sont prises en compte par hot reload
  • NocoBase utilise automatiquement le nom du package du plugin comme namespace de traduction, les traductions des différents plugins n'entrent donc pas en conflit

Utilisation dans le Plugin : this.t()

Dans la classe Plugin, this.t() injecte automatiquement le nom du package du plugin courant comme namespace, vous n'avez pas à passer ns manuellement :

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

export class MyPlugin extends Plugin<any, Application> {
  async load() {
    // Utilise automatiquement le nom du package du plugin courant comme ns
    console.log(this.t('Hello')); // -> "你好"

    // Utilise this.t() pour traduire le titre lors de l'enregistrement de la page de configuration
    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'),
    });
  }
}

Utilisation dans un composant : useT()

Dans un composant React, on ne peut pas utiliser directement this.t(). Le squelette de plugin génère automatiquement un fichier locale.ts qui fournit le hook useT() :

// src/client-v2/locale.ts (généré automatiquement par le squelette)
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'] });
}

Utilisation dans un composant :

// 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 fonction t renvoyée par useT() est déjà liée au namespace du plugin, il suffit de passer la clé.

Utilisation dans FlowModel : tExpr()

FlowModel.define() et registerFlow() s'exécutent au chargement du module ; à ce moment, i18n n'est pas encore initialisé et on ne peut pas appeler t() directement. Dans ce cas, utilisez tExpr() — il génère une chaîne d'expression de traduction différée, résolue à l'exécution :

// 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 produit une chaîne du type '{{t("My block")}}', traduite à l'exécution
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 résumé : this.t() et useT() servent à la traduction au runtime, tExpr() à la traduction différée pour les définitions statiques.

Astuce

tExpr a deux origines : le locale.ts généré automatiquement par le plugin et @nocobase/flow-engine. La différence : le tExpr de locale.ts est déjà lié au nom du package comme namespace, tandis que le tExpr importé directement depuis @nocobase/flow-engine n'a pas de namespace. Dans le code de plugin, utilisez toujours le tExpr exporté par locale.ts afin que les traductions correspondent correctement aux fichiers de langue propres au plugin.

Aide-mémoire des trois usages

ScénarioUtilisationOrigine
Dans load() du Pluginthis.t('key')Fournie par la classe Plugin
Dans un composant Reactconst t = useT(); t('key')locale.ts
Définition statique d'un FlowModeltExpr('key')locale.ts

Liens connexes