Flutter怎麼樣做國際化

什麼是國際化

國際化是指在設計軟件時,將軟件與特定語言及地區脫鉤的過程。當軟件被移植到不同的語言地區時,軟件本身不用做內部工程上的改變或修正。

本地化則是指當移植軟件時,加上與特定區域設置有關的資訊和翻譯文件的過程。 國際化和本地化之間的區別雖然微妙,但卻很重要。國際化意味着產品有適用於任何地方的潛力;本地化則是爲了更適合於特定地方的使用,而另外增添的特色。用一項產品來說,國際化只需做一次,但本地化則要針對不同的區域各做一次。 這兩者之間是互補的,並且兩者結合起來才能讓一個系統適用於各地。

國際化實現中的困難

開發軟件時,國際化和本地化對開發者是一個有挑戰性的任務,特別是當軟件當初設計時沒有考慮這個問題時。通常做法是將文本和其他環境相關的資源與程序代碼相分離。這樣在理想的情況下,應對變化的環境時無需修改代碼,只要修改資源,從而顯著簡化了工作。

Flutter的國際化

Flutter中的國際化包括Flutter組件的國際化和其他文本的國際化兩者;

Flutter組件的國際化

Flutter給我們提供的Widget默認情況下就是支持國際化,但是在沒有進行特別的設置之前,它們無論在什麼環境都是以英文的方式顯示的。

如果想要添加其他語言,你的應用必須指定額外的 MaterialApp 屬性並且添加一個單獨的 package,叫做 flutter_localizations

截至 2020 年 11 月,該軟件包支持 78 種語言。

pubspec添加依賴

想要使用 flutter_localizations 的話,我們需要在 pubspec.yaml 文件中添加它作爲依賴:

dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter

設置MaterialApp

  • 在localizationsDelegates中指定哪些Widget需要進行國際化

    • 用於生產本地化值集合的工廠
    • 我們這裏指定了Material、Widgets、Cupertino都使用國際化
  • supportedLocales指定要支持哪些國際化

    • 我們這裏指定中文和英文(也可以指定國家編碼)
MaterialApp(
  localizationsDelegates: [
    GlobalMaterialLocalizations.delegate, // 指定本地化的字符串
    GlobalCupertinoLocalizations.delegate, // 對應的Cupertino風格
    GlobalWidgetsLocalizations.delegate // 指定默認的文本排列方向, 由左到右或由右到左
  ],
  supportedLocales: [
    Locale("en"),
    Locale("zh")
  ],
)

注意:如果要指定語言代碼、文字代碼和國家代碼,可以進行如下指定方式:

// Full Chinese support for CN, TW, and HK
supportedLocales: [
  const Locale.fromSubtags(languageCode: 'zh'), // generic Chinese 'zh'
  const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans'), // generic simplified Chinese 'zh_Hans'
  const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant'), // generic traditional Chinese 'zh_Hant'
  const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'CN'), // 'zh_Hans_CN'
  const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: 'TW'), // 'zh_Hant_TW'
  const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: 'HK'), // 'zh_Hant_HK'
],

Flutter中自定義文本的國際化

創建本地化類

該類用於定義我們需要進行本地化的字符串等信息:

  • 1.我們需要一個構造器,並且傳入一個Locale對象
  • 2.定義一個Map,其中存放我們不同語言對應的文本
  • 3.定義它們對應的getter方法,根據語言環境返回不同的結果
import 'package:flutter/material.dart';

class QWLocalizations {
  final Locale locale;

  QWLocalizations(this.locale);

  static Map<String, Map<String, String>> _localizedValues = {
    "fr": {"title": "Titre", "hello": "Bonjour"},
    "zh": {"title": "首頁", "hello": "你好"}
  };

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

  String get hello {
    return _localizedValues[locale.languageCode]?["hello"] ?? 'hello';
  }

  static QWLocalizations of(BuildContext context) {
    return Localizations.of(context, QWLocalizations);
  }
}

自定義Delegate

上面的類定義好後,我們在什麼位置或者說如何對它進行初始化呢?
我們可以像Flutter Widget中的國際化方式一樣對它們進行初始化,也就是我們可以定義一個對象的Delegate類,並且將其傳入localizationsDelegates中;

Delegate的作用就是當Locale發生改變時,調用對應的load方法,重新加載新的Locale資源

HYLocalizationsDelegate需要繼承自LocalizationsDelegate,並且有三個方法必須重寫:
isSupported,shouldReload,load

import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';

import 'QWLocalizations.dart';

class QWLocalizationsDelegate extends LocalizationsDelegate<QWLocalizations> {
  //是否在我們支持的語言範圍
  @override
  bool isSupported(Locale locale) {
    return ["fr", "zh"].contains(locale.languageCode);
  }

  /*
  * 當Localizations Widget重新build時,是否調用load方法重新加載Locale資源
    一般情況下,Locale資源只應該在Locale切換時加載一次,不需要每次Localizations重新build時都加載一遍;
    所以一般情況下返回false即可;
  * */
  @override
  bool shouldReload(LocalizationsDelegate<QWLocalizations> old) {
    return false;
  }

  /*
  * 當Locale發生改變時(語言環境),加載對應的HYLocalizations資源
  這個方法返回的是一個Future,因爲有可能是異步加載的;
  但是我們這裏是直接定義的一個Map,因此可以直接返回一個同步的Future(SynchronousFuture)
  * */
  @override
  Future<QWLocalizations> load(Locale locale) {
    return SynchronousFuture(QWLocalizations(locale));
  }

  static QWLocalizationsDelegate delegate = QWLocalizationsDelegate();
}

異步加載數據

假如我們的數據是異步加載的,比如來自Json文件或者服務器,應該如何處理呢?

QWLocalizations類中如下面代碼:

  static Map<String, Map<String, String>> _localizedValues = {};

  Future<bool> loadJson() async {
    // 1.加載json文件
    String jsonString = await rootBundle.loadString("assets/json/i18n.json");
    
    // 2.轉成map類型
    final Map<String, dynamic> map = json.decode(jsonString);
    
    // 3.注意:這裏是將Map<String, dynamic>轉成Map<String, Map<String, String>>類型
    _localizedValues = map.map((key, value) {
      return MapEntry(key, value.cast<String, String>());
    });
    return true;
  }

在QWLocalizationsDelegate中使用異步進行加載:

  @override
  Future<QWLocalizations> load(Locale locale) async {
    final localization = QWLocalizations(locale);
    await localization.loadJson();
    return localization;
  }

使用本地化類

接着我們可以在代碼中使用HYLocalization類。

  • 我們可以通過QWLocalizations.of(context)獲取到QWLocalizations對象
Text(
  QWLocalizations.of(context).hello,
)

國際化的工具---Intl

認識arb文件

目前我們已經可以通過加載對應的json文件來進行本地化了。

但是還有另外一個問題,我們在進行國際化的過程中,下面的代碼依然需要根據json文件手動編寫

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

String get hello {
  return _localizedValues[locale.languageCode]?["hello"] ?? 'hello';
}

有沒有一種更好的方式,讓我們可以快速在本地化文件即dart代碼文件直接來轉換呢?答案就是arb文件

  • arb文件全稱Application Resource Bundle,表示應用資源包,目前已經得到Google的支持;
  • 其本質就是一個json文件,但是可以根據該文件轉成對應的語言環境;
  • arb的說明文檔:https://github.com/google/app-r

使用IDE插件來進行arb和dart文件之間的轉換

  • 初始化intl

選擇工具欄Tools - Flutter Intl - Initialize for the Project

完成上面的操作之後會自動生成如下文件目錄:

  • generated是自動生成的dart代碼
  • I10n是對應的arb文件目錄

使用intl

在localizationsDelegates中配置生成的class,名字是S

  • 1.添加對應的delegate
  • 2.supportedLocales使用S.delegate.supportedLocales
localizationsDelegates: [
  GlobalMaterialLocalizations.delegate,
  GlobalWidgetsLocalizations.delegate,
  GlobalCupertinoLocalizations.delegate,
  S.delegate
],
supportedLocales: S.delegate.supportedLocales,

因爲我們目前還沒有對應的本地化字符串,所以需要在intl_en.arb文件中編寫:

{
  "title": "home",
  "hello": "hello"
}
  • 編寫後ctrl(command) + s保存即可;

之後按照如下格式在代碼中使用

S.of(context).title

添加中文

如果希望添加中文支持:add local

  • 在彈出框中輸入zh即可

我們會發現,會生成對應的intl_zh.arb和messages_zh.dart文件

arb其它語法

如果我們希望在使用本地化的過程中傳遞一些參數:

  • 比如hello kobe或hello james
  • 比如你好啊,李銀河或你好啊,王小波

修改對應的arb文件:

  • {name}:表示傳遞的參數
{
  "title": "home",
  "hello": "hello",
  "sayHello": "hello {name}"
}

在使用時,傳入對應的參數即可:

Text(S.of(context).sayHello("李銀河")),

總結

文本的國際化實質就是根據系統提供的locale信息去獲取對應的文本和對UI做相應操作(指從左到右還是從右到左展示),locale信息是指國家代碼、地區代碼等,通常我們本地需要做的就是把文案按照{國家代碼:{通用文本:本地化文案}}的格式進行組織排列。這裏國家代碼比如中國是zh,美國是en。通用文本一般用英文。最後就是根據locale信息和通用文本去字典獲取本地化文本值的過程。

參考:

Flutter國際化

https://zhuanlan.zhihu.com/p/145992691

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章