前端国际化是什么
简单说就是翻译一下,切换中英文,但绝不是把整个语言包放进去,那可没必要,只是按需处理即可,那么如何制定一个优雅的国际化方案,才是需要重点研究的。
装配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会让你很头疼,很痛苦的事情,像是一种习惯,还是早养成为好,等最后再去弄,你会感叹怎么这么多东西要翻译,所以一套整合好的国际化解决方案就很有用。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END