ترجمة التطبيق في React Native

أثناء تطوير أحد تطبيقاتنا ، احتجنا إلى تقديم دعم متعدد اللغات. كانت المهمة هي إعطاء المستخدم القدرة على تغيير اللغة (الروسية والإنجليزية) لواجهة التطبيق. في نفس الوقت ، يجب ترجمة النص والمحتوى "على الطاير".



للقيام بذلك ، كان علينا حل مشكلتين:



  1. تحديد اللغة الحالية للتطبيق.
  2. استخدام الحالة العالمية للترجمة الفورية.


سأحاول في هذه المقالة أن أصف بالتفصيل كيف حللنا هذه المشاكل. وهكذا ذهبنا.



حدد لغة الجهاز الحالية



لتحديد اللغة الحالية ، يمكنك بالطبع استخدام مكتبة رد فعل-أصلية-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 ، وللروبوت عبر I18nManager الأصلي.



الآن وقد تلقينا لغة الجهاز الحالية ، يمكننا حفظها في AsyncStorage والمتابعة في المهمة الثانية.



نترجم "على الطاير"



نحن نستخدم 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);
  }


أعتقد أن كل شيء واضح هنا.



الآن يأتي الجزء الممتع. قررنا تخزين الترجمات نفسها في قاموس بسيط. للقيام بذلك ، قم بإنشاء ملف js بجوار LangModel ، حيث سنضع ترجماتنا:



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


بعد ذلك ، سنقوم بتنفيذ طريقة أخرى في LangModel ، والتي ستقبل النص كمدخل ويعيد نص الترجمة الحالية:



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>


يمكنك أن ترى كيف يعمل في تطبيقنا في AppStore و Google Play (انقر فوق الرمز (!) في الجزء العلوي الأيمن ، قم بالتمرير لأسفل)



علاوة



بالطبع ، كتابة LangModel.rk في كل مرة ليس أمرًا رائعًا. لذلك ، يمكننا إنشاء مكون نص خاص بنا واستخدام 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))



ملاحظة: ربما يبدو هذا النهج ليس معيارًا ، لكننا أحببناه أكثر من ذلك الذي قدمه رد الفعل الأصلي - i18n



On هذا كل شيء بالنسبة لي. شكرا للجميع!)



All Articles