はじめに
アプリの国際化に伴い、多言語に対応する必要が出てくると思います。
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に全て上げました。