HimeraSearchDB
Carding_EbayThief
triada
CrackerTuch
JustinSun

НОВОСТИ Локализуем приложение на React Native

NewsBot
Оффлайн

NewsBot

.
.
Регистрация
21.07.20
Сообщения
40.408
Реакции
1
Репутация
0
В ходе разработки одного из наших приложений нам понадобилось сделать поддержку мультиязычности. Задача была дать пользователю возможность менять язык (русский и английский) интерфейса приложения. При этом текста и контент должны переводиться «на лету».

Для этого нам нужно было решить 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 и приступить ко второй задаче.

Переводим «на лету»


Для управления глобальным состоянием мы используем 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), который будет менять язык:


@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 для локализация текста:


{LangModel.rk("Привет, Мир!")}


Посмотреть как это работает можно в нашем приложении в и (Нажать на иконку (!) справа вверху, пролистать вниз)

Бонус


Конечно, писать каждый раз 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) => (
{props.notTranslate ? props.children : LangModel.rk(props.children)}
));


Так же нам может понадобиться, например, менять логотип приложения в зависимости от текущей локализации. Для этого можно просто менять контент в зависимости от LangModel.lang (не забудьте обернуть ваш компонент в observer(MobX))

P.S.: Возможно такой подход вам покажется не стандартным, но он нам понравился больше чем то, что предлагает

На этом у меня все. Всем спасибо!)
 
Сверху Снизу