Практически ни одно мобильное приложение не обходится без локализации, и любой опытный разработчик знает — закладывать ее нужно сразу. Да, есть шанс, что приложение до конца своих дней будет говорить только на одном языке, зато при необходимости добавить поддержку новой локали удастся обойтись малой кровью. Да и менять формулировки в едином строковом файле гораздо удобнее. Матерые программисты даже готовят почву для смены языков на лету без необходимости перезапуска приложения. Давайте теперь посмотрим на Flutter-проект сквозь призму мультиязычности.
Библиотеки для локализации
В открытом доступе находятся несколько библиотек, позволяющих локализовать строки во Flutter. Наиболее популярные:
- flutter_i18n, разработанная long1eu
- flutter_i18n за авторством ilteoood
При использовании любой из них необходимо для начала добавить в pubspec.yaml следующие строки:
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
И не забудьте удостовериться, что в Info.plist вашего iOS-проекта указаны поддерживаемые языки:
<key>CFBundleLocalizations</key>
<array>
<string>en</string>
<string>ru</string>
</array>
Интеграция flutter_i18 от longleu
Чтобы воспользоваться первой библиотекой, в main.dart понадобится прописать:
MaterialApp(
localizationsDelegates: <LocalizationsDelegate<dynamic>>[
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
DefaultCupertinoLocalizations.delegate
],
supportedLocales: S.delegate.supportedLocales,
localeResolutionCallback: (Locale locale, Iterable<Locale>supportedLocales) {
if (locale == null) {
return supportedLocales.first;
}
for (final Locale supportedLocale in supportedLocales) {
if (locale.countryCode == supportedLocale.countryCode) {
return supportedLocale;
}
}
return supportedLocales.first;
}
),
Здесь мы отдаем S.delegate в качестве делегата локализации, а также вытаскиваем из библиотеки поддерживаемые локали. Заодно прописываем в localeResolutionCallback что делать, если установленный на устройстве язык не поддерживается приложением.
Библиотека работает с .arb файлами, которые хранятся в папке res/values. В названиях файлов указывается локаль (strings_ru.arb), а сами они представляют из себя json без вложенности:
{
"yes": "Да",
"no": "Нет",
"myButtonTitle": "Это кнопка!"
}
Первый файл для английской локали генерируется автоматически и считается главным. Если какого-то ключа нет в файле strings_en.arb, строка по этому ключу будет недоступна даже при наличии ее в файле strings_ru.arb.
Преимущество использования .arb заключается в том, что многие сервисы, вроде Google Translate, способны употреблять и переваривать этот формат, помогая переводчикам.
Использовать же строку в коде и того проще:
S.of(context).myButtonTitle;
Здесь context — BuildCotext, используемый для отрисовки текущего виджета. Контекст необходим для определения текущей локали приложения.
Библиотека поддерживает строки с параметрами:
{
"verificationResendWithParams": "Отправить повторно через $seconds",
}
S.of(context).verificationResendWithParams(30.toString());
А вот с поддержкой plurals есть небольшие сложности. Для начала пропишем строки:
{
"friendZero": "друзей",
"friendOne": "друг",
"friendFew": "друга",
"friendOther": "друзей",
}
Обратиться за нужной формой можно следующим образом:
S.of(context).friend("0")
S.of(context).friend("1")
S.of(context).friend("few")
S.of(context).friend("other")
Но условия, при которых необходимо передать тот или иной аргумент, придется определять самостоятельно.
Чтобы сменить язык приложения программно, не заходя в настройки устройства, нужно будет сохранить локаль в MaterialApp state, после чего изменить его, обновив все приложение. Подробнее тут.
Интеграция flutter_i18 от ilteoood
В данном случае настройка осуществляется следующим образом:
MaterialApp(
localizationsDelegates: <LocalizationsDelegate>[
FlutterI18nDelegate(false, "en", "res/locales"),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate
],
supportedLocales: <Locale>[
const Locale("en", "US"),
const Locale("ru", "RU")
],
);
Здесь мы инициализируем FlutterI18nDelegate, указывая, что не хотим использовать код страны (нам будет достаточно кода языка), по умолчанию собираемся использовать английскую локаль, а строковые файлы у нас лежат в папке «res/locales». Эти самые строковые файлы представляют собой обычный .json или .yaml. Библиотека, кстати, поддерживает вложенность типа:
"error": {
"registration": {
"invalid_name": "Пожалуйста, введите имя пользователя",
"invalid_email": "Пожалуйста, введите корректный e-mail",
},
"default":"Повторите попытку позже"
}
Не забудьте прописать путь к файлам локализации в pubspec.yaml:
flutter:
# To add assets to your application, add an assets section, like this:
assets:
- res/locales/en.json
- res/locales/ru.json
Использовать методы плагина можно следующим образом:
FlutterI18n.translate(context, "error.registration.invalid_name")
Здесь, как и в предыдущем примере, context - BuildContext, используемый для отрисовки текущего виджета.
Библиотека поддерживает строки с параметрами:
{
"required_more": "Необходимо еще %number",
}
FlutterI18n.translate(context, "required_more", {"number": 15.toString()})
Реализация plurals мало чем отличается от аналога в предыдущем примере:
{
"game_participants": {
"number-0": "участников",
"number-1": "участник",
"number-2": "участника",
"number-3": "участников"
},
}
FlutterI18n.plural(context, "game_participants.number", 0)
FlutterI18n.plural(context, "game_participants.number", 1)
FlutterI18n.plural(context, "game_participants.number", 2)
FlutterI18n.plural(context, "game_participants.number", 3)
Определять, какое значение передать в метод, снова придется самим.
Огромным плюсом является возможность смены языка на лету при помощи одного-единственного метода:
await FlutterI18n.refresh(buildContext, languageCode, {countryCode});
Что же выбрать
Рассмотрев оба варианта, остановились все-таки на библиотеке от ilteoood. Возможность использовать вложенность при организации строк позволяет лучше структурировать json-файл, а передача ключа в качестве параметра дает больше свободы, позволяет манипулировать строками при реализации логики приложения. Да и возможность в одну строчку прикрутить смену локали без перезапуска приложения лишней не будет.
Что дальше
Озаботившись локализацией и заранее настроив ее, можно переходить к другому немаловажному выбору — архитектуре будущего приложения. А вариантов здесь немало. В следующей статье попробуем подискутировать на эту тему.