アプリの国際化に伴い、多言語に対応する必要が出てくると思います。
Reactを使っている場合、react-intlを利用すると簡単に実現することが可能です。
この記事では以下のことをやります。
Reactアプリを国際化するためのライブラリです。
文字列、日付、数字など様々なフォーマットに対応しています。
フックが用意されていたり、結構柔軟に対応できます。
サンプルプロジェクト作成
$ 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
動作確認
$ 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}"
}
プロジェクト直下に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;
};
まず、先ほど作成した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');
}
};
...
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>
);
};
...
<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>
);
};
...
端末の言語設定を英語にして実行した場合のキャプチャです。
en.jsonには、message
というkeyがないので日本語にフォールバックされてます。
useIntl()
フックを使うことで実現できます。
import {useIntl} from 'react-intl';
...
const {formatMessage} = useIntl();
const name = formatMessage({id: 'name'}, {name: '太郎'})
みたいに使えます。
最後まで読んでいただきありがとうございます。
誰かの参考になれば嬉しいです。
この記事に書いたコードは、GitHubに全て上げました。
Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。
また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!
こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?
コメント