Localisation de l'application dans React Native

Lors du dĂ©veloppement de l'une de nos applications, nous avons dĂ» faire un support multilingue. La tĂąche Ă©tait de donner Ă  l'utilisateur la possibilitĂ© de changer la langue (russe et anglais) de l'interface de l'application. Dans le mĂȘme temps, le texte et le contenu doivent ĂȘtre traduits «à la volĂ©e».



Pour ce faire, nous avons dû résoudre 2 problÚmes:



  1. DĂ©terminez la langue actuelle de l'application.
  2. Utilisation de l'état global pour la traduction à la volée.


Dans cet article, je vais essayer de décrire en détail comment nous avons résolu ces problÚmes. Et ainsi nous sommes allés.



DĂ©terminer la langue actuelle de l'appareil



Pour déterminer la langue actuelle, vous pouvez, bien sûr, utiliser la bibliothÚque react-native-i18n, mais nous avons décidé de nous en passer, car vous pouvez le faire sans bibliothÚques tierces. Pour ce faire, écrivez ce qui suit:



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


Pour iOS, nous extrayons le langage de l'application via SettingsManager, et pour Android via le I18nManager natif.



Maintenant que nous avons reçu la langue actuelle du périphérique, nous pouvons l'enregistrer sur AsyncStorage et passer à la deuxiÚme tùche.



Nous traduisons "à la volée"



Nous utilisons MobX pour gérer l'état global, mais vous pouvez utiliser une solution différente.



Et donc nous devons créer une classe (j'aime l'appeler "modÚle") qui sera responsable de l'état global de la localisation actuelle. Nous créons:



//   ,      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();


Lorsque nous initialisons notre modÚle, nous appelons la méthode init, qui prend la locale soit depuis AsyncStorage, s'il y en a une, soit nous extrayons la langue actuelle du périphérique et la plaçons dans AsyncStorage.



Ensuite, nous devons écrire une méthode (action) qui changera la langue:



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


Je pense que tout est clair ici.



Vient maintenant la partie amusante. Nous avons dĂ©cidĂ© de stocker les traductions elles-mĂȘmes dans un dictionnaire simple. Pour ce faire, crĂ©ez un fichier js Ă  cĂŽtĂ© de notre LangModel, dans lequel nous mettrons nos traductions:



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


Ensuite, nous implémenterons une autre méthode dans LangModel, qui acceptera le texte comme entrée et retournera le texte de la localisation actuelle:



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];
  }


VoilĂ , notre LangModel est prĂȘt.



Code LangModel complet
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();




Nous pouvons maintenant utiliser la méthode rk pour localiser le texte:



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


Vous pouvez voir comment cela fonctionne dans notre application dans l' AppStore et Google Play (Cliquez sur l'icÎne (!) En haut à droite, faites défiler vers le bas)



Prime



Bien sûr, écrire LangModel.rk à chaque fois n'est pas cool. Par conséquent, nous pouvons créer notre propre composant Text et déjà utiliser LangModel.rk dedans



//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>
));


Nous pouvons Ă©galement avoir besoin, par exemple, de changer le logo de l'application en fonction de la localisation actuelle. Pour ce faire, vous pouvez simplement changer le contenu en fonction de LangModel.lang (n'oubliez pas d'envelopper votre composant dans l'observateur (MobX))



PS: Peut-ĂȘtre que cette approche ne semblera pas un standard, mais nous l'avons plus aimĂ©e que celle proposĂ©e par react-native-i18n



On c'est tout pour moi. Merci Ă  tous!)



All Articles