首页   >  新闻资讯   >  资讯详情
Flutter 独立APP实现完整国际化语言
时间:2020-07-09 08:01:02
鉴于 Flutter 高性能渲染和跨平台的优势,闪点清单在移动端 APP 上,使用了完整的 Flutter 框架来开发。既然是完整 APP,架构搭建完全不受历史 Native APP 的影响,没有历史包袱的沉淀,设计也能更灵活和健壮。

鉴于 Flutter 高性能渲染和跨平台的优势,闪点清单在移动端 APP 上,使用了完整的 Flutter 框架来开发。既然是完整 APP,架构搭建完全不受历史 Native APP 的影响,没有历史包袱的沉淀,设计也能更灵活和健壮。

国际化语言的支持,是很多 APP 都有的一个强需求,APP 无论大小,只要还不想放弃国外的客户,一般就需要支持国际化。

官方支持

Flutter 官方方案提供了国际化的基础支持,如 Flutter 内置组件的国际化、语言代理、Widget 使用语言包、语言设置回调等,并支持自定义第三方类来扩展,可以参考 Flutter 国际化文档。 官方支持代码示例:

        class DemoLocalizations { DemoLocalizations(this.locale); final Locale locale; static DemoLocalizations of(BuildContext context) { return Localizations.of
            (context, DemoLocalizations); } static Map
                > _localizedValues = { 'en': { 'title': 'Hello World', }, 'es': { 'title': 'Hola Mundo', }, }; String get title { return _localizedValues[locale.languageCode]['title']; } }
    

官方方案的缺陷

官方的支持有几个缺陷:

  1. 依赖于 BuildContext 对象,在非 Widget 中调用时,需要层层传递 BuildContext 对象,或存储全局 BuildContext 对象。
  2. 在 MaterialApp 初始化前无法使用国际化(原因也是依赖于 BuildContext 对象)。
  3. 语言包定义推荐使用 Map 方式,无法利用静态语言的优势(语法提示、错误检查等);而为语言包每个属性自定义类和类字段,成本较高、使用和更新灵活性差。

i18n 介绍

鉴于 Flutter 官方支持的缺陷,我们调研了很多第三方库,最终发现了 i18n,并在此基础上、结合 Flutter 官方支持和自身封装,实现了更灵活易用的方案。

undefined

基础使用

i18n 使用 yaml 格式来定义语言包,同时提供构建脚本一键生成 Dart 语言包 Class 。如下:

        lib/messages.i18n.yaml button: save: Save load: Load users: welcome(String name): "Hello $name!" logout: Logout
    

该配置会生成几个 Class:Messages 、ButtonMessages 、UserMessages,生成后的 Dart 文件使用方式如下:

        Messages m = Messages(); debugPrint(m.users.logout); debugPrint(m.users.welcome('World'));
    

生成的 Dart 文件预览(开发时无需关心):

        class Messages { const Messages(); ButtonMessages get button => ButtonExampleMessages(this); UsersMessages get users => UsersExampleMessages(this); } class ButtonMessages { final Messages _parent; const ButtonMessages(this._parent); String get save => "Save"; String get load => "Load"; } class UsersMessages { final Messages _parent; const UsersMessages(this._parent); String get logout => "Logout"; String welcome(String name) => "Hello $name!"; }
    

进阶功能

下面讲解一些进阶用法。

函数定义

i18n 支持函数定义,并支持传参,如上述的 welcome函数:

        debugPrint(m.users.welcome('World'));
    

参数定义基本没有限制,可以随意定义参数个数和类型。

内置函数

i18n 支持了一些内置函数,用于做不同语言解析的体验优化,如:plural 、cardinal 、ordinal 。具体规则和使用,可以参考这里:  http://cldr.unicode.org/index/cldr-spec/plural-rules

使用 Dart 字符串模板

Dart 字符串模板是非常强大的,而在 i18n 中,你可以使用字符串模板(这点非常赞),如:

        count(int cnt): "You have created $cnt ${_plural(cnt, one:'invoice', many:'invoices')}."
    

前置编译

i18n 依然依赖了 Dart 官方提供的 builder_runner 工具,来从 yaml 文件生成 Dart 文件,使用方式:  flutter pub run build_runner build

语言包使用

前置编译后,每个语言包会生成 N 个 Class (语言包的每一个分类或组合会生成一个 Class 文件),然后会生成一个根 Class,我们可以直接使用根 Class (当然也可以使用任何一个分类层级的 Class )。

比如两个语言包文件:  AppMessages.i18n.yamlAppMessages_en.i18n.yaml(未加语言后缀的,会认为是默认语言包,因此 AppMessages.i18n.yaml 是默认语言包),会生成 2 个根 Dart Class:  class AppMessagesclass AppMessages_en extends AppMessages

AppMessages_en自动继承自 AppMessages,因此我们可以直接使用 AppMessages类型来存储语言包,并在语言切换时重新为其实例化对应的子类:

        AppMessages appMessages = new AppMessages(); resetLocalLang(String localeName) { switch (localeName) { case 'en': appMessages = AppMessages_en(); break; case 'zh': default: appMessages = AppMessages(); break; } }
    

然后你可以在任意地方使用语言包:

        debugPrint('Load Button: ${appMessages.button.load}'); FlatButton( child: Text(appMessages.button.save), onPressed: () { /// 干点什么 }, )
    

Flutter 集成

集成到 Flutter,依然要依赖于官方的支持,在 MaterialApp 中设置和监听本地语言包:

        @override Widget build(BuildContext context) { return MaterialApp( localeResolutionCallback: (Locale locale, Iterable
            supportedLocales) { /// Local changed }, localizationsDelegates: [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, ], supportedLocales: ['zh', 'en'] .map((loc) => new Locale(loc)) .toList(growable: false), /// ... ); }
    

结尾

国际化支持,是一个移动端 APP 框架层的基础能力,设计原则应该是使用无感知、灵活易扩展;但维护成本是难免有增加的,比如每次改文案要所有语言包同时更改。

讲到这里,还并没有完成基础框架的搭建,后面我们会讲解更多的 Flutter 架构设计内容,比如:通知、分享、UI 设计等等。

作者:vifird

行业开发案例
新闻推荐