preface

We have written about the use of multiple languages of Flutter before. If you are interested in using multiple languages of Flutter in $T form, you can go to the internationalization of Flutter App for detailed introduction

This article focuses on how to package a plugin for Flutter to reduce the workload of developers with multiple languages of Flutter

Access to the use of

FlutterLocalization

The effect

Multi-language Plugin features

  • Flutter connects to Efox multilanguage platform and supports switching between local and Efox platforms and languages
  • Support language customization, support simple and traditional Chinese
  • Open configuration items:
    • Supported languages
    • The default language
    • Language mapping
    • Local language path
    • Efox platform language path
    • Whether to load local multilanguage
  • Accessible method
    • If access to load the local language: AppLocalizations isLocale
    • If modify the loading local language: AppLocalizations changeIsLocale (true | false).
    • Gets the current language: AppLocalizations localeLang
    • $t(‘title_page’)

Plugin packages and Dart Packages

  • Plugin packages are used when you need to expose Native apis to others, using Platform Channels and containing Androiod/iOS Native logic. There is also an internal Flutter project under the Example directory that can be run directly for code testing
  • Dart packages are used when you need to develop a pure Dart component (such as a custom Weidget). There is no Native code inside. Code tests can be introduced via a local path through your own build up of the Flutter project

The new package

Multilanguage plugins create a new Dart package for code lifting

Step 1:

Access to the multiple languages of flutter specifies localizationsDelegates and supportedLocales of the MaterialApp, while localeResolutionCallback is called back when the app retrievesthe user-set language region

localizationsDelegates: AppLocal.localizationsDelegates,
supportedLocales: AppLocal.supportedLocales,
localeResolutionCallback: AppLocal.localeResolutionCallback,
Copy the code

Therefore, you need to provide an AppLocal class to implement the three instances

class AppLocal {
  static 可迭代<LocalizationsDelegate<dynamic>> _localizationsDelegates = [
    GlobalMaterialLocalizations.delegate,
    GlobalWidgetsLocalizations.delegate,
    GlobalCupertinoLocalizations.delegate,
    AppLocalizationsDelegate()
  ];
  // The supported language
  static 可迭代<Locale> _supportedLocales = ConfigLanguage.supportedLocales;
  // The application gets a locale callback
  static Locale _localeResolutionCallback(deviceLocale, supportedLocal) {
    print(
        'deviceLocale: $deviceLocale, supportedLocale: $supportedLocal}');
    // null [] [en_US] [en_US, zh_CH] Locale('en', 'US') Different mobile phones have different return parameters for obtaining the language, so you need to perform corresponding processing according to the return parameters
    var useDeciceLocale;
    if(deviceLocale ! =null &&
        deviceLocale.runtimeType.toString().contains('List') &&
        deviceLocale.isNotEmpty) {
      useDeciceLocale = deviceLocale[0];
    } else {
      if (deviceLocale.runtimeType.toString() == 'Locale') {
        useDeciceLocale = deviceLocale;
      } else {
        useDeciceLocale = null; }}print(
        'Phone gets matched language: $useDeciceLocale,${useDeciceLocale.runtimeType.toString()} ${useDeciceLocale.runtimeType.toString() == 'Locale'}');
    Locale _locale;
    bool hasLanguage = false;

    if(useDeciceLocale ! =null) {
      for (num i = 0; i < supportedLocal.length; i++) {
        if (useDeciceLocale.scriptCode == 'Hant') {
          if (useDeciceLocale.languageCode == supportedLocal[i].languageCode &&
              useDeciceLocale.scriptCode == supportedLocal[i].scriptCode) {
            hasLanguage = true;
            useDeciceLocale = supportedLocal[i];
            print($useDeciceLocale = $useDeciceLocale);
            break; }}else {
          if (useDeciceLocale.languageCode == supportedLocal[i].languageCode) {
            hasLanguage = true;
            useDeciceLocale = supportedLocal[i];
            print('Normal language code matches: $useDeciceLocale');
            break;
          }
        }
      }
      _locale = hasLanguage
          ? useDeciceLocale
          : Locale.fromSubtags(
              languageCode: ConfigLanguage.defaultLanguage['language_code'],
              scriptCode: ConfigLanguage.defaultLanguage['script_code']);print(
          '${hasLanguage ? 'Mobile system language supported by this app, using the system specified language: $_locale' : 'Mobile system language is not supported by this app, use the app to specify the default language: $_locale'}');
    } else {
      _locale = Locale.fromSubtags(
        languageCode: ConfigLanguage.defaultLanguage['language_code'],
        scriptCode: ConfigLanguage.defaultLanguage['script_code']);print('Mobile system language is not supported by this app, use the app to specify the default language: $_locale');
    }
    return _locale;
  }
  static get supportedLocales => _supportedLocales;
  static get localizationsDelegates => _localizationsDelegates;
  static get localeResolutionCallback => _localeResolutionCallback;
}
Copy the code

Step 2:

In localizationsDelegates, in addition to the delegate provided by the flutter itself, we will need to specify a localization delegate as follows, AppLocalizations is our data store and Controller class, and it is used to determine whether the language supports loading and reloading logic processing. AppLocalizations are our data store and Controller class

class AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
  AppLocalizationsDelegate();
  @override
  bool isSupported(Locale locale) {
    return ConfigLanguage.supportedLocales.contains(locale);
  }
  @override
  Future<AppLocalizations> load(Locale locale) async {
    LocalStorage.get('lang').then((lang) async {
      print('Locally cached language: $lang');
      AppLocalizations._localeLang = lang;
      if (lang == null) {
        print('Locale: $locale');
        return await AppLocalizations.init(locale);
      } else {
        print('Language to be loaded lang: $lang');
        List<String> langCode = lang.split(The '-');
        if (langCode.length > 1) {
          return await AppLocalizations.init(Locale.fromSubtags(
              languageCode: langCode[0], scriptCode: langCode[1]));
        } else {
          return await AppLocalizations.init(Locale(langCode[0])); }}}); }@override
  bool shouldReload(LocalizationsDelegate<AppLocalizations> old) {
    // When false, the above overwrite function is not executed
    return false; }}Copy the code

Step 3:

AppLocalizations class, which stores multilingual data, data processing and data initialization, provides developers with methods under this class. Methods to be implemented:

  • Receive the addSupportLanguage method for parameter configurations (supported language, default language, language mapping, local path, efox platform path, whether to load local or not)
  • Gets the localeLang method for the language
  • Page refresh is required during language switch, so you need to save setState’s setProxy method (set language switch proxy)
  • The internal init method and the getLanguageJson method to load the language package
  • Need to load the changeIsLocale method of the local language
  • Modify the changeLanguage method for the current language
  • The $t method of the read language
class AppLocalizations {
  Locale _locale;
  static AppLocalizations _inst; / / AppLocalizations instance
  static Map<String.dynamic> _jsonLanguage = {}; / / language pack
  static Function _setState; // Top-level parent setState
  static BuildContext _context;
  static String _localePath; // Local multilingual path
  static String _I18nHost; // Efox platform multilanguage path
  static bool _isLocale = true; // Whether to load local multilanguage
  static bool _hasConfigLocale = false; // Whether to manually configure whether to load local multilanguages
  static String _localeLang; // Cache local multilingual languages
  static bool get isLocale => _isLocale;
  static String get localeLang {
    return _localeLang ?? Localizations.localeOf(_context).toString();
  }
  AppLocalizations(this._locale);
  // Add supported languages
  static void addSupportLanguage({
    List<Locale> supportedLocales,
    Map<String.String> defaultLanguage,
    Map<String.String> mapLanguage,
    String localePath,
    String I18nHost,
    bool isLocale,
  }) {
    if(supportedLocales ! =null) {
      ConfigLanguage.supportedLocales.addAll(supportedLocales);
    }
    if(defaultLanguage ! =null) {
      ConfigLanguage.defaultLanguage.addAll(defaultLanguage);
    }
    if(mapLanguage ! =null) {
      ConfigLanguage.mapLanguage.addAll(mapLanguage);
    }
    if(isLocale ! =null) {
      _hasConfigLocale = true;
      _isLocale = isLocale;
    }
    _localePath = localePath;
    _I18nHost = I18nHost;
  }
  // Set the language switch agent
  static void setProxy(Function setState, BuildContext context) async {
    _setState = setState;
    _context = context;
  }
  // Initialize localizations
  static Future<AppLocalizations> init(Locale locale) async {
    _inst = AppLocalizations(locale);
    await getLanguageJson();
    _setState(() {}); // Multilanguage package update
    return _inst;
  }
  // Get the language package
  static Future getLanguageJson() async {
    if(! _hasConfigLocale) { _isLocale = (await LocalStorage.get('isLocale')) = ='false' ? false : true;
    }
    Locale _tmpLocale = _inst._locale;
    String jsonLang;
    String lang = ConfigLanguage.mapLanguage[_tmpLocale.toString()] ??
        _tmpLocale.toString();
    if (_isLocale) {
      try {
        print('Language package path: $_localePath/$lang.json');
        jsonLang = await rootBundle.loadString('$_localePath/$lang.json');
        // print(${json.decode(jsonLang)}');
      } catch (e) {
        print('Multilingual local path: $_localePath/$lang.json');
        print('Multilingual local load path does not exist, load default language data :$e');
        _inst._locale = Locale.fromSubtags(
            languageCode: ConfigLanguage.defaultLanguage['language_code'],
            scriptCode: ConfigLanguage.defaultLanguage['script_code']);
        _tmpLocale = _inst._locale;
        lang = ConfigLanguage.mapLanguage[_tmpLocale.toString()] ??
            _tmpLocale.toString();
        jsonLang = await rootBundle.loadString('$_localePath/$lang.json'); }}else {
      try {
        print('Language package path: $_I18nHost/$lang.json');
        jsonLang = (await Http.get(url: '$_I18nHost/$lang.json')).toString();
        // print(${(json.decode(jsonLang))}');
      } catch (e) {
        print('Multilingual platform path: $_I18nHost/$lang.json');
        print('Multilingual platform loading path does not exist, load default language data: $e');
        _inst._locale = Locale.fromSubtags(
            languageCode: ConfigLanguage.defaultLanguage['language_code'],
            scriptCode: ConfigLanguage.defaultLanguage['script_code']);
        _tmpLocale = _inst._locale;
        lang = ConfigLanguage.mapLanguage[_tmpLocale.toString()] ??
            _tmpLocale.toString();
        jsonLang = (await Http.get(url: '$_I18nHost/$lang.json')).toString();
      }
    }
    _jsonLanguage['$_tmpLocale'] = json.decode(jsonLang.toString());
    print('Whether to load the local language: $_isLocale');
    print(
        Get the language information for the language pack:${_inst._locale}, $lang, $_tmpLocale, ${_tmpLocale.languageCode}.${_tmpLocale.scriptCode}');
    print("Multilanguage loaded data: $_jsonLanguage");
  }
  // Change whether to load local multilanguage
  static void changeIsLocale(isLocale) async {
    _isLocale = isLocale;
    LocalStorage.set('isLocale', isLocale.toString());
    init(_inst._locale);
  }
  // Switch languages
  static void changeLanguage([Locale locale]) {
    if (locale == null || locale.languageCode == null) {
      print('Modify language language code cannot be null');
      locale = Locale.fromSubtags(
          languageCode: ConfigLanguage.defaultLanguage['language_code'],
          scriptCode: ConfigLanguage.defaultLanguage['script_code']);
    }
    if(locale.scriptCode ! =null) {
      _localeLang = '${locale.languageCode}-${locale.scriptCode}';
    } else {
      _localeLang = '${locale.languageCode}';
    }
    LocalStorage.set('lang', _localeLang);
    init(Locale.fromSubtags(
        languageCode: locale.languageCode,
        scriptCode: locale.scriptCode)); // Get the corresponding internationalization file according to the language
  }
  static String $t(String key) {
    Locale _tmpLocale = _inst == null
        ? Locale.fromSubtags(
            languageCode: ConfigLanguage.defaultLanguage['language_code'],
            scriptCode: ConfigLanguage.defaultLanguage['script_code'])
        : _inst._locale;
    var _array = key.split('. ');
    var _dict = _jsonLanguage['$_tmpLocale']???? {};var retValue = ' ';
    try {
      _array.forEach((item) {
        if(! _dict.containsKey(item) || _dict[item].runtimeType ==Null) {
          retValue = key;
          return;
        }
        if(_dict[item].runtimeType ! =String) {
          _dict = _dict[item];
        } else{ retValue = _dict[item]; }}); retValue = retValue.isEmpty ? _dict : retValue; }catch (e) {
      print('i18n exception');
      print(e);
      retValue = key;
    }
    return retValue ?? ' '; }}Copy the code

The last

Welcome more friends learning about flutter to join QQ group Flutter UI: 798874340

Stay tuned to github: YYDev

The author