Flutter了解之入門篇15(國際化)

目錄

  1. 國際化Material組件庫
  2. 國際化開發(fā)人員的UI(Localizations)

國際化:支持多種語言。為應(yīng)用程序支持的每種語言設(shè)置本地化值,如文本(語言差異)、布局(閱讀方向差異)、圖片(國旗)。

默認(rèn)情況下,flutterSDK(為了盡可能小而且簡單)中的組件僅提供美國英語值的本地化資源(文本)。為Material組件庫添加其他語言的支持,須添加一個名為“flutter_localizations”的包依賴,然后在MaterialApp中進(jìn)行一些配置。
/*
flutter_localizations 包(Material組件庫的本地化實(shí)現(xiàn))包含:
  GlobalMaterialLocalizations和GlobalWidgetsLocalizations的本地化接口的多語言實(shí)現(xiàn)。
*/
Material組件庫和開發(fā)人員的UI都需要進(jìn)行國際化:
  1. Material組件庫
    比如:日歷組件默認(rèn)在任何環(huán)境下都會以英文顯示,所以需要國際化。
    需要依賴flutter_localizations包。
  2. 開發(fā)人員的UI。
    需要實(shí)現(xiàn)Localizations。
/*
iOS需要在info.plist中添加Localizations項(xiàng)(在其中添加語言,默認(rèn)有一個英文)
*/

簡介

  1. 獲取當(dāng)前區(qū)域Locale
Locale類(包括語言和國家兩個標(biāo)志)用來標(biāo)識用戶的語言環(huán)境。
Locale('zh', 'CN') // 中文簡體

獲取應(yīng)用的當(dāng)前區(qū)域Locale

// Localizations 組件一般位于widget樹中其它業(yè)務(wù)組件的頂部,它的作用是定義區(qū)域Locale以及設(shè)置子樹依賴的本地化資源。 
// 如果系統(tǒng)的語言環(huán)境發(fā)生變化,WidgetsApp將創(chuàng)建一個新的Localizations 組件并重建它,這樣子樹中通過Localizations.localeOf(context) 獲取的Locale就會更新。
Locale myLocale = Localizations.localeOf(context);
// 返回:zh_CN
myLocale.toString()
  1. 監(jiān)聽系統(tǒng)語言切換
當(dāng)更改系統(tǒng)語言設(shè)置時,APP中的Localizations組件會重新構(gòu)建,Localizations.localeOf(context) 獲取的Locale就會更新,最終界面會重新build達(dá)到切換語言的效果。
因?yàn)長ocalizations組件內(nèi)部使用了InheritedWidget ,當(dāng)子組件的build函數(shù)引用了InheritedWidget時會創(chuàng)建對InheritedWidget的隱式依賴關(guān)系,因此當(dāng)InheritedWidget發(fā)生更改時(即Localizations的Locale設(shè)置發(fā)生更改時)將重建所有依賴它的子組件。
這個過程是隱式完成的,并沒有主動去監(jiān)聽系統(tǒng)語言切換。

但是有時需要在系統(tǒng)語言發(fā)生改變時做一些事,比如系統(tǒng)語言切換為一種APP不支持的語言時,需要設(shè)置一個默認(rèn)的語言,這時就需要 監(jiān)聽locale改變事件。

可以通過MaterialApp的以下2種方法來監(jiān)聽locale改變的事件

方法1. localeResolutionCallback回調(diào)函數(shù):
  Locale Function(Locale locale, Iterable<Locale> supportedLocales)

方法2. localeListResolutionCallback(推薦)回調(diào)函數(shù):
  // 和localeResolutionCallback唯一的不同就在第一個參數(shù)類型
  Locale Function(List<Locale> locales, Iterable<Locale> supportedLocales)

說明:
  1. locale:當(dāng)前手機(jī)系統(tǒng)設(shè)置的語言區(qū)域。
    當(dāng)應(yīng)用啟動時或用戶動態(tài)改變系統(tǒng)語言設(shè)置時此locale即為系統(tǒng)的當(dāng)前l(fā)ocale。
    如果locale為null,則表示Flutter未能獲取到設(shè)備的Locale信息,所以在使用locale之前一定要先判空。
    注意:當(dāng)開發(fā)者手動指定APP的locale時(MaterialApp的locale屬性),那么此locale參數(shù)代表開發(fā)者指定的locale,將忽略系統(tǒng)locale,不再會因?yàn)樵O(shè)備語言改變而發(fā)生變化。
  2. supportedLocales:當(dāng)前應(yīng)用支持的locale列表,MaterialApp通過supportedLocales屬性注冊的。
  3. 返回值:一個Locale(應(yīng)用最終使用的Locale)
    通常會在不支持的語言區(qū)域時返回一個默認(rèn)的Locale(默認(rèn)使用語言)。

  4. locales
    用戶在較新的Android系統(tǒng)手機(jī)設(shè)置中可以設(shè)置一個語言列表,應(yīng)用通常的處理方式就是按照列表的順序依次嘗試加載相應(yīng)的Locale,如果某一種語言加載成功則會停止。
    在Flutter中,應(yīng)該優(yōu)先使用localeListResolutionCallback,不必?fù)?dān)心Android系統(tǒng)的差異性,如果在低版本的Android系統(tǒng)中,F(xiàn)lutter會自動處理這種情況,這時Locale列表只會包含一項(xiàng)。
  1. 引用本地化值

通過Localizations.of(context,type)來引用本地化值。
Localizations組件用于加載和查找應(yīng)用當(dāng)前語言下的本地化值或資源。

本地化值由Localizations組件的LocalizationsDelegates列表加載。 每個委托必須定義一個異步load() 方法,以生成封裝了一系列本地化值的對象。通常這些對象為每個本地化值定義一個方法。

在大型應(yīng)用程序中,不同模塊或Package可能會與自己的本地化值捆綁在一起, 這就是為什么要用Localizations組件 管理對象表的原因。 要使用由LocalizationsDelegate的load方法之一產(chǎn)生的對象,可以指定一個BuildContext和對象的類型來找到它。
例如:Material組件庫的本地化字符串由MaterialLocalizations類定義,此類的實(shí)例由MaterialApp類提供的LocalizationDelegate創(chuàng)建。通過如下方式獲取到:
Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);

這個特殊的Localizations.of()表達(dá)式會經(jīng)常使用,所以MaterialLocalizations類提供了一個便捷方法:
static MaterialLocalizations of(BuildContext context) {
  return Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);
}
使用:MaterialLocalizations.of(context).國際化字段名  

1. 國際化Material組件庫

第一步: 在pubspec.yaml文件中添加flutter_localizations依賴包(支持十幾種語言),并下載

dependencies:
  flutter_localizations:
    sdk: flutter

第二步:配置MaterialApp(指定localizationsDelegates和supportedLocales)

import 'package:flutter_localizations/flutter_localizations.dart';
new MaterialApp(
 localizationsDelegates: [
   // 本地化的代理類
   GlobalMaterialLocalizations.delegate,
   GlobalWidgetsLocalizations.delegate,
 ],
 supportedLocales: [  // 應(yīng)用支持的語言
    const Locale('en', 'US'), // 美國英語(美國地區(qū)的英文)
    const Locale('zh', 'CN'), // 中文簡體
    // 其它Locales
  ],
  // ...
)

說明:
  1. 多數(shù)應(yīng)用程序都是通過MaterialApp(對WidgetsApp進(jìn)行了包裝)為入口,低級別的WidgetsApp為入口時也可以使用相同的類和邏輯進(jìn)行國際化。與MaterialApp類為入口的應(yīng)用不同, 對基于WidgetsApp類為入口的應(yīng)用程序進(jìn)行國際化時,不需要GlobalMaterialLocalizations.delegate。
  2. localizationsDelegates列表中的元素是生成本地化值集合的工廠。
    每個委托必須定義一個異步load() 方法,以生成封裝了一系列本地化值的對象。
    GlobalMaterialLocalizations.delegate為Material組件庫提供的本地化的字符串和其他值,它可以使Material 組件支持多語言。GlobalMaterialLocalizations.delegate是一個產(chǎn)生GlobalMaterialLocalizations實(shí)例的LocalizationsDelegate。
    GlobalWidgetsLocalizations.delegate定義組件默認(rèn)的文本方向,從左到右或從右到左。
  3. supportedLocales接收一個Locale數(shù)組,表示應(yīng)用支持的語言列表。
    當(dāng)沒有精確匹配(語言和地區(qū)同時匹配)時,使用語言。
    如果沒有匹配則使用supportedLocales列表項(xiàng)的第一個。

2. 國際化開發(fā)人員的UI(Localizations)

示例

第一步:實(shí)現(xiàn)Localizations資源類(提供本地化資源值,如文本)

// Locale資源類 
// 會根據(jù)當(dāng)前的語言(獲取本地化資源值)返回不同的文本??梢詫⑺行枰С侄嗾Z言的文本都在此類中定義,該類的實(shí)例會在Delegate類的load方法中創(chuàng)建。

class DemoLocalizations {
  DemoLocalizations(this.isZh);
  // 是否為中文
  bool isZh = false;

  // 為了使用方便,定義一個靜態(tài)方法
  static DemoLocalizations of(BuildContext context) {
    // MaterialApp組件內(nèi)部嵌套了Localizations組件,通過第三步配置MaterialApp的localizationsDelegates,會將DemoLocalizationsDelegate傳給Localizations組件
    // 獲取DemoLocalizations實(shí)例
    return Localizations.of<DemoLocalizations>(context, DemoLocalizations);
  }

  // Locale相關(guān)值,title為應(yīng)用標(biāo)題
  String get title {
    return isZh ? "Flutter應(yīng)用" : "Flutter APP";
  }
  //... 其它的值  
}

/*
方式2

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'];
  }
}
*/

第二步:實(shí)現(xiàn)Delegate類(在Locale改變時會從DemoLocalizations中加載新的本地化資源值)

// Locale代理類
// Delegate類需要繼承自LocalizationsDelegate類并實(shí)現(xiàn)相應(yīng)的接口。有一個load方法。

class DemoLocalizationsDelegate extends LocalizationsDelegate<DemoLocalizations> {
  const DemoLocalizationsDelegate();

  // 是否支持某個Local
  @override
  bool isSupported(Locale locale) => ['en', 'zh'].contains(locale.languageCode);

  // Flutter會調(diào)用此類加載相應(yīng)的Locale資源類
  @override
  Future<DemoLocalizations> load(Locale locale) {
    print("$locale");
    return SynchronousFuture<DemoLocalizations>(
        DemoLocalizations(locale.languageCode == "zh")
    );
  }

  // shouldReload的返回值決定當(dāng)Localizations組件重新build時,是否調(diào)用load方法重新加載Locale資源。一般情況下,Locale資源只應(yīng)該在Locale切換時加載一次,不需要每次在Localizations重新build時都加載,所以返回false即可。
  // 事實(shí)上,無論shouldReload返回true還是false,每當(dāng)Locale改變時Flutter都會再調(diào)用load方法加載新的Locale。
  @override
  bool shouldReload(DemoLocalizationsDelegate old) => false;

  static DemoLocalizationsDelegate delegate = const DemoLocalizationsDelegate();
}

/*
class DemoLocalizationsDelegate extends LocalizationsDelegate<DemoLocalizations> {
  const DemoLocalizationsDelegate();
  @override
  bool isSupported(Locale locale) => ['en', 'es'].contains(locale.languageCode);
  @override
  Future<DemoLocalizations> load(Locale locale) {
    return new SynchronousFuture<DemoLocalizations>(new DemoLocalizations(locale));
  }
  @override
  bool shouldReload(DemoLocalizationsDelegate old) => false;
  static DemoLocalizationsDelegate delegate = const DemoLocalizationsDelegate();
}
*/

第三步:配置MaterialApp的localizationsDelegates

在MaterialApp或WidgetsApp的localizationsDelegates列表中添加Delegate實(shí)例即可完成注冊

localizationsDelegates: [
 // 本地化的代理類
 GlobalMaterialLocalizations.delegate,
 GlobalWidgetsLocalizations.delegate,
 // 注冊我們的Delegate
 DemoLocalizationsDelegate(), // 或DemoLocalizationsDelegate.delegate
],

第四步:在Widget中使用本地化資源值

return Scaffold(
  appBar: AppBar(
    // 使用Locale title  
    title: Text(DemoLocalizations.of(context).title),
  ),
  ... //省略無關(guān)代碼
 )

完整代碼如下

import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter/foundation.dart' show SynchronousFuture;

void main() {
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.yellow,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home:MyHomePage(),
      localizationsDelegates: [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        DemoLocalizationsDelegate.delegate,
      ],
      supportedLocales: [
        const Locale('zh', 'CH'),
        const Locale('en', 'US'),
      ],
    );
  }
}
class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: new Text(DemoLocalizations.of(context).title),
      ),
      body: new Center(
        child: new Text(DemoLocalizations.of(context).content),
      ),
    );
  }
}


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': 'Home',
      'content': 'Hello World'
    },
    'zh': {
      'title': '首頁',
      'content': '世界 你好'
    },
  };
  String get title {
    return _localizedValues[locale.languageCode]['title'];
  }
  String get content {
    return _localizedValues[locale.languageCode]['content'];
  }
}
class DemoLocalizationsDelegate extends LocalizationsDelegate<DemoLocalizations> {
  const DemoLocalizationsDelegate();
  @override
  bool isSupported(Locale locale) => ['en', 'zh'].contains(locale.languageCode);
  @override
  Future<DemoLocalizations> load(Locale locale) {
    return new SynchronousFuture<DemoLocalizations>(new DemoLocalizations(locale));
  }
  @override
  bool shouldReload(DemoLocalizationsDelegate old) => false;
  static DemoLocalizationsDelegate delegate = const DemoLocalizationsDelegate();
}
中文

英文

當(dāng)要支持的語言不是兩種而是8種甚至20幾種時,如果為每個文本屬性都要分別去判斷到底是哪種Locale從而獲取相應(yīng)語言的文本將會是一件非常復(fù)雜的事。而且通常情況下翻譯人員并不是開發(fā)人員,能不能像i18n或l10n標(biāo)準(zhǔn)那樣可以將翻譯單獨(dú)保存為一個arb文件交由翻譯人員去翻譯,翻譯好之后開發(fā)人員再通過工具將arb文件轉(zhuǎn)為代碼呢。

解決:使用Intl包

使用Intl包的好處:
  1. 輕松實(shí)現(xiàn)國際化
  2. 將字符串文本分離成單獨(dú)的文件,方便開發(fā)人員和翻譯人員分工協(xié)作。

2. 使用Intl包

第一步:添加依賴、創(chuàng)建必要目錄

添加依賴

dependencies:
  #...省略無關(guān)項(xiàng)
  intl: ^0.15.7 
dev_dependencies:
   #...省略無關(guān)項(xiàng)
  intl_translation: ^0.17.2

說明:
  1. intl_translation包主要包含了一些工具,它在開發(fā)階段主要主要的作用是從代碼中提取要國際化的字符串到單獨(dú)的arb文件和根據(jù)arb文件生成對應(yīng)語言的dart代碼。
  2. intl包主要是引用和加載intl_translation生成后的dart代碼。

創(chuàng)建必要目錄

在項(xiàng)目根目錄下創(chuàng)建一個l10n-arb目錄,該目錄保存接下來通過intl_translation命令生成的arb文件。
在lib目錄下創(chuàng)建一個l10n的目錄,該目錄用于保存從arb文件生成的dart代碼文件。

/*
arb文件示例(通過intl_translation命令自動生成)JSON格式:
{
  "@@last_modified": "2020-09-23T12:54:51.602843",
  "@@locale":"zh_CH",
  "title": "Flutter應(yīng)用",
  "@title": {
    "description": "Title for the Demo application",
    "type": "text",
    "placeholders": {}
  }
 }
*/

第二步:實(shí)現(xiàn)Localizations類(添加需要國際化的屬性)和Delegate類

在lib/l10n目錄下新建一個“l(fā)ocalization_intl.dart”的文件

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'messages_all.dart'; // 該文件會由intl_translation命令生成(從arb文件生成的dart代碼)

// 可以在DemoLocalizations類中添加需要國際化的屬性或方法
class DemoLocalizations {
  static Future<DemoLocalizations> load(Locale locale) {
    final String name = locale.countryCode.isEmpty ? locale.languageCode : locale.toString();
    final String localeName = Intl.canonicalizedLocale(name);
    // intl_translation命令會生成對應(yīng)的方法
    // initializeMessages()用來加載翻譯的字符串
    return initializeMessages(localeName).then((b) {
      Intl.defaultLocale = localeName;
      return new DemoLocalizations();
    });
  }

  static DemoLocalizations of(BuildContext context) {
    return Localizations.of<DemoLocalizations>(context, DemoLocalizations);
  }
  
  // 添加屬性
  String get title {
    return Intl.message(  // Intl.message用來查找
      'Flutter APP',
      name: 'title',
      desc: 'Title for the Demo application',
    );
  }
/*
  // 添加方法
  String helloTitle(String name) {
    return Intl.message(  // Intl.message用來查找
      'hello $name',
      name: 'helloTitle',
      desc: 'helloTitle',
      args:[name],
    );
  }
*/
  // Intl.plural方法可以在howMany值不同時輸出不同的提示信息
  remainingEmailsMessage(int howMany) => Intl.plural(howMany,
      zero: 'There are no emails left',
      one: 'There is $howMany email left',
      other: 'There are $howMany emails left',
      name: "remainingEmailsMessage",
      args: [howMany],
      desc: "How many emails remain after archiving.",
      examples: const {'howMany': 110, 'userName': 'Fred'});
}


// Locale代理類
class DemoLocalizationsDelegate extends LocalizationsDelegate<DemoLocalizations> {
  const DemoLocalizationsDelegate();

  // 是否支持某個Local
  @override
  bool isSupported(Locale locale) => ['en', 'zh'].contains(locale.languageCode);

  // Flutter會調(diào)用此類加載相應(yīng)的Locale資源類
  @override
  Future<DemoLocalizations> load(Locale locale) {
    return  DemoLocalizations.load(locale);
  }

  // 當(dāng)Localizations Widget重新build時,是否調(diào)用load重新加載Locale資源.
  @override
  bool shouldReload(DemoLocalizationsDelegate old) => false;

  static DemoLocalizationsDelegate delegate = const DemoLocalizationsDelegate();
}

第三步: 生成arb文件、根據(jù)arb文件生成dart代碼

生成arb文件

提取localization_intl.dart代碼中的字符串到一個arb文件(通intl_translation包的工具),運(yùn)行如下命令:
flutter pub pub run intl_translation:extract_to_arb --output-dir=l10n-arb lib/l10n/localization_intl.dart

運(yùn)行此命令后,會將之前通過Intl包的API標(biāo)識的屬性和字符串提取到“l(fā)10n-arb/intl_messages.arb”文件中,看看其內(nèi)容:
{
  "@@last_modified": "2020-09-23T12:54:51.602843",
  "title": "Flutter APP",
  "@title": {
    "description": "Title for the Demo application",
    "type": "text",
    "placeholders": {}
  },
  "remainingEmailsMessage": "{howMany,plural, =0{There are no emails left}=1{There is {howMany} email left}other{There are {howMany} emails left}}",
  "@remainingEmailsMessage": {
    "description": "How many emails remain after archiving.",
    "type": "text",
    "placeholders": {
      "howMany": {
        "example": 110
      }
    }
  }
}

// 中文簡體:intl_zh_Hans_CN.arb
// const Locale.fromSubtags(languageCode: 'zh',scriptCode: 'Hans',countryCode: 'CN'),
這個是默認(rèn)的Locale資源文件,如果現(xiàn)在要支持中文大陸,只需要在該文件同級目錄創(chuàng)建一個"intl_zh_CN.arb"文件,然后將"intl_messages.arb"的內(nèi)容拷貝到"intl_zh_CN.arb"文件,接下來將英文翻譯為中文即可,翻譯后的"intl_zh_CN.arb"文件內(nèi)容如下:
{
  "@@last_modified": "2018-12-10T15:46:20.897228",
  "@@locale":"zh_CN",
  "title": "Flutter應(yīng)用",
  "@title": {
    "description": "Title for the Demo application",
    "type": "text",
    "placeholders": {}
  },
  "remainingEmailsMessage": "{howMany,plural, =0{沒有未讀郵件}=1{有{howMany}封未讀郵件}other{有{howMany}封未讀郵件}}",
  "@remainingEmailsMessage": {
    "description": "How many emails remain after archiving.",
    "type": "text",
    "placeholders": {
      "howMany": {
        "example": 42
      }
    }
  }
}

必須要翻譯title和remainingEmailsMessage字段,description是該字段的說明,通常給翻譯人員看,代碼中不會用到。

注意:
    1. 如果某個特定的arb中缺失某個屬性,那么應(yīng)用將會加載默認(rèn)的arb文件(intl_messages.arb)中的相應(yīng)屬性,這是Intl的托底策略。
    2. 每次運(yùn)行提取命令時,intl_messages.arb都會根據(jù)代碼重新生成,但其他arb文件不會,所以當(dāng)要添加新的字段或方法時,其他arb文件是增量的,不用擔(dān)心會覆蓋。
    3. arb文件是標(biāo)準(zhǔn)的。通常會將arb文件交給翻譯人員,當(dāng)他們完成翻譯后,再通過下面的步驟根據(jù)arb文件生成最終的dart代碼。

生成dart代碼

根據(jù)arb生成dart文件:
flutter pub pub run intl_translation:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/l10n/localization_intl.dart l10n-arb/intl_*.arb

在首次運(yùn)行時會在"lib/l10n"目錄下生成多個文件,對應(yīng)多種Locale,這些代碼便是最終要使用的dart代碼。

優(yōu)化第三步

在根目錄下創(chuàng)建一個intl.sh的腳本,內(nèi)容為:
flutter pub pub run intl_translation:extract_to_arb --output-dir=l10n-arb lib/l10n/localization_intl.dart
flutter pub pub run intl_translation:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/l10n/localization_intl.dart l10n-arb/intl_*.arb

授予執(zhí)行權(quán)限:
chmod +x intl.sh

執(zhí)行intl.sh
./intl.sh

完整代碼

main.dart

import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'l10n/localization_intl.dart';

void main() {
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.yellow,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home:MyHomePage(),
      localizationsDelegates: [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        DemoLocalizationsDelegate.delegate,
      ],
      supportedLocales: [
        const Locale('zh', 'CH'),
        const Locale('en', 'US'),
      ],
    );
  }
}
class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: new Text(DemoLocalizations.of(context).title),
      ),
      body: new Center(
        child: new Text(DemoLocalizations.of(context).content),
      ),
    );
  }
}

localization_intl.dart

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'messages_all.dart'; // intl_translation從arb文件生成的dart代碼

class DemoLocalizations {
  static Future<DemoLocalizations> load(Locale locale) {
    final String name = locale.countryCode.isEmpty ? locale.languageCode : locale.toString();
    final String localeName = Intl.canonicalizedLocale(name);
    // initializeMessages()方法和"messages_all.dart"文件一樣,是同時生成的。
    // initializeMessages()用來加載翻譯的字符串
    return initializeMessages(localeName).then((b) {
      Intl.defaultLocale = localeName;
      return new DemoLocalizations();
    });
  }
  static DemoLocalizations of(BuildContext context) {
    return Localizations.of<DemoLocalizations>(context, DemoLocalizations);
  }

  // Intl.message用來查找
  String get title {
    return Intl.message(
      'Home',
      name: 'title',
      desc: 'Title for the Demo application',
    );
  }
  String get content {
    return Intl.message(
      'Hello world',
      name: 'content',
      desc: 'Content for the Demo application',
    );
  }
  remainingEmailsMessage(int howMany) => Intl.plural(howMany,
      zero: 'There are no emails left',
      one: 'There is $howMany email left',
      other: 'There are $howMany emails left',
      name: "remainingEmailsMessage",
      args: [howMany],
      desc: "How many emails remain after archiving.",
      examples: const {'howMany': 110, 'userName': 'Fred'});
}

// Locale代理類
class DemoLocalizationsDelegate extends LocalizationsDelegate<DemoLocalizations> {
  const DemoLocalizationsDelegate();
  //是否支持某個Local
  @override
  bool isSupported(Locale locale) => ['en', 'zh'].contains(locale.languageCode);
  // Flutter會調(diào)用此類加載相應(yīng)的Locale資源類
  @override
  Future<DemoLocalizations> load(Locale locale) {
    return  DemoLocalizations.load(locale);
  }
  // 當(dāng)Localizations Widget重新build時,是否調(diào)用load重新加載Locale資源.
  @override
  bool shouldReload(DemoLocalizationsDelegate old) => false;
  static DemoLocalizationsDelegate delegate = const DemoLocalizationsDelegate();
}

常見問題

  1. 默認(rèn)語言區(qū)域不對

在一些非大陸行貨渠道買的一些Android和iOS設(shè)備,會出現(xiàn)默認(rèn)的Locale不是中文簡體的情況。這屬于正?,F(xiàn)象,但是為了防止設(shè)備獲取的Locale與實(shí)際的地區(qū)不一致,所有的支持多語言的APP都必須提供一個手動選擇語言的入口。

  1. 對應(yīng)用標(biāo)題進(jìn)行國際化

MaterialApp有一個title屬性,用于指定APP的標(biāo)題。在Android系統(tǒng)中,APP的標(biāo)題會出現(xiàn)在任務(wù)管理器中。所以也需要對title進(jìn)行國際化。

但是問題是很多國際化的配置都是在MaterialApp上設(shè)置的,無法在構(gòu)建MaterialApp時通過Localizations.of來獲取本地化資源,如:
MaterialApp(
  title: DemoLocalizations.of(context).title, //不能正常工作!
  localizationsDelegates: [
    // 本地化的代理類
    GlobalMaterialLocalizations.delegate,
    GlobalWidgetsLocalizations.delegate,
    DemoLocalizationsDelegate() // 設(shè)置Delegate
  ],
);
運(yùn)行后,DemoLocalizations.of(context).title會報錯,這里DemoLocalizations.of(context)會返回null,這里的context找不到MaterialApp,繼而找不到DemoLocalizationsDelegate。


只需要設(shè)置一個onGenerateTitle回調(diào)即可:
MaterialApp(
  onGenerateTitle: (context){
    // 此時context在Localizations的子樹中
    return DemoLocalizations.of(context).title;
  },
  localizationsDelegates: [
    DemoLocalizationsDelegate(),
    ...
  ],
);
  1. 為英語系的國家指定同一個locale

英語系的國家非常多,如美國、英國、澳大利亞等,這些英語系國家雖然說的都是英語,但也會有一些區(qū)別。如果APP只想提供一種英語(如美國英語)供所有英語系國家使用則在localeListResolutionCallback中來做兼容。

localeListResolutionCallback:
    (List<Locale> locales, Iterable<Locale> supportedLocales) {
  // 判斷當(dāng)前l(fā)ocale是否為英語系國家,如果是直接返回Locale('en', 'US')     
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容