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

mac2025-07-21  3

在做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。如果你有解决方法,欢迎留言指导。

最新回复(0)