使用Next.js的主要目的是实现SSR优化SEO,当然在使用过程中遇到过很多问题。非常感谢 luffyZh 分享的文章,让我少走很多弯路。(Tip:刚开始搭建的同学, 建议先去看 luffyZh 和 官方文档 了解基础知识再来看本文章哦 #^.^# )。
言归正传,antd官方是推荐使用react-intl实现国际化的,下面分享一下我在Next.js中实现国际化的过程,有不对或可优化的欢迎指正哦。
一、npm 加载 react-intl
- npm i react-intl -S
二、在根目录添加locales文件夹,用于存放不同语言的js文件
示例:
locales/
--| en.js // 存放语言文本键值对
--| en-US.js // 导出存放语言的对象
--| zh.js
--| zh-CN.js
zh.js
tip: 默认对象只能使用"debugObj.hello"这种方式,咱们常用的 debugObj{ hello:'你好' } 在这里使用会导致组件中取不到值。
export default {
title:'标题',
dynamicName:" 动态赋值:{val}",
"debugObj.hello":"你好 Debug对象",
};
- 优化 - 使用flat将对象扁平化,从而支持debugObj{ hello:'你好' }对象包裹
- 加载flat npm 包
npm i flat -S
- 修改zh.js结构
export default { home: { title:'标题' } };
- 在_app.js中引入flat,将语言包对象扁平化传给react-intl message
zh-CN.js
import appLocaleData from 'react-intl/locale-data/zh';
import zhMessages from './zh.js';
import antdZh from 'antd/lib/locale-provider/zh_CN'; //antd语言包
let appLocale = {
messages: {
...zhMessages,
},
antd: antdZh,
locale: 'zh-CN',
data: appLocaleData,
};
export default appLocale;
三、在/pages/_app.js中引入LocaleProvide(antd组件语言)和IntlProvider(react-intl)组件,在render函数中包裹根组件。
_app.js
import { Fragment } from 'react';
import App, { Container } from 'next/app';
import { LocaleProvider } from 'antd';
import { addLocaleData, IntlProvider } from 'react-intl';
// 处理对象嵌套
// import Flat from 'flat';
//导入中英文对象
import _ZH from '../locales/zh-CN';
import _EN from '../locales/en-US';
let appLocale = {
messages: {
...zhMessages,
},
antd: antdZh,
locale: 'zh-CN',
data: appLocaleData,
};
class PageContainer extends App {
getLocale(languages){
const appLocale = this.getLocaleDatas(languages);
addLocaleData(...appLocale.data);
return appLocale;
}
getLocaleDatas(lang) {
let result = {};
switch (lang) {
case 'zh-CN':
result = _ZH;
break;
case 'en-US':
result = _EN;
break;
default:
result = _ZH;
}
return result;
}
// 该render在F5刷新,服务端执行一次后再到前端客户端执行一次
render () {
const { Component, pageProps, router } = this.props;
// router.query.lang当前语言 - 需要通过修改server.js传入query.lang
// 根据url设置语言
const languages = router.query.lang || 'zh-CN';
const appLocale = this.getLocale(languages);
return (
<Fragment>
<Container>
{/* antd语言 */}
<LocaleProvider locale={appLocale.antd}>
{/* 引用语言包 */}
<IntlProvider
locale={appLocale.locale}
{/* 语言包对象嵌套 */}
// messages={Flat(appLocale.messages)}
{/* 默认 */}
messages={appLocale.messages}
formats={appLocale.formats}
>
<Component {...pageProps} router={router} />
</IntlProvider>
</LocaleProvider>
</Container>
</Fragment>
);
}
}
export default appLocale;
四、修改server.js,通过正则匹配URL语言名称,该名称可以在_app.js的render函数中this.props获取
server.js
const express = require('express');
const cp = require('child_process');
const next = require('next');
const PORT = '3006';
const dev = true;
const app = next({ dev });
const handle = app.getRequestHandler();
app.prepare()
.then(() => {
const server = express();
// 处理语言包
const LangExp = new RegExp(/^(\/zh-CN)|(\/en-US)/i);
server.get(LangExp, (req, res) => {
let path = req.path;
let lang = path.match(LangExp)[0];
let url = path.replace(lang, '');
lang = lang.slice(1, lang.length);
// 跳转默认主页
if (!url){
url = `/home`;
}
console.log(url, lang, '----------route---');
return app.render(req, res, url, { lang });
});
// 重定向默认路径
server.get('/', (req, res) => {
res.redirect("/zh-CN/home");
});
server.get('*', (req, res) => {
return handle(req, res);
});
server.listen(PORT, err => {
if (err) throw err;
const serverUrl = `http://localhost:${PORT}`;
console.log(`> Ready on ${serverUrl}`);
});
});
五、配置完成后,在组件中使用
home.js
//FormattedMessage编译后是一个span标签
import { Component } from 'react';
import {Button} from 'antd';
import Link from 'next/link';
import { FormattedMessage,injectIntl, intlShape } from 'react-intl';
class Home extends Component {
static propTypes = {
intl: intlShape.isRequired
}
constructor(props){
super(props);
this.intl = this.props.intl;
this.lang = {
title:{
id:'title'
}
};
}
render(){
return (
{/* 用法一 */}
<FormattedMessage id="title"/>
<br/>
{/* 对象 */}
<FormattedMessage id="debugObj.hello"/>
<br/>
{/* 动态赋值 */}
<FormattedMessage id="dynamicName" values={{val:'888999'}}/>
<br/>
{/* 用法二 */}
<FormattedMessage {...this.lang.title}/>
<br/>
{/* 在input placeholder中使用 */}
<input placeholder={ this.intl.formatMessage({id:'title.title'}) } />
<br/>
<br/>
{/* 语言切换 */}
<Link href={`/zh-CN/home`}>
<Button type="primary">中文</Button>
</Link>
<Link href={`/en-US/home`}>
<Button type="primary">English</Button>
</Link>
);
}
}
export default injectIntl(Home)