React + i18next + antd组件国际化

2,493 阅读8分钟

React + i18next + antd组件国际化

前言:现在流行的国际化组件有非常多,各种国际化的方向也非常多,比如:React-i18next 和 react-intl, React-i18next主要是用于文字翻译,react-intl主要用于数字,符号等翻译。 antd 国际化的是组件, 只提供部分组件的国际化,它不支持非组件的文字国际化。因此,需要国际化组件, 还需要将这两种方案结合起来。

i18next官网初始化配置

React项目国际化介绍(react-i18next、react-intl) - 简书

-.React-i18next

1.模块介绍

首先,它是一个基于i18next的强大的国际化框架,常用于各种框架,Vue, React, React-native应用。

主要特点:

  1. 适用广泛,学习一种方案,也可以在其它框架找到同构的国际化库。

    其中 i18n 是一个比较大的系统组件, 它在react 上的库为 React-i18n , vue上的库为 vue-i18n.

    不同的框架,对应的代码也会有所差异。

  2. 可以用于服务端渲染,提供不同的方式进行国际化语言切换。

  3. 提供插件支持,可以使用插件检测当前的语言环境。

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;

显示结果

image-20211229165621262 image-20211229165636466

- 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;

显示结果:

image-20211229170852385 image-20211229170902950

- 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.展示生产的轮子

image-20211229174511811 image-20211229174523153

大功告成