一个“中用”的前端国际化方案,请注意查收

这是我参与更文挑战的第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会让你很头疼,很痛苦的事情,像是一种习惯,仍是早养成为好,等最后再去弄,你会感叹怎么这么多东西要翻译,因此一套整合好的国际化解决方案就颇有用。

实行该国际化方案的🌰

相关文章
相关标签/搜索