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 则为开发者提供了快速实现的捷径。