前言

本文主要介绍在项目中如何加入多语言的环境和开发规范,主要依赖 react-intl 这个包,外部打包依赖 babel-plugin-react-intl 抽取 id 来实现高效注入和便携开发。
用友云平台战略项目交付团队

第一章 快速开始

1.1 准备多语环境

安装 react-intl 后在项目页面的主入口注入多语对象,如下示例编写通用多语组件,该组件可放在 common 目录中。

  1. import React from 'react';
  2. import ReactDOM from 'react-dom';
  3. import mirror, { connect,withRouter } from 'mirrorx';
  4. import { Locale } from 'tinper-bee';
  5. import { IntlProvider, addLocaleData } from 'react-intl';
  6. const chooseLocale = (locale) => {
  7. let lang;
  8. switch (locale) {
  9. case 'en_US':
  10. //两个默认的语言包
  11. lang = require('./lang/en_US.js').default
  12. break;
  13. default:
  14. lang = require('./lang/zh_CN.js').default
  15. }
  16. return lang;
  17. }
  18. let intlModel = {
  19. name: "intl",
  20. initialState: {
  21. locale: 'zh_CN',
  22. localeData: chooseLocale('zh_CN') || {}
  23. },
  24. reducers: {
  25. setLocale(state, locale) {
  26. return {
  27. ...state,
  28. locale,
  29. localeData: chooseLocale(locale)
  30. };
  31. }
  32. }
  33. }
  34. //使用mirror切换语言包,也可采用静态的。
  35. mirror.model(intlModel);
  36. export default connect(state => state.intl)((props) => {
  37. let { children, localeData } = props;
  38. let { tinperLocale, locale, messages } = localeData;
  39. return (
  40. <Locale locale={tinperLocale}>
  41. <IntlProvider
  42. key={locale}
  43. locale={locale}
  44. messages={messages}
  45. defaultLocale={locale}
  46. onError={(error) =>{}}
  47. >
  48. <div>
  49. {children}
  50. </div>
  51. </IntlProvider>
  52. </Locale>
  53. )
  54. });

1.2 注入多语环境

在页面的主入口使用上面编写的组件包裹,如在路由中使用或者在统一门户组件中使用

  • 门户组件 ```javascript import React, { Component } from ‘react’; import ReactDOM from ‘react-dom’; import { actions } from ‘mirrorx’; import { FormattedMessage} from ‘react-intl’;

import LocalePortal from ‘components/LocalePortal’; import ‘./index.less’ class MainLayout extends Component { constructor(props){ super(props) this.state = { value: ‘zh_CN’ } } handleChange = value => { this.setState({value}, () => { actions.intl.setLocale(value); }); };
render() { let { children } = this.props; return (

标题,侧边栏等等

//业务界面 {children}

  1. </div>
  2. )
  3. }

}

export default MainLayout;

  1. - 使用门户组件包裹业务界面,如下包裹路由
  2. ```javascript
  3. import 'core-js'
  4. import React from 'react';
  5. import mirror, {
  6. render,
  7. Router,
  8. Route
  9. } from 'mirrorx';
  10. import MainLayout from 'components/MainLayout';
  11. import Routers from "./routes";
  12. mirror.defaults({
  13. historyMode: "hash"
  14. });
  15. render(
  16. <MainLayout >
  17. <Router>
  18. <Route path={`/`} component={Routers}/>
  19. </Router>
  20. </MainLayout>
  21. ,
  22. document.getElementById('app')
  23. );

1.3 在业务组件中使用多语

通过上述过程做好准备工作后,业务开发人员就可以在业务界面中加入多语言的代码,且不用操心维护外部如何操作,如下:

  1. import { FormattedMessage, defineMessages, injectIntl} from 'react-intl';
  2. /**
  3. * 实际使用的时候有两周方式如下
  4. * */
  5. //1.标签使用
  6. <FormattedMessage
  7. id = 'Demo.text'
  8. defaultMessage = '我是文本'
  9. />
  10. //2.API使用
  11. const locales = defineMessages({
  12. content: {
  13. id: 'Demo.context',
  14. defaultMessage: '我是文本',
  15. }
  16. });
  17. class App extends Component {
  18. render() {
  19. const _this = this;
  20. //调取 intl 对象
  21. let { intl:{formatMessage} } = _this.props;
  22. return (
  23. <div className="form-panel">
  24. {
  25. formatMessage(locales.content)
  26. }
  27. </div>
  28. )
  29. }
  30. }
  31. //注入 intl 对象
  32. export default injectIntl(App);

第二章 项目规约

2.1 项目结构

在自己的模块中增加 lang 目录,这里建议使用 injectIntl API方式使用 intl。之后再每一个组件中通过高阶方法增加 intl 对象。

  1. ├── components
  2. ├── lang
  3. └── index.js #国际化脚本
  4. ├── routes
  5. ├── app.js
  6. ├── container.js
  7. ├── index.html
  8. ├── model.js
  9. └── service.js
  • lang/index.js
  1. import { defineMessages} from 'react-intl';
  2. //不同的组件单独命名
  3. export const orderGridLang = defineMessages({
  4. content: {
  5. id: 'js.context',
  6. defaultMessage: '我是{name}'
  7. },
  8. text: {
  9. id: 'js.lib.test.text',
  10. defaultMessage: '一段字符'
  11. },
  12. });
  13. export const sreachPanelLang = defineMessages({
  14. name: {
  15. id: 'js.item.test.name',
  16. defaultMessage: '我不是{name}'
  17. },
  18. });
  • 注入对象
  1. export default injectIntl(App);
  • 使用定义的语言对象
  1. import { FormattedMessage, defineMessages, injectIntl} from 'react-intl';
  2. import { sreachPanelLang } from './lang'
  3. class App extends Component {
  4. render() {
  5. const _this = this;
  6. //调取 intl 对象
  7. let { intl:{formatMessage} } = _this.props;
  8. return (
  9. <div className="form-panel">
  10. {
  11. formatMessage(sreachPanelLang.name)
  12. }
  13. </div>
  14. )
  15. }
  16. }
  17. //注入 intl 对象
  18. export default injectIntl(App);

2.2 命名规范

为避免 id 重复我们强约束字符的 id 命名规范。

  • 1.模块中
    js.[项目名].[模块名].[ID名]
  • 2.通用字符命名
    通用字符如:确认、取消等,常用字符我们会提炼出来生成通用语言包,他的命名规范是
    js.common.[ID名]

第三章 自动化构建抽取ID

文章最开始提到了 babel-plugin-react-intl ,这个插件是用来抽取项目中定义的多语变量 ID 的,在 ucf.config.js 中添加配置到 babel_plugins 配置中即可,需要抽取时执行一次即可,不要在开发态下开启此功能。

  1. babel_plugins: [
  2. [require.resolve("babel-plugin-react-intl"), {
  3. "messagesDir": "./intl/"
  4. }]
  5. ],