@embra/i18n (英语)
轻量级活性国际化图书馆.
安装
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`.