Это третья публикация в серии из 4 частей о создании приложения для iOS и Android для uncovercity с использованием React Native. Вы можете найти другие здесь:

  1. Ускорение создания приложения с ужином-сюрпризом в React Native с помощью Expo
  2. Боевое тестирование API райдшеринга и MapView от React Native в Expo
  3. Поддержка нескольких языков в React Native с Expo Localization
  4. Тонкости вложения навигаторов в React Native с использованием реакции-навигации

Привет! Вот еще один пост о моем опыте создания приложения для iOS и Android для стартапа-сюрприза uncovercity. Сегодня я расскажу вам, как перевести весь контент приложения (тексты, видео, изображения) с испанского на английский.

Поскольку приложение основано на React Native и Expo, наиболее очевидным делом было поискать какое-то существующее решение в экосистемах этих двух инструментов.

И вуаля, в Expo есть модуль Локализация. Хотя он все еще находится в DangerZone (пространстве имен, которое Expo использует для группировки функций, которые еще не были полностью одобрены), я был рад, что у меня есть надежная отправная точка для моей задачи.

ВАЖНО: это сообщение в блоге основано на Expo версии 30.0.0 и ниже. Expo недавно выпустила версию 31.0.0, которая включает полностью переработанный модуль Локализация. Наиболее значительным изменением является то, что теперь вы можете получать информацию о локализации со своего устройства синхронно, а не асинхронным способом, о котором я расскажу в этом сообщении блога.

Если вы уже знакомы с основами, вы можете сразу перейти к Распространенным ошибкам, где я расскажу о некоторых более сложных темах локализации.

Если вы используете React Native без Expo, вы можете получить что-то похожее на модуль локализации с response-native-languages. Однако для реальной задачи перевода строк вам все равно понадобится что-то вроде react-i18next.

Основы локализации

Документация, которую предоставляет Expo для этого модуля, очень проста и запутана (что и побудило меня написать этот пост). Но реализовать это на самом деле довольно просто. Все, что вам нужно сделать, это:

  1. создайте файл JSON для каждого языка, который вы хотите предоставить
  2. инициализировать языковое хранилище с помощью модуля Expo Localizaton
  3. установить текущий язык пользователя в магазине
  4. замените весь контент в своем приложении соответствующими ключами магазина

Чтобы получить полный пример работы следующего кода, ознакомьтесь с Закуской, которую я создал, где я инициализирую хранилище строк из отдельного файла и добавляю небольшую кнопку переключения для переключения между английским (США) и испанским (США).

Теперь позвольте мне более подробно рассказать вам о каждом из этих шагов:

1. Создайте файл JSON для каждого языка.

Первое, что вам нужно сделать, это поместить все переводимые строки в хранилище JSON. Не имеет значения, делаете ли вы это в одном файле или импортируете их из отдельного файла с помощью import. Я объясню, почему я предпочитаю последнее, на шаге 2.

const localizedStrings = {
  en_GB: { title: "Hello", subtitle: "Welcome" },
  es_ES: { title: "Hola", subtitle: "Bienvenido" }
}

Ключи этого объекта - это идентификаторы языка / региона (т.е. en_GB означает английский / Великобритания), а значение ключа - это все переводимые строки, которые будут доступны после инициализации хранилища, т. Е. localeStore.subtitle выведет «Bienvenido», если ваш язык был установлен на испанский (es_ES).

2. Инициализировать магазин с помощью модуля Expo Localizaton.

Во-вторых, мы инициализируем хранилище и запускаем на нем логику инициализации. Импортируйте модуль Localization из Expo следующим образом:

import { DangerZone } from 'expo'
const { Localization } = DangerZone
// localizedStrings is the object we created in step 1
const localeStore = new Localization.LocaleStore(localizedStrings)

Будет проще, если вы поместите эту строку кода там, где находятся ваши локализованные строки, чтобы у вас был доступ к ним в любом месте, где вам нужно. Поэтому вместо того, чтобы напрямую экспортировать объект JSON, экспортируйте созданное хранилище следующим образом:

const localeStore = new Localization.LocaleStore(localizedStrings)
export default localeStore;

Это localeStore теперь дает нам доступ к двум важным функциям:

  • установка текущей локали, которую мы хотим показать нашим пользователям (см. следующий шаг)
  • используя локализованные строки в наших представлениях, например: { localeStore.title }

3. Установите текущий язык пользователя в магазине.

Затем нам нужно выяснить, какой язык предпочитает пользователь. Мы можем использовать функцию getCurrentLocaleAsync из модуля Localization, которая возвращает идентификатор языка / региона. Затем мы передаем это значение в наше ранее инициализированное хранилище, используя setLocale.

import { localeStore } from "./localeStore"
...
constructor() {
  super();
  Localization.getCurrentLocaleAsync().then(currentLocale => 
    localeStore.setLocale(locale)
  );
}

Функция getCurrentLocaleAsync возвращает идентификатор языка / региона пользовательского устройства в виде строки (с которой вы уже должны быть знакомы), например: en_GB Это асинхронный вызов, поэтому нам нужно убедиться, что где бы мы ни разместили это необходимо выполнить таким образом, используя _18 _ / _ 19_ или Promise.

Помните, что вместо использования getCurrentLocaleAsync вы также можете получить доступ к коду страны или предпочитаемому региону устройства, используя соответствующие функции getCurrentDeviceCountryAsync или getPreferredLocalesAsync.

В дополнение к этому, вы, вероятно, захотите использовать какое-то решение для управления состоянием или React Context API, чтобы сделать вашу текущую локаль доступной где угодно, а не только в состоянии корневого компонента.

4. Заменить все переводимые строки

И последнее, но не менее важное: мы должны использовать локализованные строки из нашего магазина вместо жестко закодированных строк в наших представлениях пользовательского интерфейса.

// we want to replace this...
<Text>Hello</Text>
// ...with that:
<Text>{ localeStore.title }</Text>

Это максимально просто с точки зрения интернационализации. Для более сложных функций перевода, таких как множественное число и форматирование, вы, вероятно, захотите использовать более полную библиотеку, такую ​​как react-i18next (которая, кстати, отлично работает в сочетании с модулем Expo Localization, но также намного сложнее).

Распространенные ошибки и их исправления

Все это звучит довольно просто. Но есть несколько менее очевидных проблем, с которыми вы столкнетесь только в том случае, если вы создадите полностью локализованное приложение с несколькими экранами и небольшим взаимодействием.

Установка и применение языка по умолчанию

Вы можете задаться вопросом, что произойдет, если язык пользователя не охвачен ни одним из ваших файлов перевода, и вы сделаете что-то вроде setLocale(locale) там, где языковой стандарт, который вы пытаетесь установить, не существует.

Или что, если вы хотите обеспечить одинаковый перевод для обоих, en_GB и en_US, но предпочитаете не дублировать весь массив строк?

Самым простым решением, которое сработало для меня, является явная установка языкового стандарта на основе некоторого условия, а не просто передача языкового стандарта вслепую функции setLocale магазина.

// instead of blindly relying on this...
Localization.getCurrentLocaleAsync().then(currentLocale => 
  localeStore.setLocale(currentLocale)
);
// ...make sure to explicitly set one of your existing locales
Localization.getCurrentLocaleAsync().then(currentLocale => {
  const locale = currentLocale.includes('es') ? 'es_ES' : 'en_GB'; 
  localeStore.setLocale(locale)
);

В дополнение к этому, вы, вероятно, захотите защитить свое представление от рендеринга чего-либо, пока мы не назначили сначала языковой стандарт пользователя по умолчанию:

if (!this.state.currentLocale) {
  return;
}
return <App />

Переключение между языками

Теперь, когда мы знаем, как установить язык по умолчанию, что, если пользователь действительно хочет переключиться на другой язык?

Вызов setLocale на localeStore и обновление нашего состояния до нового языка должны помочь:

localeStore.setLocale(newlocale)

К сожалению, в результате наши взгляды не обновятся. Какое разочарование!

Это потому, что обновление localeStore происходит вне области состояния React, поэтому React не будет ничего повторно отображать. Кроме того, setLocale - это асинхронный вызов, поэтому любые изменения, которые мы хотим применить после обновления, должны обрабатываться в функции обратного вызова, которую она принимает в качестве второго аргумента.

localeStore.setLocale(locale, 
  () => this.setState(() => ({ currentLocale: locale })))
});

Теперь наши компоненты будут реагировать на изменение состояния и соответственно обновляться.

Заголовки навигации с реактивной навигацией

Если вы раньше работали с реагирующей навигацией, вы, вероятно, знакомы со статическим navigationOptionsobject, который позволяет изменять стиль и содержимое панели заголовка.

Оказывается, поскольку это статическое свойство, любые свойства, которые вы устанавливаете внутри него, будут оцениваться до, который мы установим наш исходный язык, а не при монтировании фактического экрана.

static navigationOptions = {
  title: localeStore.title // this won't re-render
}

Решение здесь - вернуть функцию, возвращающую объект. Таким образом, каждый раз, когда в нашем приложении происходит изменение состояния (например, вызванное обратным вызовом setLocale), эти реквизиты также будут обновляться.

static navigationOptions = () => {
  return {
    title: localeStore.title // this uses the updated locale
  }
}

Продвинутые решения по локализации

Модуль локализации Expo довольно ограничен. Он дает вам доступ к языку пользовательского устройства и позволяет вам переводить объект строк на его основе.

Вероятно, это все, что вам нужно для множества небольших проектов. Но если ваше приложение становится немного сложнее, чем это, и вам нужны такие параметры интернационализации, как заполнители, плюрализация и форматирование валюты / часового пояса, вы можете рассмотреть одно из следующих, более сложных решений:







Вы когда-нибудь переводили приложение React Native на несколько языков? Каким был ваш опыт? Какие инструменты вы использовали и с какими проблемами сталкивались?

Надеюсь, вам понравился этот пост и он оказался полезным. Если у вас есть отзывы, пожалуйста, оставьте комментарий. Спасибо!