乐趣区

关于前端:一个中用的前端国际化方案请注意查收

前端国际化是什么

简略说就是翻译一下,切换中英文,但绝不是把整个语言包放进去,那可没必要,只是按需解决即可,那么如何制订一个优雅的国际化计划,才是须要重点钻研的。


拆卸 react-intl

react-intl是一个 Yahoo 公司出品的,有趣味能够自行理解一下。

装置:

    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>
        );
    }
}

剖析:

  1. 首先应用的是 IntlProvider 包裹一下。
  2. 而后传入两个参数:

    • locale: 以后语言环境
    • messages:按需配置的语言包(上面重点剖析)。

至此根本拆卸够用了,其余“高玩”的配置有趣味的能够持续探索。


重点说一下 配置语言包 的形式

“传统”的模式:

配置语言包:

咱们以 login 为例:

en-US:

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:

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,就能取得翻译的值。

但有弊病:

  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 会让你很头疼,很苦楚的事件,像是一种习惯,还是早养成为好,等最初再去弄,你会感叹怎么这么多货色要翻译, 所以一套整合好的国际化解决方案就很有用。
履行该国际化计划的🌰

退出移动版