從零開始構建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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章