基于create-react-app官方脚手架搭建dva模式的项目(三)

项目配置到这里之后,接下来就需要处理以下配置了:

  • 国际化
  • store的数据存储结构

 国际化和store数据结构的设计:

此处采用immutable数据格式(immutable一款很棒的数据操作工具,此处不做详解,有兴趣的同学可自行学习),把数据存于model,同样国际化的判断参数定为: i18n 存于app的model中,取值来源于浏览器的本地缓存localStorage用户若设置了某种语言,则存在这里,用户下次访问系统,也依然能唤起上次所选中的语言,当初次访问时,语言默认先取自浏览器,若依然取不到则默认咱们的中文。

这里我们只实现 中文,英文,繁体 三种语言的国家化即可,因为引用了antd组件库,故此处国际化部分包括两部分,

(1) antd库的国际化,比如antd中的固定组件里的 ‘确定,OK’‘下一页,Next Page’等,antd官网有详细说明

(2) 业务框架自己的国际化,比如“用户名,Username”,“密码,Password”等,这里采用react-intl国际化组件实现


1 安装immutable和react-intl

[javascript]  view plain  copy
  1. cnpm i immutable react-intl --save  

修改models目录下的app.js中引入import {Map} from 'immutable'; 修改如下:

[javascript]  view plain  copy
  1. import {Map} from 'immutable';  
  2.   
  3. const initState = Map({  
  4.     i18n: 'zh_CN'  
  5. })  
  6.   
  7. export default {  
  8.   
  9.     namespace: 'app',  
  10.     
  11.     state:initState,  
  12.     
  13.     subscriptions: {  
  14.         
  15.     },  
  16.     
  17.     effects: {  
  18.   
  19.       * changeLang ({  
  20.         payload: {value},  
  21.         }, { put }) {  
  22.             yield put({ type: 'updateLang', payload: {value}});  
  23.         },  
  24.           
  25.     },  
  26.     
  27.     reducers: {  
  28.         updateLang (state,{payload:{value}}) {  
  29.             return state.set('i18n',value);  
  30.         },  
  31.     },  
  32.     
  33.   };  
  34.     

修改routes目录下BBB.js,如下:

[javascript]  view plain  copy
  1. import React, { Component } from 'react';  
  2. import {connect} from 'dva';  
  3. import { Link } from 'dva/router';  
  4. import {Row, Col, Dropdown, Menu, Button} from 'antd'  
  5.   
  6. class BBB extends Component {  
  7.   
  8.   changeLang=(e)=>{  
  9.     const {dispatch} = this.props;  
  10.     dispatch({  
  11.       type:'app/changeLang',  
  12.       payload:{  
  13.         value:e.key  
  14.       }  
  15.     })  
  16.   }  
  17.   
  18.   render() {  
  19.     const {i18n} = this.props;  
  20.     const menu=(  
  21.       <Menu   
  22.         onClick={this.changeLang}  
  23.         selectedKeys={[i18n]}  
  24.       >  
  25.       <Menu.Item key="zh_CN">  
  26.         中文  
  27.       </Menu.Item>  
  28.       <Menu.Item key="en_US">  
  29.         英文  
  30.       </Menu.Item>  
  31.       <Menu.Item key="zh_HK">  
  32.         繁体  
  33.       </Menu.Item>  
  34.     </Menu>  
  35.     )  
  36.   
  37.     return (  
  38.       <div>  
  39.         <p>  
  40.           BBB页  
  41.         </p>  
  42.         <Link to={'/aaa'}>去AAA页面</Link>  
  43.         <br />  
  44.         <Link to={'/ccc'}>去CCC页面</Link>  
  45.   
  46.         <Row>  
  47.           <Col offset={2}>  
  48.             <Dropdown trigger={['click']} overlay={menu}>  
  49.               <Button>{i18n=='zh_CN'?'中文':i18n=='en_US'?'英文':'繁体'}</Button>  
  50.             </Dropdown>  
  51.           </Col>  
  52.         </Row>  
  53.            
  54.       </div>  
  55.     );  
  56.   }  
  57. }  
  58.   
  59. export default connect(({  
  60.   app  
  61. })=>({  
  62.   i18n:app.get('i18n')  
  63. }))(BBB)  

以上修改主要功能:

app.js 增加初始语言参数i18n,增加effects和reducers方法,用于接收语言切换的action,并存储选中的key值

BBB.js 增加connect使当前组件接入store数据,增加切换组件,用于点击切换语言,并dispatch一个action给app/changeLang,触发修改修改model数据

效果如图:




到这里,数据层面的流程已经打通,接下来处理国际化逻辑,国际化组件必然放于跟组件或最外层组件包裹,这样全局均可以使用并生效

2 src目录下新建文件locale.js,此组件仅用于接入国际化使用:

[javascript]  view plain  copy
  1. import {connect} from 'dva';  
  2. import React from 'react';  
  3. import { LocaleProvider } from 'antd' //antd国际化组件  
  4. import {IntlProvider} from 'react-intl' //业务文案的国际化组件  
  5. import {ANT_LANGPACKAGE, LANGPACKAGE} from './locales';  
  6.   
  7.   
  8. const Locale=({ children,i18n })=>{  
  9.   
  10.   return (  
  11.     <LocaleProvider locale={ANT_LANGPACKAGE[i18n]}>  
  12.       <IntlProvider  
  13.         locale={i18n}  
  14.         messages={LANGPACKAGE[i18n]}  
  15.       >  
  16.         {children}  
  17.       </IntlProvider>  
  18.     </LocaleProvider>  
  19.   );  
  20. }  
  21.   
  22. export default connect(({  
  23.   app  
  24. })=>({  
  25.   i18n:app.get('i18n')  
  26. }))(Locale)  

3 src目录下,新建locales目录,里面新建index.js文件,此目录用于存放所有项目的文案国际化文件

其中index.js代码:

[javascript]  view plain  copy
  1. import {addLocaleData} from 'react-intl';  
  2. import en from 'react-intl/locale-data/en';  
  3. import zh from 'react-intl/locale-data/zh';  
  4. import zgh from 'react-intl/locale-data/zgh';  
  5. import en_US_ant from 'antd/lib/locale-provider/en_US';  
  6. import zh_CN_ant from 'antd/lib/locale-provider/zh_CN';  
  7. import zh_HK_ant from 'antd/lib/locale-provider/zh_TW';  
  8.   
  9. const zh_CN={  
  10.     "App.username""用户名",  
  11.     "App.password""密码"  
  12. }  
  13. const en_US={  
  14.     "App.username""Username",  
  15.     "App.password""Password"  
  16. }  
  17. const zh_HK={  
  18.     "App.username""用戶名",  
  19.     "App.password""密碼"  
  20. }  
  21.   
  22.   
  23. addLocaleData([  
  24.   ...en,   
  25.   ...zh,   
  26.   ...zgh,  
  27.   {locale: "en_US", parentLocale: "en"},  
  28.   {locale: "zh_CN", parentLocale: "zh"},  
  29.   {locale: "zh_HK", parentLocale: "zgh"},  
  30. ]);  
  31.   
  32.   
  33.   
  34. export const ANT_LANGPACKAGE = {  
  35.   en_US: en_US_ant,  
  36.   zh_CN: zh_CN_ant,  
  37.   zh_HK: zh_HK_ant  
  38. }  
  39.   
  40. export const LANGPACKAGE = {  
  41.   en_US,  
  42.   zh_CN,  
  43.   zh_HK  
  44. }  

此时项目目录如下:



4 修改router.js文件,包裹组件<Locale>

[javascript]  view plain  copy
  1. import React from 'react';  
  2. import { Router, Route, Switch } from 'dva/router';  
  3. import dynamic from 'dva/dynamic'  
  4. import Locale from './locale'  
  5. import {config} from './utils'  
  6. const { menuGlobal } = config  
  7.   
  8. function RouterConfig({ history, app }) {  
  9.   
  10.   return (  
  11.     <Locale>  
  12.       <Router history={history}>  
  13.         <Switch>  
  14.           {  
  15.             menuGlobal.map(({path,...dynamics},index)=>(  
  16.               <Route  
  17.                 key={index}   
  18.                 path={path}   
  19.                 exact   
  20.                 component={dynamic({  
  21.                   app,  
  22.                   ...dynamics  
  23.                 })}   
  24.               />  
  25.             ))  
  26.           }  
  27.         </Switch>  
  28.       </Router>  
  29.     </Locale>  
  30.   );  
  31. }  
  32.   
  33. export default RouterConfig;  


5 修改BBB.js 增加injectIntl国际化的接入,并增加三个示例,两个antd组件 分页和日期,一个业务文案 用户名和密码

[javascript]  view plain  copy
  1. import React, { Component } from 'react';  
  2. import {connect} from 'dva';  
  3. import { Link } from 'dva/router';  
  4. import {Row, Col, Dropdown, Menu, Button, Pagination, Calendar} from 'antd'  
  5. import { injectIntl } from 'react-intl';  
  6.   
  7. class BBB extends Component {  
  8.   
  9.   changeLang=(e)=>{  
  10.     const {dispatch} = this.props;  
  11.     dispatch({  
  12.       type:'app/changeLang',  
  13.       payload:{  
  14.         value:e.key  
  15.       }  
  16.     })  
  17.   }  
  18.   
  19.   render() {  
  20.     const {i18n, intl:{formatMessage}} = this.props;  
  21.     const menu=(  
  22.       <Menu   
  23.         onClick={this.changeLang}  
  24.         selectedKeys={[i18n]}  
  25.       >  
  26.       <Menu.Item key="zh_CN">  
  27.         中文  
  28.       </Menu.Item>  
  29.       <Menu.Item key="en_US">  
  30.         英文  
  31.       </Menu.Item>  
  32.       <Menu.Item key="zh_HK">  
  33.         繁体  
  34.       </Menu.Item>  
  35.     </Menu>  
  36.     )  
  37.   
  38.     return (  
  39.       <div>  
  40.         <p>  
  41.           BBB页  
  42.         </p>  
  43.         <Link to={'/aaa'}>去AAA页面</Link>  
  44.         <br />  
  45.         <Link to={'/ccc'}>去CCC页面</Link>  
  46.   
  47.         <Row>  
  48.           <Col offset={2} span={10}>  
  49.             <Dropdown trigger={['click']} overlay={menu}>  
  50.               <Button>{i18n=='zh_CN'?'中文':i18n=='en_US'?'英文':'繁体'}</Button>  
  51.             </Dropdown>  
  52.           </Col>  
  53.           <Col span={12}>  
  54.             <p>{formatMessage({id:'App.username'})}</p>  
  55.             <p>{formatMessage({id:'App.password'})}</p>  
  56.             <div>  
  57.               <Pagination defaultCurrent={1} total={20} showSizeChanger />  
  58.               <Calendar fullscreen={false}  />  
  59.             </div>  
  60.           </Col>  
  61.         </Row>  
  62.            
  63.       </div>  
  64.     );  
  65.   }  
  66. }  
  67.   
  68. export default connect(({  
  69.   app  
  70. })=>({  
  71.   i18n:app.get('i18n')  
  72. }))(injectIntl(BBB))  

好了,保存文件,重启运行npm start,跑起来咯!





项目已然OK,但是需知,复杂的项目,国际化文案很庞大,我们需要把翻译文件提出来,不能放于locales/index.js文件中。

6 在locales目录下新建三个目录:en_US zh_CN zh_HK 用于存放翻译文件

7 在中英繁对应目录下创建如下文件:


以zh_CN目录为例

index.js代码:

[javascript]  view plain  copy
  1. import App from './app.json'  
  2. import Aaa from './aaa.json'  
  3. import Bbb from './bbb.json'  
  4.   
  5. const document = {  
  6.   ...App,  
  7.   ...Aaa,  
  8.   ...Bbb,  
  9. }  
  10.   
  11. export default document;  

app.json代码:

[javascript]  view plain  copy
  1. {  
  2.   "App.username""用户名",  
  3.   "App.password""密码"  
  4. }  

aaa.json代码:

[javascript]  view plain  copy
  1. {  
  2.   "Aaa.title""A标题"  
  3. }  

bbb.json代码:

[javascript]  view plain  copy
  1. {  
  2.   "Bbb.title""B标题"  
  3. }  

同样zh_HK和en_US目录页一样:



8 修改locales目录下index.js文件:

[javascript]  view plain  copy
  1. import {addLocaleData} from 'react-intl';  
  2. import en from 'react-intl/locale-data/en';  
  3. import zh from 'react-intl/locale-data/zh';  
  4. import zgh from 'react-intl/locale-data/zgh';  
  5. import en_US_ant from 'antd/lib/locale-provider/en_US';  
  6. import zh_CN_ant from 'antd/lib/locale-provider/zh_CN';  
  7. import zh_HK_ant from 'antd/lib/locale-provider/zh_TW';  
  8. import zh_CN from './zh_CN';  
  9. import en_US from './en_US';  
  10. import zh_HK from './zh_HK';  
  11.   
  12. addLocaleData([  
  13.   ...en,   
  14.   ...zh,   
  15.   ...zgh,  
  16.   {locale: "en_US", parentLocale: "en"},  
  17.   {locale: "zh_CN", parentLocale: "zh"},  
  18.   {locale: "zh_HK", parentLocale: "zgh"},  
  19. ]);  
  20.   
  21.   
  22. export const ANT_LANGPACKAGE = {  
  23.   en_US: en_US_ant,  
  24.   zh_CN: zh_CN_ant,  
  25.   zh_HK: zh_HK_ant  
  26. }  
  27.   
  28. export const LANGPACKAGE = {  
  29.   en_US,  
  30.   zh_CN,  
  31.   zh_HK  
  32. }  

修改BBB.js文件:

[javascript]  view plain  copy
  1. import React, { Component } from 'react';  
  2. import {connect} from 'dva';  
  3. import { Link } from 'dva/router';  
  4. import {Row, Col, Dropdown, Menu, Button, Pagination, Calendar} from 'antd'  
  5. import { injectIntl } from 'react-intl';  
  6.   
  7. class BBB extends Component {  
  8.   
  9.   changeLang=(e)=>{  
  10.     const {dispatch} = this.props;  
  11.     dispatch({  
  12.       type:'app/changeLang',  
  13.       payload:{  
  14.         value:e.key  
  15.       }  
  16.     })  
  17.   }  
  18.   
  19.   render() {  
  20.     const {i18n, intl:{formatMessage}} = this.props;  
  21.     const menu=(  
  22.       <Menu   
  23.         onClick={this.changeLang}  
  24.         selectedKeys={[i18n]}  
  25.       >  
  26.       <Menu.Item key="zh_CN">  
  27.         中文  
  28.       </Menu.Item>  
  29.       <Menu.Item key="en_US">  
  30.         英文  
  31.       </Menu.Item>  
  32.       <Menu.Item key="zh_HK">  
  33.         繁体  
  34.       </Menu.Item>  
  35.     </Menu>  
  36.     )  
  37.   
  38.     return (  
  39.       <div>  
  40.         <p>  
  41.           BBB页  
  42.         </p>  
  43.         <Link to={'/aaa'}>去AAA页面</Link>  
  44.         <br />  
  45.         <Link to={'/ccc'}>去CCC页面</Link>  
  46.   
  47.         <Row>  
  48.           <Col offset={2} span={10}>  
  49.             <Dropdown trigger={['click']} overlay={menu}>  
  50.               <Button>{i18n=='zh_CN'?'中文':i18n=='en_US'?'英文':'繁体'}</Button>  
  51.             </Dropdown>  
  52.           </Col>  
  53.           <Col span={12}>  
  54.             <p>{formatMessage({id:'App.username'})}</p>  
  55.             <p>{formatMessage({id:'App.password'})}</p>  
  56.             <p>{formatMessage({id:'Aaa.title'})}</p>  
  57.             <p>{formatMessage({id:'Bbb.title'})}</p>  
  58.             <div>  
  59.               <Pagination defaultCurrent={1} total={20} showSizeChanger />  
  60.               <Calendar fullscreen={false}  />  
  61.             </div>  
  62.           </Col>  
  63.         </Row>  
  64.            
  65.       </div>  
  66.     );  
  67.   }  
  68. }  
  69.   
  70. export default connect(({  
  71.   app  
  72. })=>({  
  73.   i18n:app.get('i18n')  
  74. }))(injectIntl(BBB))  

OK,重启刷新,查看页面:



项目至此,基础架构已搭建完毕,剩下的就是布局Layout和公用组件components的封装了,比如业务组件转化器,业务组件过滤器等等。

给大家推荐几个常用并且好用的工具,也许对你有帮助,可自行网上查阅资料,研究使用:

path-to-regexp

react-helmet

classnames

moment


目前的项目基础已经形成,可以说非常干净的一个流程,没有复杂化的东西,希望对你有所帮助!

Thank!