前言
在 react 技术栈里怎么使用多语言开发? 一般多语言开发都是基于 i18n 相关的插件来开发多语言的,只要配置了语言源即可使用对应的语言文件翻译,react 也不例外。
在我开发的 react 项目里主要是使用react-i18next和i18next这两个库,其中最重要的就是i18next,它是一个纯 Js 框架无关的国际化语言框架。
i18next
是一个纯 JS 国际化的框架
- 管理语言资源
- 支持语言切换
- 支持嵌套翻译等
react-i18next
是一个桥梁 or 适配器,本质上是包装了i18next的一些 api+react 语法糖和响应式,可以让我们在 react 里优雅的使用 i18next,它的主要功能是:
- 提供翻译的 hooks 用于动态更新 UI 里的语言(
useTranslation - 提供
<Trans>组件,支持复杂的嵌套翻译 - 提供了
I18nextProvider
总结
i18next是翻译引擎,react-i18next则是把这个引擎嵌入到react里来
实战
先安装这两个库
npm i i18next react-i18next
然后在 src 目录新建一个 i18n 的文件夹(文件夹名字不是强绑定的可以任意取,但是为了规范还是老实用 i18n 比较好。
再从 i18n 里新建一个名为locales的文件夹,它主要是存放语言源文件,语言资源文件可以是json,js或者是ts格式的。
两者的区别简单来说js和ts更灵活,直接
// locales/en.ts
const en = {
translation: {
welcome: "Welcome",
logout: "Log out",
},
};
export default en;
而json则可以部署时分离资源。
export default {
"welcome": "Welcome"
}
由于我的项目用的是 json 格式的,所以我下面会用 json 的语言资源来讲解。
刚才创建了 locales 文件夹,此时我们可以选择要翻译的语言创建对应的文件夹(如 en/cn 等。
定义一个语言枚举用于声明本项目可供翻译的语言
export enum Language {
EN = "en_US",
Bengal = "bn_BD",
}
在 i18next 文件夹下新建一个 index.ts 文件并引入这个枚举和刚安装的这两个翻译库,引入 locales 里的语言资源。
// ./i18n/index.ts
import { initReactI18next } from 'react-i18next'
import i18n from 'i18next'
import { Language } from '@/constants/Language'
import bengal from './locales/bengal'
import en from './locales/en'
export const resources = {
[Language.EN]: {
translation: en,
},
[Language.Bengal]: {
translation: bengal,
},
}
...
export default i18n
声明一个方法用于触发手动切换语言,里面最重要的是 i18n.changLanguage(lang),其他逻辑自行添加删除。
// ./i18n/index.ts
export const handleChangeLanguage = (lang: Language) => {
localStorage.setItem("languague", lang);
i18n.changeLanguage(lang);
window.location.reload();
};
补充说明:
我的window.location.reload()是因为接手时这个项目用的是i18next的t函数来翻译而不是react-i18next的useTranslation,导致手动触发更新必须reaload。页面刷新其实用户体验是非常不好的,所以大家不要直接用i18next的t方法。
声明一个方法用于获取当前的语言 上面我每次切换语言时都把语言放到了 localstrong 里,所以我们只需要判断下每次去取 localStrong 里的值即可
// ./i18n/index.ts
export const defaultLanguage = (() => {
let lang = localStorage.getItem("languague");
if (!lang) {
lang =
process.env.NODE_ENV === "production" ? Language.Bengal : Language.EN;
localStorage.setItem("languague", lang);
}
return lang;
})();
补充:这里其实可以直接读取i18n.language,它会返回当前实时的语言,项目历史原因上面的方法也能拿的到。
给 react 项目注入 i18next
// ./i18n/index.ts
i18n.use(initReactI18next).init({
resources,
lng: defaultLanguage,
// fallbackLng: language,
// interpolation: {
// escapeValue: false,
// },
// detection: {
// caches: ['localStorage'],
// },
});
通过 i18n.use(initReactI18next)的方式加入多语言,则可以免去通过react-i18next提供的Provider挂载到根目录(注意!!!,我们使用了 i18n.use(initReactI18next)...后 下面的 provider 是不需要的,我只是补充下省得后面会忘记)
// app.ts
import { I18nextProvider } from 'react-i18next'
import i18n from '@/i18n'
...
return (
<I18nextProvider i18n={i18n}>
<App />
</I18nextProvider>
)
I18nextProvider就是给 react 包了一层 context,把i18n传递给子组件,仅此而已(所以这个代码是可有可无的。
但我们并不需要通过读取 context 里的 i18n,我们直接使用useTranslation更方面快捷。
回到项目中来,我们在入口文件里去执行 i18n 里的代码
// index.ts
import "./i18n";
直接导入即可,因为它里面自动执行了 i18n.use 。
后续在组件里使用直接useTranslation,我们也可以封装个高阶组件把useTranslation放进去。
后续有时间再更新 next(app router 模式) i18n 的方法