一個「中用」的前端國際化方案,請注意查收

這是我參與更文挑戰的第4天,活動詳情查看: 更文挑戰前端

前端國際化是什麼

簡單說就是翻譯一下,切換中英文,但毫不是把整個語言包放進去,那可不必,只是按需處理便可,那麼如何制定一個優雅的國際化方案,纔是須要重點研究的。react


裝配react-intl

react-intl是一個 Yahoo 公司出品的,有興趣能夠自行了解一下。git

安裝:

yarn add react-intl
複製代碼

使用:

...
    import { IntlProvider } from "react-intl";
    ...
    class Root extends Component {
    render() {
        const {
        global: { locale },//可枚舉的值爲"zh"和"en"
        } = this.props;
        return (
        <IntlProvider locale={locale} messages={language.getData()[locale]}> <App /> </IntlProvider>
        );
    }
}
複製代碼

分析:github

  1. 首先使用的是IntlProvider包裹一下。
  2. 而後傳入兩個參數:
    • locale: 當前語言環境
    • messages:按需配置的語言包(下面重點分析)。

至此基本裝配夠用了,其餘「高玩」的配置有興趣的能夠繼續探究。shell


重點說一下 配置語言包 的方式

「傳統」的模式:

配置語言包:

我們以 login 爲例:markdown

en-US:app

const login = {
  "login.title": "User Center",
  "login.username": "please enter username",
  "login.usernameEmpty": "username cannot be empty!",
  "login.maxLength": "username is no more than 100 characters",
};

export default login;
複製代碼

zh-CN:ide

const login = {
  "login.title": "用戶中心",
  "login.username": "請輸入用戶名",
  "login.usernameEmpty": "用戶名不能爲空!",
  "login.maxLength": "用戶名不得多於100個字符",
};

export default login;
複製代碼

開發使用

...
/* 引入 */
import { injectIntl } from "react-intl";
...
/* 注入專屬國際化數據 */
@injectIntl
class Login extends React.Component {
    constructor(props) {
        super(props);
        const {
            intl: { formatMessage },
        } = this.props;
        /* 使用 */
        message.warning(formatMessage({ id: "login.maxLength" }))
    }
    ...
}
複製代碼

分析:函數

  1. 首先在配置語言包上,須要分別在en-USzh-CN目錄下配置兩個結構相同,值不一樣的文件。
  2. 基於以前的裝配,咱們就能夠經過react-intl提供的injectIntl高階組件對所在組件進行包裝,從而能夠直接經過 props 得到國際化數據intl
  3. intl中得到formatMessage,傳入片斷id,就能得到翻譯的值。

但有弊端:oop

  1. 配置上要配置兩組大致同樣就是值不一樣的數據,一是重複了,二是還得人工對仗着寫,這就很噁心了。

  2. 全部頁面都能「用」一個總體🤔️???這很差維護啊,權限沒控制好啊,頁面對翻譯片斷的依賴會愈來愈混亂,最好仍是借鑑 mobx 這種倉庫的思想,你依賴啥我給你啥,不依賴就不給你。

  3. 每次使用我都須要formatMessage翻譯,文件很少片斷很少還行,要是都多呢?那豈不是要寫不少行,每次 render 都去執行翻譯函數,慄 🌰:

...
    render() {
        const {
        intl: { formatMessage },
        } = this.props;
        const { codeImage } = this.state;
        const usernamePlaceholder = formatMessage({ id: "login.username" });
        const usernameEmpty = formatMessage({ id: "login.usernameEmpty" });
        const passwordPlaceholder = formatMessage({ id: "login.password" });
        const passwordEmpty = formatMessage({ id: "login.passwordEmpty" });
        const codePlaceholder = formatMessage({ id: "login.code" });
        const maxLength = formatMessage({ id: "login.maxLength" });
        const pwdMaxLength = formatMessage({ id: "header_pwdMaxLength" });
        const codeEmpty = formatMessage({ id: "login.codeEmpty" });
        return (
        <div className="loginpagewrap"> ... </div>)
        }
    ...
複製代碼

這明顯不合理啊,得想個轍啊。

優化的模式:

首先針對傳統模式各個環節進行優化。

首先從配置方式入手

import BaseIntl from "./baseIntl";

let config = {
  light_searchSelect: {
    en: "searchSelect",
    zh: "聯想select",
  },
  light_baseSelect: {
    en: "baseSelect",
    zh: "基本select",
  },
  light_computeNum: {
    en: "computeNum",
    zh: "計算值",
  },
};

export default new BaseIntl({ config });
複製代碼

一個文件解決,這多好,而且經過baseIntl進行擴展,主要爲其補充了共用的翻譯片斷,這樣大大的解決了重複翻譯片斷的問題。

而後基於react-intl定製一個屬於咱們的高階組件intlHoc

作這個高階組件前,得先明確咱們不是破壞react-intl,而是擴展 ta。

直接上代碼:

import React from "react";
import { inject, observer } from "mobx-react";
import { injectIntl } from "react-intl";
import language from "SRC/language";

function hoc(id) {
  return function (WrappedComponent) {
    @injectIntl
    @inject("global")
    class IntlHoc extends React.Component {
      constructor(props) {
        super(props);
        const {
          global: { locale },
        } = this.props;
        this.state = {
          formatedMessage: this.formatMessage(),
          localeFlag: locale,
        };
      }

      formatMessage() {
        const { intl } = this.props;
        const { formatMessage } = intl;
        let targetArr = language.getIntlById(id);
        let trmpArr = {};
        for (let key in targetArr) {
          trmpArr[key] = formatMessage({ id: key });
        }
        return trmpArr;
      }
      shouldComponentUpdate() {
        const {
          global: { locale },
        } = this.props;
        if (this.state.localeFlag !== locale) {
          this.setState({
            localeFlag: locale,
            formatedMessage: this.formatMessage(),
          });
        }
        return true;
      }
      render() {
        const { formatedMessage } = this.state;
        const props = Object.assign({}, this.props, {
          intlData: formatedMessage,
        });
        return <WrappedComponent {...props} />;
      }
    }
    return IntlHoc;
  };
}

export default hoc;
複製代碼

而後代替injectIntl進行包裝,慄 🌰:

...
import injectInternational from "COMMON/hocs/intlHoc";
...
@injectInternational("light")
class TempEdit extends Component {
     const {
            intlData
        } = this.props;
    render(){
        return <div>{intlData.light_editting}</div>
    }
}
複製代碼

分析:

  1. 首先在高階組件包裝的方式上別無二致。
  2. 替代了以前使用formatMessage翻譯的方式,並將其在高階組件內部統一作好以後,再將數據注入到組件props中,經過intlData獲取,直接經過「.」的方式就能得到翻譯,既簡化了流程又避免了函數的多餘執行。
  3. 最關鍵的是能夠爲頁面傳入指定的國際化模塊,這太舒服了,分開維護,真的很棒。

效果展現


結語

國際化是一件若是你不在乎ta,ta會讓你很頭疼,很痛苦的事情,像是一種習慣,仍是早養成爲好,等最後再去弄,你會感嘆怎麼這麼多東西要翻譯,因此一套整合好的國際化解決方案就頗有用。

實行該國際化方案的🌰

相關文章
相關標籤/搜索