React国际化方案初探

1,217 阅读3分钟
入口文件(src/index.js)

import React from "react";
import ReactDOM from "react-dom";
import registerServiceWorker from "./registerServiceWorker";

import {Provider} from "react-redux";
import {createStore, applyMiddleware} from "redux";
import {rootReducer} from "./store";

import Router from "./route";
import "./assets/styles/base.less";
import "./assets/styles/antd-override.less";

import createSagaMiddleware from "redux-saga"; // 引入redux-saga中的createSagaMiddleware函数
import rootSaga from "./sagas"; // 引入saga

import {LocaleProvider} from 'antd';
import zhCN from "antd/lib/locale-provider/zh_CN";
import enUS from "antd/lib/locale-provider/en_US";

import {addLocaleData, IntlProvider} from 'react-intl';
// 引入多语言环境
import en from 'react-intl/locale-data/en';
import zh from 'react-intl/locale-data/zh';
import enMessages from '@/locales/en_US';
import zhMessages from '@/locales/zh_CN';

addLocaleData([...en, ...zh]);

// 从缓存中读取国际化语言值
// const {IMS_INF_LANG = 'en'} = localStorage;
const {IMS_INF_LANG = 'en'} = localStorage;

/*
	设置缺省值,避免其他IMS_INF_LANG为其他非法值("en"/"zh"之外的值,如"null""undefined")时
	locale、antLocale、messages均为undefined,导致页面国际化异常
* */
let locale = "en";
let antLocale = enUS;
let messages = enMessages;
if (IMS_INF_LANG === 'zh') {
  locale = 'zh';
  antLocale = zhCN;
  messages = zhMessages;
} else if (IMS_INF_LANG === 'en') {
  locale = 'en';
  antLocale = enUS;
  messages = enMessages;
}

const sagaMiddleware = createSagaMiddleware(); // 创建saga中间件
const store = createStore(rootReducer, applyMiddleware(sagaMiddleware)); // 创建store
sagaMiddleware.run(rootSaga); // 运行saga中间件

ReactDOM.render(
  <IntlProvider locale={locale}
      messages={messages}
  >
    <Provider store={store}>
      <LocaleProvider locale={antLocale}>
        <Router/>
      </LocaleProvider>
    </Provider>
  </IntlProvider>,
  document.getElementById("root")
);
registerServiceWorker();





一般标准化的前端国际化方案,包括两个方面:
1、业务组件静态文本的国际化
2、UI组件内部的国际化
正常情况下,如果只需实现中英文国际化,前端只需要将英文内容作为key,维护一份中文对照库,但是如果在下述情况下,那么也需要维护一份英文对照库
如下:
中英文目标中包含a标签,此时无法将该文本作为key进行国际化,需要指定一个特殊的key,结合dangerouslySetInnerHTML渲染HTML字符串实现国际化

<span dangerouslySetInnerHTML={{__html: this.renderFormattedString("C_PAM")}}></span>

英文对照

const en_US = {
  "C_PAM": "Please confirm your account information in <a href='/home/paymentInfo'>Payment account management</a>"
};

export default en_US;

中文对照

const zh_CN = {
  "C_PAM": "请前往<a href='/home/paymentInfo'>收款账号管理</a>确认您的账号信息"
};

export default zh_CN;

在组件文件中进行国际化
首先,在组件头部引入react-intl

import {injectIntl, FormattedMessage} from 'react-intl';

组件外部包裹injectIntl

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(withRouter(injectIntl(Comp)));

然后,利用FormattedMessage或intl.formatMessage方法实现国际化

renderFormattedMessage = (key) => {
	return <FormattedMessage
		id={key}
		defaultMessage={key}
	/>
};

renderFormattedString = (key) => {
	const {intl} = this.props;
	let res = intl.formatMessage({
		id: key,
		defaultMessage: key
	});
	return res;
};

主要通过两种方式实现
第1种、将key映射为span标签,如"English"=><span>English</span>(英文映射)/"English"=><span>英文</span>(英文映射)
第2种、将key映射为纯字符串,如"English"=>"English"/"English"=>"英文"
在组件文件中,通过this.props.intl.locale判断当前语言是中文还是英文

<div className="our-partner">
	{
		this.props.intl.locale === "en" &&
		<span>
			{"Participating"}
			<br/>
			{"Brands"}
		</span>
	}
	{
		this.props.intl.locale === "zh" &&
		<span>
			{
				"我们的"
			}
			<br/>
			{
				"合作伙伴"
			}
		</span>
	}
</div>

另外,前端项目多语言需要结合localStorage或cookie实现,因为多语言对于用户来说往往是一种固定偏好,不会在短时间内发生变化,所以需要浏览器通过缓存策略记住这种偏好。
在退出或会话token失效需要清空系统缓存时,切记不要清除用户语言缓存,如下:

componentWillReceiveProps(nextProps) {
	if (nextProps.logout_result.success !== this.props.logout_result.success) {
		if (nextProps.logout_result.success) {
			message.success(nextProps.logout_result.msg);
			const lang = localStorage.getItem("IMS_INF_LANG");
			localStorage.clear();
			localStorage.setItem("IMS_INF_LANG", lang);
			this.props.history.push('/login');
		}
	}
}