React+Redux アプリを react-i18next で多言語化する
React+Redux アプリを多言語化する方法を調べてみました。
前提
TypeScript
多言語化ライブラリー
JavaScript で多言語化するライブラリーはいろいろありますが、React で使えるものは限られてくるようです。
いくつか調べて、i18next と react-i18next を使うことにしました。
i18next の方は、特定のプラットフォームやフレームワークに依存しない多言語化ライブラリーで、react-i18next は i18next を React で使いやすくするプラグインです。
インストール
まず、ライブラリーをインストールします:
npm install -S i18next react-i18next npm install -D @types/i18next @types/react-i18next
セットアップ
i18next の設定ファイルを作成します。
// I18n.ts import * as i18next from 'i18next'; const i18n = i18next .init({ resources: { en: { general: { appName: 'Super App', } }, ja: { general: { appName: 'スーパーアプリ', } } }, fallbackLng: 'en', // string or array of namespaces to load ns: ['general'], defaultNS: 'general', interpolation: { escapeValue: false, // not needed for react formatSeparator: ',', }, // react-i18next special options (optional) react: { wait: true, // true: wait for loaded in every translated hoc } }); export default i18n;
i18next のサンプルと比べて、import
の書き方が違ったり、init()
の返り値をエクスポートしたりしていますが、TypeScript の場合、こうしないと動かないようです。
Ref. Using with typescript · react-i18next documentation
また、オプションについては、以下のページを参照してください:
プロバイダーの組み込み
トップレベルの Component(index.tsx や App.tsx)に I18nextProvider を組み込みます:
// index.tsx ..... import { I18nextProvider } from 'react-i18next'; import i18n from './I18n'; ..... ReactDOM.render( <Provider store={store}> <I18nextProvider i18n={i18n} > <App /> </I18nextProvider> </Provider>, document.getElementById('root') as HTMLElement );
Componentでの使用
ここまでで準備ができたので、Component に組み込みます:
..... import { translate } from 'react-i18next'; interface IProps { t?: any; } class ToolbarComponent extends React.Component<IProps> { render() { const { t } = this.props; return ( <div>{t('appName')}</div> ); } } export default translate()(ToolbarComponent);
翻訳結果を得るのは t()
という関数です。
これはプロパティに自動的に入ってくるので、TypeScript ではインタフェースで定義してあげます。
なお、t
のインタフェース定義は省略して any で逃げています。
また、オプショナルにしていますが、自分でセットしていないので、念ためです。
最後の export のところで、translate()()
をかませます。
translate()
のパラメーターにネームスペースを指定することもできます。
言語の指定
ブラウザーの言語を使用する場合は、i18next-browser-languageDetector を入れます。
UIで指定できるようにする場合は、changeLanguage()
を使います。
僕は、まだどちらも試していません。
翻訳リソースの分離
上の例では、設定ファイルに翻訳リソースを直書きしていましたが、翻訳データが多くなると別ファイルに分離したくなります。
1つの方法としては、言語ごとに .ts
ファイルを作成し、そこでは翻訳データのみを記述してエクスポートし、それを設定ファイルで読み込むことができます:
// I18nEn.ts export const general = { appName: 'Super App', ..... };
// I18n.ts ..... import * as en from './I18nEn'; const i18n = i18next .init({ resources: { en: { general: en.general, }, .....
あと、init()
の中に書くのもあまりきれいではないので、addResourceBundle()
を使って、別で読み込むこともできます。
もしかすると、JSONファイルにして読み込ませることもできるかもしれませんが、まだ調べ切れていません。
あとがき
i18next は結構多機能で、翻訳リソースをバックエンドサーバーに置いて、言語が切り替わったら読み込む、みたいなこともできそうなので、後で調べてみたいと思います。