將 Flutter 應用程式國際化
如果您的應用程式可能會部署給說其他語言的使用者,那麼您就需要將其國際化。這表示您需要以某種方式編寫應用程式,使其可以針對應用程式支援的每種語言或語言環境本地化文字和版面配置等值。Flutter 提供了有助於國際化的 Widget 和類別,而 Flutter 程式庫本身也已國際化。
本頁涵蓋使用 MaterialApp
和 CupertinoApp
類別來本地化 Flutter 應用程式所需的概念和工作流程,因為大多數應用程式都是以這種方式編寫的。但是,使用較低層級的 WidgetsApp
類別編寫的應用程式也可以使用相同的類別和邏輯進行國際化。
Flutter 中的本地化簡介
#本節提供了關於如何建立和國際化新的 Flutter 應用程式的教學,以及目標平台可能需要的任何其他設定。
您可以在 gen_l10n_example
中找到此範例的原始碼。
設定國際化的應用程式:Flutter_localizations 套件
#依預設,Flutter 僅提供美國英語本地化。若要新增對其他語言的支援,應用程式必須指定額外的 MaterialApp
(或 CupertinoApp
)屬性,並包含一個名為 flutter_localizations
的套件。截至 2023 年 12 月,此套件支援 115 種語言和語言變體。
首先,使用 flutter create
命令在您選擇的目錄中建立新的 Flutter 應用程式。
flutter create <name_of_flutter_app>
若要使用 flutter_localizations
,請將該套件和 intl
套件作為相依性新增到您的 pubspec.yaml
檔案中
flutter pub add flutter_localizations --sdk=flutter
flutter pub add intl:any
這會建立一個具有下列條目的 pubspec.yml
檔案
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
intl: any
然後匯入 flutter_localizations
程式庫,並為您的 MaterialApp
或 CupertinoApp
指定 localizationsDelegates
和 supportedLocales
import 'package:flutter_localizations/flutter_localizations.dart';
return const MaterialApp(
title: 'Localizations Sample App',
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: [
Locale('en'), // English
Locale('es'), // Spanish
],
home: MyHomePage(),
);
在引入 flutter_localizations
套件並新增先前的程式碼後,現在應該在 115 種支援的語言環境之一中正確本地化 Material
和 Cupertino
套件。Widget 應該會根據本地化的訊息進行調整,並具有正確的從左到右或從右到左的版面配置。
嘗試將目標平台的語言環境切換為西班牙文 (es
),訊息應該會本地化。
基於 WidgetsApp
的應用程式類似,只是不需要 GlobalMaterialLocalizations.delegate
。
建議使用完整的 Locale.fromSubtags
建構函式,因為它支援 scriptCode
,但 Locale
預設建構函式仍然完全有效。
localizationsDelegates
清單中的元素是產生本地化值集合的工廠。GlobalMaterialLocalizations.delegate
為 Material Components 程式庫提供本地化的字串和其他值。GlobalWidgetsLocalizations.delegate
為 widget 程式庫定義預設文字方向,即從左到右或從右到左。
有關這些應用程式屬性、它們所依賴的類型以及國際化的 Flutter 應用程式通常如何建構的更多資訊,請參閱本頁。
覆寫地區設定
#Localizations.override
是 Localizations
widget 的工廠建構函式,允許在應用程式的某個部分需要本地化為與裝置設定的語言環境不同的語言環境(通常很少見)時使用。
若要觀察此行為,請新增對 Localizations.override
的呼叫和一個簡單的 CalendarDatePicker
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// Add the following code
Localizations.override(
context: context,
locale: const Locale('es'),
// Using a Builder to get the correct BuildContext.
// Alternatively, you can create a new widget and Localizations.override
// will pass the updated BuildContext to the new widget.
child: Builder(
builder: (context) {
// A toy example for an internationalized Material widget.
return CalendarDatePicker(
initialDate: DateTime.now(),
firstDate: DateTime(1900),
lastDate: DateTime(2100),
onDateChanged: (value) {},
);
},
),
),
],
),
),
);
}
熱重載應用程式,CalendarDatePicker
widget 應該以西班牙文重新呈現。
新增您自己的本地化訊息
#在新增 flutter_localizations
套件後,您可以設定本地化。若要將本地化的文字新增到您的應用程式,請完成下列指示
新增
intl
套件作為相依性,提取由flutter_localizations
釘選的版本flutter pub add intl:any
開啟
pubspec.yaml
檔案,並啟用generate
旗標。此旗標位於 pubspec 檔案的flutter
區段中。yaml# The following section is specific to Flutter. flutter: generate: true # Add this line
將新的 yaml 檔案新增到 Flutter 專案的根目錄。將此檔案命名為
l10n.yaml
並包含下列內容yamlarb-dir: lib/l10n template-arb-file: app_en.arb output-localization-file: app_localizations.dart
此檔案會設定本地化工具。在此範例中,您已完成下列操作
- 將 應用程式資源套件 (
.arb
) 輸入檔案放在${FLUTTER_PROJECT}/lib/l10n
中。.arb
會為您的應用程式提供本地化資源。 - 將英文範本設定為
app_en.arb
。 - 告知 Flutter 在
app_localizations.dart
檔案中產生本地化。
- 將 應用程式資源套件 (
在
${FLUTTER_PROJECT}/lib/l10n
中,新增app_en.arb
範本檔案。例如json{ "helloWorld": "Hello World!", "@helloWorld": { "description": "The conventional newborn programmer greeting" } }
在同一個目錄中新增另一個名為
app_es.arb
的套件檔案。在此檔案中,新增相同訊息的西班牙文翻譯。json{ "helloWorld": "¡Hola Mundo!" }
現在,執行
flutter pub get
或flutter run
,程式碼產生會自動進行。您應該會在${FLUTTER_PROJECT}/.dart_tool/flutter_gen/gen_l10n
中找到產生的檔案。或者,您也可以執行flutter gen-l10n
來產生相同的檔案,而無需執行應用程式。在您對
MaterialApp
的建構函式呼叫中新增關於app_localizations.dart
和AppLocalizations.delegate
的匯入陳述式dartimport 'package:flutter_gen/gen_l10n/app_localizations.dart';
dartreturn const MaterialApp( title: 'Localizations Sample App', localizationsDelegates: [ AppLocalizations.delegate, // Add this line GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], supportedLocales: [ Locale('en'), // English Locale('es'), // Spanish ], home: MyHomePage(), );
AppLocalizations
類別也提供自動產生的localizationsDelegates
和supportedLocales
清單。您可以使用這些清單,而無需手動提供。dartconst MaterialApp( title: 'Localizations Sample App', localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, );
一旦 Material 應用程式啟動,您就可以在應用程式中的任何地方使用
AppLocalizations
dartappBar: AppBar( // The [AppBar] title text should update its message // according to the system locale of the target platform. // Switching between English and Spanish locales should // cause this text to update. title: Text(AppLocalizations.of(context)!.helloWorld), ),
如果目標裝置的語言環境設定為英文,則此程式碼會產生一個顯示「Hello World!」的 Text
widget,如果目標裝置的語言環境設定為西班牙文,則會顯示「¡Hola Mundo!」。在 arb
檔案中,每個項目的索引鍵會當作 getter 的方法名稱,而該項目的值則包含本地化的訊息。
gen_l10n_example
使用此工具。
若要本地化您的裝置應用程式描述,請將本地化的字串傳遞給 MaterialApp.onGenerateTitle
return MaterialApp(
onGenerateTitle: (context) => DemoLocalizations.of(context).title,
預留位置、複數和選取
#您也可以在訊息中包含應用程式值,使用特殊的語法,該語法使用預留位置來產生方法而不是 getter。預留位置必須是有效的 Dart 識別碼名稱,它會成為 AppLocalizations
程式碼中產生的方法的位置參數。藉由將預留位置包在花括號中來定義預留位置名稱,如下所示
"{placeholderName}"
在應用程式的 .arb
檔案中的 placeholders
物件中定義每個預留位置。例如,若要使用 userName
參數定義 hello 訊息,請將下列項目新增至 lib/l10n/app_en.arb
"hello": "Hello {userName}",
"@hello": {
"description": "A message with a single parameter",
"placeholders": {
"userName": {
"type": "String",
"example": "Bob"
}
}
}
此程式碼片段會將 hello
方法呼叫新增至 AppLocalizations.of(context)
物件,而且該方法會接受 String
類型的參數;hello
方法會傳回字串。重新產生 AppLocalizations
檔案。
將傳遞到 Builder
中的程式碼取代為下列程式碼
// Examples of internationalized strings.
return Column(
children: <Widget>[
// Returns 'Hello John'
Text(AppLocalizations.of(context)!.hello('John')),
],
);
您也可以使用數值預留位置來指定多個值。不同的語言有不同的單字複數形式。語法也支援指定單字應如何複數化。複數化的訊息必須包含 num
參數,指出如何在不同情況下將單字複數化。例如,英文將「person」複數化為「people」,但這還不夠完整。message0
複數形式可能是「no people」或「zero people」。messageFew
複數形式可能是「several people」、「some people」或「a few people」。messageMany
複數形式可能是「most people」或「many people」,或「a crowd」。只需要更通用的 messageOther
欄位即可。下列範例顯示了可用的選項
"{countPlaceholder, plural, =0{message0} =1{message1} =2{message2} few{messageFew} many{messageMany} other{messageOther}}"
先前的運算式會由與 countPlaceholder
值對應的訊息變體 (message0
、message1
...) 取代。只需要 messageOther
欄位即可。
下列範例定義複數化單字「wombat」的訊息
"nWombats": "{count, plural, =0{no wombats} =1{1 wombat} other{{count} wombats}}",
"@nWombats": {
"description": "A plural message",
"placeholders": {
"count": {
"type": "num",
"format": "compact"
}
}
}
藉由傳入 count
參數來使用複數方法
// Examples of internationalized strings.
return Column(
children: <Widget>[
...
// Returns 'no wombats'
Text(AppLocalizations.of(context)!.nWombats(0)),
// Returns '1 wombat'
Text(AppLocalizations.of(context)!.nWombats(1)),
// Returns '5 wombats'
Text(AppLocalizations.of(context)!.nWombats(5)),
],
);
與複數類似,您也可以根據 String
預留位置選擇一個值。這最常用於支援帶有性別的語言。語法如下
"{selectPlaceholder, select, case{message} ... other{messageOther}}"
下一個範例定義選取性別代名詞的訊息
"pronoun": "{gender, select, male{he} female{she} other{they}}",
"@pronoun": {
"description": "A gendered message",
"placeholders": {
"gender": {
"type": "String"
}
}
}
藉由將性別字串作為參數傳遞來使用此功能
// Examples of internationalized strings.
return Column(
children: <Widget>[
...
// Returns 'he'
Text(AppLocalizations.of(context)!.pronoun('male')),
// Returns 'she'
Text(AppLocalizations.of(context)!.pronoun('female')),
// Returns 'they'
Text(AppLocalizations.of(context)!.pronoun('other')),
],
);
請記住,使用 select
陳述式時,參數與實際值之間的比較會區分大小寫。也就是說,AppLocalizations.of(context)!.pronoun("Male")
會預設為「other」情況,並傳回「they」。
逸出語法
#有時,您必須使用 Token,例如 {
和 }
,作為一般字元。若要忽略要剖析的此類 Token,請藉由將下列項目新增至 l10n.yaml
來啟用 use-escaping
旗標
use-escaping: true
剖析器會忽略以一對單引號包裝的任何字串。若要使用一般的單引號字元,請使用一對連續的單引號。例如,下列文字會轉換為 Dart String
{
"helloWorld": "Hello! '{Isn''t}' this a wonderful day?"
}
產生的字串如下
"Hello! {Isn't} this a wonderful day?"
具有數字和貨幣的訊息
#數字,包括代表貨幣值的數字,在不同的語言環境中顯示方式會非常不同。flutter_localizations
中的本地化產生工具會使用 intl
套件中的 NumberFormat
類別,根據語言環境和所需的格式來格式化數字。
int
、double
和 number
類型可以使用下列任何 NumberFormat
建構函式
訊息「格式」值 | 1200000 的輸出 |
---|---|
compact | 「1.2M」 |
compactCurrency * | 「$1.2M」 |
compactSimpleCurrency * | 「$1.2M」 |
compactLong | 「120 萬」 |
currency * | 「USD1,200,000.00」 |
decimalPattern | "1,200,000" |
decimalPatternDigits * | "1,200,000" |
decimalPercentPattern * | "120,000,000%" |
percentPattern | "120,000,000%" |
scientificPattern | 「1E6」 |
simpleCurrency * | "$1,200,000" |
表格中加上星號的 NumberFormat
建構函式提供選用、已命名的參數。這些參數可以指定為預留位置的 optionalParameters
物件的值。例如,若要為 compactCurrency
指定選用的 decimalDigits
參數,請對 lib/l10n/app_en.arg
檔案進行下列變更
"numberOfDataPoints": "Number of data points: {value}",
"@numberOfDataPoints": {
"description": "A message with a formatted int parameter",
"placeholders": {
"value": {
"type": "int",
"format": "compactCurrency",
"optionalParameters": {
"decimalDigits": 2
}
}
}
}
具有日期的訊息
#日期字串的格式會根據語言環境和應用程式的需求而有許多不同的格式。
類型為 DateTime
的預留位置值會使用 intl
套件中的 DateFormat
來格式化。
共有 41 種格式變化,以它們的 DateFormat
工廠建構子名稱來識別。在以下範例中,helloWorldOn
訊息中出現的 DateTime
值會以 DateFormat.yMd
格式化。
"helloWorldOn": "Hello World on {date}",
"@helloWorldOn": {
"description": "A message with a date parameter",
"placeholders": {
"date": {
"type": "DateTime",
"format": "yMd"
}
}
}
在地區設定為美國英文的應用程式中,以下運算式會產生「7/9/1959」。在俄羅斯地區設定中,則會產生「9.07.1959」。
AppLocalizations.of(context).helloWorldOn(DateTime.utc(1959, 7, 9))
針對 iOS 進行本地化:更新 iOS 應用程式套件
#雖然本地化是由 Flutter 處理,您仍需要在 Xcode 專案中新增支援的語言。這可確保您在 App Store 中的條目能正確顯示支援的語言。
若要設定您的應用程式支援的地區設定,請使用以下指示。
開啟您專案的
ios/Runner.xcodeproj
Xcode 檔案。在「專案導覽器」中,選取「專案」下方的
Runner
專案檔案。在專案編輯器中,選取「
Info
」標籤。在「本地化」區段中,按一下「
Add
」按鈕(+
)以將支援的語言和地區新增至您的專案。當要求選擇檔案和參考語言時,只需選取「Finish
」。Xcode 會自動建立空的
.strings
檔案,並更新ios/Runner.xcodeproj/project.pbxproj
檔案。App Store 會使用這些檔案來判斷您的應用程式支援哪些語言和地區。
進一步自訂的高階主題
#本節涵蓋自訂本地化 Flutter 應用程式的其他方法。
進階地區設定定義
#某些有多種變體的語言,需要的不只是語言代碼,才能適當區分。
例如,若要完全區分中文的所有變體,需要指定語言代碼、腳本代碼和國家/地區代碼。這是因為存在簡體和繁體腳本,以及同一腳本類型中字元寫法上的地區差異。
為了完整表達國家/地區代碼 CN
、TW
和 HK
的所有中文變體,支援的地區設定清單應包含
supportedLocales: [
Locale.fromSubtags(languageCode: 'zh'), // generic Chinese 'zh'
Locale.fromSubtags(
languageCode: 'zh',
scriptCode: 'Hans'), // generic simplified Chinese 'zh_Hans'
Locale.fromSubtags(
languageCode: 'zh',
scriptCode: 'Hant'), // generic traditional Chinese 'zh_Hant'
Locale.fromSubtags(
languageCode: 'zh',
scriptCode: 'Hans',
countryCode: 'CN'), // 'zh_Hans_CN'
Locale.fromSubtags(
languageCode: 'zh',
scriptCode: 'Hant',
countryCode: 'TW'), // 'zh_Hant_TW'
Locale.fromSubtags(
languageCode: 'zh',
scriptCode: 'Hant',
countryCode: 'HK'), // 'zh_Hant_HK'
],
這種明確的完整定義可確保您的應用程式可以區分並為這些國家/地區代碼的所有組合提供完整細微的本地化內容。如果未指定使用者偏好的地區設定,Flutter 會選擇最接近的相符項,這可能與使用者期望的有差異。Flutter 只會解析為 supportedLocales
中定義的地區設定,並為常用的語言提供腳本代碼區分的本地化內容。請參閱 Localizations
,以了解如何解析支援的地區設定和偏好的地區設定。
雖然中文是一個主要範例,但其他語言(如法語 (fr_FR
、fr_CA
))也應完全區分,以進行更細微的本地化。
追蹤地區設定:Locale 類別和 Localizations Widget
#Locale
類別會識別使用者的語言。行動裝置支援設定所有應用程式的地區設定,通常是使用系統設定選單。國際化的應用程式會顯示特定於地區設定的值來回應。例如,如果使用者將裝置的地區設定從英文切換為法文,則原本顯示「Hello World」的 Text
小工具會以「Bonjour le monde」重建。
Localizations
小工具會為其子項及其所依賴的本地化資源定義地區設定。WidgetsApp
小工具會建立 Localizations
小工具,並在系統的地區設定變更時重建它。
您隨時可以使用 Localizations.localeOf()
查詢應用程式目前的地區設定。
Locale myLocale = Localizations.localeOf(context);
指定應用程式的 supportedLocales 參數
#雖然 flutter_localizations
程式庫目前支援 115 種語言和語言變體,但預設只提供英文翻譯。開發人員可以自行決定要支援哪些語言。
MaterialApp
的 supportedLocales
參數會限制地區設定變更。當使用者變更裝置上的地區設定時,應用程式的 Localizations
小工具只有在新地區設定是此清單的成員時才會跟著變更。如果找不到裝置地區設定的完全相符項,則會使用第一個具有相符 languageCode
的支援地區設定。如果失敗,則會使用 supportedLocales
清單中的第一個元素。
想要使用不同「地區設定解析」方法的應用程式可以提供 localeResolutionCallback
。例如,讓您的應用程式無條件接受使用者選擇的任何地區設定
MaterialApp(
localeResolutionCallback: (
locale,
supportedLocales,
) {
return locale;
},
);
設定 l10n.yaml 檔案
#l10n.yaml
檔案可讓您設定 gen-l10n
工具以指定以下內容
- 所有輸入檔案的所在位置
- 所有輸出檔案的建立位置
- 要給本地化委派的 Dart 類別名稱
如需完整選項清單,請在命令列執行 flutter gen-l10n --help
,或參閱下表
選項 | 描述 |
---|---|
arb-dir | 範本和翻譯的 arb 檔案所在的目錄。預設值為 lib/l10n 。 |
output-dir | 寫入產生的本地化類別的目錄。如果您想在 Flutter 專案中的其他位置產生本地化程式碼,此選項才相關。您也需要將 synthetic-package 旗標設為 false。應用程式必須從此目錄匯入 output-localization-file 選項中指定的檔案。如果未指定,此預設為與 arb-dir 中指定的輸入目錄相同的目錄。 |
template-arb-file | 用作產生 Dart 本地化和訊息檔案基礎的範本 arb 檔案。預設值為 app_en.arb 。 |
output-localization-file | 輸出本地化和本地化委派類別的檔案名稱。預設值為 app_localizations.dart 。 |
untranslated-messages-file | 描述尚未翻譯的本地化訊息的檔案位置。使用此選項會在目標位置建立一個 JSON 檔案,格式如下"locale": ["message_1", "message_2" ... "message_n"] 如果未指定此選項,則會在命令列上印出尚未翻譯的訊息摘要。 |
output-class | 用於輸出本地化和本地化委派類別的 Dart 類別名稱。預設值為 AppLocalizations 。 |
preferred-supported-locales | 應用程式的偏好支援地區設定清單。依預設,此工具會依字母順序產生支援的地區設定清單。使用此旗標可預設為不同的地區設定。 例如,如果裝置支援,請傳入 [ en_US ] 以預設為美式英文。 |
header | 要附加至產生的 Dart 本地化檔案的標頭。此選項會採用字串。 例如,傳入 "/// All localized files." 可將此字串附加至產生的 Dart 檔案。或者,請查看 header-file 選項以傳入文字檔,以取得較長的標頭。 |
header-file | 要附加至產生的 Dart 本地化檔案的標頭。此選項的值是檔案的名稱,其中包含插入至每個產生的 Dart 檔案頂端的標頭文字。 或者,請查看 header 選項以傳入字串,以取得較簡單的標頭。此檔案應放置在 arb-dir 中指定的目錄中。 |
[no-]use-deferred-loading | 指定是否產生使用延後匯入的地區設定 Dart 本地化檔案,以便在 Flutter 網頁中延遲載入每個地區設定。 這可以透過減少 JavaScript 套件的大小來縮短網頁應用程式的初始啟動時間。當此旗標設定為 true 時,只有在 Flutter 應用程式需要時才會下載並載入特定地區設定的訊息。對於具有許多不同地區設定和許多本地化字串的專案,延後載入可以提高效能。對於只有少量地區設定的專案,差異可以忽略不計,且與將本地化與應用程式的其餘部分捆綁在一起相比,可能會減慢啟動速度。 請注意,此旗標不會影響其他平台,例如行動裝置或桌面電腦。 |
gen-inputs-and-outputs-list | 指定時,此工具會產生一個 JSON 檔案,其中包含工具的輸入和輸出,名稱為 gen_l10n_inputs_and_outputs.json 。這對於追蹤在產生最新一組本地化時使用了 Flutter 專案的哪些檔案會很有用。例如,Flutter 工具的建置系統會使用此檔案來追蹤何時在熱重載期間呼叫 gen_l10n。 此選項的值是產生 JSON 檔案的目錄。當為 null 時,將不會產生 JSON 檔案。 |
synthetic-package | 決定產生的輸出檔案是產生為合成套件還是產生在 Flutter 專案中的指定目錄。此旗標預設為 true 。當 synthetic-package 設定為 false 時,它會依預設在 arb-dir 指定的目錄中產生本地化檔案。如果指定了 output-dir ,則會在該處產生檔案。 |
project-dir | 指定時,此工具會使用傳遞到此選項的路徑作為根 Flutter 專案的目錄。 當為 null 時,會使用目前工作目錄的相對路徑。 |
[no-]required-resource-attributes | 要求所有資源 ID 都包含對應的資源屬性。 依預設,簡單訊息不需要中繼資料,但強烈建議使用,因為這會為讀者提供訊息含義的背景資訊。 複數訊息仍然需要資源屬性。 |
[no-]nullable-getter | 指定本地化類別 getter 是否可為 null。 依預設,此值為 true,以便 Localizations.of(context) 傳回可為 null 的值,以實現回溯相容性。如果此值為 false,則會在 Localizations.of(context) 的傳回值上執行 null 檢查,從而無需在使用者程式碼中進行 null 檢查。 |
[no-]format | 指定時,會在產生本地化檔案後執行 dart format 命令。 |
use-escaping | 指定是否啟用使用單引號作為逸出語法。 |
[no-]suppress-warnings | 指定時,會隱藏所有警告。 |
[no-]relax-syntax | 指定時,語法會放寬,以便如果特殊字元「{」後面沒有有效的預留位置,則會將其視為字串;如果「}」沒有關閉任何先前被視為特殊字元的「{」,則會將其視為字串。 |
[no-]use-named-parameters | 是否為產生的本地化方法使用具名參數。 |
Flutter 中的國際化運作方式
#本節涵蓋 Flutter 中本地化運作方式的技術細節。如果您打算支援自己的一組本地化訊息,以下內容會很有幫助。否則,您可以略過本節。
載入和擷取本地化值
#Localizations
小部件用於載入和查詢包含本地化數值集合的物件。應用程式使用 Localizations.of(context,type)
來參照這些物件。如果裝置的地區設定變更,Localizations
小部件會自動載入新地區設定的值,然後重建使用它的部件。之所以會這樣,是因為 Localizations
的運作方式就像一個 InheritedWidget
。當一個建構函式參照一個繼承的小部件時,會建立一個對繼承小部件的隱式依賴關係。當一個繼承的小部件變更時(當 Localizations
小部件的地區設定變更時),其相依的 context 會被重建。
本地化數值由 Localizations
小部件的 LocalizationsDelegate
列表載入。每個委派必須定義一個非同步的 load()
方法,該方法會產生一個封裝本地化數值集合的物件。通常,這些物件會為每個本地化數值定義一個方法。
在大型應用程式中,不同的模組或套件可能會與它們自己的本地化捆綁在一起。這就是為什麼 Localizations
小部件會管理一個物件表格,每個 LocalizationsDelegate
一個。若要檢索由其中一個 LocalizationsDelegate
的 load
方法產生的物件,請指定一個 BuildContext
和該物件的類型。
例如,Material Components 小部件的本地化字串由 MaterialLocalizations
類別定義。此類別的實例由 MaterialApp
類別提供的 LocalizationDelegate
建立。它們可以使用 Localizations.of()
來檢索。
Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);
這個特定的 Localizations.of()
運算式經常使用,因此 MaterialLocalizations
類別提供了一個方便的簡寫。
static MaterialLocalizations of(BuildContext context) {
return Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);
}
/// References to the localized values defined by MaterialLocalizations
/// are typically written like this:
tooltip: MaterialLocalizations.of(context).backButtonTooltip,
定義應用程式本地化資源的類別
#建構一個國際化的 Flutter 應用程式通常從封裝應用程式本地化數值的類別開始。以下範例是此類別的典型代表。
此應用程式的 intl_example
的完整原始碼。
此範例基於 intl
套件提供的 API 和工具。應用程式本地化資源的替代類別 章節描述了一個不依賴 intl
套件的 範例。
DemoLocalizations
類別(在以下程式碼片段中定義)包含應用程式的字串(範例中只有一個),這些字串已翻譯成應用程式支援的地區設定。它使用 Dart 的 intl
套件產生的 initializeMessages()
函式和 Intl.message()
來查詢它們。
class DemoLocalizations {
DemoLocalizations(this.localeName);
static Future<DemoLocalizations> load(Locale locale) {
final String name =
locale.countryCode == null || locale.countryCode!.isEmpty
? locale.languageCode
: locale.toString();
final String localeName = Intl.canonicalizedLocale(name);
return initializeMessages(localeName).then((_) {
return DemoLocalizations(localeName);
});
}
static DemoLocalizations of(BuildContext context) {
return Localizations.of<DemoLocalizations>(context, DemoLocalizations)!;
}
final String localeName;
String get title {
return Intl.message(
'Hello World',
name: 'title',
desc: 'Title for the Demo application',
locale: localeName,
);
}
}
基於 intl
套件的類別會匯入一個產生的訊息目錄,該目錄會為 Intl.message()
提供 initializeMessages()
函式和每個地區設定的後端儲存。訊息目錄由 intl
工具產生,該工具會分析包含 Intl.message()
呼叫的類別的原始碼。在此例中,只會是 DemoLocalizations
類別。
新增對新語言的支援
#需要支援 GlobalMaterialLocalizations
中未包含的語言的應用程式必須進行額外的工作:它必須為該地區設定提供約 70 個單詞或短語的翻譯(「本地化」),以及日期模式和符號。
請參閱以下範例,了解如何新增對挪威尼諾斯克語的支援。
一個新的 GlobalMaterialLocalizations
子類別定義了 Material 函式庫所依賴的本地化。還必須定義一個新的 LocalizationsDelegate
子類別,作為 GlobalMaterialLocalizations
子類別的工廠。
這是完整 add_language
範例的原始碼,不包括實際的尼諾斯克語翻譯。
特定地區設定的 GlobalMaterialLocalizations
子類別稱為 NnMaterialLocalizations
,而 LocalizationsDelegate
子類別稱為 _NnMaterialLocalizationsDelegate
。NnMaterialLocalizations.delegate
的值是委派的實例,這就是使用這些本地化的應用程式所需要的全部。
委派類別包含基本的日期和數字格式本地化。所有其他本地化都由 NnMaterialLocalizations
中的 String
值屬性 getter 定義,如下所示
@override
String get moreButtonTooltip => r'More';
@override
String get aboutListTileTitleRaw => r'About $applicationName';
@override
String get alertDialogLabel => r'Alert';
當然,這些是英文翻譯。若要完成這項工作,您需要將每個 getter 的傳回值變更為適當的尼諾斯克語字串。
getter 會傳回帶有 r
前綴的「原始」Dart 字串,例如 r'About $applicationName'
,因為有時字串會包含帶有 $
前綴的變數。這些變數會由參數化的本地化方法展開
@override
String get pageRowsInfoTitleRaw => r'$firstRow–$lastRow of $rowCount';
@override
String get pageRowsInfoTitleApproximateRaw =>
r'$firstRow–$lastRow of about $rowCount';
還需要指定地區設定的日期模式和符號,這些模式和符號在原始碼中定義如下
const nnLocaleDatePatterns = {
'd': 'd.',
'E': 'ccc',
'EEEE': 'cccc',
'LLL': 'LLL',
// ...
}
const nnDateSymbols = {
'NAME': 'nn',
'ERAS': <dynamic>[
'f.Kr.',
'e.Kr.',
],
// ...
}
這些值需要針對地區設定進行修改,才能使用正確的日期格式。遺憾的是,由於 intl
函式庫對於數字格式沒有相同的彈性,因此必須使用現有地區設定的格式作為 _NnMaterialLocalizationsDelegate
中的替代方案
class _NnMaterialLocalizationsDelegate
extends LocalizationsDelegate<MaterialLocalizations> {
const _NnMaterialLocalizationsDelegate();
@override
bool isSupported(Locale locale) => locale.languageCode == 'nn';
@override
Future<MaterialLocalizations> load(Locale locale) async {
final String localeName = intl.Intl.canonicalizedLocale(locale.toString());
// The locale (in this case `nn`) needs to be initialized into the custom
// date symbols and patterns setup that Flutter uses.
date_symbol_data_custom.initializeDateFormattingCustom(
locale: localeName,
patterns: nnLocaleDatePatterns,
symbols: intl.DateSymbols.deserializeFromMap(nnDateSymbols),
);
return SynchronousFuture<MaterialLocalizations>(
NnMaterialLocalizations(
localeName: localeName,
// The `intl` library's NumberFormat class is generated from CLDR data
// (see https://github.com/dart-lang/i18n/blob/main/pkgs/intl/lib/number_symbols_data.dart).
// Unfortunately, there is no way to use a locale that isn't defined in
// this map and the only way to work around this is to use a listed
// locale's NumberFormat symbols. So, here we use the number formats
// for 'en_US' instead.
decimalFormat: intl.NumberFormat('#,##0.###', 'en_US'),
twoDigitZeroPaddedFormat: intl.NumberFormat('00', 'en_US'),
// DateFormat here will use the symbols and patterns provided in the
// `date_symbol_data_custom.initializeDateFormattingCustom` call above.
// However, an alternative is to simply use a supported locale's
// DateFormat symbols, similar to NumberFormat above.
fullYearFormat: intl.DateFormat('y', localeName),
compactDateFormat: intl.DateFormat('yMd', localeName),
shortDateFormat: intl.DateFormat('yMMMd', localeName),
mediumDateFormat: intl.DateFormat('EEE, MMM d', localeName),
longDateFormat: intl.DateFormat('EEEE, MMMM d, y', localeName),
yearMonthFormat: intl.DateFormat('MMMM y', localeName),
shortMonthDayFormat: intl.DateFormat('MMM d'),
),
);
}
@override
bool shouldReload(_NnMaterialLocalizationsDelegate old) => false;
}
如需有關本地化字串的更多資訊,請查看 flutter_localizations README。
實作語言專用的 GlobalMaterialLocalizations
和 LocalizationsDelegate
子類別之後,您需要在應用程式中新增語言和委派實例。以下程式碼將應用程式的語言設定為尼諾斯克語,並將 NnMaterialLocalizations
委派實例新增至應用程式的 localizationsDelegates
清單
const MaterialApp(
localizationsDelegates: [
GlobalWidgetsLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
NnMaterialLocalizations.delegate, // Add the newly created delegate
],
supportedLocales: [
Locale('en', 'US'),
Locale('nn'),
],
home: Home(),
),
其他國際化工作流程
#本節說明國際化 Flutter 應用程式的不同方法。
應用程式本地化資源的替代類別
#先前的範例是根據 Dart intl
套件定義的。您可以選擇自己的方法來管理本地化數值,以便簡化或整合不同的 i18n 框架。
minimal
應用程式的完整原始碼。
在以下範例中,DemoLocalizations
類別將其所有翻譯直接包含在每個語言的 Maps 中
class DemoLocalizations {
DemoLocalizations(this.locale);
final Locale locale;
static DemoLocalizations of(BuildContext context) {
return Localizations.of<DemoLocalizations>(context, DemoLocalizations)!;
}
static const _localizedValues = <String, Map<String, String>>{
'en': {
'title': 'Hello World',
},
'es': {
'title': 'Hola Mundo',
},
};
static List<String> languages() => _localizedValues.keys.toList();
String get title {
return _localizedValues[locale.languageCode]!['title']!;
}
}
在最小應用程式中,DemoLocalizationsDelegate
略有不同。它的 load
方法會傳回 SynchronousFuture
,因為不需要進行非同步載入。
class DemoLocalizationsDelegate
extends LocalizationsDelegate<DemoLocalizations> {
const DemoLocalizationsDelegate();
@override
bool isSupported(Locale locale) =>
DemoLocalizations.languages().contains(locale.languageCode);
@override
Future<DemoLocalizations> load(Locale locale) {
// Returning a SynchronousFuture here because an async "load" operation
// isn't needed to produce an instance of DemoLocalizations.
return SynchronousFuture<DemoLocalizations>(DemoLocalizations(locale));
}
@override
bool shouldReload(DemoLocalizationsDelegate old) => false;
}
使用 Dart intl 工具
#在使用 Dart intl
套件建構 API 之前,請檢閱 intl
套件的文件。以下清單概述了本地化依賴於 intl
套件的應用程式的流程
示範應用程式依賴於一個名為 l10n/messages_all.dart
的產生原始檔,該檔案定義了應用程式使用的所有可本地化字串。
重建 l10n/messages_all.dart
需要兩個步驟。
將應用程式的根目錄作為目前目錄,從
lib/main.dart
產生l10n/intl_messages.arb
dart run intl_translation:extract_to_arb --output-dir=lib/l10n lib/main.dart
intl_messages.arb
檔案是一個 JSON 格式的 map,其中每個main.dart
中定義的Intl.message()
函式都有一個項目。此檔案用作英文和西班牙文翻譯的範本,分別是intl_en.arb
和intl_es.arb
。這些翻譯由您(開發人員)建立。將應用程式的根目錄作為目前目錄,為每個
intl_<locale>.arb
檔案產生intl_messages_<locale>.dart
,並產生匯入所有訊息檔案的intl_messages_all.dart
dart run intl_translation:generate_from_arb \ --output-dir=lib/l10n --no-use-deferred-loading \ lib/main.dart lib/l10n/intl_*.arb
Windows 不支援檔案名稱萬用字元。 請改為列出由
intl_translation:extract_to_arb
命令產生的 .arb 檔案。dart run intl_translation:generate_from_arb \ --output-dir=lib/l10n --no-use-deferred-loading \ lib/main.dart \ lib/l10n/intl_en.arb lib/l10n/intl_fr.arb lib/l10n/intl_messages.arb
DemoLocalizations
類別會使用產生的initializeMessages()
函式(在intl_messages_all.dart
中定義)載入本地化訊息,並使用Intl.message()
來查詢它們。
更多資訊
#如果您透過閱讀程式碼學習效果最好,請查看以下範例。
minimal
minimal
範例的設計目標是盡可能簡單。intl_example
使用intl
套件提供的 API 和工具。
如果您不熟悉 Dart 的 intl
套件,請查看使用 Dart intl 工具。
除非另有說明,否則本網站上的文件會反映 Flutter 的最新穩定版本。頁面上次更新時間:2024-10-24。 檢視原始碼 或 回報問題。