Flutter國際化

一. 國際化的認(rèn)識

開發(fā)一個App德挣,如果我們的App需要面向不同的語種(比如中文内狗、英文、繁體等)考传,那么我們需要對齊進行國際化開發(fā)。

國際化的英文稱呼:internationalization(簡稱為i18n证鸥,取前后兩個字母僚楞,18表示中間省略字母的個數(shù))。

App國際化開發(fā)主要包括:文本國際化(包括文本的順序)枉层,Widget顯示的國際化泉褐,比如我們下面開發(fā)的這個App:

  • 某些文本在英文環(huán)境下應(yīng)該顯示為英文;
  • 某些Widget在中文環(huán)境下鸟蜡,應(yīng)該顯示中文(比如彈出的時間選擇器)膜赃;

二. 國際化的適配

2.1. Widget的國際化

Flutter給我們提供的Widget默認(rèn)情況下就是支持國際化,但是在沒有進行特別的設(shè)置之前揉忘,它們無論在什么環(huán)境都是以英文的方式顯示的跳座。

如果想要添加其他語言,你的應(yīng)用必須指定額外的 MaterialApp 屬性并且添加一個單獨的 package泣矛,叫做 flutter_localizations疲眷。

截至到 2020 年 2 月份,這個 package 已經(jīng)支持大約 77 種語言您朽。

2.1.1. pubspec添加依賴

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

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

2.1.2. 設(shè)置MaterialApp

  • 設(shè)置localizationsDelegates中指定哪些Widget需要進行國際化
    • 用于生產(chǎn)本地化值集合的工廠
    • 我們這里指定了Material、Widgets、Cupertino都使用國際化
  • supportedLocales指定要支持哪些國際化
    • 我們這里指定中文和英文(也可以指定國家編碼)
MaterialApp(
  localizationsDelegates: [
    GlobalMaterialLocalizations.delegate, // 指定本地化的字符串和一些其他的值
    GlobalCupertinoLocalizations.delegate, // 對應(yīng)的Cupertino風(fēng)格
    GlobalWidgetsLocalizations.delegate // 指定默認(rèn)的文本排列方向, 由左到右或由右到左
  ],
  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'
],

2.1.3. 查看Widget結(jié)果

設(shè)置完成后,我們在Android上將語言切換為中文蛋哭,查看結(jié)果:

但是對于iOS县习,將語言切換為中文,依然顯示是英文的Widget

  • 這是因為iOS定義了一些應(yīng)用的元數(shù)據(jù)谆趾,其中包括支持的語言環(huán)境准颓;
  • 我們必須將其對應(yīng)的元數(shù)據(jù)中支持的語言添加進去;
  • 元數(shù)據(jù)的設(shè)置在iOS項目中對應(yīng)的info.plist文件中棺妓;

修改iOS的info.plist文件配置:

  • 選擇 Information Property List 項;
  • Editor 菜單中選擇 Add Item炮赦,然后從彈出菜單中選擇 Localizations怜跑;
  • 為array添加一項選擇 Add Item,選擇Chinese吠勘;

配置完成后性芬,卸載之前的app,重新安裝:

2.2. 其它文本國際化

App中除了有默認(rèn)的Widget剧防,我們也希望對自己的文本進行國際化植锉,如何做到呢?

2.2.1. 創(chuàng)建本地化類

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

  1. 我們需要一個構(gòu)造器峭拘,并且傳入一個Locale對象(后續(xù)會使用到)
  2. 定義一個Map俊庇,其中存放我們不同語言對應(yīng)的顯示文本
  3. 定義它們對應(yīng)的getter方法,根據(jù)語言環(huán)境返回不同的結(jié)果

新建一個i18n文件夾鸡挠,新建localizations.dart文件如下:

import 'package:flutter/material.dart';

class HYLocalizations {
  final Locale locale;

  HYLocalizations(this.locale);

  static Map<String, Map<String, String>> _localizedValues = {
    "en": {
      "title": "home",
      "greet": "hello~",
      "picktime": "Pick a Time"
    },
    "zh": {
      "title": "首頁",
      "greet": "你好~",
      "picktime": "選擇一個時間"
    }
  };

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

  String get greet {
    return _localizedValues[locale.languageCode]["greet"];
  }

  String get pickTime {
    return _localizedValues[locale.languageCode]["picktime"];
  }
}

2.2.2. 自定義Delegate

上面的類定義好后辉饱,我們在什么位置或者說如何對它進行初始化呢?

  • 答案是我們可以像Flutter Widget中的國際化方式一樣對它們進行初始化拣展;
  • 也就是我們也定義一個對象的Delegate類彭沼,并且將其傳入localizationsDelegates中;
  • Delegate的作用就是當(dāng)Locale發(fā)生改變時备埃,調(diào)用對應(yīng)的load方法姓惑,重新加載新的Locale資源

HYLocalizationsDelegate需要繼承自LocalizationsDelegate按脚,并且有三個方法必須重寫:

  • isSupported:用于當(dāng)前環(huán)境的Locale于毙,是否在我們支持的語言范圍
  • shouldReload:當(dāng)Localizations Widget重新build時,是否調(diào)用load方法重新加載Locale資源
    • 一般情況下辅搬,Locale資源只應(yīng)該在Locale切換時加載一次望众,不需要每次Localizations重新build時都加載一遍;
    • 所以一般情況下返回false即可;
  • load方法:當(dāng)Locale發(fā)生改變時(語言環(huán)境)烂翰,加載對應(yīng)的HYLocalizations資源
    • 這個方法返回的是一個Future夯缺,因為有可能是異步加載的;
    • 但是我們這里是直接定義的一個Map甘耿,因此可以直接返回一個同步的Future(SynchronousFuture)
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:i18n_demo/i18n/localizations.dart';

class HYLocalizationsDelegate extends LocalizationsDelegate<HYLocalizations> {
  @override
  bool isSupported(Locale locale) {
    return ["en", "zh"].contains(locale.languageCode);
  }

  @override
  bool shouldReload(LocalizationsDelegate<HYLocalizations> old) {
    return false;
  }

  @override
  Future<HYLocalizations> load(Locale locale) {
    // 返回一個同步的HYLocalizations對象
    return SynchronousFuture(HYLocalizations(locale));
  }

  static HYLocalizationsDelegate delegate = HYLocalizationsDelegate();
}

然后在localizationsDelegates里面添加HYLocalizationsDelegate.delegate即可:

localizationsDelegates: [
  GlobalMaterialLocalizations.delegate,
  GlobalCupertinoLocalizations.delegate,
  GlobalWidgetsLocalizations.delegate,
  HYLocalizationsDelegate.delegate,
],

2.2.3. 使用本地化類

接著我們可以在代碼中使用HYLocalization類踊兜。

  • 我們可以通過Localizations.of(context, HYLocalizations)獲取到HYLocalizations對象
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(Localizations.of(context, HYLocalizations).title),
      ),
      body: Center(
        child: Column(
          children: <Widget>[
            Text(Localizations.of(context, HYLocalizations).greet),
            RaisedButton(
              child: Text(Localizations.of(context, HYLocalizations).pickTime),
              onPressed: () {
                showDatePicker(
                    context: context,
                    initialDate: DateTime.now(),
                    firstDate: DateTime(2019),
                    lastDate: DateTime(2022)
                ).then((pickTime) {
                });
              },
            )
          ],
        ),
      ),
    );
  }

當(dāng)然,我們可以對Localizations.of(context, HYLocalizations)進行一個優(yōu)化佳恬。

  • 給HYLocalizations定義一個of的靜態(tài)方法
class HYLocalizations {
  static HYLocalizations of(BuildContext context) {
    return Localizations.of(context, HYLocalizations);
  }
}

接下來我們就可以通過下面的方式來使用了(其它地方也是一樣):

appBar: AppBar(
  title: Text(HYLocalizations.of(context).title),
)

2.2.4. 異步加載數(shù)據(jù)

假如我們的數(shù)據(jù)是異步加載的捏境,比如來自Json文件或者服務(wù)器,應(yīng)該如何處理呢毁葱?

當(dāng)然垫言,在加載之前我們需要在pubspec.yaml文件里面添加如下配置:

  assets:
    - assets/json/

這里我們可以修改HYLocalizations的數(shù)據(jù)加載:

  // 初始數(shù)據(jù)為空
  static Map<String, Map<String, String>> _localizedValues = {};

  // 加載數(shù)據(jù)的方法
  Future<bool> loadJson() async {
    // 1.加載json文件
    String jsonString = await rootBundle.loadString("assets/json/i18n.json");

    // 2.轉(zhuǎn)成map類型
    final Map<String, dynamic> map = json.decode(jsonString);

    // 3.注意:這里是將Map<String, dynamic>轉(zhuǎn)成Map<String, Map<String, String>>類型
    _localizedValues = map.map((key, value) {
      return MapEntry(key, value.cast<String, String>());
    });
    return true;
  }

在HYLocalizationsDelegate中的load方法使用異步進行加載:

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

三. 國際化的工具

3.1. 認(rèn)識arb文件

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

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

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

  String get greet {
    return _localizedValues[locale.languageCode]["greet"];
  }

  String get pickTime {
    return _localizedValues[locale.languageCode]["picktime"];
  }

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

3.2. intl package

官方文檔推薦可以使用intl package來進行arb和dart文件之間的轉(zhuǎn)換(通過終端指令)

需要在在pubspec.yaml中添加其相關(guān)的依賴最欠,具體步驟這里不再詳細(xì)給出示罗,可以參考官方文檔。

3.3. 使用IDE插件

在之前有一個比較好用的Android Studio的插件:Flutter i18n

  • 但是這個插件已經(jīng)很久不再維護了芝硬,所以不再推薦給大家使用鹉勒;

目前我們可以使用另外一個插件:Flutter Intl

  • 該插件更新維護頻率很高,并且廣受好評吵取;
  • 另外禽额,在Android Studio和VSCode中都是支持的;

我們這里以Android Studio為例皮官,講解其使用過程:

3.3.1. 安裝插件

在Android Studio的Plugins中安裝插件:

3.3.2. 初始化intl

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

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

  • generated是自動生成的dart代碼
  • I10n是對應(yīng)的arb文件目錄

3.3.3. 使用intl

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

  1. 添加對應(yīng)的S.delegate
  2. supportedLocales使用S.delegate.supportedLocales
localizationsDelegates: [
  GlobalMaterialLocalizations.delegate,
  GlobalWidgetsLocalizations.delegate,
  GlobalCupertinoLocalizations.delegate,
  HYLocalizationsDelegate.delegate,
  S.delegate
],
supportedLocales: S.delegate.supportedLocales,

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

  • 編寫后ctrl(command) + s保存即可
{
  "title": "home",
  "greet": "hello~",
  "picktime": "Pick a time"
}

在代碼中使用即可捺氢,按照如下格式:S.of(context).title

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(S.of(context).title),
      ),
      body: Center(
        child: Column(
          children: <Widget>[
            Text(S.of(context).greet),
            RaisedButton(
              child: Text(S.of(context).picktime),
              onPressed: () {
                showDatePicker(
                    context: context,
                    initialDate: DateTime.now(),
                    firstDate: DateTime(2019),
                    lastDate: DateTime(2022)
                ).then((pickTime) {
                });
              },
            )
          ],
        ),
      ),
    );
  }

3.3.4. 添加中文

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

  • 在彈出框中輸入zh即可

我們會發(fā)現(xiàn)藻丢,會生成對應(yīng)的intl_zh.arb和messages_zh.dart文件

編寫intl_zh.arb文件:

{
  "title": "首頁",
  "greet": "您好~",
  "picktime": "選擇一個時間"
}

查看界面,會根據(jù)當(dāng)前語言顯示對應(yīng)的語言文本

3.4. arb其它語法

arb文件的本質(zhì)就是json文件加一些語法摄乒,如果我們希望在使用本地化的過程中傳遞一些參數(shù):

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

修改對應(yīng)的arb文件:

  • {name}:表示傳遞的參數(shù)
{
  "title": "home",
  "greet": "hello~",
  "picktime": "Pick a time",
  "sayHello": "hello {name}"
}

在使用時,傳入對應(yīng)的參數(shù)即可:

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

arb還有更多的語法斋否,大家可以在之后慢慢學(xué)習(xí)和發(fā)掘梨水。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市茵臭,隨后出現(xiàn)的幾起案子疫诽,更是在濱河造成了極大的恐慌,老刑警劉巖旦委,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奇徒,死亡現(xiàn)場離奇詭異,居然都是意外死亡缨硝,警方通過查閱死者的電腦和手機摩钙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來查辩,“玉大人胖笛,你說我怎么就攤上這事∫巳猓” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵翎碑,是天一觀的道長谬返。 經(jīng)常有香客問我,道長日杈,這世上最難降的妖魔是什么遣铝? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮莉擒,結(jié)果婚禮上酿炸,老公的妹妹穿的比我還像新娘。我一直安慰自己涨冀,他們只是感情好填硕,可當(dāng)我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鹿鳖,像睡著了一般扁眯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上翅帜,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天姻檀,我揣著相機與錄音,去河邊找鬼涝滴。 笑死绣版,一個胖子當(dāng)著我的面吹牛胶台,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播杂抽,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼诈唬,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了默怨?” 一聲冷哼從身側(cè)響起讯榕,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎匙睹,沒想到半個月后愚屁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡痕檬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年霎槐,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片梦谜。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡丘跌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出唁桩,到底是詐尸還是另有隱情闭树,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布荒澡,位于F島的核電站报辱,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏单山。R本人自食惡果不足惜碍现,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望米奸。 院中可真熱鬧昼接,春花似錦、人聲如沸悴晰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吕喘。三九已至,卻和暖如春稼稿,著一層夾襖步出監(jiān)牢的瞬間佃却,已是汗流浹背者吁。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留饲帅,地道東北人复凳。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓瘤泪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親育八。 傳聞我的和親對象是個殘疾皇子对途,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 概述 國際化的認(rèn)識 國際化的適配 國際化的工具 一、國際化的認(rèn)識 開發(fā)一個App髓棋,如果我們的App需要面向不同的語...
    IIronMan閱讀 716評論 0 2
  • 前言 如果APP有需要支持多種語言,就需要支持國際化,無論是android和ios,現(xiàn)在針對flutter的國際化...
    lebonbill閱讀 10,701評論 4 6
  • 如果App的用戶使用的是不同語言实檀,那進行國際化是必要的。國際化主要包括文案的國際化(不同的語言展示不同的文案)和布...
    chonglingliu閱讀 2,203評論 5 6
  • Flutter 國際化 詳情鏈接[https://book.flutterchina.club/chapter13...
    土豆騎士閱讀 1,791評論 1 5
  • 國際化命令:flutter --no-color pub global run intl_utils:genera...
    iLeooooo閱讀 3,009評論 0 1