如果您的應用可能會部署給講其他語言的使用者,那麼您需要對其進行國際化。這意味著您需要以一種方式編寫應用,使其能夠針對應用支援的每種語言或區域設定對文字和佈局等值進行本地化。Flutter 提供了有助於國際化的小部件和類,Flutter 庫本身也經過國際化。

本頁介紹了使用 MaterialAppCupertinoApp 類本地化 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,請將該包作為依賴項新增到您的 pubspec.yaml 檔案中,以及 intl

flutter pub add flutter_localizations --sdk=flutter
flutter pub add intl:any

這將建立一個包含以下條目的 pubspec.yml 檔案

yaml
dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter
  intl: any

然後匯入 flutter_localizations 庫,併為您的 MaterialAppCupertinoApp 指定 localizationsDelegatessupportedLocales

dart
import 'package:flutter_localizations/flutter_localizations.dart';
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 包並新增前面的程式碼後,MaterialCupertino 包現在應該在 115 個支援的區域設定之一中正確本地化。小部件應該適應本地化訊息,並具有正確的從左到右或從右到左的佈局。

嘗試將目標平臺的區域設定切換到西班牙語 (es),訊息應該會本地化。

基於 WidgetsApp 的應用程式類似,只是不需要 GlobalMaterialLocalizations.delegate

首選完整的 Locale.fromSubtags 建構函式,因為它支援 scriptCode,儘管 Locale 預設建構函式仍然完全有效。

localizationsDelegates 列表的元素是生成本地化值集合的工廠。GlobalMaterialLocalizations.delegate 為 Material Components 庫提供本地化字串和其他值。GlobalWidgetsLocalizations.delegate 為小部件庫定義預設文字方向,即從左到右或從右到左。

本頁介紹了有關這些應用程式屬性、它們所依賴的型別以及 Flutter 國際化應用程式通常如何構造的更多資訊。

覆蓋區域設定

#

Localizations.overrideLocalizations 小部件的工廠建構函式,它允許(通常很少見)應用程式的某個部分需要本地化到與裝置配置的區域設定不同的區域設定。

要觀察此行為,請新增對 Localizations.override 和一個簡單的 CalendarDatePicker 的呼叫

dart
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 小部件應以西班牙語重新渲染。

新增您自己的本地化訊息

#

新增 flutter_localizations 包後,您可以配置本地化。要將本地化文字新增到您的應用程式,請完成以下說明

  1. 新增 intl 包作為依賴項,引入 flutter_localizations 鎖定的版本

    flutter pub add intl:any
  2. 開啟 pubspec.yaml 檔案並啟用 generate 標誌。此標誌位於 pubspec 檔案中的 flutter 部分。

    yaml
    # The following section is specific to Flutter.
    flutter:
      generate: true # Add this line
  3. 將一個新的 yaml 檔案新增到 Flutter 專案的根目錄。將此檔案命名為 l10n.yaml 幷包含以下內容

    yaml
    arb-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 檔案中生成本地化。
  4. ${FLUTTER_PROJECT}/lib/l10n 中,新增 app_en.arb 模板檔案。例如

    json
    {
      "helloWorld": "Hello World!",
      "@helloWorld": {
        "description": "The conventional newborn programmer greeting"
      }
    }
  5. 在同一目錄中新增另一個名為 app_es.arb 的包檔案。在此檔案中,新增相同訊息的西班牙語翻譯。

    json
    {
        "helloWorld": "¡Hola Mundo!"
    }
  6. 現在,執行 flutter pub getflutter run,程式碼生成會自動進行。您應該在您使用 arb-diroutput-dir 選項指定的路徑的目錄中找到生成的檔案。或者,您也可以執行 flutter gen-l10n 以在不執行應用程式的情況下生成相同的檔案。

  7. 在您呼叫 MaterialApp 的建構函式時,新增 app_localizations.dartAppLocalizations.delegate 的匯入語句

    dart
    import 'l10n/app_localizations.dart';
    dart
    return 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 類還提供自動生成的 localizationsDelegatessupportedLocales 列表。您可以使用這些而不是手動提供它們。

    dart
    const MaterialApp(
      title: 'Localizations Sample App',
      localizationsDelegates: AppLocalizations.localizationsDelegates,
      supportedLocales: AppLocalizations.supportedLocales,
    );
  8. Material 應用程式啟動後,您可以在應用程式中的任何位置使用 AppLocalizations

    dart
    appBar: 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 小部件;如果目標裝置的區域設定設定為西班牙語,則顯示“¡Hola Mundo!”。在 arb 檔案中,每個條目的鍵用作 getter 的方法名稱,而該條目的值包含本地化訊息。

gen_l10n_example 使用此工具。

要本地化您的裝置應用描述,請將本地化字串傳遞給 MaterialApp.onGenerateTitle

dart
return MaterialApp(
  onGenerateTitle: (context) => DemoLocalizations.of(context).title,

佔位符、複數和選擇

#

您還可以使用特殊語法在訊息中包含應用程式值,該語法使用**佔位符**生成方法而不是 getter。佔位符必須是有效的 Dart 識別符號名稱,它將成為 AppLocalizations 程式碼中生成方法的位置引數。透過將其括在花括號中來定義佔位符名稱,如下所示

json
"{placeholderName}"

在應用程式的 .arb 檔案中的 placeholders 物件中定義每個佔位符。例如,要定義一個帶有 userName 引數的問候訊息,請將以下內容新增到 lib/l10n/app_en.arb

json
"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 的程式碼替換為以下內容

dart
// 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 欄位。以下示例顯示了可用的選項

json
"{countPlaceholder, plural, =0{message0} =1{message1} =2{message2} few{messageFew} many{messageMany} other{messageOther}}"

前面的表示式被訊息變體(message0message1、...)替換,該變體對應於 countPlaceholder 的值。只需要 messageOther 欄位。

以下示例定義了一條將單詞“wombat”複數化的訊息

json
"nWombats": "{count, plural, =0{no wombats} =1{1 wombat} other{{count} wombats}}",
"@nWombats": {
  "description": "A plural message",
  "placeholders": {
    "count": {
      "type": "num",
      "format": "compact"
    }
  }
}

透過傳入 count 引數使用複數方法

dart
// 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 佔位符選擇一個值。這最常用於支援帶性別的語言。語法如下

json
"{selectPlaceholder, select, case{message} ... other{messageOther}}"

下一個示例定義了一條根據性別選擇代詞的訊息

json
"pronoun": "{gender, select, male{he} female{she} other{they}}",
"@pronoun": {
  "description": "A gendered message",
  "placeholders": {
    "gender": {
      "type": "String"
    }
  }
}

透過將性別字串作為引數傳遞來使用此功能

dart
// 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”。

轉義語法

#

有時,您必須將令牌(例如 {})用作普通字元。要忽略此類令牌的解析,請透過將以下內容新增到 l10n.yaml 來啟用 use-escaping 標誌

yaml
use-escaping: true

解析器會忽略任何用一對單引號括起來的字串。要使用普通的單引號字元,請使用一對連續的單引號。例如,以下文字轉換為 Dart String

json
{
  "helloWorld": "Hello! '{Isn''t}' this a wonderful day?"
}

結果字串如下

dart
"Hello! {Isn't} this a wonderful day?"

包含數字和貨幣的訊息

#

數字,包括代表貨幣值的數字,在不同的區域設定中顯示方式差異很大。flutter_localizations 中的本地化生成工具使用 intl 包中的 NumberFormat 類根據區域設定和所需格式格式化數字。

intdoublenum 型別可以使用以下任何 NumberFormat 建構函式

訊息“格式”值1200000 的輸出
緊湊“1.2M”
緊湊貨幣*“$1.2M”
緊湊簡單貨幣*“$1.2M”
緊湊長“120 萬”
貨幣*“USD1,200,000.00”
小數模式"1,200,000"
小數模式數字*"1,200,000"
小數百分比模式*"120,000,000%"
百分比模式"120,000,000%"
科學模式“1E6”
簡單貨幣*"$1,200,000"

表中帶星號的 NumberFormat 建構函式提供可選的命名引數。這些引數可以指定為佔位符的 optionalParameters 物件的值。例如,要為 compactCurrency 指定可選的 decimalDigits 引數,請對 lib/l10n/app_en.arb 檔案進行以下更改

json
"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 格式化

json
"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”。

dart
AppLocalizations.of(context).helloWorldOn(DateTime.utc(1959, 7, 9))

iOS 本地化:更新 iOS 應用包

#

儘管本地化由 Flutter 處理,但您需要在 Xcode 專案中新增支援的語言。這可確保您在 App Store 中的條目正確顯示支援的語言。

要配置您的應用程式支援的區域設定,請使用以下說明

  1. 開啟您專案的 ios/Runner.xcodeproj Xcode 檔案。

  2. 在**專案導航器**中,選擇**專案**下的 Runner 專案檔案。

  3. 在專案編輯器中選擇 Info 選項卡。

  4. 在**本地化**部分,單擊 新增 按鈕 (+) 將支援的語言和區域新增到您的專案。當要求選擇檔案和參考語言時,只需選擇 完成

  5. Xcode 會自動建立空的 .strings 檔案並更新 ios/Runner.xcodeproj/project.pbxproj 檔案。這些檔案由 App Store 用於確定您的應用程式支援哪些語言和區域。

進一步自定義的高階主題

#

本節介紹了自定義本地化 Flutter 應用程式的其他方法。

高階區域設定定義

#

某些具有多個變體的語言需要不僅僅是語言程式碼才能正確區分。

例如,要完全區分所有中文變體,需要指定語言程式碼、指令碼程式碼和國家/地區程式碼。這是由於存在簡體和繁體指令碼,以及同一種指令碼型別內字元書寫方式的區域差異。

為了完全表達國家程式碼 CNTWHK 的所有中文變體,支援的區域設定列表應包括

dart
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_FRfr_CA))也應完全區分,以實現更細緻的本地化。

跟蹤區域設定:Locale 類和 Localizations 小部件

#

Locale 類標識使用者的語言。移動裝置支援為所有應用程式設定區域設定,通常使用系統設定選單。國際化應用程式透過顯示區域設定特定的值來響應。例如,如果使用者將裝置的區域設定從英語切換到法語,那麼最初顯示“Hello World”的 Text 小部件將以“Bonjour le monde”重建。

Localizations 小部件為其子項及其依賴的本地化資源定義區域設定。WidgetsApp 小部件建立 Localizations 小部件,並在系統區域設定更改時重建它。

您始終可以使用 Localizations.localeOf() 查詢應用程式的當前區域設定

dart
Locale myLocale = Localizations.localeOf(context);

指定應用的 supportedLocales 引數

#

儘管 flutter_localizations 庫目前支援 115 種語言和語言變體,但預設情況下僅提供英語翻譯。由開發人員決定具體支援哪些語言。

MaterialAppsupportedLocales 引數限制了區域設定的更改。當用戶更改其裝置上的區域設定時,應用程式的 Localizations 小部件僅在新區域設定是此列表的成員時才遵循。如果未找到裝置區域設定的精確匹配項,則使用第一個具有匹配 languageCode 的支援區域設定。如果失敗,則使用 supportedLocales 列表的第一個元素。

希望使用不同“區域設定解析”方法的應用程式可以提供 localeResolutionCallback。例如,要讓您的應用程式無條件接受使用者選擇的任何區域設定

dart
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 Web 中延遲載入每個區域設定。

這可以透過減小 JavaScript 包的大小來縮短 Web 應用程式的初始啟動時間。當此標誌設定為 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 是否可為空。

預設情況下,此值為 true,以便 Localizations.of(context) 返回一個可為空的值以實現向後相容性。如果此值為 false,則會對 Localizations.of(context) 的返回值執行空檢查,從而無需在使用者程式碼中進行空檢查。
[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 小部件的區域設定更改時),其依賴上下文會重建。

本地化值由 Localizations 小部件的 LocalizationsDelegate 列表載入。每個委託必須定義一個非同步 load() 方法,該方法生成一個封裝本地化值集合的物件。通常這些物件為每個本地化值定義一個方法。

在大型應用程式中,不同的模組或包可能會捆綁自己的本地化。這就是 Localizations 小部件管理一個物件表的原因,每個 LocalizationsDelegate 對應一個物件。要檢索由 LocalizationsDelegateload 方法之一生成的物件,請指定一個 BuildContext 和物件的型別。

例如,Material Components 小部件的本地化字串由 MaterialLocalizations 類定義。此類的例項由 MaterialApp 類提供的 LocalizationDelegate 建立。它們可以使用 Localizations.of() 檢索

dart
Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);

這個特定的 Localizations.of() 表示式經常使用,因此 MaterialLocalizations 類提供了一個方便的簡寫

dart
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() 來查詢它們。

dart
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 包的類匯入一個生成的 message catalog,該 catalog 提供 initializeMessages() 函式和 Intl.message() 的每個區域設定後端儲存。message catalog 由 intl 工具生成,該工具分析包含 Intl.message() 呼叫的類的原始碼。在這種情況下,它只是 DemoLocalizations 類。

新增對新語言的支援

#

需要支援不包含在 GlobalMaterialLocalizations 中的語言的應用程式必須做一些額外的工作:它必須為單詞或短語以及區域設定的日期模式和符號提供大約 70 個翻譯(“本地化”)。

請參閱以下示例,瞭解如何新增對挪威新挪威語的支援。

一個新的 GlobalMaterialLocalizations 子類定義了 Material 庫所依賴的本地化。還必須定義一個新的 LocalizationsDelegate 子類,該子類充當 GlobalMaterialLocalizations 子類的工廠。

這是完整 add_language 示例的原始碼,減去實際的新挪威語翻譯。

區域設定特定的 GlobalMaterialLocalizations 子類名為 NnMaterialLocalizationsLocalizationsDelegate 子類名為 _NnMaterialLocalizationsDelegateNnMaterialLocalizations.delegate 的值是委託的一個例項,是使用這些本地化的應用程式所需的一切。

委託類包括基本的日期和數字格式本地化。所有其他本地化都由 NnMaterialLocalizations 中的 String 值屬性 getter 定義,如下所示

dart
@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',因為有時字串包含帶有 $ 字首的變數。變數由引數化本地化方法展開

dart
@override
String get pageRowsInfoTitleRaw => r'$firstRow–$lastRow of $rowCount';

@override
String get pageRowsInfoTitleApproximateRaw =>
    r'$firstRow–$lastRow of about $rowCount';

還需要指定區域設定的日期模式和符號,它們在原始碼中定義如下

dart
const nnLocaleDatePatterns = {
  'd': 'd.',
  'E': 'ccc',
  'EEEE': 'cccc',
  'LLL': 'LLL',
  // ...
}
dart
const nnDateSymbols = {
  'NAME': 'nn',
  'ERAS': <dynamic>['f.Kr.', 'e.Kr.'],

這些值需要針對區域設定進行修改,以使用正確的日期格式。不幸的是,由於 intl 庫對於數字格式不具有相同的靈活性,因此必須在 _NnMaterialLocalizationsDelegate 中使用現有區域設定的格式作為替代

dart
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

實現語言特定的 GlobalMaterialLocalizationsLocalizationsDelegate 子類後,您需要將語言和委託例項新增到您的應用程式。以下程式碼將應用程式的語言設定為新挪威語,並將 NnMaterialLocalizations 委託例項新增到應用程式的 localizationsDelegates 列表

dart
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 類直接在每個語言 Map 中包含其所有翻譯

dart
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,因為不需要進行非同步載入。

dart
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 需要兩個步驟。

  1. 以應用程式的根目錄作為當前目錄,從 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 格式的對映,其中包含 main.dart 中定義的每個 Intl.message() 函式的一個條目。此檔案用作英語和西班牙語翻譯 intl_en.arbintl_es.arb 的模板。這些翻譯由您(開發人員)建立。

  2. 以應用程式的根目錄作為當前目錄,為每個 intl_<locale>.arb 檔案和 intl_messages_all.dart 生成 intl_messages_<locale>.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() 查詢它們。

更多資訊

#

如果您透過閱讀程式碼學得最好,請檢視以下示例。

如果您不熟悉 Dart 的 intl 包,請檢視 使用 Dart intl 工具