интернационализация флаттера с использованием динамической строки в файле json
До сих пор я использовал динамические строки, как показано в решении этого сообщения: Интернационализация Flutter - Динамические строки
Вот пример:
AppLocalizations.of(context).userAge(18)
И на AppLocalizations.dart:
userAge(age) => Intl.message(
"My age is $age",
name: "userAge",
args: [age]);
// Return "My age is 18"
Но затем я прочитал эту статью об интернационализации флаттера: https://medium.com/flutter-community/flutter-internationalization-the-easy-way-using-provider-and-json-c47caa4212b2 которой показано, как локализовать с использованием файлов json как файлы ресурсов для строк. Это выглядит намного удобнее, поэтому я предпочитаю использовать этот метод, но не знаю, как получить строки из json-файла с динамическими значениями.
Любое решение?
4 ответа
Чтобы получить строку с динамическим значением из файла JSON, вы можете использовать
final age = 18 //user input.
final ageString = 'user_age'
.localisedString()
.replaceAll(new RegExp(r'\${age}'), age)
en.json
{
"user_age": "My age is ${age}",
"user_name_age": "My name is ${name} and age is ${age}"
}
string_extension.dart
extension Localisation on String {
String localisedString() {
return stringBy(this) ?? '';
}
}
Кроме того, вы можете сделать что-то вроде
String localisedString(Map<String, String> args) {
String str = localisedString();
args.forEach((key, value) {
str = str.replaceAll(new RegExp(r'\${'+key+'}'), value);
});
return str;
}
//usecase
final userName = 'Spider Man'
final age = '18'
final nameAgeString = 'user_name_age'.localisedString({'name': userName, 'age': age})
app_localisation.dart
Map<String, dynamic> _language;
String stringBy(String key) => _language[key] as String ?? 'null';
class AppLocalisationDelegate extends LocalizationsDelegate {
const AppLocalisationDelegate();
// override the following method if you want to specify the locale you are supporting.
final _supportedLocale = ['en'];
@override
bool isSupported(Locale locale) => _supportedLocale.contains(locale.languageCode);
@override
Future load(Locale locale) async {
String jsonString = await rootBundle
.loadString("assets/strings/${locale.languageCode}.json");
_language = jsonDecode(jsonString) as Map<String, dynamic>;
print(_language.toString());
return SynchronousFuture<AppLocalisationDelegate>(
AppLocalisationDelegate());
}
@override
bool shouldReload(AppLocalisationDelegate old) => false;
}
Создайте папку скажите
json
в вашемassets
каталог. Поместите в него свои языковые файлы.assets json - en.json // for English - ru.json // for Russian
Сейчас в
en.json
напишите вашу строку, например.{ "myAge": "My age is" }
Точно так же в
ru.json
,{ "myAge": "Мой возраст" }
Добавьте это в
pubspec.yaml
файл (обратите внимание на пробелы)flutter: uses-material-design: true assets: - assets/json/
Пробег
flutter pub get
Первоначальная работа сделана. Перейдем к стороне кода.
Скопируйте этот шаблонный код в свой файл:
Map<String, dynamic> language;
class AppLocalizations {
static AppLocalizations of(BuildContext context) {
return Localizations.of<AppLocalizations>(context, AppLocalizations);
}
String getText(String key) => language[key];
String userAge(int age) => '${getText('myAge')} $age';
}
class AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
const AppLocalizationsDelegate();
@override
bool isSupported(Locale locale) => ['en', 'ru'].contains(locale.languageCode);
@override
Future<AppLocalizations> load(Locale locale) async {
final string = await rootBundle.loadString('assets/json/${locale.languageCode}.json');
language = json.decode(string);
return SynchronousFuture<AppLocalizations>(AppLocalizations());
}
@override
bool shouldReload(AppLocalizationsDelegate old) => false;
}
Настроить несколько вещей в MaterialApp
виджет:
void main() {
runApp(
MaterialApp(
locale: Locale('ru'), // switch between "en" and "ru" to see effect
localizationsDelegates: [const AppLocalizationsDelegate()],
supportedLocales: [const Locale('en'), const Locale('ru')],
home: HomePage(),
),
);
}
Теперь вы можете просто использовать вышеуказанный делегат:
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
var age = AppLocalizations.of(context).userAge(18);
// prints "My age is 18" for 'en' and "Мой возраст 18" for 'ru' locale.
print(age);
return Scaffold();
}
}
Я пробовал различные решения для реализации локализации, и лучшее, что я нашел, - это плагин Flutter Intl для VS Code или Android Studio/IntelliJ, созданный https://localizely.com/ (не аффилированным).
С его помощью вы в основном устанавливаете плагин с помощью библиотеки marketplace / plugin, а затем инициализируете свой проект с помощью параметра меню. Это создает английскую локаль по умолчанию в lib/l10n/intl_en.arb (что звучит пугающе, но на самом деле это просто JSON) и настраивает все строительные леса для интернационализации вlib/generated
.
Вы также должны добавить в свои зависимости следующее.
flutter_localizations:
sdk: flutter
Затем вы можете добавить ключи в этот файл, и они будут автоматически доступны в вашем приложении, импортировав generated/l10n.dart
который содержит класс S.
Чтобы заставить его использовать флаттер, где бы вы ни инициализировали свой MaterialApp, обязательно передайте S.delegate
в параметр LocalizationsDelegates MaterialApp (скорее всего, как часть массива с GlobalMaterialLocalizations.delegate
, GlobalWidgetsLocalizations.delegate
, и возможно GlobalCupertinoLocalizations.delegate
.) Вы также должны добавить S.delegate.supportedLocales
в MaterialApp supportedLocales
.
Чтобы добавить больше локалей, используйте опцию в меню (по крайней мере, в intellij) или просто создайте больше файлов intl_.arb, и плагин автоматически распознает это и настроит соответствующий код.
Допустим, у вас есть файл intl_en со следующим:
{ "name": "Name" }
Затем вы использовали бы S.of(context).name
чтобы использовать строку в вашем коде.
Все это более красноречиво объясняется на сайте localizely.
Теперь, чтобы использовать ключи в этих файлах.arb, вам просто нужно обернуть их в {...}. Так например:
{ "choose1OfNumOptions": "Choose 1 of {numoptions} options" }
приведет к использованию S.of(context).choose1OfNumOptions(numOptions);
. Я не знаю, поддерживает ли плагин полную спецификацию ARB, но он поддерживает, по крайней мере, основы.
Кроме того, я не использую Localizely, но похоже, что это был бы довольно полезный способ управления переводами, и плагин интегрируется автоматически, хотя я думаю, что он также ужасно завышен - по крайней мере, для моего приложения, которое, как оказалось, имеет тонна текста. На самом деле у меня просто есть таблица Google, в которой я храню все свои переводы, и когда пришло время ее обновить, я загрузил ее как.tsv и написал небольшой простой парсер для записи в файлы.arb.
flutter_localizations:
app_en.arb:
{
"contactDetailsPopupEmailCopiedMessage": "Copied {email} to clipboard",
"@contactDetailsPopupEmailCopiedMessage": {
"description": "Message being displayed in a snackbar upon long-clicking email in contact details popup",
"placeholders": {
"email": {
"type": "String",
"example": "example@gmail.com"
}
}
}
}
app_localizations.dart:
abstract class AppLocalizations {
/// Message being displayed in a snackbar upon long-clicking email in contact details popup
///
/// In en, this message translates to:
/// **'Copied {email} to clipboard'**
String contactDetailsPopupEmailCopiedMessage(String email);
}
Применение:
l10n.contactDetailsPopupEmailCopiedMessage("example@gmail.com")
Подробности смотрите здесь .