使用react-intl-universal进行组件和非组件的国际化

在做react项目时,用到了国际化。本想使用i8next等第三方国际化文件,但想了想作为react项目应该有自持的js作为国际化的支持。于是在github上搜索了一下,发现了两个:

一个是react-intl,从星级可以看出是react最常用的一种国际化,最大的好处是它用props的方式注入语言包,也就是可以在不刷新页面的情况下直接更改显示的语言。

但是只支持React.Component,也就是貌似只能在React.Compoent中使用,并不能解决组件以外的国际化以及表单校验的国际化,比如我用react+antd搭建的项目,如果想使用antd的表格组件那么,效果如下

我会这么使用:

                <Table bordered rowKey={record => record.uid}
                    dataSource={this.state.list}
                    columns={this.state.columns}
                    rowSelection={rowSelection}
                    pagination={{
                        showSizeChanger: true,
                        // showQuickJumper: true,
                        total: this.state.list.length, // 数据总数
                        pageSize: this.state.pageSize, // 每页条数
                        current: this.state.current, // 当前页码
                        showTotal: ((total) => {
                            return `Total: ${total} items`;
                        })
                    }}
                    loading={this.state.loading}
                    onChange={this.handleTableChange}
                    onRow={(record, rowkey) => {
                        return {
                            onClick: this.showRow.bind(this, record, rowkey)
                        }
                    }} />;

为了美观,并不是所有的东西都会定义在一个组件中,现在我把table组件中的column抽离出来,形成了一个单独的文件

称为columns.js

const columns = [
    {
        title: "Start NAS IP",
        dataIndex: "startNASIp",
        key: "startNASIp",
        sorter: true
    },
    {
        title: "End NAS IP",
        dataIndex: "endNASIp",
        key: "endNASIp",
        sorter: true
    },
    {
        title: "Nas Name",
        dataIndex: "nasName",
        key: "nasName",
        sorter: true
    },
    {
        title: "Description",
        dataIndex: "description",
        key: "description",
        sorter: true
    },
    {
        title: "DM Attributes",
        dataIndex: "dm_attributes",
        key: "dm_attributes",
        sorter: true
    },
];
export default columns

那么我在列表组建中引入这个js文件即可使用,效果就像上面的那样。

我现在的需求就会变成怎样使用react-intl或者react-intl-universal 怎样让一个普通的js非组件化的文件也能实现国际化呢?

在网上搜罗了一番,千篇一律,只是谈到了react-intl-universal的一般用法,简单的在组件中使用,官方也给出了例子,好多的博客也都是参考官网做的例子。没有我想要的效果。

先看一看网上千篇一律的基本实现方法吧。

1.安装react-intl-universal

对,无论使用什么第三方组件,第一步肯定是安装/下载

使用webpack时,我们直接使用npm/yarn去直接安装。因为国际化文件是需要打包发布到我们的生产环境的,所以会安装到dependencies中

npm i react-intl-universal -s

2.引入react-intl-universal

先看一下结构目录,我是使用create-react-app去创建的react应用,目录结构都是一样的,删除了index.js中原始的东西,然后加入了App.js

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(<App />, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

App.js便是我的整个应用的入口文件,定义如下

import React, { Component } from 'react';
import { BrowserRouter, browserHistory } from 'react-router-dom'
import { Provider } from 'react-redux'
import { emit } from "./components/utils/emit";
import intl from 'react-intl-universal';
import hisroty from './history/History'
import Layouts from '../src/layouts/Layouts'
import store from './stores/store'
import locales from './locales/locales'
import './App.css';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      antdLang: locales.en_US,  // 修改antd  组件的国际化
    }
  }
  componentDidMount() {
    emit.on('change_language', lang => this.loadLocales(lang)); // 监听语言改变事件
    this.loadLocales(); // 初始化语言
  }
  loadLocales(lang = 'en-US') {
    intl.init({
      currentLocale: lang,  // 设置初始语音
      locales,
    }).then(() => {
      this.setState({
        antdLang: lang === 'zh-CN' ? locales.zh_CN : locales.en_US
      });
    });
  }
  render() {
    return (
      <Provider store={store} locale={this.state.antdLang}>
        <BrowserRouter hisroty={hisroty} >
          <Layouts />
        </BrowserRouter>
      </Provider>

    );
  }
}

export default App;

emit.on用于监听国际化语言发生变化,在切换国际化语言时,就会重新加载设置的语言this.loadLocales()。

intl.init用于初始化默认的语言设置,这里我默认的是英文。

locales.js中定义了引入的国际化文件

import en_US from './en_US.json'
import zh_CN from './zh_CN.json'
const locales = {
    'en-US': en_US,
    'zh-CN': zh_CN,
};

export default locales

en_US.json

{
    "samp": {
        "index": {
            "logout": "Logout"
        },
        "policyEngine": {
            "title": "Policy Engine",
            "nasClients": {
                "title": "Nas Clients",
                "item": {
                    "nasName": "NAS Name",
                    "startNASIp": "Start NAS IP",
                    "endNASIp": "End NAS IP",
                    "description": "Description",
                    "dm_attributes": "DM Attributes"
                }
            }
        }
    }
}

zh_CH.json

{
    "samp": {
        "index":{
            "logout":"退出"
        },
        "policyEngine": {
            "title": "策略中心",
            "nasClients": {
                "title": "NAS客户端",
                "listTitle": "Nas客户端列表",
                "item": {
                    "nasName": "NAS名字",
                    "startNASIp": "NAS起始IP",
                    "endNASIp": "NAS结束IP",
                    "description": "描述",
                    "dm_attributes": "DM属性"
                }
            }
        }
    }
}

在这里监听时间使用了第三方组件events.js。emit的定义如下:

import EventEmitter from 'events'
const emit = new EventEmitter(); 
export { emit };

这样准备工作基本上完成了。接下来就是怎么使用了

应用中我会分为,登录功能和主页面,现在在主页面中使用国际化切换来改变不同的显示。

我在IndexLayout中定义了国际化切换操作,并且使用logout作为效果展示。

首先需要在IndexLayout.js中导入

import intl from 'react-intl-universal';

然后在页面使用intl.get("youKey")来绑定

<span className="samp-index-header-logout"><Icon type="logout"></Icon>{intl.get("samp.index.logout")}</span>

现在看一下页面展示情况,目前是英文,显示的也是英文

当我切换语言时,绑定了事件,前面我们已经在App.js中使用emit去监听语言切换的事件,那么在切换语言方法中,应该去触发这个事件,才能让国际化语言发生变化。

    //国际化
    handleChange = (val) => {
        console.log("val:",val)
        // 发送消息
        emit.emit('change_language', val);
    }

这样就完成了国际化的切换,并且可以正常展示

刚开始看到这种效果时,欣喜若狂。注意,这里的intl.get操作是在组件中使用的。

天真的我就觉得在所有文件中应该都能使用,于是我就常识性的在非组件的js文件中使用,发现并不起作用,比如我定义的table的列名也及时表头的显示:

import intl from 'react-intl-universal';
const columns = [
    {
        title: intl.get("samp.policyEngine.nasClients.item.startNASIp"),
        dataIndex: "startNASIp",
        key: "startNASIp",
        sorter: true
    },
    {
        title: intl.get("samp.policyEngine.nasClients.item.endNASIp"),
        dataIndex: "endNASIp",
        key: "endNASIp",
        sorter: true
    },
    {
        title: intl.get("samp.policyEngine.nasClients.item.nasName"),   
        dataIndex: "nasName",
        key: "nasName",
        sorter: true
    },
    {
        title: intl.get("samp.policyEngine.nasClients.item.description"),
        dataIndex: "description",
        key: "description",
        sorter: true
    },
    {
        title: intl.get("samp.policyEngine.nasClients.item.dm_attributes"),
        dataIndex: "dm_attributes",
        key: "dm_attributes",
        sorter: true
    },
];
export default columns

发现,我曹,表头没有了????

这时何等的我曹-----------

于是就想打印一下看看到底是什么鬼 

发现,结果全是空???

想了想应该是TM不支持。然后就去antd官网找了一下,官方针对antd的组件提供了国际化支持,但那不是我要的

https://ant.design/docs/react/i18n

我把columns的定义移到component中,发现是可以国际化的,但是放到单独的js中却是不支持的

比如,我修改了table的column的绑定

                <Table bordered rowKey={record => record.uid}
                    dataSource={this.state.list}
                    columns={columns}
                    rowSelection={rowSelection}
                    pagination={{
                        showSizeChanger: true,
                        // showQuickJumper: true,
                        total: this.state.list.length, // 数据总数
                        pageSize: this.state.pageSize, // 每页条数
                        current: this.state.current, // 当前页码
                        showTotal: ((total) => {
                            return `Total: ${total} items`;
                        })
                    }}
                    loading={this.state.loading}
                    onChange={this.handleTableChange}
                    onRow={(record, rowkey) => {
                        return {
                            onClick: this.showRow.bind(this, record, rowkey)
                        }
                    }} />;

并且在render函数中定义了

const columns = [
            {
                title: intl.get("samp.policyEngine.nasClients.item.startNASIp"),
                dataIndex: "startNASIp",
                key: "startNASIp",
                sorter: true
            },
            {
                title: intl.get("samp.policyEngine.nasClients.item.endNASIp"),
                dataIndex: "endNASIp",
                key: "endNASIp",
                sorter: true
            },
            {
                title: intl.get("samp.policyEngine.nasClients.item.nasName"),   
                dataIndex: "nasName",
                key: "nasName",
                sorter: true
            },
            {
                title: intl.get("samp.policyEngine.nasClients.item.description"),
                dataIndex: "description",
                key: "description",
                sorter: true
            },
            {
                title: intl.get("samp.policyEngine.nasClients.item.dm_attributes"),
                dataIndex: "dm_attributes",
                key: "dm_attributes",
                sorter: true
            },
        ];

然后尝试了一下,确实可以

但这就与我最初的设计有出入,显然是不合适的。

于是就想到,既然可以在组件中使用,那我能不能定义一个管道或者定义一个中间件去实现,这种组件到非组件之间的转换呢?

定义了一个用于转换的js文件,用法很简单,就是在非组件中引入组件js,然后在组件中进行国际化,在返回国际化后的值,就可以了。

于是定义了一个IntlTranslation.js

import React, { Component } from 'react';
import intl from 'react-intl-universal';
class IntlTranslation extends Component {
    constructor(props) {
        super(props);
        this.state = {  }
    }
    render() { 
        return ( 
            <span>{intl.get(this.props.intlKey)}</span>
         );
    }
}
 
export default IntlTranslation;

然后修改了一下columns.js,

import React from 'react';
import IntlTranslation from '../../../components/utils/IntlTranslation'
const columns = [
    {
        title: <IntlTranslation intlKey = "samp.policyEngine.nasClients.item.startNASIp"/>,
        dataIndex: "startNASIp",
        key: "startNASIp",
        sorter: true
    },
    {
        title: <IntlTranslation intlKey = "samp.policyEngine.nasClients.item.endNASIp"/>,
        dataIndex: "endNASIp",
        key: "endNASIp",
        sorter: true
    },
    {
        title: <IntlTranslation intlKey = "samp.policyEngine.nasClients.item.nasName"/>,   
        dataIndex: "nasName",
        key: "nasName",
        sorter: true
    },
    {
        title: <IntlTranslation intlKey = "samp.policyEngine.nasClients.item.description"/>,
        dataIndex: "description",
        key: "description",
        sorter: true
    },
    {
        title: <IntlTranslation intlKey = "samp.policyEngine.nasClients.item.dm_attributes"/>,
        dataIndex: "dm_attributes",
        key: "dm_attributes",
        sorter: true
    },
];
export default columns

测试了一下,居然好用!!!!!!!!!!!

目前只想到这种解决方案,也许react-intl-universal能实现,但是没有发现有类似的demo。如果你有解决方法,欢迎留言指导。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值