Flutter 國際化 (i18n) 完全指南:從入門到精通
在現代移動應用開發中,支持多語言是觸達全球用戶的基本要求。Flutter 提供了強大且靈活的國際化 (i18n) 和本地化 (l10n) 支持。本文將帶你從零開始,一步步深入掌握在 Flutter 中實現國際化的幾種主流方法。
目錄
- 核心概念
- 準備工作:添加依賴
- 方法一:使用 flutter_localizations 基礎方案
- 方法二:使用 intl 包和 ARB 文件(推薦)
- 方法三:使用 easy_localization 第三方庫
- 動態切換語言
- 處理語言環境相關的數據
- 最佳實踐與總結
核心概念
· 國際化 (Internationalization, i18n): 指在應用設計和開發過程中,使其能夠輕松適配不同語言和地區的流程。它是在開發階段完成的。
· 本地化 (Localization, l10n): 指為國際化的應用添加特定語言環境(Locale)的翻譯和格式的過程。它是在國際化之后進行的。
· Locale: 是一個用于標識用戶語言和地區偏好的對象,例如 en-US(美國英語)、zh-CN(簡體中文)、zh-TW(繁體中文)。
Flutter 中的國際化主要涉及:
- 為文本(字符串)提供多種語言的翻譯。
- 格式化地區相關的數據,如日期、時間、數字和貨幣。
- 根據語言環境(如 LTR 或 RTL)調整布局。
準備工作:添加依賴
首先,在你的 pubspec.yaml 文件中添加必要的依賴。我們將使用官方推薦的 intl 包。
dependencies:flutter:sdk: flutterflutter_localizations: # 提供內置組件的本地化資源和基礎類sdk: flutterintl: ^0.18.1 # 用于高級國際化功能,如消息翻譯、日期/數字格式化dev_dependencies:flutter_test:sdk: flutterflutter_lints: ^2.0.0build_runner: ^2.4.0 # 用于生成代碼intl_translation: ^0.9.0 # 用于從 ARB 文件提取和生成本地化代碼
運行 flutter pub get 來安裝這些依賴。
方法一:使用 flutter_localizations 基礎方案
這種方法適用于簡單的應用,手動管理所有字符串。
- 配置 MaterialApp/CupertinoApp
在 lib/main.dart 中,配置你的主 Widget 以支持國際化。
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',// 支持的語言列表supportedLocales: const [Locale('en', ''), // 英語Locale('zh', 'CN'), // 簡體中文Locale('es', ''), // 西班牙語],// 本地化代理,用于加載翻譯和設置特定的本地化功能localizationsDelegates: const [// 提供默認的 Flutter 控件本地化字符串(如按鈕文本)GlobalMaterialLocalizations.delegate,GlobalCupertinoLocalizations.delegate,GlobalWidgetsLocalizations.delegate,// 稍后我們會添加自己的代理// AppLocalizations.delegate,],// 當語言環境不在 supportedLocales 中時,回退到哪個語言localeResolutionCallback: (locale, supportedLocales) {for (var supportedLocale in supportedLocales) {if (supportedLocale.languageCode == locale?.languageCode) {return supportedLocale;}}return supportedLocales.first; // 回退到第一個支持的語言},home: const MyHomePage(),);}
}
- 創建自定義本地化類
創建一個文件 lib/l10n/app_localizations.dart。
import 'dart:async';
import 'package:flutter/material.dart';class AppLocalizations {final Locale locale;AppLocalizations(this.locale);static AppLocalizations of(BuildContext context) {return Localizations.of<AppLocalizations>(context, AppLocalizations)!;}static const LocalizationsDelegate<AppLocalizations> delegate =_AppLocalizationsDelegate();// 靜態變量存儲翻譯Mapstatic Map<String, Map<String, String>> _localizedStrings = {'en': {'title': 'Hello World!','message': 'Welcome to my Flutter app.',},'zh_CN': {'title': '你好,世界!','message': '歡迎使用我的 Flutter 應用。',},'es': {'title': '?Hola Mundo!','message': 'Bienvenido a mi aplicación Flutter.',},};// 獲取翻譯的方法String translate(String key) {return _localizedStrings[locale.toString()]![key] ?? '** $key not found **';}// 也可以使用 getter 方法,使代碼更清晰String get title => translate('title');String get message => translate('message');
}// 本地化代理,負責加載具體的本地化資源
class _AppLocalizationsDelegateextends LocalizationsDelegate<AppLocalizations> {const _AppLocalizationsDelegate(); bool isSupported(Locale locale) {// 支持的語言列表return ['en', 'zh', 'es'].contains(locale.languageCode);}Future<AppLocalizations> load(Locale locale) async {// 這里通常是異步加載資源的地方(例如從JSON文件)// 我們這個例子是同步的return SynchronousFuture<AppLocalizations>(AppLocalizations(locale));} bool shouldReload(_AppLocalizationsDelegate old) => false;
}
- 使用翻譯
在 Widget 中,使用 AppLocalizations.of(context) 來獲取翻譯后的文本。
// lib/pages/my_home_page.dart
import 'package:flutter/material.dart';
import '../l10n/app_localizations.dart';class MyHomePage extends StatelessWidget {const MyHomePage({super.key});Widget build(BuildContext context) {var loc = AppLocalizations.of(context);return Scaffold(appBar: AppBar(title: Text(loc.title), // 使用 getter),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[Text(loc.message), // 使用 getterText(loc.translate('title')), // 或者使用 translate 方法],),),);}
}
優點:簡單直接,無需代碼生成。 缺點:手動管理所有鍵值對,容易出錯,難以維護大量翻譯。
方法二:使用 intl 包和 ARB 文件(推薦)
這是 Google 官方推薦的方法,它使用 .arb (Application Resource Bundle) 文件來管理翻譯,并通過代碼生成來自動創建本地化類。
- 項目結構
首先,創建以下目錄結構:
lib/l10n/intl_*.arb # ARB 翻譯文件app_localizations.dart # 生成的代碼會在這里
- 創建 ARB 文件
lib/l10n/intl_en.arb (主資源文件,必須)
{"@@locale": "en","title": "Hello World!","@title": {"description": "The title of the app on the home page"},"message": "Welcome to {appName}","@message": {"description": "A welcome message","placeholders": {"appName": {"type": "String","example": "My Flutter App"}}},"buttonText": "Click Me"
}
lib/l10n/intl_zh_CN.arb
{"@@locale": "zh_CN","title": "你好,世界!","message": "歡迎使用 {appName}","buttonText": "點擊我"
}
lib/l10n/intl_es.arb
{"@@locale": "es","title": "?Hola Mundo!","message": "Bienvenido a {appName}","buttonText": "Haz Clic"
}
- 提取和生成代碼
在項目根目錄運行以下命令,從代碼中提取需要翻譯的字符串到 ARB 文件(如果還沒有的話): flutter pub run intl_translation:extract_to_arb --output-dir=lib/l10n lib/l10n/app_localizations.dart
然后,根據 ARB 文件生成 Dart 代碼: flutter pub run intl_translation:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/l10n/app_localizations.dart lib/l10n/intl_*.arb
這將在 lib/l10n 目錄下生成一系列 messages_*.dart 文件和一個 app_localizations.dart 文件。
- 使用生成的代碼
生成的 app_localizations.dart 文件包含了所有邏輯。現在你可以在 Widget 中這樣使用:
import '../l10n/app_localizations.dart';Text(AppLocalizations.of(context)!.title),
Text(AppLocalizations.of(context)!.message('My Awesome App')),
Text(AppLocalizations.of(context)!.buttonText),
優點:翻譯與代碼分離,易于管理和協作(可與翻譯平臺集成);支持帶參數的文本;代碼自動生成,減少錯誤。 缺點:需要設置構建步驟。
方法三:使用 easy_localization 第三方庫
對于追求快速開發和簡單配置的開發者,easy_localization 是一個極佳的選擇。
- 添加依賴
dependencies:flutter:sdk: fluttereasy_localization: ^3.0.3dev_dependencies:flutter_test:sdk: flutterbuild_runner: ^2.4.0easy_localization_generator: ^3.0.0 # 用于代碼生成(可選但推薦)
- 創建資源文件
在項目根目錄創建 assets/translations 文件夾,并添加 JSON 或 CSV 文件。
assets/translations/en.jsonzh-CN.jsones.json
en.json
{"title": "Hello World!","message": "Welcome to {appName}","buttonText": "Click Me"
}
zh-CN.json
{"title": "你好,世界!","message": "歡迎使用 {appName}","buttonText": "點擊我"
}
- 配置 pubspec.yaml
聲明資源文件。
flutter:assets:- assets/translations/
- 配置 Main App
import 'package:easy_localization/easy_localization.dart';void main() async {WidgetsFlutterBinding.ensureInitialized(); // 需要先初始化await EasyLocalization.ensureInitialized(); // 初始化 EasyLocalizationrunApp(EasyLocalization(supportedLocales: const [Locale('en'), Locale('zh', 'CN'), Locale('es')],path: 'assets/translations', // 資源文件路徑fallbackLocale: const Locale('en'), // 回退語言child: const MyApp(), // 你的應用),);
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return MaterialApp(localizationsDelegates: context.localizationDelegates,supportedLocales: context.supportedLocales,locale: context.locale,home: const MyHomePage(),);}
}
- 使用翻譯
Text('title'.tr()), // 簡單文本
Text('message'.tr(args: ['My App'])), // 帶參數的文本
// 或者使用生成代碼(如果用了 easy_localization_generator)
Text(LocaleKeys.title.tr()),
優點:設置簡單,API 非常簡潔,功能強大(支持復數、性別等)。 缺點:依賴第三方庫。
動態切換語言
無論使用哪種方法,動態切換語言的邏輯是相似的。通常使用 Provider 或 Riverpod 來管理狀態。
// 一個簡單的 Provider 例子
import 'package:flutter/material.dart';class LocaleProvider with ChangeNotifier {Locale? _locale;Locale? get locale => _locale;void setLocale(Locale newLocale) {_locale = newLocale;notifyListeners();}void clearLocale() {_locale = null;notifyListeners();}
}// 在 MaterialApp 中使用
return MaterialApp(locale: context.watch<LocaleProvider>().locale, // 來自 Provider...
);// 在設置頁面切換語言
ListTile(title: Text('English'),onTap: () {context.read<LocaleProvider>().setLocale(Locale('en'));Navigator.pop(context);},
),
ListTile(title: Text('簡體中文'),onTap: () {context.read<LocaleProvider>().setLocale(Locale('zh', 'CN'));Navigator.pop(context);},
),
處理語言環境相關的數據
使用 intl 包來格式化數據。
import 'package:intl/intl.dart';String formatDate(DateTime date, BuildContext context) {return DateFormat.yMMMMd( Localizations.localeOf(context).toString() ).format(date);
}String formatCurrency(double amount, BuildContext context) {return NumberFormat.currency(locale: Localizations.localeOf(context).toString(),symbol: '' // 可能需要根據貨幣調整).format(amount);
}// 在 Widget 中使用
Text( formatDate(DateTime.now(), context) ),
Text( formatCurrency(29.99, context) ),
最佳實踐與總結
- 選擇合適的方法:
· 小型項目/原型:easy_localization 最快。
· 中大型項目/團隊協作:官方 intl + ARB 文件最規范,易于維護。
· 簡單演示:手動管理 Map 也可以。 - 鍵名要有意義:使用類似 homePageWelcomeMessage 的鍵名,而不是 msg1。
- 提供上下文描述:在 ARB 文件中使用 @key 的 description 字段,幫助翻譯者理解上下文。
- 處理文本方向:注意 RTL (Right-to-Left) 語言(如阿拉伯語、希伯來語)的布局適配。Directionality Widget 可以幫你。
- 不要拼接字符串:類似 ‘Hello ’ + name 的拼接在其他語言中語序可能不同,務必使用帶參數的翻譯。
- 測試:務必在不同語言環境下測試你的應用,檢查布局是否錯亂,翻譯是否完整。
總結:Flutter 提供了從簡單到復雜的多種國際化方案。intl + ARB 的組合是官方推薦的“黃金標準”,平衡了功能性和可維護性。而 easy_localization 則為開發者提供了快速實現的捷徑。