@embra/i18n

中文文档
0.1.0发布于 2026/07/23MITTypeScriptESM + CJS零依赖构建来源已验证近 30 天下载 1.1万

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

中文使用文档
文档目录17 节
  1. 安装
  2. 特征
  3. 使用量
  4. 检测语言
  5. 信件格式
  6. 多元化
  7. 修改匹配
  8. 反应 I18n
  9. 命名空间
  10. 热模块替换
  11. 动态导入
  12. 反应
  13. 分层 I18n 供应商
  14. 切换组件
  15. 天体
  16. VSCode 扩展
  17. 为 AI 编码代理

@embra/i18n (英语)

Docs Build Status npm-version Coverage Status

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

export size

安装

npm add @embra/reactivity @embra/i18n

特征

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

使用量

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

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 带有预装动态位置的示例 :

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 标签和子标签 被支持.

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`),
);

信件格式

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

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 组件数字 :

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

它还与阵列配合:

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

多元化

很容易用 修改匹配。 。 。 .

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

修改匹配

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

例如:

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

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

见 多元化 需要更多的实例.

反应 I18n

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

见 @embra/反应 更多细节.

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实例可以便宜地创造. 您可以为不同的命名空间创建多个实例 .

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

热模块替换

用于 维特高棉 本地地址 :

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 导入 静态获取所有本地的信息. 这样您就可以在不更改源代码的情况下添加或删除地块 .

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> 在翻译模板信件中插入反应元素的组件.
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上下文.

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

切换组件

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

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

输出 :

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

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

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

输出 :

<>
  a<h1>B</h1>c
</>

天体

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

---
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槽时:

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

输出 :

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

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

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

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

VSCode 扩展

@embra/i18n 与 i18n 联盟,用于i18n的受欢迎的VSCode扩展. 您可以使用它来管理您的本地文件 .

为 AI 编码代理

@embra/i18n 包含 页:1 aI编码代理快速指南.

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

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

版本与发布记录

@embra/i18n 共发布 11 个版本,首次发布于 2025/10/06;过去 12 个月发布了 7 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.1.0latest2026/07/233 个月前
0.0.102026/06/025 个月前
0.0.92026/05/315 个月前
0.0.82026/05/295 个月前
0.0.72026/04/037 个月前
0.0.62026/03/118 个月前

关于 @embra/i18n 的常见问题

@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 和提交记录判断。