ReactNativeでのアプリのローカライズ

アプリケーションの開発中に、多言語サポートを行う必要がありました。タスクは、ユーザーがアプリケーションインターフェイスの言語(ロシア語と英語)を変更できるようにすることでした。同時に、テキストとコンテンツは「オンザフライ」で翻訳する必要があります。



これを行うには、2つの問題を解決する必要がありました。



  1. アプリケーションの現在の言語を判別します。
  2. オンザフライ変換にグローバル状態を使用します。


この記事では、これらの問題をどのように解決したかを詳しく説明します。そして、私たちは行きました。



現在のデバイス言語を決定する



もちろん、現在の言語を判別するために、react-native-i18nライブラリを使用できますが、サードパーティのライブラリがなくても実行できるため、これを使用しないことにしました。これを行うには、次のように記述します。



import {NativeModules, Platform} from 'react-native';

let deviceLanguage = (Platform.OS === 'ios'
        ? NativeModules.SettingsManager.settings.AppleLocale ||
          NativeModules.SettingsManager.settings.AppleLanguages[0] // iOS 13
        : NativeModules.I18nManager.localeIdentifier


iosの場合はSettingsManagerを介してアプリケーション言語を取得し、androidの場合はネイティブI18nManagerを介してアプリケーション言語を取得します。



現在のデバイス言語を受け取ったので、それをAsyncStorageに保存して、2番目のタスクに進むことができます。



「オンザフライ」で翻訳します



MobXを使用してグローバル状態を管理しますが、別のソリューションを使用することもできます。



そのため、現在のローカリゼーションのグローバルな状態を担当するクラス(「モデル」と呼びたい)を作成する必要があります。私たちは作成します:



//   ,      lang
const STORE = '@lang-store';
//    
const RU_LANGS = [
  'ru',
  'az',
  'am',
  'by',
  'ge',
  'kz',
  'kg',
  'md',
  'tj',
  'tm',
  'uz',
  'ua',
];

class LangModel {
  @observable
  lang = 'ru'; //  

  constructor() {
    this.init();
  }

  @action
  async init() {
    const lang = await AsyncStorage.getItem(STORE);
    if (lang) {
      this.lang = lang;
    } else {
      let deviceLanguage: string = (Platform.OS === 'ios'
        ? NativeModules.SettingsManager.settings.AppleLocale ||
          NativeModules.SettingsManager.settings.AppleLanguages[0] // iOS 13
        : NativeModules.I18nManager.localeIdentifier
      ).toLowerCase();

      if (
        RU_LANGS.findIndex((rulang) => deviceLanguage.includes(rulang)) === -1
      ) {
        this.lang = 'en';
      }
      AsyncStorage.setItem(STORE, this.lang);
    }
}

export default new LangModel();


モデルを初期化するときに、initメソッドを呼び出します。このメソッドは、ロケールが存在する場合はAsyncStorageから取得するか、現在のデバイス言語を抽出してAsyncStorageに配置します。



次に、言語を変更するメソッド(アクション)を作成する必要があります。



  @action
  changeLang(lang: string) {
    this.lang = lang;
    AsyncStorage.setItem(STORE, lang);
  }


ここではすべてが明確だと思います。



今、楽しい部分が来ます。翻訳自体を簡単な辞書に保存することにしました。これを行うには、LangModelの横にjsファイルを作成します。このファイルに、翻訳を配置します。



// translations.js
// ,     . 
export default const translations = {
  ", !": {en: "Hello, World!"},
}


次に、LangModelにもう1つのメソッドを実装します。このメソッドは、入力としてテキストを受け入れ、現在のローカリゼーションのテキストを返します。



import translations from './translations';

  ...
  rk(text) {
    if (!text) {
      return text;
    }
    //   ru,    
    if (this.lang === 'ru') {
      return text;
    }
    //   ,   
    if (translations[text] === undefined || translations[text][this.lang] === undefined) {
      console.warn(text);
      return text;
    }
    return translations[text][this.lang];
  }


これで、LangModelの準備が整いました。



完全なLangModelコード
import {NativeModules, Platform} from 'react-native';
import {observable, action} from 'mobx';
import AsyncStorage from '@react-native-community/async-storage';
import translations from './translations';

const STORE = '@lang-store';
//  ru  
const RU_LANGS = [
  'ru',
  'az',
  'am',
  'by',
  'ge',
  'kz',
  'kg',
  'md',
  'tj',
  'tm',
  'uz',
  'ua',
];

class LangModel {
  @observable
  lang = 'en';

  constructor() {
    this.init();
  }

  @action
  async init() {
    //     AsyncStorage
    const lang = await AsyncStorage.getItem(STORE);
    if (lang) {
      this.lang = lang;
    } else {
      let deviceLanguage: string = (Platform.OS === 'ios'
        ? NativeModules.SettingsManager.settings.AppleLocale ||
          NativeModules.SettingsManager.settings.AppleLanguages[0] // iOS 13
        : NativeModules.I18nManager.localeIdentifier
      ).toLowerCase();

      if (
        RU_LANGS.findIndex((rulang) => deviceLanguage.includes(rulang)) > -1
      ) {
        this.lang = 'ru';
      }
      AsyncStorage.setItem(STORE, this.lang);
  }

  @action
  changeLang(lang: string) {
    this.lang = lang;
    AsyncStorage.setItem(STORE, lang);
  }

  rk(text) {
    if (!text) {
      return text;
    }
    //   ru,    
    if (this.lang === 'ru') {
      return text;
    }
    //   ,   
    if (translations[text] === undefined || translations[text][this.lang] === undefined) {
      console.warn(text);
      return text;
    }
    return translations[text][this.lang];
  }
}
export default new LangModel();




これで、rkメソッドを使用してテキストをローカライズできます。



<Text>{LangModel.rk(", !")}</Text>


それは我々のアプリケーションでどのように機能するかあなたが見ることができるのAppStoreGoogleのプレイ(右上のアイコン(!)をクリックし、スクロールダウン)



ボーナス



もちろん、毎回LangModel.rkを書くのはクールではありません。したがって、独自のTextコンポーネントを作成し、その中ですでにLangModel.rkを使用できます。



//components/text.js
import React from 'react';
import {Text} from 'react-native';
import {observer} from 'mobx-react';
import {LangModel} from 'models';

export const MyText = observer((props) => (
   <Text {...props}>{props.notTranslate ? props.children : LangModel.rk(props.children)}</Text>
));


たとえば、現在のローカリゼーションに応じて、アプリケーションのロゴを変更する必要がある場合もあります。これを行うには、単にLangModel.langに応じて内容を変更することができます(オブザーバー(MobX)でコンポーネントをラップすることを忘れないでください)



PS:おそらく、このアプローチは、標準ではないように見えるだろうが、我々はより多くによって提供されるものよりもそれが好き反応ネイティブ-i18nの



オンそれが私にとってすべてです。ありがとうございます!)



All Articles