美文网首页
Flutter 多语言方案调研对比

Flutter 多语言方案调研对比

作者: 曾大稳丶 | 来源:发表于2021-09-02 10:29 被阅读0次

根据资料选择了大众的两种方式进行调研和对比,主要是 Map方式和intl方式。

Map方式

  1. yaml添加本地化依赖
  flutter_localizations:
    sdk: flutter
  1. 定义资源包:
import 'package:flutter/material.dart';

class DemoLocalizations {
  DemoLocalizations(this.locale);

  final Locale locale;

  static DemoLocalizations of(BuildContext context) {
    return Localizations.of<DemoLocalizations>(context, DemoLocalizations);
  }

  static Map<String, Map<String, String>> _localizedValues = {
    'en': {
      'title': 'Hello World',
    },
    'es': {
      'title': 'Hola Mundo',
    },
    'zh': {
      'title': '你好呀',
    },
  };

  String get title {
    return _localizedValues[locale.languageCode]['title'];
  }
}
  1. 创建代理:
import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:international_demo/localization_src.dart';

class DemoLocalizationsDelegate
    extends LocalizationsDelegate<DemoLocalizations> {
  const DemoLocalizationsDelegate();

  @override
  bool isSupported(Locale locale) =>
      ['en', 'es', 'zh'].contains(locale.languageCode);

  @override
  Future<DemoLocalizations> load(Locale locale) {
    return SynchronousFuture<DemoLocalizations>(DemoLocalizations(locale));
  }

  @override
  bool shouldReload(DemoLocalizationsDelegate old) => false;
}
  1. 使用代理:
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      localizationsDelegates: [
        DemoLocalizationsDelegate(),// 这是我们新建的代理
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
      ],
      supportedLocales: [
        const Locale('en', 'US'), // English
        const Locale('he', 'IL'), // Hebrew
        const Locale('zh', ''),   // 新添中文,后面的countryCode暂时不指定
      ],
      home: new MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

  1. 获取字符串:
DemoLocalizations localizations = DemoLocalizations.of(context);
DemoLocalizations.of(context).title

优点:定义简单,使用简单。
缺点:翻译协作不是太友好,语言包没有单独隔离。

intl

  1. yaml添加本地化依赖
  flutter_localizations:
    sdk: flutter
  1. 安装Flutter Intl插件
    Flutter Intl
  1. 使用插件初始化项目和添加语言


    初始化
  1. 定义翻译字段以及生成对应的文件
翻译字段

l10n/intl_en.arb intl_pt_BR.arb intl_zh_CN.arb:语言包定义

arb

generated/intl/messages_en.dart,messages_pt_BR.dart,messages_zh_CN.dart:具体的语言包实现类

具体的语言包实现类

generated/intl/messages_all.dart: 负责分发语言包的具体实现类

dart

generated/l10n.dart:生成的语言代理类,负责load资源包和使用字符串

l10n
  1. 使用代理:
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
        //...
      localizationsDelegates: [
        S.delegate,// 这是我们的代理
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
      ],
      supportedLocales: S.delegate.supportedLocales,
     //...
    );
  }
}

  1. 获取字符串:
S.of(context).cacheClean
S.current.cacheClean

优点:语言包管理独立清晰,方便协作,使用简介,有插件配合简化使用。
缺点:文件结构稍显复杂。

对比 intl Map
复杂度 稍微难一点,但是有插件协作
协作性 友好 不友好
容错率 因为语言包和调用分开,容错高 因为语言包和调用分开,容错低

出于翻译人员,开发人员协作方面考虑,最终采用intl方式

遇到的问题:

多package设置localizationsDelegates只有第一个生效

在设置localizationsDelegates的时候,只能加载第一个顺序配置的语言代理,后续配置的代理将会失效。这个多模块各自管理各自的语言包是有很大的阻碍的。

问题点深入挖掘:

l10n.dart:

static Future<S> load(Locale locale) {
final name = (locale.countryCode?.isEmpty ?? false) ? locale.languageCode : locale.toString();
final localeName = Intl.canonicalizedLocale(name);
return initializeMessages(localeName).then((_) {
  Intl.defaultLocale = localeName;
  S.current = S();

  return S.current;
});
}


//messages_all.dart:
/// User programs should call this before using [localeName] for messages.
Future<bool> initializeMessages(String localeName) async {
  var availableLocale = Intl.verifiedLocale(
    localeName,
    (locale) => _deferredLibraries[locale] != null,
    onFailure: (_) => null);
  if (availableLocale == null) {
    return new Future.value(false);
  }
  var lib = _deferredLibraries[availableLocale];
  await (lib == null ? new Future.value(false) : lib());
  initializeInternalMessageLookup(() => new CompositeMessageLookup());
  
  //这里 messageLookup:CompositeMessageLookup
  messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor);
  return new Future.value(true);
}

message_lookup_by_library.dart:  
/// If we do not already have a locale for [localeName] then
  /// [findLocale] will be called and the result stored as the lookup
  /// mechanism for that locale.
  void addLocale(String localeName, Function findLocale) {
    //
    if (localeExists(localeName)) return;
    var canonical = Intl.canonicalizedLocale(localeName);
    var newLocale = findLocale(canonical);
    if (newLocale != null) {
      availableMessages[localeName] = newLocale;
      availableMessages[canonical] = newLocale;
      // If there was already a failed lookup for [newLocale], null the cache.
      if (_lastLocale == newLocale) {
        _lastLocale = null;
        _lastLookup = null;
      }
    }
  }
  
/// Return true if we have a message lookup for [localeName].
bool localeExists(localeName) => availableMessages.containsKey(localeName);

最终是调用实现的MessageLookup里面的addLocale函数有判断,如果这个语言已经添加过,那么就不会添加了,就造成了localizationsDelegates配置只有第一个生效的现象。

解决方式

我们只需要自己实现一个MessageLookup维护多模块的语言包映射关系,然后将这个MessageLookup替换掉原来的CompositeMessageLookup即可。具体实现也有人实现了出来,参考:multiple_localization

class MultipleLocalizations {
  static _MultipleLocalizationLookup? _lookup;

  static void _init() {
    assert(intl_private.messageLookup is intl_private.UninitializedLocaleData);
    _lookup = _MultipleLocalizationLookup();
    intl_private.initializeInternalMessageLookup(() => _lookup);
  }

  /// Load messages for localization and create localization instance.
  ///
  /// Use [setDefaultLocale] to set loaded locale as [Intl.defaultLocale].
  static Future<T> load<T>(InitializeMessages initializeMessages, Locale locale,
      FutureOr<T> Function(String locale) builder,
      {bool setDefaultLocale = false}) {
    if (_lookup == null) _init();
    final name = locale.toString();
    final localeName = Intl.canonicalizedLocale(name);

    return initializeMessages(localeName).then((_) {
      if (setDefaultLocale) {
        Intl.defaultLocale = localeName;
      }

      return builder(localeName);
    });
  }
}

class _MultipleLocalizationLookup implements intl_private.MessageLookup {
  final Map<Function, CompositeMessageLookup> _lookups = {};

  @override
  void addLocale(String localeName, Function findLocale) {
    final lookup = _lookups.putIfAbsent(
      findLocale,
      () => CompositeMessageLookup(),
    );
    lookup.addLocale(localeName, findLocale);
  }

  @override
  String? lookupMessage(String? messageStr, String? locale, String? name,
      List<Object>? args, String? meaning,
      {MessageIfAbsent? ifAbsent}) {
    for (final lookup in _lookups.values) {
      var isAbsent = false;
      final res = lookup.lookupMessage(messageStr, locale, name, args, meaning,
          ifAbsent: (s, a) {
        isAbsent = true;
        return '';
      });

      if (!isAbsent) return res;
    }

    return ifAbsent == null ? messageStr : ifAbsent(messageStr, args);
  }
}

将生成的l10n.dart的load函数更改即可:

static Future<S> load(Locale locale) {
    // final name = (locale.countryCode?.isEmpty ?? false) ? locale.languageCode : locale.toString();
    // final localeName = Intl.canonicalizedLocale(name);
    // return initializeMessages(localeName).then((_) {
    //   Intl.defaultLocale = localeName;
    //   S.current = S();
    //
    //   return S.current;
    // });

    return MultipleLocalizations.load(initializeMessages, locale, (String l) {
      S.current = S();
      return S.current;
    }, setDefaultLocale: true);
  } 

和native保持一致的语言

主要使用native维护语言名称,flutter通过MethodChannel获取即可

flutter切换语言

runApp里面添加监听,然后setstate即可。


void main() async{
    //从native获取语言
  String lang = await ApplicationPlugin.currentLocal();
  runApp(FocoApp(init, appStyle, lang));
}

class FocoApp extends StatefulWidget {
  String lang;

  App( String lang) {
    this.lang = lang;
  }

  // This widget is the root of your application.
  @override
  _AppState createState() => _AppState();
}


class _AppState extends State<App> {
     @override
  void initState() {
    super.initState();
    

    EventManager.shared().on(Event.langChangeEvent, this, (params) async {
      /// 更新当前语言环境 (启动时执行),从native层获取语言
      widget.lang = await ApplicationPlugin.currentLocal();
      if (mounted) {
        setState(() {});
      }
    });
  }
  
   @override
  void dispose() {
    super.dispose();
    EventManager.shared().off(Event.langChangeEvent, this);
  }
  
  @override
  Widget build(BuildContext context) {
   List<LocalizationsDelegate> localizationsDelegates = [];
    //package 的delegate
    for (LocalizationsDelegate delegate in _localizationsDelegates) {
      localizationsDelegates.add(delegate);
    }
    localizationsDelegates.add(S.delegate);
    localizationsDelegates.add(GlobalMaterialLocalizations.delegate);
    localizationsDelegates.add(GlobalWidgetsLocalizations.delegate);
    localizationsDelegates.add(GlobalCupertinoLocalizations.delegate);

    List<Locale> supportedLocales = S.delegate.supportedLocales;
    return MaterialApp(
      //..
      locale: Locale(widget.lang),
      localizationsDelegates: localizationsDelegates,
      supportedLocales: supportedLocales,
      home: home,
    );
  }
}

参考链接:
国际化Flutter App
multiple_localization
Localization for Dart package
Flutter 基于intl的国际化多语言
Flutter International 国际化,Localization 本地化, 使用字符串Map

相关文章

  • Flutter 多语言方案调研对比

    根据资料选择了大众的两种方式进行调研和对比,主要是 Map方式和intl方式。 Map方式 yaml添加本地化依赖...

  • Flutter多语言 2022-11-15 周二

    方案选择 Flutter对多语言是支持的,不够功能有限,也不是很好用。 GetX中对多语言的支持做得很好,果断选择...

  • Flutter技术学习贴

    1,flutter技术调研 Flutter技术调研报告

  • 深入解析 Flutter 初始化流程

    在调研 Flutter 动态化方案的时候,需要了解 Flutter 加载 dart 产物的流程,于是梳理了一遍 F...

  • flutter 多语言配置—中文—转

    flutter 多语言配置—中文

  • Flutter动态化方案调研

    一、Flutter.framework的大小 腾讯课堂14M今日头条3M闲鱼22M百度贴吧13M蚂蚁财富56.8M...

  • Flutter简介

    1、Web容器时代 2、泛Web容器方案 3、自绘引擎开发方案 4、总对比图 5、Flutter架构 6、Flut...

  • Flutter 开发笔记 1

    最近flutter刚刚发出beta版本,虽然只有0.2.3 但是横向对比了所有的cross-platform方案之...

  • Flutter

    跨平台方案对比: Flutter原理解读: Skia自绘引擎: Dart高性能之JIT & AOT: Framew...

  • Google跨平台技术Flutter介绍

    今天我们聊聊跨平台解决方案,通过此文,我们可以了解到 跨平台技术的主流解决方案,对比 flutter的原理、优势 ...

网友评论

      本文标题:Flutter 多语言方案调研对比

      本文链接:https://www.haomeiwen.com/subject/oimqwltx.html