從0開始設計Flutter獨立APP | 第二篇: 完整的國際化語言支持

鑑於Flutter高性能渲染和跨平臺的優點,閃點清單在移動端APP上,使用了完整的Flutter框架來開發。既然是完整APP,架構搭建徹底不受歷史Native APP的影響,沒有歷史包袱的沉澱,設計也能更靈活和健壯。markdown

國際化語言的支持,是不少APP都有的一個強需求,APP不管大小,只要還不想放棄國外的客戶,通常就須要支持國際化。 架構

flutter國際化

官方支持

Flutter官方方案提供了國際化的基礎支持,如Flutter內置組件的國際化、語言代理、Widget使用語言包、語言設置回調等,並支持自定義第三方類來擴展,能夠參考Flutter國際化文檔。 官方支持代碼示例:app

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官方支持和自身封裝,實現了更靈活易用的方案。ide

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函數:ui

debugPrint(m.users.welcome('World'));
複製代碼

參數定義基本沒有限制,能夠隨意定義參數個數和類型。

內置函數

i18n支持了一些內置函數,用於作不一樣語言解析的體驗優化,如:plural、cardinal、ordinal。具體規則和使用,能夠參考這裏:cldr.unicode.org/index/cldr-…

使用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上的開發經驗。閃點清單,一款懸浮清單軟件:

閃點清單,一款懸浮清單軟件
相關文章
相關標籤/搜索