利用 React-Router 實作多國語系網址參數
目標
做出 URL 可以符合 https://{domain/language/component} 的 single page app
需要的套件
這邊我們使用 i18next 來作為多國語系的範例套件
$ npm install i18next i18next-xhr-backend
i18next 設定檔
設定需要使用的語系為英文 (en) 與中文 (zh),並設定所需要的模式
import i18n from 'i18next';
import XHR from 'i18next-xhr-backend';i18n
.use(XHR)
.init({
whitelist: ['en', 'zh'],
load: 'currentOnly',
lowerCaseLng: true,
ns: ['app'],
defaultNS: 'app',
debug: false,
});module.exports = i18n;
你可以從官方文件看到更多設定與說明: i18next-doc
routerSetting 定義 App 路由
這邊我們定義整個 App 的路由設定,透過 React-Router 來做!設定完畢只要把 routerSetting 餵給 index.js,那麼 React-Router 就會幫你管理 App 路由!
import React, { Component } from 'react';
import { Router, Route, browserHistory, IndexRoute } from 'react-router';
import { syncHistoryWithStore, routerReducer } from 'react-router-redux';import store from '../store/store';
import App from './global/app';
import Home from './home/home';
import AComponent from './functional/AComponent';
import GetParam from './functional/getParam';
import NotFound from './functional/notFound';const history = syncHistoryWithStore(browserHistory, store)const RouterSetting = (props) => (
<Router history={browserHistory}>
<Route path='/:lang/' component={App}>
<IndexRoute component={Home} />
<Route path='AComponent' component={AComponent} />
<Route path='getParam/:toShow' component={GetParam} />
</Route>
<Route path="*" component={NotFound} />
</Router>
);module.exports = RouterSetting;
簡單說明一下,讓 App 負責 /language/ 這個 URL 的路由,那麼 App 本身並沒有 UI 只 render child,把 UI 部分讓底下的 child Component 來決定!
- Home 對應到 domain/language/
- AComponent 對應到 domain/language/AComponent
- getParam 對應到 domain/language/getParam/(toShow 參數)
- NotFound 則是路由都沒有對應到,就會自動被導到客製化的 NotFound 頁面
讓 App.js 來負責偵測 Language 路由的部分
import React, { Component } from 'react';
import { connect } from 'react-redux';
import i18n from '../../i18n';class AppContainer extends Component {
constructor(props) {
super(props);
this.toggleLanguage = this.toggleLanguage.bind(this);
}
componentWillMount() {
// 偵測轉換當前語系
this.toggleLanguage(this.props.params.lang)
}
toggleLanguage(newLanguage) {
if(newLanguage !== i18n.language) {
// 若 URL 語系與當前語系不同,則轉換語系
i18n.changeLanguage(newLanguage);
}
}
render() {
const {
children,
} = this.props; return(<div>{children}</div>);
}
};module.exports = App;
這邊則是實作 App Component 來偵測目前的 URL 路由上的語系,使用 i18n 的 changeLanguage API 來轉換語系!
使用 High-Order Component 或是使用 Class decorator 來更簡單的套用在需要的 Component 上
import React, { Component } from 'react';
import { translate } from 'react-i18next';// HOC
const MultiLanguageComponent = ({ t }) => (
<div>
{t('app:testMultiLang')}
</div>
);module.exports = translate(
['app'],
{ wait: true }
)(MultiLanguageComponent);// Decorator
@translate(['app'], { wait: true })
class MultiLanguageComponent extends Component{
constructor(props) {
super(props);
}
render() {
const { t } = this.props;
return(
<div>
{t('app:testMultiLang')}
</div>
);
}
}module.exports = MultiLanguageComponent;
這邊使用 t 這個 API 來幫我們轉換多國語系,裡面則是指定 app 這個 namespace 來做為區別!在 translate 第一個參數指定要使用的檔案名稱,第二個則是可選的選項!
那 Multi-Language 的定義檔要放在哪呢?
在 output 資料夾中新增一個 locales 資料夾,資料夾結構與範例資料如下:
// Folder structure
.
├── index.html
└── locales
├── en
│ └── app.json
└── zh
└── app.json// project/output/locales/en/app.json
{
"testMultiLang": "test multi language"
}// project/output/locales/zh/app.json
{
"testMultiLang": "測試多國語系"
}
會遇到的問題
為什麼我重新整理後會找不到正確的路徑?
舉例:我到了 localhost/en/home 這個路徑底下,按下重新整理,卻沒辦法正確顯示剛才的路由畫面?
因為你必須在 webpack-dev-server 加上一個設定
const server = new WebpackDevServer(webpack(config), {
historyApiFallback: true,
...otherSettings
}
這樣再重新整理的時候就會正確幫你重新導到正確的路由囉!