# @embra/i18n 中文文档

规范地址：https://npmcn.com/package/%40embra/i18n/

版本：0\.1\.0
npm 元数据更新时间：2026-07-23T02:52:57.166Z
中文译文更新时间：2026-11-13T15:10:47.159Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40embra/i18n
原文对照：https://npmcn.com/package/%40embra/i18n/?lang=original
项目主页：https://github.com/embrajs/i18n#readme
源码仓库：https://github.com/embrajs/i18n

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：65 kB，24 个文件
- 模块格式：ESM + CJS
- TypeScript 类型：包内自带
- 运行依赖：0 个
- peer 依赖：@embra/reactivity \<=1，@types/react \>=18\.0\.0，astro \>=4\.0\.0，react \>=18\.0\.0
- 首次发布：2025-10-05T17:11:11.093Z
- 最新版本发布：2026-07-23T02:52:56.811Z
- 近 30 天下载量：11435（2026-10-14 至 2026-11-12，npm 官方下载统计）
- 已发布版本数：11（近 12 个月 7 个）

## 中文简介

轻量级活性国际化图书馆.

## 安装

```sh
npm install @embra/i18n
```

也可以使用：`pnpm add @embra/i18n`、`yarn add @embra/i18n`、`bun add @embra/i18n`

## README

# [@embra/i18n (英语)](https://github.com/embrajs/i18n)

![](https://raw.githubusercontent.com/embrajs/i18n/main/assets/logo.svg)

[![Docs](https://img.shields.io/badge/Docs-read-%23fdf9f5)](https://embrajs.github.io/i18n) [![Build Status](https://github.com/embrajs/i18n/actions/workflows/build.yml/badge.svg)](https://github.com/embrajs/i18n/actions/workflows/build.yml) [![npm-version](https://img.shields.io/npm/v/@embra/i18n.svg)](https://www.npmjs.com/package/@embra/i18n) [![Coverage Status](https://embrajs.github.io/i18n/coverage-badges/@embra/i18n.svg)](https://embrajs.github.io/i18n/coverage/)

轻量级活性国际化图书馆.

![export size](https://embrajs.github.io/i18n/assets/export-size.svg)

## 安装

```bash
npm add @embra/reactivity @embra/i18n
```

## 特征

-   可分解反应 `lang$`, (中文(简体) ), `t$` 财务报告和审定财务报表 `locales$`。 。 。 .
-   轻而易举 `t()` 翻译.
-   巢穴地语信息.
-   消息格式化和复数化.
-   方便的动态本地装入 .
-   地方命名空间.
-   一等反应支持.

## 使用量

创建静态本地化的 I18n 实例 :

```ts
import { I18n, type Locales } from "@embra/i18n";

const locales: Locales = {
  en: {
    stock: {
      fruit: "apple",
    },
  },
};

const i18n = new I18n("en", locales);
i18n.t("stock.fruit"); // apple

// add more locales later
const zhCN = await import(`./locales/zh-CN.json`);
i18n.addLocale("zh-CN", zhCN);

// or replace all locales manually
const zhTW = await import(`./locales/zh-TW.json`);
i18n.locales$.set({ "zh-TW": zhTW });
```

您也可以创建 `I18n` 带有预装动态位置的示例 :

```ts
import { I18n } from "@embra/i18n";

const i18n = await I18n.preload("en", lang => import(`./locales/${lang}.json`));
// Locale `./locales/en.json` is preloaded

await i18n.switchLang("zh-CN"); // Locale `./locales/zh-CN.json` is loaded
```

### 检测语言

您可以通过 : `detectLang`。 。 。 . [BCP 47 标签和子标签](https://www.rfc-editor.org/rfc/rfc4647.html#section-3.4) 被支持.

```ts
import { detectLang } from "@embra/i18n";

detectLang(); // "en-US"

const i18n = await I18n.preload(
  // language sub-tag is matched
  detectLang(["en", "zh-CN"]) || "zh-TW", // "en"
  lang => import(`./locales/${lang}.json`),
);
```

### 信件格式

信件密钥被双卷括号所包围 :

```ts
import { I18n, type Locales } from "@embra/i18n";

const locales: Locales = {
  en: {
    stock: {
      fruit: "apple",
    },
    fav_fruit: "I love {{fruit}}",
  },
};

const i18n = new I18n("en", locales);
const fruit = i18n.t("stock.fruit"); // apple
i18n.t("fav_fruit", { fruit }); // I love apple
```

没有相应值的占位符被保存. 这使其余的占位符可以在稍后处理,例如由 [`Trans` 组件](https://npmcn.com/package/%40embra/i18n/#readme-trans-component)数字 :

```ts
i18n.t("fav_fruit", {}); // I love {{fruit}}
```

它还与阵列配合:

```ts
import { I18n, type Locales } from "@embra/i18n";

const locales: Locales = {
  en: {
    fav_fruit: "I love {{0}} and {{1}}",
  },
};

const i18n = new I18n("en", locales);
i18n.t("fav_fruit", ["apple", "banana"]); // I love apple and banana
```

### 多元化

很容易用 [修改匹配](https://npmcn.com/package/%40embra/i18n/#readme-modifier-matching)。 。 。 .

```ts
import { I18n, type Locales } from "@embra/i18n";

const locales: Locales = {
  en: {
    apple: "{{@}} apples", // fallback
    "apple@0": "No apple", // matching { "@": 0 } or { "@": "0" }
    "apple@1": "An apple", // matching { "@": 1 } or { "@": "1" }
  },
};

const i18n = new I18n("en", locales);
i18n.t("apples", { "@": 0 }); // No apple
i18n.t("apples", { "@": 1 }); // An apple
i18n.t("apples", { "@": 3 }); // 3 apples
```

### 修改匹配

你用得着 `@` 以匹配不同的修饰符.

例如:

```ts
i18n.t("a.b.c", { "@": "d" });
```

它会寻找 `"a.b.c@d"` 返回到 `"a.b.c"` 如果找不到的话.

见 [多元化](https://npmcn.com/package/%40embra/i18n/#readme-pluralization) 需要更多的实例.

### 反应 I18n

`i18n.lang$`, (中文(简体) ), `i18n.t$` 财务报告和审定财务报表 `i18n.locales$` 是可分解的值.

见 [@embra/反应](https://github.com/embrajs/reactivity) 更多细节.

```js
i18n.lang$.reaction(lang => {
  // logs lang on changed
  console.log(lang);
});

i18n.lang$.subscribe(lang => {
  // logs lang immediately and on changed
  console.log(lang);
});
```

### 命名空间

I18n实例可以便宜地创造. 您可以为不同的命名空间创建多个实例 .

```ts
import { I18n } from "@embra/i18n";

// Module Login
async function moduleLogin() {
  const i18n = await I18n.preload("en", lang => import(`./locales/login/${lang}.json`));

  console.log(i18n.t("password"));
}

// Module About
async function moduleAbout() {
  const i18n = await I18n.preload("en", lang => import(`./locales/about/${lang}.json`));

  console.log(i18n.t("author"));
}
```

## 热模块替换

用于 [维特高棉](https://vitejs.dev/guide/api-hmr.html) 本地地址 :

```ts
const i18n = await I18n.preload("en", lang => import(`./locales/${lang}.json`));

if (import.meta.hot) {
  import.meta.hot.accept(["./locales/en.json", "./locales/zh-CN.json"], ([en, zhCN]) => {
    i18n.locales$.set({
      ...i18n.locales,
      en: en?.default || i18n.locales.en,
      "zh-CN": zhCN?.default || i18n.locales["zh-CN"],
    });
  });
}
```

## 动态导入

尽管你可以简单地使用 `import()` 以动态加载本地语,使用捆绑的 API 可以做更多的.

例如,用Vite 就可以使用 [glob 导入](https://vitejs.dev/guide/features.html#glob-import) 静态获取所有本地的信息. 这样您就可以在不更改源代码的情况下添加或删除地块 .

```ts
import { I18n, detectLang, type Locale, type LocaleLang } from "@embra/i18n";

export const i18nLoader = (): Promise<I18n> => {
  const localeModules = import.meta.glob<boolean, string, Locale>("./locales/*.json", { import: "default" });

  const localeLoaders = Object.keys(localeModules).reduce(
    (loaders, path) => {
      if (localeModules[path]) {
        const langMatch = path.match(/\/([^/]+)\.json$/);
        if (langMatch) {
          loaders[langMatch[1]] = localeModules[path];
        }
      }
      return loaders;
    },
    {} as Record<LocaleLang, () => Promise<Locale>>,
  );

  const langs = Object.keys(localeLoaders);

  return I18n.preload(detectLang(langs) || (localeLoaders.en ? "en" : langs[0]), lang => localeLoaders[lang]());
};
```

## 反应

-   `<I18nProvider>` 提供 `i18n` 上下文为子孙成分.
-   `useTranslate` 选中此项以订阅并获取最新消息 `i18n.t`。 。 。 .
-   `useLang` 选中此项以订阅并获取最新消息 `i18n.lang`。 。 。 .
-   `useI18n` 选中此项以订阅并获取最新消息 `i18n` 举个例子.
-   `<Trans>` 在翻译模板信件中插入反应元素的组件.

```tsx
import { I18n } from "@embra/i18n";
import { I18nProvider, useTranslate } from "@embra/i18n/react";

const i18n = new I18n("en", { en: { fruit: "apple" } });

const MyComponent = () => {
  const t = useTranslate();
  return <div>{t("fruit")}</div>;
};

const App = () => {
  return (
    <I18nProvider i18n={i18n}>
      <MyComponent />
    </I18nProvider>
  );
};
```

### 分层 I18n 供应商

你可以筑多个巢 `<I18nProvider>`s)创建相干i18n上下文.

```tsx
import { I18n } from "@embra/i18n";
import { I18nProvider, useTranslate } from "@embra/i18n/react";

const baseI18n = new I18n("en", { en: { confirm: "Confirm" } });
const loginI18n = new I18n("en", { en: { login: "Login" } });

const MyComponent = () => {
  const t = useTranslate();
  return (
    <div>
      <button>{t("confirm")}</button>
      <button>{t("login")}</button>
    </div>
  );
};

const App = () => {
  return (
    <I18nProvider i18n={baseI18n}>
      <I18nProvider i18n={loginI18n}>
        <MyComponent />
      </I18nProvider>
    </I18nProvider>
  );
};
```

### 切换组件

在翻译信件中插入反应元素:

```tsx
import { I18n, I18nProvider } from "@embra/i18n";
import { Trans, useTranslate } from "@embra/i18n/react";

const locales = {
  en: {
    author: "CRIMX",
    fruit: "apple",
    eat: "{{name}} eats {{fruit}}.",
  },
};

const i18n = new I18n("en", locales);

const MyComponent = () => {
  const t = useTranslate();

  return (
    <Trans message={t("eat")}>
      <strong data-t-slot="name">{t("author")}</strong>
      <i style={{ color: "red" }} data-t-slot="fruit">
        {t("fruit")}
      </i>
    </Trans>
  );
};

const App = () => {
  return (
    <I18nProvider i18n={i18n}>
      <MyComponent />
    </I18nProvider>
  );
};
```

输出 :

```tsx
<>
  <strong data-t-slot="name">CRIMX</strong> eats <i style={{ color: "red" }} data-t-slot="fruit">apple</i>.
<>
```

`data-t-slot` 如果只有一个占位符,则可以忽略.

```tsx
<Trans message="a{{b}}c">
  <h1>B</h1>
</Trans>
```

输出 :

```tsx
<>
  a<h1>B</h1>c
</>
```

## 天体

对于静态Astro页面,创建缓存的翻译帮助并获取 `t` 从路由语言:

```astro
---
import { createStaticI18n } from "@embra/i18n/astro";

const locales = {
  en: {
    title: "Fruit stock",
    author: "CRIMX",
    fruit: "apple",
    eat: "{{name}} eats {{fruit}}.",
  },
  zh: {
    title: "水果库存",
    author: "CRIMX",
    fruit: "苹果",
    eat: "{{name}} 吃 {{fruit}}。",
  },
};

const { getT } = createStaticI18n({ locales, fallback: "en" });
const t = getT(Astro.params.lang);
---

<h1>{t("title")}</h1>
```

`fallback` 当 `lang` 被省略,并在缺少密钥时作为后退语言.

使用天体 `<Trans>` 当翻译信件需要Astro槽时:

```astro
---
import { createStaticI18n, Trans } from "@embra/i18n/astro";

const { getT } = createStaticI18n({ locales, fallback: "en" });
const t = getT(Astro.params.lang);
---

<Trans message={t("eat")}>
  <strong slot="name">{t("author")}</strong>
  <i slot="fruit" style="color: red">{t("fruit")}</i>
</Trans>
```

输出 :

```html
<strong>CRIMX</strong> eats <i style="color: red">apple</i>.
```

当只有一个占位符时,可以使用默认槽:

```astro
<Trans message="a{{b}}c">
  <strong>B</strong>
</Trans>
```

`@embra/i18n/astro` 由 Astro 在构建时或服务器上提供。 它不单独添加客户端语言切换;如果翻译组件需要在浏览器中更新,则使用框架岛.

## VSCode 扩展

`@embra/i18n` 与 [i18n 联盟](https://github.com/lokalise/i18n-ally),用于i18n的受欢迎的VSCode扩展. 您可以使用它来管理您的本地文件 .

## 为 AI 编码代理

`@embra/i18n` 包含 [页:1](https://npmcn.com/package/%40embra/i18n/llms.txt) aI编码代理快速指南.

将此添加到 `AGENTS.md` 或者说 `CLAUDE.md` 应使用i18n执行的项目 `@embra/i18n`数字 :

```md
Implement internationalization with `@embra/i18n`. Usage reference: `node_modules/@embra/i18n/llms.txt`.
```

## 常见问题

### @embra/i18n 是什么？

轻量级活性国际化图书馆\.

### 如何安装 @embra/i18n？

在项目目录运行 npm install @embra/i18n，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：@embra/reactivity（\<=1）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @embra/i18n 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @embra/i18n 支持 ESM 还是 CommonJS？

同时提供 ES Module 与 CommonJS 入口，import 和 require\(\) 都可以使用，打包工具会自动选择合适的版本。

### @embra/i18n 可以商用吗？

@embra/i18n 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @embra/i18n 的下载量有多少？

根据 npm 官方下载统计，@embra/i18n 在 2026/10/14 – 2026/11/12 这 30 天内被下载 11,435 次，日均约 381 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @embra/i18n 还在维护吗？

最新版本 0\.1\.0 发布于 2026/07/23（3 个月前）；至今共发布 11 个版本，其中过去 12 个月发布了 7 个；首次发布于 2025/10/06。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

