共计 3924 个字符,预计需要花费 10 分钟才能阅读完成。
前端国际化是什么
简略说就是翻译一下,切换中英文,但绝不是把整个语言包放进去,那可没必要,只是按需解决即可,那么如何制订一个优雅的国际化计划,才是须要重点钻研的。
拆卸 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>
);
}
}
剖析:
- 首先应用的是
IntlProvider
包裹一下。 -
而后传入两个参数:
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"}))
}
...
}
剖析:
- 首先在配置语言包上,须要别离在
en-US
和zh-CN
目录下配置两个构造雷同,值不同的文件。 - 基于之前的拆卸,咱们就能够通过
react-intl
提供的injectIntl
高阶组件对所在组件进行包装,从而能够间接通过 props 取得国际化数据intl
。 - 从
intl
中取得formatMessage
, 传入片段id
,就能取得翻译的值。
但有弊病:
- 配置上要配置两组大体一样就是值不同的数据,一是反复了,二是还得人工对仗着写,这就很恶心了。
- 所有页面都能“用”一个整体🤔️???这不好保护啊,权限没管制好啊,页面对翻译片段的依赖会越来越凌乱,最好还是借鉴 mobx 这种仓库的思维,你依赖啥我给你啥,不依赖就不给你。
- 每次应用我都须要
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>
}
}
剖析:
- 首先在高阶组件包装的形式上别无二致。
- 代替了之前应用
formatMessage
翻译的形式,并将其在高阶组件外部对立做好之后,再将数据注入到组件 props 中,通过 intlData 获取,间接通过“.”的形式就能取得翻译,既简化了流程又防止了函数的多余执行。 - 最要害的是能够为页面传入指定的国际化模块,这太难受了,离开保护,真的很棒。
成果展现
结语
国际化是一件如果你不在意 ta,ta 会让你很头疼,很苦楚的事件,像是一种习惯,还是早养成为好,等最初再去弄,你会感叹怎么这么多货色要翻译, 所以一套整合好的国际化解决方案就很有用。
履行该国际化计划的🌰
正文完