27

从0开始设计Flutter独立APP | 第二篇: 完整的国际化语言支持 - 前端的自我修炼 - Segm...

 4 years ago
source link: https://segmentfault.com/a/1190000023035385?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

从0开始设计Flutter独立APP | 第二篇: 完整的国际化语言支持

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

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

flutter国际化

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

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',
    },
  };

  String get title {
    return _localizedValues[locale.languageCode]['title'];
  }
}

官方方案的缺陷

官方的支持有几个缺陷:

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

i18n介绍

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

flutter国际化

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...

使用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

flutter国际化

语言包使用

前置编译后,每个语言包会生成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<Locale> supportedLocales) {
      /// Local changed
    },
    localizationsDelegates: [
      GlobalMaterialLocalizations.delegate,
      GlobalWidgetsLocalizations.delegate,
    ],
    supportedLocales: ['zh', 'en']
        .map((loc) => new Locale(loc))
        .toList(growable: false),
    /// ...
  );
}

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

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


持续分享闪点清单在Flutter上的开发经验。闪点清单,一款悬浮清单软件:

闪点清单,一款悬浮清单软件


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK