一、简介
i18next
是一个用 JavaScript 编写的国际化框架。i18next 不仅仅是提供标准的 i18n 功能,例如 复数, 上下文,插值,格式。它提供了一个完整的解决方案。这篇文章不是专门解读其 api, 主要梳理使用流程,进一步降低 React 中,国际化使用的入门门槛。
二、资源
- i18next 是一个使用 JavaScript 写的国际化框架。
- react-i18next 是一个基于 i18next 的强大的国际化 React/React Native 包。
- locize 持续本地化管理平台,助力您的开发和翻译。
- next-i18next 翻译 NextJs 应用的最简单方法。
- remix-i18next 翻译 Remix 应用的最简单方法。
- ...
三、最简单的使用流程
- 安装依赖
- 定义 options 并调用 init 函数初始化
- 获取翻译
t
函数 - 在 React 中使用 i18n
##四、 安装依赖
- i18next 核心包:
sh
pnpm add i18next
- react 相关包
sh
pnpm add react-i18next
五、初始化 init 函数
传入一个对象 initOptions 进行初始化:
ts
import i18next from 'i18next';
i18next.init(initOptions, callback?);
// 基础 t 函数翻译
i18next.t('your_config_options_key');
除此使用之外还支持三种使用方式:callback/promise/async-await
都是 javascript 种经典的使用模式。
六、dom 示例
使用 async-await 的异步方式
ts
await i18next.init({
lng: 'en', // if you're using a language detector, do not define the lng option
debug: true,
resources: {
en: {
translation: {
"key": "hello world"
}
}
}
});
// initialized and ready to go!
document.getElementById('output').innerHTML = i18next.t('key');
七、react 示例
- 初始化 i18n:
ts
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
i18n
.use(initReactI18next) // passes i18n down to react-i18next
.init({
resources: {
en: {
translation: {
"Welcome to React": "Welcome to React and react-i18next"
}
}
},
lng: "en", // if you're using a language detector, do not define the lng option
fallbackLng: "en",
interpolation: {
escapeValue: false // react already safes from xss => https://www.i18next.com/translation-function/interpolation#unescape
}
});
- 在 React 组件中使用 useTranslation hooks 中获取 t 翻译函数:
ts
import React from "react";
import { createRoot } from 'react-dom/client';
import { useTranslation } from "react-i18next";
function App() {
const { t } = useTranslation();
return <h2>{t('Welcome to React')}</h2>;
}
const root = createRoot(document.getElementById('root'));
root.render(
<App />
);
- withTranslation 高阶函数
- Translation 组件 props
- Trans 组件
八、I18nextProvider 组件
ts
import { I18nextProvider } from 'react-i18next';
import i18n from './i18n';
import App from './App';
<I18nextProvider i18n={i18n}>
<App />
</I18nextProvider>
九、SSR with hooks
ts
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import { I18nextProvider } from 'react-i18next';
import i18n from './i18n';
import { InitSSR } from './InitSSR';
const initialI18nStore = i18n.services.resourceStore.data;
const initialLanguage = 'en';
const appMarkup = ReactDOMServer.renderToString(
<I18nextProvider i18n={i18n}>
<InitSSR initialI18nStore={initialI18nStore} initialLanguage={initialLanguage} />
</I18nextProvider>
);
const html = `
<html>
<body>
<div id="app">${appMarkup}</div>
</body>
</html>
`;
当然后除了 hooks 的方式,也提供了 withSSR
作为高阶函数 HOC 来处理服务端渲染。因为现在主流是 hooks, 高阶函数九不再复述了。
九、TypeScript 与 resources 配合
定义 i18next.d.ts 声明文件:
ts
import "i18next";
import ns1 from "locales/en/ns1.json";
import ns2 from "locales/en/ns2.json";
declare module "i18next" {
interface CustomTypeOptions {
defaultNS: "ns1";
resources: {
ns1: typeof ns1;
ns2: typeof ns2;
};
}
}
typescript 在我们使用 t
函数进行翻译的时候十分有用。ns 使用 typeof 获取当前的键值。在翻译时,能取得良好的代码提示效果。
十、i18next 插件
i18next 使用 use 方法进行插件处理。
- 常用插件安装
sh
pnpm add i18next-fs-backend i18next-browser-languagedetector i18next-http-backend react-i18next
- 使用插件
ts
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
i18n
.use(initReactI18next) // 初始化React组件
.init({
resources: {
// 在这里配置您的翻译资源
},
lng: 'en', // 设置默认语言
fallbackLng: 'en', // 设置回退语言
});
export default i18n;
- Backend 插件使用示例
ts
import i18next from 'i18next';
import Backend from 'i18next-http-backend';
i18next.use(Backend).init({
backend: {
loadPath: '/locales/{{lng}}/{{ns}}.json', // 指定翻译资源的加载路径
},
lng: 'en', // 设置默认语言
fallbackLng: 'en', // 设置回退语言
});
i18n
使用 use
方法添加插件,此处以 Backend
为例,可以定义 backend
字段,通过 i18next-http-backend
来定义字段中的内容文件路径。
十一、其他国际化方案
- react-intl 用于国际化 React 组件,提供 React 组件和 API 来格式化日期,数字,字符串(包括单复数和翻译)。
- FormatJS 在客户端和服务器上国际化您的 Web 应用程序。
十二、小结
本文主要讲解了 i18n
和 i18next
以及 React 相关包的用法, 梳理的使用流程、重点关注初始化和插件的用法,以及 TypeScript 使用 typeof 的支持类型,国际化在 Web 中是一个难题,但是社区已经做了很好的支持,在大多数情况下我们不用再重新造轮子。