記一次大型React項目的國際化方案探索

背景

  • 項目:大型數據管理系統,涉及硬件設備數據監控、平常業務信息管理等
  • 技術:先後端分離,前端主要基於React-Redux
  • 需求:前端一鍵無縫切換多國語言

使用 react-intl

提起React項目國際化,首先想到著名的 react-intl 庫,這個庫提供了針對組件、日期、數字、字符串等多種國際化方法。使用方法也很簡單:javascript

  1. 將不一樣語言的翻譯文件放在各自的js文件中,同一處文本的多種語言翻譯使用相同的key前端

    // en_US.js
    const en_US = {
        "intl_hello": "Hello!",
    }
    export default en_US;
    
    // zh_CN.js
    const zh_CN = {
        "intl_hello": "你好!",
    }
    export default zh_CN;
    複製代碼
  2. 在入口文件中配置 react-intl 庫java

    // index.js
    import { addLocaleData, IntlProvider } from 'react-intl';
    // 引入多語言環境
    import en from 'react-intl/locale-data/en';
    import zh from 'react-intl/locale-data/zh';
    addLocaleData([...en, ...zh]); 
    // 引入翻譯文本
    import en_US from '.../intl/en_US.js';
    import zh_CN from '.../intl/zh_CN.js';
    const messagesMap = {
        en: en_US,
        zh: zh_CN
    }
    const locale = 'zh'; // 此處作了簡化,下文將從redux中獲取語言環境
    render((
    	// 使用<IntlProvicer>包裝項目組件,配置語言環境和翻譯文本
        <IntlProvider locale={local} messages={messages[local]}>
            //···
        </IntlProvider>
    ), document.getElementById("root"));
    複製代碼
  3. 使用 react-intl 中內置的組件或方法替換須要作多語言的字符串、時間等,具體可參考 API文檔react


react-intl與redux

因爲項目使用redux來管理狀態,將語言環境與翻譯文本都放入reducer中,使用相關action來觸發語言切換:git

// actions.js
export const switchLocal = local => ({
    type: 'SWITCH_INTL_LOCAL',
    payload: { local },
});

// reducers.js
import en_US from '.../intl/en_US.js';
import zh_CN from '.../intl/zh_CN.js';
const messagesMap = {
    en: en_US,
    zh: zh_CN
}
const defaultLocal = { //默認語言環境,也可從瀏覽器或用戶配置數據中獲取
    local: 'zh',
    messages: messagesMap.zh
};
export const intlLocal = (state=defaultLocal, action) => {
    switch(action.type) {
        case 'SWITCH_INTL_LOCAL':
            return {
                local: action.payload.local,
                messages: messagesMap[action.payload.local],
            }
        default:
            return state;
    }
}

// index.js
// 略去了文件中的redux配置等代碼
const { local, messages } = store.getState().intlLocal; // 從store中獲取語言配置
render((
	// react-redux中的Provider須要包在IntlProvider以外,IntlProvider才能訪問到store
    <Provider store={store}>
        <IntlProvider locale={local} messages={messages}>
            //···
        </IntlProvider>
	</Provider>
), document.getElementById("root"));
複製代碼

完成以後發現初始化的時候能夠訪問到store,使用指定的默認語言環境,但切換語言無效,排查後發現觸發action後reducer確實更改了,但沒有觸發組件更新。查閱相關文檔後,使用react內部的key屬性來強制觸發更新:github

// index.js
render((
    <Provider store={store}> // 加入key屬性來強制觸發更新 <IntlProvider key={local} locale={local} messages={messages}> //··· </IntlProvider> </Provider>
), document.getElementById("root"));
複製代碼

存在問題與方案探討

在上一步中使用key來強制觸發更新,對於通常簡單的網站或前端系統來講,到這一步就能夠了。web

萬惡的可是,因爲接手的系統過於複雜,使用key強制觸發組件更新時,會引發此<IntlProvider>包裹下的全部組件所有被更新,致使相似於頁面總體被刷新的效果,從而出現websocket重連、數據丟失等一系列問題,因爲不便於動用其餘模塊,思考事後剩下兩種方案:redux

  1. 語言切換時給予相應提示,而後跳轉到歡迎界面,這樣從新進入各子系統時會從新發起各類鏈接。因爲並不會常常切換語言,並且語言切換通常也就是發生在剛進入系統的時候,因此這個方案是最實用也最省力的。又是萬惡的可是,因爲項目背景比較複雜,上面領導的意思是像那些大型網站同樣「無縫」切換中英文,一跳轉就「有縫」了。。根本不考慮一個網站和一個大型B/S系統的差別,因而在需求降級可能性微乎其微的條件下,這個最合適的方案也只能做爲緊急備用方案了。
  2. 修改 react-intl 庫,須要包裝庫中用到的每一個方法,將數據源由Context改成redux的store。作的時候發現基本只是在處理字符串,就乾脆去掉了 react-intl 庫的依賴,手寫了個相似於intl庫中的 <FormattedMessage> 組件,使用的時候又發現只能用於組件的侷限性,又參考阿里的 react-intl-universal ,寫了個直接由key生成翻譯文本的方法。而這個方案目前還在完善和測試中。
相關文章
相關標籤/搜索