iT邦幫忙

2024 iThome 鐵人賽

DAY 22
0

若你的應用程式會發佈給一些使用其他語言的使用者,那麼就會需要支援各種語系。這表示我們需要讓應用程式支援其他語言或本地化呈現的資料。Flutter 當然提供了支援國際化的組件和類別且 Flutter 的函式庫本身就是國際化的。

下面我們會介紹相關概念和實作以讓我們的應用程式實現本地化,這裡主要會使用 MaterialAppCupertinoApp 類別來實現,大部分的應用都是使用上面 2 者來處理多國語系,但底層的 WidgetApp 也可以實作相同的邏輯。

Flutter 本地化

首先,我們須執行 flutter_localizations 相關設定。預設情況 Flutter 僅提供英文語系,為了支援其他語系我們必須在 MaterialAppCupertinoApp 組件加入其他屬性設定,並且使用 flutter_localizations 套件。截止 2023 年底,該套件支援 大約 100 多種語言

我們先建立一個範例專案:

$ flutter create i18n_demo

接著安裝 flutter_localizationsintl

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

或者在 pubspec.yaml 設定

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

上面指令加上 --sdk 是因為這是從 Fluttter 框架安裝而不是從 pub.dev 安裝。而 any 的部分則是任意版本皆可。

然後我們開啟我們的專案,在 main.dart 匯入 flutter_localizations 並且為 MaterialApp 或者 CupertinoApp 加入 localizationsDelegatessupportedLocales

@override
Widget build(BuildContext context) {
  return const MaterialApp(
    title: '多國語系應用',
    localizationsDelegates: [
      GlobalMaterialLocalizations.delegate,
      GlobalWidgetsLocalizations.delegate,
      GlobalCupertinoLocalizations.delegate,
    ],
    supportedLocales: [
      Locale('en'),
      Locale('es'),
      Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: 'TW'),
    ],
    home: MyHomePage(),
  );
}

在匯入 flutter_localizations 套件並且加入上面程式碼之後,MaterialCupertino 套件中的組件現在應可在 100 多種支援的區域設定其中一種本地化除了語系還有排版例如由右到左閱讀的排版。

基於 WidgetApp 的使用也類似,除了不需要 GlobalMaterialLocalizations.delegate

另外,雖然上面使用了預設的 Locale 建構子可以滿足多數情況,不過還是建議使用 Locale.fromSubtags ,因為它支持 scriptCode

localizationsDelegates 列表包含本地化資源的委派處理器,其概念類似於工廠模式,但專門用於異步載入和管理本地化資源,作用就是產生本地化「值」的集合。GlobalMaterialLocalizations.delegate 提供字串等給 Material 組件,GlobalCupertinoLocalizations.delegate 則是 Cupertino 組件,GlobalWidgetsLocalizations.delegate 負責處理預設文案的方向,右到左或左到右。

覆寫語系相關

Localizations.override 是一個 Localizations 組件的工廠建構子。它用來處理一種不太常見的情況;當我們應用程式中某部分需要和裝置設定不同的語言或地區設定時。

例如:手機設定為中文,但你希望程式中的日曆顯示英文。這個時候就可以使用 Localizations.override

要觀察其行為,我們可以呼叫 Localizations.override 搭配 CalendarDatePicker

Widget build(BuildContext context) {
  return Scaffold(
  	appBar: AppBar(
    	title: Text(widget.title),
    ),
    body: Center(
    	child: Column(
      	mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Localizations.override(
          	context: context,
            locale: const Locale("en"),
            // 使用 Builder 取得當前的 BuildContext
            child: Builder(
            	builder: (context) {
                return CalendarDatePicker(
                	initialDate: DateTime.now(),
                  firstDate: DateTime(1900),
                  lastDate: DateTime(2100),
                  onDateChanged: (value) {},
                );
              }
            ),
          ),
          
        ]
      ),
    ),
  );
}

如果不使用 Builder 直接讓 CalendarDatePicker 作為子組件可能會導致本地化無法正常,又或者你可以建立一個新組件

class LocalizedCalendar extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // 這個 context 現在包含了 Localizations.override 的設定
    return CalendarDatePicker(
      initialDate: DateTime.now(),
      firstDate: DateTime(1900),
      lastDate: DateTime(2100),
      onDateChanged: (value) {},
    );
  }
}

概念上就是我們傳入舊的 context 覆寫後需要一種方式取得新的 context

加入本地化訊息

在安裝 flutter_localizations 套件之後,你可以設定字典檔:

  1. 安裝 intl 套件

  2. 開啟 pubspec.yaml 啟動 generate

    flutter:
      uses-material-design: true
      generate: true // <- 新增此行
    
  3. 在專案根目錄新增 l10n.yaml 並設定如下:

    arb-dir: lib/l10n
    template-arb-file: app_en.arb
    output-localization-file: app_localizations.dart
    

    這個檔案用來設定本地化工具,在這個例子中,我們完成了以下幾件事:

    • 我們資源檔 Application Resource Bundle .arb 檔案設定在 lib/l10n 資料夾底下。
    • 英文字典檔命名為 app_en.arb
    • Flutter 根據我們提供的 .arb 自動生成 app_localizations.dart 檔案

    工作流程為:開發者創建 ARB 文件,Flutter 的工具讀取這些文件,自動生成 Dart 代碼,使得這些翻譯可以在應用中使用。

  4. lib/l10n 目錄下,加入 app_en.arb 樣板檔案:

    {
      "helloWorld": "Hello World!",
      "@helloWorld": {
        "description": "The conventional newborn programmer greeting"
      }
    }
    
  5. 加入其他語系檔 app_es.arbapp_zh.arb

    {
        "helloWorld": "哈囉,世界!"
    }
    

    上面帶 @ 的鍵是用來加入更多 meta 資訊。

  6. 現在執行 flutter pub getflutter run 你應該會在 .dart_tool/flutter_gen/gen_l10n 目錄下產生檔案。又或者可以使用 flutter gen-l10n

  7. 在程式中匯入 app_localizations.dartAppLocalizations.delegate

    import 'package:flutter_gen/gen_l10n/app_localizations.dart';
    // ...
    
    return const MaterialApp(
      title: 'Localizations Sample App',
      localizationsDelegates: [
        AppLocalizations.delegate, // 加入此行
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      supportedLocales: [
        Locale('en'), // English
        Locale('es'), // Spanish
      ],
      home: MyHomePage(),
    );
    

    AppLocalizations 類別除了 delegate 之外也自動提供 localizationsDelegatessupportedLocales 列表,你可以使用這些列表,而不需要手動提供它們。

    const MaterialApp(
      title: '多語系',
      localizationsDelegates: AppLocalizations.localizationsDelegates,
      supportedLocales: AppLocalizations.supportedLocales,
    );
    
  8. 最後,我們可以在 MaterialApp 下任何地方使用:

    appBar: AppBar(
      title: Text(AppLocalizations.of(context)!.helloWorld),
    ),
    

    MaterialApp 必須實例化且初始化 AppLocalizations 否則會造成 null 例外。

上面程式碼加入 Text 組件,如果目標裝置的語言設置為英語,它會顯示 "Hello World!";如果設置為西班牙語,則顯示 "¡Hola Mundo!"。在 arb 文件中,每個項目的「鍵」被用作 getter 方法。

Placeholder、複數、選擇語句

若你使用 VS Code 可以安裝 arb-editor ,該擴展套件可以支援 .arb 語法高亮、診斷、快速修正等。

你還可以在翻譯訊息中使用特殊語法代入應用程式中動態的「值」,使用佔位符而不只是單純的 getter。不過這個佔位符號必須是個有效的變數,因為它會成為 AppLocalizations 方法中的一個位置參數。簡單來說,佔位符就是在翻譯文本中留下的"空位",讓你可以在實際使用時填入動態的內容。

{
  "greeting": "Hello, {name}!"
}

上面範例是在 .arb 檔案中,定義佔位符。語法就是使用 {}
後續在程式碼中,可以如下代入值:

AppLocalizations.of(context)!.greeting('andyyou')

除此之外,還可以使用數字化的佔位符來指定多個值。不同的語言有不同組織「複數」單字的方式。這個語法同時支援設定如何呈現。一個複數單字須包含數字參數來判定如何顯示文字。例如英文中複數的 “person” 為 "people",但不只如此。 message0 複數可能是 “no people” 或 "zero people" 。一些的複數 messageFew 可能是 “several people” 或 “some people”

{
	"nCats": "{count, plural, =0{no cats} =1{1 cat} other{{count} cats}} !",
}
Text(AppLocalizations.of(context)!.nCats(0)) // no cats !
Text(AppLocalizations.of(context)!.nCats(1)) // 1 cat !
Text(AppLocalizations.of(context)!.nCats(2)) // 2 cats !

類似複數形式,我們也可以基於 String 佔位符選擇「值」。這常用來支援性別區分的語言例如法文、西班牙文

{
  "greeting": "{gender, select, male{歡迎先生} female{歡迎女士} other{歡迎}}"
}
Text(AppLocalizations.of(context)!.greeting('male')) // 歡迎先生
Text(AppLocalizations.of(context)!.greeting('female')) // 歡迎女士

注意,使用 select 語句時,參數和實際值之間是區分大小寫。也就是說,AppLocalizations.of(context)!.greeting("Male") 為「other」情況,並傳回「歡迎」。

脫逸語法

有時候,你需要將 {} 這樣的符號當作普通字元來使用 。為了避免符號被錯誤解析,這個時候可以在 l10n.yaml 增加設定:

use-escaping: true

此時解析器會忽略任何被使用單引號包起來的字串。此時要使用一般單引號需要連續兩個 ''

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

輸出:Hello! {Isn't} this a wonderful day?

數字和貨幣

數字包含表示貨幣的數字,在不同地區中顯示方式也非常不同。flutter_localizations 的生成工具使用 intl 中的 NumberFormat 類別進行基於本地的格式化。

int double number 型別可以使用 NumberFormat 任意的建構子函式。這裡以數字 1200000 為例展示不同的輸出結果:

格式方法 描述 輸出範例 (1200000)
compact 簡潔格式 "1.2M"
compactCurrency* 簡潔貨幣格式 "$1.2M"
compactSimpleCurrency* 簡化的簡潔貨幣格式 "$1.2M"
compactLong 長格式的簡潔表示 "1.2 million"
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"

上面表格中標註 * 的,表示提供可選或具名參數。這些參數可以設定佔位符的 optionalParameters 物件值。例如, compactCurrency 指定可選的 decimalDigits 參數

"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 專案的 Info.plist 檔案中應會定義關鍵的應用程式設定資料,包含支援的語系。如果要設定支援的語系須依照下面步驟設定:

  1. 使用 Xcode 開啟 ios/Runner.xcworkspace
  2. Runner 下開啟 Info.plist
  3. 選擇 Information Property List。然後從編輯器選擇 + 或者有件 「Add Row」
  4. 選擇 「Localizations」,然後加入你要支援的語系。這個列表應和 supportedLocales 一致
  5. 加入所有支援語系之後存檔。

進階本地化設定

一些語系具有多種變體,除了語系代碼之後還需要其他設定來正確區分。

例如:中文就包含多種變體需要額外指定語言代碼、腳本代碼和國家代碼

supportedLocales: [
  const Locale.fromSubtags(languageCode: 'zh'), // 通用中文 'zh'
  const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans'), // 通用簡體中文 'zh_Hans'
  const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant'), // 通用繁體中文 '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 會選擇最接近的,但可能和使用者期望的不同。

除了我們舉例的中文,其他如法語 fr_FR fr_CA 也應該區分。

取得語系:Locale 和 Localizations 組件

Locale 類別可以識別使用者的語系。行動裝置可以為全部應用程式設定語系,通常使用系統設定選單來設定。

支援多個國家的應用程式通常會根據使用者的語言設定,自動顯示對應語言的內容。

Localizations 組件則是為子組件定義了支援的語言設定和翻譯資源。WidgetsApp 組件會建立一個 Localizations 組件,並在系統語言變更時重新建立它。

你可以使用下面程式碼查詢應用程式目前的語言設定:

Locale currentLocale = Localizations.localeOf(context);

應用程式的 supportedLocales 參數

儘管 flutter_localizations 庫目前支援 100 多種語言和語言變體,但預設只提供英語翻譯。開發者需要決定具體支援哪些語言。

MaterialAppsupportedLocales 參數限制了語系的支援。當使用者變更裝置的語系時,應用程式的 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 生成本地化類別的目錄。只有在當你想在專案其他地方產生類別才需要設定。且還需要設定 synthetic-package 為 false。匯入時除了目錄還需要搭配 output-localization-file 的檔名。如果沒有設定預設跟 arb-dir 一樣
template-arb-file 模板 arb 檔案作為產生 Dart 字典檔的基礎。預設為 app_en.arb
output-localization-file 輸出 delegate 類別的檔名,預設為 app_localizations.dart
untranslated-messages-file 列出尚未翻譯檔案資訊。這個選項會在指定路徑建立 JSON 檔案內容就是未翻譯檔案資訊。如果沒有設定,則在指令 Console 介面輸出未翻譯訊息的摘要。
output-class 設定 Localization 和 Localization Delegate 的 Dart 類別名稱。預設為 AppLocalizations
preferred-supported-locales 偏好支援的語系列表。預設,工具會依據字母順序產生語系列表,使用此設定預設語系,例如 en_US 可以在裝置支援的情況下預設為美式英文。
header 預先生成到 Dart 本地化檔案的開頭加入內容。通常會用來加入版權聲明。例如 /// Copyright @ xxxx 所有產生的檔案都會加入。另外還可以使用 header-file 選項來傳入比較長的內容。
header-file 如同上面說明,可以將內容存放在一個檔案。然後指定這個檔案的內容會加入檔頭。
[no-]use-deferred-loading 設定是否支援延遲匯入,支援 Flutter Web 延遲載入。這可以有效減少 web 初始化時載入的 JavaScript 的大小
gen-inputs-and-outputs-list 當設定時,工具會產生一個 JSON 檔案,包含工具的輸入輸出,預設 gen_l10n_inputs_and_outputs.json。這可以用來追蹤專案中最新產生的檔案。Flutter 工具會使用此檔案來確認 Hot Relaod 期間何時呼叫 gen_l10n 。此選項的值為 JSON 的目錄。當為 null 時,不會生成。
synthetic-package 決定生成檔案時事合成還是在指定路徑
project-dir 設定時,工具使用此選項的值作為根目錄路徑
[no-]required-resource-attributes 全部資源 ID 須包含對應屬性。預設簡單的訊息不會需要額外的資訊,但推薦使用。
{
  "welcomeMessage": "歡迎使用我們的應用!",
  "@welcomeMessage": {
    "description": "顯示在應用首頁的歡迎文字"
  }
}
選項 說明
[no-]nullable-getter 設定 Localizations 類別的 getter 是否 nullable。預設是 nullable
[no-]format 設定時,生成檔案後要執行 dart format 指令。簡單說就是要不要自動為生成的檔案格式化。
use-escaping 是否支援單引號脱逸語法
[no-]suppress-warnings 是否關閉警告訊息的輸出

Fluuter 語系的運作原理

下面我們會說明 Flutter 支援多語系的運作機制。如果你預計會支援自己自訂的字典檔下面的內容可能對你有幫助。

載入和檢索本地化值

Localizations 組件是用來載入以及搜尋本地化資源的物件。應用程式通常使用 Localizations.of(context, type) 來取得這些物件。如果裝置變更語系,Localizations 組件會自動載入新的語系資源並重新渲染使用這些值的組件。這是因為 Localizations 的運作方式類似於 InheritedWidget 。當 build 函式引用 InheritedWidget 時,就會建立一個依賴關係。當 InheritedWidget 改變時(例如 Localizations 組件的語言設定變更),所有依賴它的組件都會重新渲染。

本地化資源使用 Localizations 組件的 LocalizationsDelegate 列表載入。每一個 delegate 必須定義非同步的 load() 方法生成本地化資源集合的物件。通常,每一個值都會定義一個方法。

在大型應用程式中,不同模組或套件可能封裝自己的本地化資源。這也是為何 Localizations 組件需要管理物件表,每一個 LocalizationsDelegate 一個表格。

要取得由 LocalizationsDelegateload 方法生成的物件,需要指定 BuildContext 和物件型別

舉例來說 Material 組件的本地化字串,定義在 MaterialLocalizations 類別中。這個類別由 MaterialApp 類別的 LocalizationDelegate 建立。可以使用 Localizations.of() 取得。

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

由於 Localizations.of() 使用頻繁,因此 MaterialLocalizations 類別提供了方便簡短的語法:

static MaterialLocalizations of(BuildContext context) {
  return Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);
}

tooltip: MaterialLocalizations.of(context).backButtonTooltip,

為應用程式本地化資源定義類別

除了上面通用的方式取得語系物件外,通常支援多語系的應用程式會封裝自己的語系資源。簡單的說下面範例介紹了自訂類似 MaterialLocalizations 的類別。

這個範例使用 intl 套件提供的 API 和工具。下面「本地化資源替代類別」會介紹其他非 intl 套件的方式。

DemoLocalizations 類別包含了應用程式使用的內容翻譯。通過 initializeMessages() 使用 intl 的功能生成。

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:intl/intl.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 localName = Intl.cannonicalizedLocale(name);
    return initializeMessages(localeName).then((_) {
      return DemoLocalizations(localeName);
    });
  }
  
  static DemoLocalizations of(BuildContext context) {
    return Localizations.of<DemoLocalizations>(context, DemoLocalizations)!;
  }
  
  String get title {
    return Intl.message(
      'Hello World',
      name: 'title',
      desc: 'Title for the Demo application',
      locale: localeName,
    );
  }
}

intl 套件會導入一個自動生成包含所有翻譯文字的檔案,這個目錄提供了 initializeMessage()Intl.message() 。簡單來說,DemoLocalizations 類別定義了全部所需的文字,intl 工具會掃描這個類別,找出全部需要翻譯的文字,進而生成字典檔。當使用 initializeMessages() 時會載入翻譯。

彙整來說:

  • arb: 其他語言或框架中的字典檔,各語言的翻譯內容
  • Intl.message :標記須翻譯的「鍵」,去 arb 查對照翻譯
  • Localizations.of 取得當前語系下對應翻譯的物件
  • 自訂 Localizations:組織管理全部的翻譯。簡化語法,不用每次都呼叫 Intl.messageLocalizations.of

Intl 負責提供字符串轉換,Localizations 管理組件結構,處理動態語言切換和相應的 UI 更新。

加入新語言

如果你的應用程式要加入 GlobalMaterialLocalizations 不支援的語系,則需要額外的處理。須為該新語言提供大約 70 個翻譯還有語言的日期格式等。下面示範如何新增支援挪威尼諾斯克語(Norwegian Nynorsk)。

一個新的 GlobalMaterialLocalizations 子類別定義 Material 函式庫需要的翻譯。LocalizationsDelegate 的子類別,該子類作為 GlobalMaterialLocalizations 子類的工廠。

具體實作請參考範例Material and Cupertino Libraries Localizations

本地化資源替代類別

本節將介紹不同實現多語系的方式。

上面的範例通過 Intl 套件,為了簡化或者可能為了與其他 i18n 框架整合,你可以選擇自己的方法來管理本地化的值。

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

Dart intl 工具

Dart intl 工具是處理多語言翻譯的強大助手。它能夠從我們的程式碼中識別並提取需要翻譯的文本。

在開發初期,我們可以專注於功能實現,使用單一語言(通常是英文)編寫界面文本。例如,直接使用 Text('Hello')Text('Welcome to my app')

當需要支援多語言時,我們需要稍作調整,將普通的文本替換為 Intl 的特殊語法。例如,將 Text('Hello') 改寫為 Text(Intl.message('Hello', name: 'helloText', desc: 'Greeting'))

接下來,我們使用 extract_to_arb 命令掃描代碼,提取所有使用 Intl.message() 的文本,並生成一個包含所有待翻譯文本的 ARB 文件。

流程主要包含兩個步驟:

  1. 提取需要翻譯的文本,例如為我們的 lib/main.dart 提取:

     $ dart run intl_translation:extract_to_arb --output-dir=lib/l10n lib/main.dart
    

    此命令會從 main.dart 中提取所有標記的文本,並在 lib/l10n 目錄下生成 ARB 文件。

  2. 為每個語系生成 Dart 文件

    $ dart run intl_translation:generate_from_arb \
        --output-dir=lib/l10n --no-use-deferred-loading \
        lib/main.dart lib/l10n/intl_*.arb
    

    這個步驟會為每個 intl_<locale>.arb 文件生成對應的 messages_<locale>.dart 文件。同時會生成 messages_all.dart,它導入了所有語言的消息文件。

總結

總結來說 Flutter 多語系支援的核心概念:

  1. 使用 flutter_localizations 包來啟用多語系支援。
  2. MaterialAppCupertinoApp 中設置 localizationsDelegatessupportedLocales
  3. 建立 ARB 文件 (.arb) 存放不同語言的翻譯文本。
  4. 使用 flutter gen-l10n 指令生成 Dart 程式碼。
  5. 在程式碼中使用 AppLocalizations.of(context).messageKey 來取得翻譯文本。
  6. 可以使用佔位符、複數和選擇語句來處理複雜的翻譯場景。
  7. 對於特殊需求,可以自定義 LocalizationsDelegate

最後我們介紹了 Dart intl 工具,協助我們更有效率的開發。


上一篇
Day 21 動畫
下一篇
Day 23 處理 API 請求
系列文
Flutter 開發實戰 - 30 天逃離新手村26
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言