Skip to content

i18n 国际化

Spaceflow 基于 i18next 实现国际化,采用纯函数式设计,不依赖 NestJS DI。

语言检测优先级

  1. 环境变量 SPACEFLOW_LANG
  2. spaceflow.json 中的 lang 字段
  3. 系统 locale(process.env.LANG / process.env.LC_ALL
  4. 回退到 zh-CN

核心 API

initI18n(lang?: string)

同步初始化 i18next。必须在命令模块加载前调用(CLI 运行时会自动调用)。

typescript
import { initI18n } from "@spaceflow/core";

initI18n(); // 自动检测语言
initI18n("en"); // 手动指定

t(key: string, options?: Record<string, unknown>)

全局翻译函数,装饰器和运行时均可使用。

typescript
import { t } from "@spaceflow/core";

// 公共 key(默认命名空间)
t("common.executionFailed", { error: msg });

// Extension 命名空间(用 : 分隔)
t("build:description");
t("review:options.dryRun");

addLocaleResources(ns: string, resources: Record<string, Record<string, string>>)

注册 Extension 的语言资源到指定命名空间。

typescript
import { addLocaleResources } from "@spaceflow/core";

addLocaleResources("hello", {
  "zh-CN": { description: "打招呼命令" },
  en: { description: "Say hello" },
});

命名空间规则

类型命名空间示例
公共 key默认(translationcommon.*, config.*, extensionLoader.*
内部 ExtensionExtension 名称build:, dev:, commit:, install:
外部 ExtensionExtension 名称review:, publish:, scripts:

语言包结构

core 公共语言包

text
core/src/locales/
├── zh-cn/
│   └── translation.json    # 公共 key
└── en/
    └── translation.json

Extension 语言包

text
packages/core/src/commands/<name>/locales/
├── zh-cn/
│   └── <name>.json
├── en/
│   └── <name>.json
└── index.ts                # 导入并注册资源

Extension 中使用 i18n

1. 创建语言包文件

json
// locales/zh-cn/hello.json
{
  "description": "打招呼命令",
  "options.name": "名字",
  "greeting": "你好,{{name}}!"
}

2. 注册语言资源

typescript
// locales/index.ts
import zhCN from "./zh-cn/hello.json";
import en from "./en/hello.json";
import { addLocaleResources } from "@spaceflow/core";

export const helloLocales = { "zh-CN": zhCN, en };

// Side-effect: 立即注册
addLocaleResources("hello", helloLocales);

3. 在扩展中使用

typescript
import { defineExtension, t } from "@spaceflow/core";

// 必须在 defineExtension 之前导入 locales(side-effect)
import "./locales";

export default defineExtension({
  name: "hello",
  commands: [
    {
      name: "hello",
      description: t("hello:description"),
      run: async (args, options, ctx) => {
        ctx.output.info(t("hello:greeting", { name: args[0] || "World" }));
      },
    },
  ],
});

插值语法

使用 i18next 默认的 语法:

typescript
t("hello:greeting", { name: "World" });
// → "你好,World!"

注意事项

  • 同步初始化initI18n() 使用 initSync,确保模块加载时语言包已就绪
  • 加载时机initI18n() 由 CLI 运行时的 exec() 自动调用,无需手动处理
  • Side-effect import — 每个 Extension 的 locales/index.ts 在导入时立即调用 addLocaleResources
  • key 不存在时 — i18next 默认返回 key 本身,不会报错

基于 MIT 许可发布