For AI agents: the complete documentation index is available at /zh/llms.txt, the full documentation bundle is available at /zh/llms-full.txt, and this page is available as Markdown at /zh/guides/advanced-features/international/best-practices.md.
  • 简体中文
  • 最佳实践

    资源文件组织

    命名空间(Namespace)用于按业务模块拆分翻译文件,默认命名空间为 translation。按模块拆分可以减少初始加载体积,只在用到时按需加载对应命名空间。

    推荐目录结构:

    locales/
    ├── en/
    │   ├── translation.json  ← 默认命名空间,放通用文案
    │   ├── common.json       ← 按钮、标签等公共 UI 文案
    │   └── errors.json       ← 错误信息
    └── zh/
        ├── translation.json
        ├── common.json
        └── errors.json

    声明多个命名空间:

    // src/modern.runtime.ts
    export default defineRuntimeConfig({
      i18n: {
        initOptions: {
          ns: ['translation', 'common', 'errors'],
          defaultNS: 'translation',
        },
      },
    });

    在组件中使用指定命名空间:

    import { useTranslation } from 'react-i18next';
    
    // 指定单个命名空间
    function MyButton() {
      const { t } = useTranslation('common');
      return <button>{t('submit')}</button>;
    }
    
    // 同时使用多个命名空间,用 namespace:key 格式访问
    function Dashboard() {
      const { t } = useTranslation(['dashboard', 'common']);
      return (
        <header>
          <h1>{t('dashboard:title')}</h1>
          <button>{t('common:button.refresh')}</button>
        </header>
      );
    }
    
    // keyPrefix 可以省略重复的前缀
    function ButtonGroup() {
      const { t } = useTranslation('common', { keyPrefix: 'button' });
      return (
        <>
          <button>{t('submit')}</button> {/* → common:button.submit */}
          <button>{t('cancel')}</button> {/* → common:button.cancel */}
        </>
      );
    }

    翻译键命名规范

    翻译键的命名质量直接影响后续维护成本,建议:

    • 用语义化词语,避免缩写:button.submit 优于 btn.sbm
    • 按模块划分前缀:dashboard.table.header、auth.login.title
    • 不要用完整中文文案当键名:键名应是稳定的标识符,而不是翻译内容本身
    • 用点号表示层级:与 JSON 嵌套结构一一对应
    // ✅ 推荐
    {
      "page": {
        "title": "用户设置",
        "description": "管理你的账号信息"
      },
      "form": {
        "username": { "label": "用户名", "placeholder": "请输入用户名" },
        "submit": "保存更改"
      }
    }
    t('page.title');
    t('form.username.label');
    t('form.submit', { defaultValue: '保存' }); // defaultValue 防止键缺失时显示 key 字符串

    复数

    i18next 根据 count 参数自动选择对应的复数形式。注意:i18next v21 之后默认使用 JSON v4 格式,复数后缀改为 CLDR 标准(_zero、_one、_other 等),旧的 _plural 写法已废弃。

    // locales/en/translation.json(JSON v4 格式)
    {
      "item_zero": "No items",
      "item_one": "{{count}} item",
      "item_other": "{{count}} items"
    }
    // locales/zh/translation.json
    {
      "item_zero": "没有条目",
      "item_other": "{{count}} 个条目"
    }
    t('item', { count: 0 }); // → "没有条目" / "No items"
    t('item', { count: 1 }); // → "没有条目" / "1 item"(中文通常只有一种形式)
    t('item', { count: 5 }); // → "5 个条目" / "5 items"

    不同语言的复数规则不同(英语有单复数,俄语有 one/few/many 等多种形式),i18next 会根据语言代码自动匹配 CLDR 规则。

    Tip

    如果项目使用了旧版 i18next 或显式配置了 compatibilityJSON: 'v3',才需要使用 _plural 写法。新项目统一使用 v4 格式。

    嵌套键

    嵌套结构可以直观反映 UI 层级,用点号访问:

    {
      "modal": {
        "confirm": {
          "title": "确认删除",
          "message": "此操作不可撤销,是否继续?",
          "actions": {
            "ok": "确认",
            "cancel": "取消"
          }
        }
      }
    }
    t('modal.confirm.title');
    t('modal.confirm.actions.ok');

    嵌套层级不建议超过 4 层,过深的结构会让键名变得冗长难维护。

    格式化插值

    通过 interpolation.format 函数统一处理数字、日期、货币等格式化,避免在每个组件里各自调用 Intl API:

    // src/modern.runtime.ts
    export default defineRuntimeConfig({
      i18n: {
        initOptions: {
          interpolation: {
            escapeValue: false, // React 已默认转义文本,关闭避免重复转义
            format(value, format, lng) {
              if (format === 'currency') {
                return new Intl.NumberFormat(lng, {
                  style: 'currency',
                  currency: lng === 'zh' ? 'CNY' : 'USD',
                }).format(Number(value));
              }
              if (format === 'date') {
                return new Intl.DateTimeFormat(lng, { dateStyle: 'medium' }).format(
                  value instanceof Date ? value : new Date(value),
                );
              }
              return value;
            },
          },
        },
      },
    });

    翻译文件中用 , format 指定格式化类型:

    {
      "price": "当前价格:{{value, currency}}",
      "expiry": "到期日:{{date, date}}"
    }
    t('price', { value: 99.5 }); // → "当前价格:¥99.50"(zh)/ "$99.50"(en)
    t('expiry', { date: new Date() }); // → "到期日:2025年5月12日"(zh)

    错误处理

    加载状态处理

    使用自定义后端时,翻译资源需要异步加载,推荐用 isResourcesReady 处理加载中状态:

    import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
    import { useTranslation } from 'react-i18next';
    
    function MyComponent() {
      const { isResourcesReady } = useModernI18n();
      const { t } = useTranslation();
    
      if (!isResourcesReady) {
        return <div>加载翻译中...</div>;
      }
    
      return <div>{t('content', { defaultValue: '默认内容' })}</div>;
    }

    静态资源(HTTP/FS 后端)加载较快,通常不需要处理加载状态;如需检查,可以用 i18n.isInitialized:

    const { t, i18n } = useTranslation();
    if (!i18n.isInitialized) return null;

    翻译键缺失处理

    通过 defaultValue 提供兜底文案,防止界面显示 key 字符串:

    t('missing.key', { defaultValue: '默认文案' });

    开发环境可以开启 saveMissing,将缺失的键输出到控制台方便排查:

    initOptions: {
      fallbackLng: 'en',
      saveMissing: true, // 仅建议开发环境开启
    }

    网络失败与资源 404

    翻译文件加载失败时,i18next 会回退到 fallbackLng 对应的资源;若仍失败,t() 直接返回 key 字符串。建议:

    • 生产环境确保 fallbackLng(通常是 en)对应的翻译文件完整且稳定
    • 使用链式后端时,本地文件作为兜底,减少对远程服务的依赖

    类型安全

    通过扩展 react-i18next 的类型定义,让 TypeScript 检查翻译键是否存在并提供自动补全:

    // types/i18n.d.ts
    import 'react-i18next';
    import type translation from '../locales/en/translation.json';
    import type common from '../locales/en/common.json';
    
    declare module 'react-i18next' {
      interface CustomTypeOptions {
        defaultNS: 'translation';
        resources: {
          translation: typeof translation;
          common: typeof common;
        };
      }
    }

    直接引用 JSON 文件的类型,比手写 interface 更不容易与实际翻译文件漂移:

    const { t } = useTranslation();
    t('welcome'); // ✅ TypeScript 自动补全
    t('nonExistent'); // ❌ TypeScript 报错
    大型项目

    翻译文件较多时,手动维护类型文件成本较高。可以使用 i18next-parser 或 i18next-resources-for-ts 从翻译文件自动生成 TypeScript 类型。