从零开始构建React下的多语言实现

          当前,React是web前端开发最流行的Javascript库之一。随着国家间的交流日益紧密,web前端需要有多语言版本,也就是国际化。通行的做法是根据用户的浏览器来自动判断用户使用的语言,显示对应的文本;但同时也提供一个语言切换按钮。本教程从零开始,介绍了React下web前端实现多语言的过程。使用Vue的同学可以自己对照进行修改。

          为了简化,本教程只实现了中/英双语言。本教程需要提前安装好node和React,参考地址为:https://reactjs.org/。同时也需要一个编辑器或者IDE(推荐Atom或者Visual Studio Code)。

一、创建一个React项目

          在工作目录下执行以下命令并等待完成。该命令用来创建一个名为multilanguage的React项目。

npx create-react-app multilanguage

          下载完所有的文件后,会有下一步操作的提示:

cd multilanguage
npm start

          执行上述命令后,会在本地打开一个浏览器窗口访问http://localhost:3000/。它显示一个React页面,只有一个大大的React Logo和学习React的超链接。恭喜你,你已经成功建立了一个React项目!
          小提示:如果你的电脑3000端口被占用了,它会提示你是否使用3001端口。

二、导入i18相关模块

          让我们先按Ctrl + C键来关掉开发服务器的运行以便进行代码编写。
          在Javascript中,我们通常使用i18next库来进行多语言的开发,想详细研究的同学请点击https://www.i18next.com/。我们这里不做介绍直接使用就行。使用常用的编辑器打开刚才创建的工程,在src/下创建一个新文件叫i18n.js,代码如下:

import i18next from 'i18next'
import {initReactI18next} from 'react-i18next'
import XHR from 'i18next-xhr-backend'
import LanguageDetector from 'i18next-browser-languagedetector'

i18next.use(XHR).use(LanguageDetector).use(initReactI18next).init({
    backend: {
        loadPath:'./locales/{{lng}}.json'
    },
    react: {
        useSuspense: true
    },
    fallbackLng: 'en',
    preload: [
        'en', 'zh','zh-cn'
    ],
    keySeparator: false,
    interpolation: {
        escapeValue: false
    }
})

export default i18next

          简要解释一下,这个js文件主要是初始化i18next库。i18next库预先装载一些预定的json文件,然后根据使用的语言将标记字符串替换成对应的语言文本。这段代码定义了json路径,预装载语言等。

          然后再编辑src/index.js。首先,将第一行代码改为import React, {Suspense} from 'react';。然后在导入语句里增加这么一行:import "./i18n";。最后将输出的元素用Suspense包装一下。修改完成的代码如下:

import React, {Suspense} from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import './i18n';

ReactDOM.render(
    <Suspense fallback = {"loading"} >
        <App />
    </Suspense>
    , document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

          此时项目还不能运行,我们需要安装i18n.js中导入的库,在项目根目录下(记住不是源码根目录src)使用命令行依次运行:

npm install i18next --save
npm install react-i18next --save
npm install i18next-xhr-backend --save
npm install i18next-browser-languagedetector --save

          小提示:类似Atom或者Visual Studio Code这种IDE有终端插件,可以直接在编辑器里打开命令行,并且默认就为项目根目录。

三、生成多语言文档

          从i18n.js中我们可以看到,它需要装载网页主目录下的locales目录下对应的json文件。这里我们使用python来转换excel表格以生成相应的json文件。在项目根目录下的public目录里(也就是网页根目录)新建locales文件夹,然后打开你的office或者wps office来新建一个multilanguage.xlsx 文档(excel表格)。内容如下:

多语言excel表格
          将该excel文件复制或者保存在locales文件夹下,然后再在该文件夹下新建一个文件convert.py。该.py文件的作用是将excel表格里的内容转成json并保存,代码为:

import xlrd # 引入xlrd模块
import json # 引入json模块


def convert():
    file = xlrd.open_workbook('multilanguage.xlsx') # 打开excel文件对象
    table = file.sheets()[0]  # 通过索引顺序获取表格
    rows = table.nrows # 总的行数
    en = {}
    zh = {}
    for r in range(1,rows): # 去除表头所有从第一行开始
        rowData = table.row_values(r) # 获取每一列的数据
        str = rowData[0]
        en_str = rowData[1]
        zh_str = rowData[2]
        en[str] = en_str
        zh[str] = zh_str
    return en,zh


def main():
    en,zh = convert()
    # Writing JSON data
    with open('en.json', 'w') as f:
        json.dump(en, f)
    with open('zh-cn.json', 'w', encoding='utf-8') as f2:
        json.dump(zh, f2,ensure_ascii=False)
    with open('zh.json', 'w', encoding='utf-8') as f3:
        json.dump(zh, f3,ensure_ascii=False)
    print("convert to json success")


main()

          随后在命令行里切换到locales目录,运行python3 convert.py。看到convert to json success提示后,localse目录下会生成三个json文件。

          这里需要提前安装python3和相应的python库。Python3安装链接:https://www.python.org/downloads/release/python-381/,库安装推荐使用pip工具。

四、应用多语言

          应用多语言就是进行对应的文本替换。打开src/App.js,在导入语句里增加一行import { useTranslation } from 'react-i18next';,然后在函数组件App里增加一行const {t} = useTranslation();。接着进行文本替换,修改完成后的代码如下:

import React from 'react';
import logo from './logo.svg';
import './App.css';
import { useTranslation } from 'react-i18next';

function App() {
  const {t} = useTranslation();
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          {t('edit')} <code>src/App.js</code> {t('save_load')}
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          {t('learn_react')}
        </a>
      </header>
    </div>
  );
}

export default App;

          这时我们在项目根目录下使用终端再次运行npm start,页面就变成中文啦。
页面变成中文

五、编写多语言按钮

          现在我们已经实现中英文双界面了(如果你把你的语言设置改成英文,就会显示英文)。但是光是这样还不够,还需要提供一个按钮让用户可以自由切换语言并保存用户的选择。这里,我们使用了React下的Material UI的多语言图标。

          首先,让我们再次按下Ctrl + C键来关闭开发服务器的运行。接着,我们在/src目录下新建components\LanguageBtn\index.js
新建一个切换按钮源文件
          将下方的代码直接复制到刚刚生成的js文件中。

import React, {useState} from 'react'
import TranslateIcon from "@material-ui/icons/Translate"
import DownIcon from '@material-ui/icons/KeyboardArrowDown';
import MenuItem from "@material-ui/core/MenuItem";
import Button from "@material-ui/core/Button";
import Menu from "@material-ui/core/Menu";
import PropTypes from "prop-types";
import {reactLocalStorage} from 'reactjs-localstorage'
import i18next from 'i18next'

const tokenId = "multilanguage_demo";
const options = ['English', "中文"];
const lngOptions = ['en', 'zh'];

function LanguageBtn({fontColor}) {
    let userProfile = reactLocalStorage.getObject(tokenId) || {};
    const language = userProfile['lng'] || i18next.language;
    const indexInit = (language === 'zh' || language === 'zh-CN' || language === 'zh-cn') ? 1 : 0;
    const [selectedIndex, setSelectedIndex] = useState(indexInit);
    const [anchorEl, setAnchorEl] = React.useState(null);

    const handleClick = event => {
        setAnchorEl(event.currentTarget);
    };
    const handleClose = () => {
        setAnchorEl(null);
    };
    const handleCloseProfile = index => async event => {
        if (index === selectedIndex) {
            return;
        }
        setAnchorEl(null);
        await i18next.changeLanguage(lngOptions[index])
        userProfile['lng'] = lngOptions[index]
        reactLocalStorage.setObject(tokenId, userProfile)
        setSelectedIndex(index)
    };

    return (
        <>
            <Button
                 aria-haspopup="true"
                 onClick={handleClick}
             >
                 <TranslateIcon  style={{color: fontColor}} />
                 <p style={{color: fontColor}} >{options[selectedIndex].toUpperCase()}</p>
                 <DownIcon style={{color: fontColor}} />
             </Button>
             <Menu
                 id="simple-menu"
                 anchorEl={anchorEl}
                 keepMounted
                 open={Boolean(anchorEl)}
                 onClose={handleClose}
              >
                  {options.map((option,index) => (
                      <MenuItem
                          key = {option}
                          selected = {index === selectedIndex }
                          onClick={handleCloseProfile(index)}
                          value = {index}
                      >
                        {option}
                      </MenuItem>
                  ))}
             </Menu>
        </>
    )
}

LanguageBtn.propTypes = {
    classes: PropTypes.object
};

export default LanguageBtn;

          简单解释一下,这里编写了一个函数组件,它包含一个多语言按钮和一个选择菜单。点击多语言按钮后会显示选择菜单,点击菜单后会根据用户选择的语言重新显示文本并保存用户的选择。按钮有一个属性叫fontColor,它用来设置按钮文本的颜色,以对应不同背景色下文字的显示问题。使用reactLocalStorage将用户的选项保存在本地存储中。这里同样需要先安装material ui库,在项目根目录下使用命令行依次运行:

npm install @material-ui/core --save
npm install @material-ui/icons --save
npm install reactjs-localstorage --save

          我们的语言切换按钮组件已经编写好了,可以应用到本项目任何页面的任何地方了。

六、应用切换按钮

          现在可以将我们的多语言切换按钮显示到我们的主页面了。修改src/App.js,导入我们刚才编写好的组件。

import LanguageBtn from './components/LanguageBtn';

          然后在输出中加入我们编写好的组件:

<div style={{textAlign:"right",marginRight:"10px"}}>
    <LanguageBtn fontColor='black'/>
</div>

          这里只进行了很简单的页面修改和css设置,主要是演示按钮的功能。

          修改后的App.js最终代码如下:

import React from 'react';
import logo from './logo.svg';
import './App.css';
import { useTranslation } from 'react-i18next';
import LanguageBtn from './components/LanguageBtn';

function App() {
  const {t} = useTranslation();
  return (
    <div className="App">
      <div style={{textAlign:"right",marginRight:"10px"}}>
        <LanguageBtn fontColor='black'/>
      </div>
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          {t('edit')} <code>src/App.js</code> {t('save_load')}
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          {t('learn_react')}
        </a>
      </header>
    </div>
  );
}

export default App;

好了,所有工作都已经完成了,让我们来再次运行npm start进行测试。点击页面右上角的按钮,可以自由切换语言哟。

七、打包发布

          打包发布时也有几点需要注意:

  • 修改项目根目录下的package.json,在依赖属性dependencies上面增加一行"homepage":".",,意思为放在任意目录下。
  • 在项目根目录下新建.env文件,里面写上这么一行:GENERATE_SOURCEMAP=false,该行指令的意思是打包后的工程不显示源代码。

          最后运行npm run build,它将在build文件夹下生成打包好的发布版本,把该目录下所有文件复制到Apache网页根目录或者任意二级目录下(比如multilanguagedemo)。

          在浏览器中访问,显示结果如下图。
最终页面

以上就是从零构建React下的多语言实现的所有流程。

由于自身水平有限,难免会出现错误或者有不完善的地方,恳请大家留言指正和改进。基于同样的原因,这个完整的示例就不放在github上了,而是把它放在码云上。
示例码云地址:=> https://gitee.com/TianCaoJiangLin/multilanguage

发布了15 篇原创文章 · 获赞 0 · 访问量 551

猜你喜欢

转载自blog.csdn.net/weixin_39430411/article/details/104009143