интернационализация флаттера с использованием динамической строки в файле 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;
}
  1. Создайте папку скажите json в вашем assetsкаталог. Поместите в него свои языковые файлы.

    assets
      json
       - en.json // for English 
       - ru.json  // for Russian
    
  2. Сейчас в en.jsonнапишите вашу строку, например.

    {
      "myAge": "My age is"
    }
    

    Точно так же в ru.json,

    {
      "myAge": "Мой возраст"
    }
    
  3. Добавьте это в 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")

Подробности смотрите здесь .

Другие вопросы по тегам