はじめに
アプリの国際化に伴い、多言語に対応する必要が出てくると思います。
Reactを使っている場合、react-intlを利用すると簡単に実現することが可能です。
この記事では以下のことをやります。
- React Nativeで作成したアプリを多言語に対応させる
- 端末の言語設定に沿って翻訳する
- 英語と日本語に対応する
- 英語の翻訳情報がない場合は日本語が表示されるようにする
- サポート外言語が設定されている場合は日本語が表示されるようにする
react-intlとは?
Reactアプリを国際化するためのライブラリです。
文字列、日付、数字など様々なフォーマットに対応しています。
フックが用意されていたり、結構柔軟に対応できます。
環境情報
- macOS
- React Native (0.60.5)
- Typescript (^3.8.3)
- react-intl (^4.1.1)
- yarn
- cocoapods
セットアップ
サンプルプロジェクト作成
$ npx react-native init LocalizationSample --version 0.60.5 --template react-native-template-typescript
$ yarn add react-intl
$ yarn add intl
$ yarn add lodash @types/lodash
$ yarn add react-native-localize
$ cd ios
$ pod install
- intlは、ReactNativeでreact-intlを利用する時に必要なので入れます。
- react-native-localizeは、端末から言語設定を取得するために入れます。
- lodashは、あとでincludesメソッドを使いたいので入れます。
動作確認
$ cd LocalizationSample
$ npx react-native run-ios
$ npx react-native run-android
iosとandroidのシュミレーター で「Welcome to React」と表示されたらOKです。
※ androidは先にシュミレータを起動しておく必要があります
ロケールファイルを作成する
プロジェクト直下にja.jsonとen.jsonを作成してください。
ja.json
{
"title": "ホーム",
"name": "{name} 様",
"message": "こんにちは"
}
en.json
{
"title": "HOME",
"name": "Mr/Ms {name}"
}
- {}で文字列を囲むと、その文字列をkeyに値を外から差し込めるようになります。
- en.json側にmessageが存在しませんが、あとで日本語にフォールバックされることを確認したいのでこうしています。
言語設定を取得する関数を用意する
プロジェクト直下にi18n.tsxを作成して、言語設定を取得するgetLocale()を記述します。
import * as React from 'react';
import * as RNLocalize from 'react-native-localize';
import {includes} from 'lodash';
const SUPPORTED_LOCALE = ['ja', 'en'];
const DEFAULT_LOCALE = 'ja';
const getLocale = (): string => {
const locales = RNLocalize.getLocales();
const languageCode = locales[0].languageCode;
if (includes(SUPPORTED_LOCALE, languageCode)) {
return languageCode;
}
return DEFAULT_LOCALE;
};
- SUPPORTED_LOCALEの値がアプリで対応する言語です。
- RNLocalize.getLocales() から端末の言語設定を取得しています。
- 取得した言語がサポート外ならばデフォルトロケールを返すようにしています。
翻訳情報を返す関数を用意する
まず、先ほど作成したi18n.tsxでロケールファイルをインポートします。
import ja from './ja.json';
import en from './en.json';
...
次に、翻訳情報を返すgetMesseges()を追記します。
...
const getMessages = (locale: string): {[key: string]: string} => {
switch (locale) {
case 'ja':
return ja;
case 'en':
return {
...getMessages('ja'),
...en,
};
default:
throw new Error('unknown locale');
}
};
...
- 'ja'が引数に指定された場合、日本語の翻訳情報を返します。
- 'en'が引数に指定された場合、英語の翻訳情報を返します。ただ、欠落している部分は日本語が入るようになっています。こうすることで英語がないとき日本語にフォールバックすることができます。
IntlProviderを設定する
react-intlを利用するため、アプリのルートコンポーネントを<IntlProvider>
でラップする必要があります。
また、Intlポリフィルも合わせてimportする必要があるので、i18n.tsxで<IntlProvider>
をラップした<IntlProviderWrapper>
作成して、それをApp.tsxで呼び出すようにしたいと思います。
まず、i18n.tsxの一行目でポリフィルをインポートします。
import 'intl';
import 'intl/locale-data/jsonp/ja';
import 'intl/locale-data/jsonp/en';
...
import 'intl/locale-data/jsonp/ja';
は、サポートする言語が増えるたび、同様に追加する必要があります。今回は日本語(ja)と英語(en)だけです。これをインポートしないと起動したときにIntlが見つからないよと怒られます。
次に、i18n.tsxに<IntlProvider>
をラップした<IntlProviderWrapper>
を返すコンポーネントを作成します。
childrenを受け取るようにするので、指定する型(ReactNode)をインポートしておきます。
...
import {ReactNode} from 'react';
...
export const IntlProviderWrapper = ({children}: {children: ReactNode}) => {
const locale = getLocale();
return (
<IntlProvider locale={locale} messages={getMessages(locale)}>
{children}
</IntlProvider>
);
};
...
- localeには翻訳するロケールを指定します
- messagesにはロケールファイルから取得したJSONオブジェクトを渡します。こうすることで、
<IntlProvider>
でラップしたコンポーネントで、keyに紐づく値を取得することができるようになります。
次に、App.tsxを開いて、作成したでルートコンポーネントをラップします。
import * as React from 'react';
import {SafeAreaView, Text} from 'react-native';
import {IntlProviderWrapper} from './i18n';
const App = () => {
return (
<SafeAreaView>
<Text>{'Hello'}</Text>
</SafeAreaView>
);
};
export default () => {
return (
<IntlProviderWrapper>
<App />
</IntlProviderWrapper>
);
};
これで設定DONE。
実際に翻訳してみる
<FormattedMessage>
を使うことで文字列の翻訳ができます。
FormattedMessage
をインポートして、<SafeAreaView>
の中身を以下のように変更します。
...
import {FormattedMessage} from 'react-intl';
...
const App = () => {
return (
<SafeAreaView>
<Text>
<FormattedMessage id={'title'} />
</Text>
<Text>
<FormattedMessage id={'name'} values={{name: '太郎'}} />
</Text>
<Text>
<FormattedMessage id={'message'} />
</Text>
</SafeAreaView>
);
};
...
- idには、keyを指定します。
- valuesには、差し込む値を指定します。ちなみに、HTMLも差し込めます。
※ こちらには、文字列以外の対応方法も載っています。
実行結果
端末の言語設定を英語にして実行した場合のキャプチャです。
en.jsonには、message
というkeyがないので日本語にフォールバックされてます。
メソッドの引数などに翻訳した文字列を使いたい場合
useIntl()
フックを使うことで実現できます。
import {useIntl} from 'react-intl';
...
const {formatMessage} = useIntl();
const name = formatMessage({id: 'name'}, {name: '太郎'})
みたいに使えます。
最後に
最後まで読んでいただきありがとうございます。
誰かの参考になれば嬉しいです。
この記事に書いたコードは、GitHubに全て上げました。
参考
https://github.com/formatjs/react-intl