Next.js 配置 react-intl 實現語言國際化

    使用Next.js的主要目的是實現SSR優化SEO,固然在使用過程當中遇到過不少問題。很是感謝 luffyZh 分享的文章,讓我少走不少彎路。(Tip:剛開始搭建的同窗, 建議先去看 luffyZh官方文檔 瞭解基礎知識再來看本文章哦 #^.^# )。前端

    言歸正傳,antd官方是推薦使用react-intl實現國際化的,下面分享一下我在Next.js中實現國際化的過程,有不對或可優化的歡迎指正哦。react

1、npm 加載 react-intl

  • npm i react-intl -S

2、在根目錄添加locales文件夾,用於存放不一樣語言的js文件

示例:express

locales/
--| en.js  // 存放語言文本鍵值對
--| en-US.js // 導出存放語言的對象
--| zh.js  
--| zh-CN.js 
複製代碼

zh.jsnpm

tip: 默認對象只能使用"debugObj.hello"這種方式,我們經常使用的 debugObj{ hello:'你好' } 在這裏使用會致使組件中取不到值。bash

export default {
  title:'標題',
  dynamicName:" 動態賦值:{val}",
  "debugObj.hello":"你好 Debug對象",
};
複製代碼
  • 優化 - 使用flat將對象扁平化,從而支持debugObj{ hello:'你好' }對象包裹
  1. 加載flat npm 包
    npm i flat -S
    複製代碼
  2. 修改zh.js結構
    export default {
     home: {
       title:'標題'
     }
    };
    複製代碼
  3. 在_app.js中引入flat,將語言包對象扁平化傳給react-intl message

zh-CN.jsantd

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;
複製代碼

3、在/pages/_app.js中引入LocaleProvide(antd組件語言)和IntlProvider(react-intl)組件,在render函數中包裹根組件。

_app.jsapp

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;
複製代碼

4、修改server.js,經過正則匹配URL語言名稱,該名稱能夠在_app.js的render函數中this.props獲取

server.jsfrontend

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}`);
    });
  });


複製代碼

5、配置完成後,在組件中使用

home.jside

//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)
複製代碼

6、BB一下

貼個摸魚羣二維碼,能夠進來一塊兒摸摸魚,探討一下。函數

相關文章
相關標籤/搜索