本地化你的 React 应用的 Tailwind 方式:无需键值和 JSON,只用代码
Source: Dev.to
请提供您希望翻译的正文内容(除代码块和 URL 之外的文字),我将把它翻译成简体中文并保持原有的 Markdown 格式。
那么,你想本地化你的应用
大多数 i18n 方案会把你引向一种熟悉的模式:
- 巨大的 JSON/YAML 文件
- 通过键名繁琐地“命名”你的翻译
- 虚构的字符串插值语法
- 不断在 UI 代码和翻译文件之间来回切换
这虽然可行,但随着时间的推移,这种繁琐的流程会让人感到有些乏味。
我记得几年前,我第一次接触 Tailwind CSS 时,惊讶于只写必要内容后,我能够更快地交付真实的 UI。我希望在 i18n 上也能获得同样的体验。
介绍 react‑scoped‑i18n 🌐
核心思想很简单:不再使用全局翻译文件和键名,而是把翻译直接放在渲染它们的组件旁边。
i18n 通常是怎样的?
// en.json
{
"profile": {
"header": "Hello, {{name}}"
}
}
// es.json
{
"profile": {
"header": "Hola, {{name}}"
}
}
// Header.tsx
export const Header = () => {
const { t } = useI18n();
return (
<h1>{t("profile.header", { name: "John" })}</h1>
);
};
这种方法可行且经过实战检验,但也带来了一些痛点:
- 没有额外工具时,TypeScript 并不能提供太多帮助。
- 翻译键几乎不提供关于实际渲染内容的上下文。
- 无法在代码库中搜索渲染的文本并直接找到使用它的组件。
- 字符串插值使用自定义语法。
“scoped” 方法是什么样的?
// Header.tsx
export const Header = () => {
const { t } = useI18n();
const name = "John";
return (
<h1>{t({
en: `Hello, ${name}`,
es: `Hola, ${name}`,
})}</h1>
);
};
没有键,没有庞大的 JSON 文件,也没有自定义插值语法——只有纯代码!
注意: 这并不是要取代完整的 i18n 平台。它是为需要快速本地化的代码驱动应用提供的一种替代方案。
这种方法的最大好处
类型安全 🩵
因为它只是代码,所有内容都是类型安全的。
createI18nContext({
languages: ["en", "es", "sl"],
fallbackLanguage: "en", // inferred from `languages` above
});
如果缺少翻译,TypeScript 会在编译时提示:
// ❌ Property 'es' is missing in type { en: string; sl: string; }
t({
en: "Hello",
sl: "Pozdravljeni",
});
缺失的翻译或不支持的语言会成为编译时错误,而不是运行时的意外。 😄
不需要为键命名
内容本身就是键。搜索渲染后的文本会直接定位到对应的组件——极大提升开发体验。 😊
无额外构建步骤
该库在 React Context 生态系统中直接工作。安装后,初始化上下文,用导出的 Provider 包裹你的应用,即可使用。
开箱即用的数字、日期、时间和货币格式化 ‼️
react‑scoped‑i18n 利用原生 Intl API,只要使用标准的地区标识符(en、en‑GB、es、es‑ES …),即可获得完整的格式化支持,无需额外配置。
类型安全的“共享”翻译
对于在整个应用中共享的字符串,请通过 commons API 定义它们:
createI18nContext({
languages: ["en", "es"],
fallbackLanguage: "en",
commons: {
continue: {
en: "Continue",
es: "Continuar",
},
},
});
然后以类型安全的方式使用它们,支持 IDE 自动完成:
{t(commons.continue)}
复数化(受 ICU 启发,但不严格遵循 ICU)
tPlural 允许每种语言仅定义它实际需要的类别。
{tPlural(count, {
en: {
one: `You have one apple.`,
many: `You have ${count} apples.`,
},
sl: {
one: `Imaš eno jabolko.`,
two: `Imaš dve jabolki.`, // dual form in Slovenian
many: `Imaš ${count} jabolk.`,
},
})}
你也可以针对 ICU 类别之外的特定数值:
{tPlural(count, {
en: {
one: `You have one apple.`,
many: `You have ${count} apples.`,
42: `You have THE PERFECT amount of apples!`,
},
es: {
one: `Tienes una manzana.`,
many: `Tienes ${count} manzanas.`,
},
})}
当这不是合适的选择
(继续你的原始内容……)
合适的情况
这种方法从设计上就是以开发者为中心的,因此如果你的工作流依赖于:
- 外部翻译人员
- “Crowdin”、 “Lokalise” 或类似工具
- 非技术编辑者编辑翻译文件
- 大量 支持的语言
那么这可能不是合适的方法。
但是如果翻译是以代码形式编写和维护的,并且你的支持语言数量是 中小规模,开发者体验会出乎意料地好。
如果你感兴趣,项目是开源的。你可以查看:
- GitHub:
- npm:
欢迎提供任何反馈!