React + i18next + antd组件国际化
前言:现在流行的国际化组件有非常多,各种国际化的方向也非常多,比如:React-i18next 和 react-intl, React-i18next主要是用于文字翻译,react-intl主要用于数字,符号等翻译。 antd 国际化的是组件, 只提供部分组件的国际化,它不支持非组件的文字国际化。因此,需要国际化组件, 还需要将这两种方案结合起来。
-.React-i18next
1.模块介绍
首先,它是一个基于i18next的强大的国际化框架,常用于各种框架,Vue, React, React-native应用。
主要特点:
-
适用广泛,学习一种方案,也可以在其它框架找到同构的国际化库。
其中 i18n 是一个比较大的系统组件, 它在react 上的库为 React-i18n , vue上的库为 vue-i18n.
不同的框架,对应的代码也会有所差异。
-
可以用于服务端渲染,提供不同的方式进行国际化语言切换。
-
提供插件支持,可以使用插件检测当前的语言环境。
2.开始使用
上面说了那么多,还不如实际操作一遍。
(1.安装环境
yarn add react-i18next i18next
/*下方模块用于检测当前浏览器的语言或者从服务器中获取配置资源*/
yarn add i18next-http-backend i18next-browser-languagedetector
(2.创建资源文件
在项目下创建 Public/ locales/ en-us.json 文件
注意:这里必须是json文件, json文件 类似于字面量对象,但它的键必须使用引号
{
"test words":"测试文字",
"home":"首页",
"welcome":"欢迎来首页"
}
在项目下创建Public/ locales/zh-cn.json 文件
{
"测试文字":"test words",
"home":"Home",
"welcome":"Welcome To Home"
}
在这里声明一下:
-
建议使用第一种方式(第2行)声明语言信息,
可以有效减少命名的困扰
, 对代码的可读性也是比较大的提高。 -
第二种方式(第3,4行)是通过声明变量的方式和访问 语言信息。 在使用国际化的时候,就必须使用这个 变量去获取对应的信息。如果需要国际化的数据太多,则不利于去命名变量的,对可读性也是比较大的影响。
(3.初始化配置
在项目下创建Public/config/i18n.jsx 文件, 其实jsx文件 和js文件后缀都没有什么区别
import LanguageDetector from 'i18next-browser-languagedetector';
import Backend from 'i18next-http-backend';
import i18n from "i18next"; // i18n 的主要模块
import enUsTrans from "../locales/en-us.json";
import zhCnTrans from "../locales/zh-cn.json";
import { initReactI18next } from 'react-i18next';
i18n
.use(Backend) // 检测当前浏览器的语言或者从服务器获取配置资源,不过也没有什么用处
.use(LanguageDetector) //嗅探当前浏览器语言
.use(initReactI18next) //init i18next
.init({
//引入资源文件
resources: {
en: {
translation: enUsTrans, // 引入json文件
// translation: {
// "home":'aaa', // 单独的json语句
// "你好":"hello"
// },
},
zh: {
translation: zhCnTrans,
},
},
//选择默认语言,选择内容为上述配置中的key,即en/zh
fallbackLng: 'zh',
debug: false,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
},
})
export default i18n;
(4.切换语言
引入初始化后的I18n文件 ,事件触发后,对 缓存 中默认的语言进行变更。
没错,react-i18n
是具有缓存的,它可以将更改事件与 显示数据分离。它内置有一个全局变量用于保存当前的语言环境,所以我们不需要使用 localStorage
或者 redux
在项目下创建Public/config/ChangeI18n.jsx ,使用国际化
import React, { Component,useState,useEffect } from 'react';
import { useTranslation, Trans, Translation } from 'react-i18next'
import i18n from './i18n';
function ChangeI18n() {
// 注意: 如果使用非响应式数据,当数据变化后 ,页面将不更新。
const [pre_lang,setPreLang] = useState("zh"); // 上一次保存的语言
const [pre_name,setPreName] = useState("切换英文"); // 上一次保存的名称
// 加载翻译组件, 可以在 i18n组件中引入, 也可以在useTranslation中引入
// let {i18n} = useTranslation() // 加载i18n组件
// 切换语言
const changeLang = ()=>{
if(pre_lang.toString() == 'zh') {
i18n.changeLanguage('en') // 更改i18n语言
setPreLang("en")
setPreName("切换中文")
} else {
i18n.changeLanguage('zh')
setPreLang("zh")
setPreName("切换英文")
}
}
// 加载组件,用于翻译,但不限于放在哪一个组件。
let { t } = useTranslation() // 加载组件
return (
<div>
<h1>下面是i18n使用的切换功能组件</h1>
<button onClick={changeLang}>
{pre_name}
</button>
{/* 3种常用使用方式 */}
<h2><Trans>home</Trans></h2>
<h1>{t('home')}</h1>
<Translation>
{t => <h3>{t('home')}</h3>}
</Translation>
</div>
)
}
export default ChangeI18n;
显示结果
- antd 国际化
1.直接引入模块
antd国际化,只对特定的几个组件有效。例如 : DatePicker, TimePicker, Canlendar, Select, Pagenation 等等。
记住,Button组件上的文字是不会改变的。
在项目下创建Public/config/ChangeAntd.jsx ,使用国际化
import React, { Component,useState } from 'react';
import enUS from 'antd/lib/locale/en_US';
import zhCN from 'antd/lib/locale/zh_CN'; // 引入语言包
import moment from 'moment';
import 'moment/locale/zh-cn';
import { ConfigProvider, Radio,DatePicker } from 'antd';
import Header from '../../Components/Header'
function ChangeAntd(){
const [locale,setLocale] = useState("enUS")
// 切换语言
const changeLocale = e => {
const localeValue = e.target.value;
setLocale(localeValue);
if (!localeValue) { // 目标为切换至 英语
moment.locale('en');
} else { // 目标为切换至 中文
moment.locale('zh-cn');
}
};
return (
<div>
<div className="change-locale">
<h1>下面是antd使用的切换功能组件</h1>
<Radio.Group value={locale} onChange={changeLocale}>
<Radio.Button key="en" value={enUS}>
English
</Radio.Button>
<Radio.Button key="cn" value={zhCN}>
中文
</Radio.Button>
</Radio.Group>
</div>
{/* antd国际化: 只对部分组件有效 */}
<ConfigProvider locale={locale}>
<DatePicker />
</ConfigProvider>
</div>
);
}
export default ChangeAntd;
显示结果:
- React-i18next + antd国际化
先说一下上面的两种实现吧!
React-i18next是有创建了一个i18n.jsx的 文件,里面对i18n初始化,并加载了对应的资源文件。然后有一个api用于切换状态, i18n.changeLanguage
, 切换状态后,可以在任何的组件内引入 **react-i18next
**模块,并可以使用三种方式显示转换结果。
antd国际化,只是国际化部分特殊的组件,主要是因为部分组件不利于自定义。 比如:日历。 但其它的需要国际化的内容,就需要自己安装组件。
这部分只是对这两种国际化进行组装,并对事件触发与结果显示分离,做成一个真正的语言切换的功能模块
组成部分:
- i18n.jsx 初始化文件
- en-us.json 和zh-cn.json 资源文件
- MyButton.jsx 事件触发文件
- Header.jsx 显示结果文件
- App.jsx 用于主要操作的模块。
1.开始生产轮子
上面的 **i18n.jsx和 en-us.json, zh-cn.json ** 不做任何修改,直接拿来复用。
(1.在项目下创建Components/MyButton.jsx文件, 用于事件触发
这个文件只用于事件触发,不涉及任何数据更改操作。
父组件 App.js向子组件传入一个函数, 子组件事件触发后,调用父组件的函数,本质上还是父组件在操作数据。
import React, { Component } from 'react';
function MyButton(props){
return (
<div>
<button onClick={props.changeLang}>{props.name}</button>
</div>
)
}
export default MyButton;
(2.在项目下创建Components/Header.jsx文件, 用于显示结果
这将数据修改与显示分离,不仅减少了多余的文件加载, 而且也使得结构更加清晰。
import React, { Component } from 'react';
import { DatePicker, TimePicker, Select, } from 'antd';
import { useTranslation, Trans, Translation } from 'react-i18next'
// import '../Public/config/i18n';
const { Option } = Select;
const { RangePicker } = DatePicker;
function Header(){
let { t } = useTranslation() // 加载组件
return (
<div className="">
<div className="example">
<hr />
<h2>下面是测试结果</h2>
<div>{t("home")}</div>
<div>{t("测试文字")}</div>
<DatePicker />
<TimePicker />
</div>
</div>
);
}
export default Header;
2.做一个核心部件
App.jsx 做主要文件。
有两个原因:
-
一是 i18n显示的位置, 如果把事件放在一个单独的文件,我们需要以那个文件为主要切换文件,对布局是非常大的困扰,特别是需要再分离出一个事件触发的组件。那结果注定需要以那个文件为 布局的主要入口文件。
-
二是antd国际化的性质。 antd国际化组件,需要使用
ConfigProvider
标签包裹,并添加 语言文件,如果是需要动态添加组件,那必须在每一个组件外面都包裹这层标签。
即使使用localStorage或者 redux 动态引入 语言文件,也会造成多次重复引入文件,导致性能的极大消耗。
import React, { Component,useState,useEffect } from 'react';
import enUS from 'antd/lib/locale/en_US'; // 引入antd 语言包
import zhCN from 'antd/lib/locale/zh_CN';
import moment from 'moment';
import 'moment/locale/zh-cn';
import i18n from './Public/config/i18n'; // 引入 i18n 初始化模块
import { ConfigProvider } from 'antd';
import Header from './Components/Header';
import MyButton from './Components/MyButton';
function App(){
// 注意: 如果使用非响应式数据,当数据变化后 ,页面将不更新。
const [pre_lang,setPreLang] = useState("zh"); // 上一次保存的语言
const [pre_name,setPreName] = useState("切换英文"); // 上一次保存的名称
const [locale,setLocale] = useState(zhCN); // 上一次保存的名称
// 加载翻译组件, 可以在 i18n组件中引入, 也可以在useTranslation中引入
// let {i18n} = useTranslation() // 加载i18n组件
// 引入组件的翻译, 是需要使用变量的形式引入 {} , 而并非是静态的字符串
// 切换语言
const changeLang = ()=>{
if(pre_lang.toString() == 'zh') {
i18n.changeLanguage('en') // 更改i18n语言
moment.locale('en'); // 更改antd组件语言
setPreLang("en")
setPreName("切换中文")
setLocale(enUS) // 引入antd组件依赖的模块
} else {
i18n.changeLanguage('zh')
moment.locale('zh-cn');
setPreLang("zh")
setPreName("切换英文")
setLocale(zhCN)
}
}
return (
<div className="change-locale">
<h1>下面是 混合使用的切换功能组件</h1> 22
{/*切换按钮, 传入触发事件的函数 和 显示文字的变量 */}
<MyButton changeLang={changeLang} name={pre_name}/>
{/* antd国际化: 只对部分组件有效 */}
<ConfigProvider locale={locale}>
<Header />
</ConfigProvider>
</div>
);
}
export default App;
3.展示生产的轮子
大功告成