记一次大型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生成翻译文本的方法。而这个方案目前还在完善和测试中。
相关文章
相关标签/搜索