i18n Internasionalisasi

Plugin NocoBase mengelola file multibahasa melalui direktori src/locale/. Setelah file terjemahan ditulis, Anda dapat mendapatkan teks terjemahan dengan menggunakan this.t() di Plugin, hook useT() di Component, dan tExpr() di definisi FlowModel.

File Terjemahan

Buat file JSON berdasarkan bahasa di src/locale/ plugin, dengan key adalah teks asli bahasa Inggris dan value adalah terjemahan dalam bahasa yang sesuai:

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

Beberapa hal yang perlu diperhatikan:

  • Gunakan teks asli bahasa Inggris untuk key, sehingga ketika terjemahan tidak ada, dapat fallback ke bahasa Inggris
  • Variabel menggunakan kurung kurawal ganda {{name}}, sintaksnya konsisten dengan i18next
  • Pertama kali menambahkan file bahasa perlu restart aplikasi agar berlaku, setelah itu modifikasi konten dapat dilakukan dengan hot update
  • NocoBase secara otomatis menggunakan nama paket plugin sebagai namespace terjemahan, sehingga terjemahan plugin yang berbeda tidak akan bentrok

Menggunakan di Plugin: this.t()

Di kelas Plugin, this.t() akan secara otomatis menyuntikkan nama paket plugin saat ini sebagai namespace, tidak perlu meneruskan ns secara manual:

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

export class MyPlugin extends Plugin<any, Application> {
  async load() {
    // Otomatis menggunakan nama paket plugin saat ini sebagai ns
    console.log(this.t('Hello')); // -> "你好"

    // Gunakan this.t() untuk menerjemahkan judul saat mendaftarkan halaman pengaturan
    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'),
    });
  }
}

Menggunakan di Component: useT()

Di Component React, Anda tidak dapat langsung menggunakan this.t(). Scaffolding plugin akan secara otomatis menghasilkan file locale.ts yang menyediakan hook useT():

// src/client-v2/locale.ts (dihasilkan otomatis oleh scaffolding)
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'] });
}

Penggunaan di Component seperti berikut:

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

Function t yang dikembalikan oleh useT() sudah ter-bind dengan namespace plugin, cukup teruskan key langsung.

Menggunakan di FlowModel: tExpr()

FlowModel.define() dan registerFlow() dieksekusi pada saat module loading, ketika i18n belum diinisialisasi, sehingga t() tidak dapat dipanggil langsung. Untuk skenario ini gunakan tExpr() — yang menghasilkan string ekspresi terjemahan tertunda yang akan di-resolve saat runtime:

// 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 menghasilkan string seperti '{{t("My block")}}', diterjemahkan saat runtime
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;
      },
    },
  },
});

Singkatnya: this.t() dan useT() digunakan untuk terjemahan saat runtime, tExpr() digunakan untuk terjemahan tertunda saat definisi statis.

Tips

tExpr memiliki dua sumber: locale.ts yang dihasilkan otomatis oleh plugin, dan @nocobase/flow-engine. Perbedaannya terletak pada tExpr di locale.ts yang sudah ter-bind dengan nama paket plugin sebagai namespace, sedangkan tExpr yang diimpor langsung dari @nocobase/flow-engine tidak memiliki binding namespace. Di code plugin, selalu gunakan tExpr yang diekspor dari locale.ts, sehingga terjemahan dapat dicocokkan dengan benar ke file bahasa plugin itu sendiri.

Ringkasan Tiga Cara Penggunaan

SkenarioPenggunaanSumber
Di dalam Plugin load()this.t('key')Bawaan kelas dasar Plugin
Di dalam Component Reactconst t = useT(); t('key')locale.ts
Definisi statis FlowModeltExpr('key')locale.ts

Tautan Terkait