接上一篇:https://blog.csdn.net/xw505501936/article/details/80625613
项目配置到这里之后,接下来就需要处理以下配置了:
- 国际化
- 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
cnpm i immutable react-intl --save
修改models目录下的app.js中引入import {Map} from 'immutable'; 修改如下:
import {Map} from 'immutable'; const initState = Map({ i18n: 'zh_CN' }) export default { namespace: 'app', state:initState, subscriptions: { }, effects: { * changeLang ({ payload: {value}, }, { put }) { yield put({ type: 'updateLang', payload: {value}}); }, }, reducers: { updateLang (state,{payload:{value}}) { return state.set('i18n',value); }, }, };
修改routes目录下BBB.js,如下:
import React, { Component } from 'react'; import {connect} from 'dva'; import { Link } from 'dva/router'; import {Row, Col, Dropdown, Menu, Button} from 'antd' class BBB extends Component { changeLang=(e)=>{ const {dispatch} = this.props; dispatch({ type:'app/changeLang', payload:{ value:e.key } }) } render() { const {i18n} = this.props; const menu=( <Menu onClick={this.changeLang} selectedKeys={[i18n]} > <Menu.Item key="zh_CN"> 中文 </Menu.Item> <Menu.Item key="en_US"> 英文 </Menu.Item> <Menu.Item key="zh_HK"> 繁体 </Menu.Item> </Menu> ) return ( <div> <p> BBB页 </p> <Link to={'/aaa'}>去AAA页面</Link> <br /> <Link to={'/ccc'}>去CCC页面</Link> <Row> <Col offset={2}> <Dropdown trigger={['click']} overlay={menu}> <Button>{i18n=='zh_CN'?'中文':i18n=='en_US'?'英文':'繁体'}</Button> </Dropdown> </Col> </Row> </div> ); } } export default connect(({ app })=>({ i18n:app.get('i18n') }))(BBB)
以上修改主要功能:
app.js 增加初始语言参数i18n,增加effects和reducers方法,用于接收语言切换的action,并存储选中的key值
BBB.js 增加connect使当前组件接入store数据,增加切换组件,用于点击切换语言,并dispatch一个action给app/changeLang,触发修改修改model数据
效果如图:
到这里,数据层面的流程已经打通,接下来处理国际化逻辑,国际化组件必然放于跟组件或最外层组件包裹,这样全局均可以使用并生效
2 src目录下新建文件locale.js,此组件仅用于接入国际化使用:
import {connect} from 'dva'; import React from 'react'; import { LocaleProvider } from 'antd' //antd国际化组件 import {IntlProvider} from 'react-intl' //业务文案的国际化组件 import {ANT_LANGPACKAGE, LANGPACKAGE} from './locales'; const Locale=({ children,i18n })=>{ return ( <LocaleProvider locale={ANT_LANGPACKAGE[i18n]}> <IntlProvider locale={i18n} messages={LANGPACKAGE[i18n]} > {children} </IntlProvider> </LocaleProvider> ); } export default connect(({ app })=>({ i18n:app.get('i18n') }))(Locale)
3 src目录下,新建locales目录,里面新建index.js文件,此目录用于存放所有项目的文案国际化文件
其中index.js代码:
import {addLocaleData} from 'react-intl'; import en from 'react-intl/locale-data/en'; import zh from 'react-intl/locale-data/zh'; import zgh from 'react-intl/locale-data/zgh'; import en_US_ant from 'antd/lib/locale-provider/en_US'; import zh_CN_ant from 'antd/lib/locale-provider/zh_CN'; import zh_HK_ant from 'antd/lib/locale-provider/zh_TW'; const zh_CN={ "App.username": "用户名", "App.password": "密码" } const en_US={ "App.username": "Username", "App.password": "Password" } const zh_HK={ "App.username": "用戶名", "App.password": "密碼" } addLocaleData([ ...en, ...zh, ...zgh, {locale: "en_US", parentLocale: "en"}, {locale: "zh_CN", parentLocale: "zh"}, {locale: "zh_HK", parentLocale: "zgh"}, ]); export const ANT_LANGPACKAGE = { en_US: en_US_ant, zh_CN: zh_CN_ant, zh_HK: zh_HK_ant } export const LANGPACKAGE = { en_US, zh_CN, zh_HK }
此时项目目录如下:
4 修改router.js文件,包裹组件<Locale>
import React from 'react'; import { Router, Route, Switch } from 'dva/router'; import dynamic from 'dva/dynamic' import Locale from './locale' import {config} from './utils' const { menuGlobal } = config function RouterConfig({ history, app }) { return ( <Locale> <Router history={history}> <Switch> { menuGlobal.map(({path,...dynamics},index)=>( <Route key={index} path={path} exact component={dynamic({ app, ...dynamics })} /> )) } </Switch> </Router> </Locale> ); } export default RouterConfig;
5 修改BBB.js 增加injectIntl国际化的接入,并增加三个示例,两个antd组件 分页和日期,一个业务文案 用户名和密码
import React, { Component } from 'react'; import {connect} from 'dva'; import { Link } from 'dva/router'; import {Row, Col, Dropdown, Menu, Button, Pagination, Calendar} from 'antd' import { injectIntl } from 'react-intl'; class BBB extends Component { changeLang=(e)=>{ const {dispatch} = this.props; dispatch({ type:'app/changeLang', payload:{ value:e.key } }) } render() { const {i18n, intl:{formatMessage}} = this.props; const menu=( <Menu onClick={this.changeLang} selectedKeys={[i18n]} > <Menu.Item key="zh_CN"> 中文 </Menu.Item> <Menu.Item key="en_US"> 英文 </Menu.Item> <Menu.Item key="zh_HK"> 繁体 </Menu.Item> </Menu> ) return ( <div> <p> BBB页 </p> <Link to={'/aaa'}>去AAA页面</Link> <br /> <Link to={'/ccc'}>去CCC页面</Link> <Row> <Col offset={2} span={10}> <Dropdown trigger={['click']} overlay={menu}> <Button>{i18n=='zh_CN'?'中文':i18n=='en_US'?'英文':'繁体'}</Button> </Dropdown> </Col> <Col span={12}> <p>{formatMessage({id:'App.username'})}</p> <p>{formatMessage({id:'App.password'})}</p> <div> <Pagination defaultCurrent={1} total={20} showSizeChanger /> <Calendar fullscreen={false} /> </div> </Col> </Row> </div> ); } } export default connect(({ app })=>({ i18n:app.get('i18n') }))(injectIntl(BBB))
好了,保存文件,重启运行npm start,跑起来咯!
项目已然OK,但是需知,复杂的项目,国际化文案很庞大,我们需要把翻译文件提出来,不能放于locales/index.js文件中。
6 在locales目录下新建三个目录:en_US zh_CN zh_HK 用于存放翻译文件
7 在中英繁对应目录下创建如下文件:
以zh_CN目录为例
index.js代码:
import App from './app.json' import Aaa from './aaa.json' import Bbb from './bbb.json' const document = { ...App, ...Aaa, ...Bbb, } export default document;
app.json代码:
{ "App.username": "用户名", "App.password": "密码" }
aaa.json代码:
{ "Aaa.title": "A标题" }
bbb.json代码:
{ "Bbb.title": "B标题" }
同样zh_HK和en_US目录页一样:
8 修改locales目录下index.js文件:
import {addLocaleData} from 'react-intl'; import en from 'react-intl/locale-data/en'; import zh from 'react-intl/locale-data/zh'; import zgh from 'react-intl/locale-data/zgh'; import en_US_ant from 'antd/lib/locale-provider/en_US'; import zh_CN_ant from 'antd/lib/locale-provider/zh_CN'; import zh_HK_ant from 'antd/lib/locale-provider/zh_TW'; import zh_CN from './zh_CN'; import en_US from './en_US'; import zh_HK from './zh_HK'; addLocaleData([ ...en, ...zh, ...zgh, {locale: "en_US", parentLocale: "en"}, {locale: "zh_CN", parentLocale: "zh"}, {locale: "zh_HK", parentLocale: "zgh"}, ]); export const ANT_LANGPACKAGE = { en_US: en_US_ant, zh_CN: zh_CN_ant, zh_HK: zh_HK_ant } export const LANGPACKAGE = { en_US, zh_CN, zh_HK }
修改BBB.js文件:
import React, { Component } from 'react'; import {connect} from 'dva'; import { Link } from 'dva/router'; import {Row, Col, Dropdown, Menu, Button, Pagination, Calendar} from 'antd' import { injectIntl } from 'react-intl'; class BBB extends Component { changeLang=(e)=>{ const {dispatch} = this.props; dispatch({ type:'app/changeLang', payload:{ value:e.key } }) } render() { const {i18n, intl:{formatMessage}} = this.props; const menu=( <Menu onClick={this.changeLang} selectedKeys={[i18n]} > <Menu.Item key="zh_CN"> 中文 </Menu.Item> <Menu.Item key="en_US"> 英文 </Menu.Item> <Menu.Item key="zh_HK"> 繁体 </Menu.Item> </Menu> ) return ( <div> <p> BBB页 </p> <Link to={'/aaa'}>去AAA页面</Link> <br /> <Link to={'/ccc'}>去CCC页面</Link> <Row> <Col offset={2} span={10}> <Dropdown trigger={['click']} overlay={menu}> <Button>{i18n=='zh_CN'?'中文':i18n=='en_US'?'英文':'繁体'}</Button> </Dropdown> </Col> <Col span={12}> <p>{formatMessage({id:'App.username'})}</p> <p>{formatMessage({id:'App.password'})}</p> <p>{formatMessage({id:'Aaa.title'})}</p> <p>{formatMessage({id:'Bbb.title'})}</p> <div> <Pagination defaultCurrent={1} total={20} showSizeChanger /> <Calendar fullscreen={false} /> </div> </Col> </Row> </div> ); } } export default connect(({ app })=>({ i18n:app.get('i18n') }))(injectIntl(BBB))
OK,重启刷新,查看页面:
项目至此,基础架构已搭建完毕,剩下的就是布局Layout和公用组件components的封装了,比如业务组件转化器,业务组件过滤器等等。
给大家推荐几个常用并且好用的工具,也许对你有帮助,可自行网上查阅资料,研究使用:
path-to-regexp
react-helmet
classnames
moment
目前的项目基础已经形成,可以说非常干净的一个流程,没有复杂化的东西,希望对你有所帮助!
Thank!