Flutter 国际化与本地化完全指南
Flutter 国际化与本地化完全指南引言国际化Internationalization和本地化Localization是构建全球化应用的关键。Flutter 提供了强大的国际化支持让应用能够轻松支持多种语言和地区。本文将深入探讨 Flutter 国际化的各种技巧和最佳实践。基础配置添加依赖dependencies: flutter_localizations: sdk: flutter intl: ^0.18.0配置 MaterialAppimport package:flutter_localizations/flutter_localizations.dart; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { override Widget build(BuildContext context) { return MaterialApp( title: Flutter Internationalization, localizationsDelegates: [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], supportedLocales: [ const Locale(en, US), const Locale(zh, CN), const Locale(ja, JP), ], home: HomePage(), ); } }高级技巧一创建本地化资源使用 intl 包// l10n/messages_all.dart import dart:async; import package:intl/intl.dart; import package:intl/message_lookup_by_library.dart; final MessageLookup messages MessageLookup(); class MessageLookup extends MessageLookupByLibrary { override String? lookupMessage( String? messageText, String? locale, String? name, ListObject? args, String? meaning, MapString, String? examples, ) { return messageText; } }创建本地化文件// l10n/intl_en.arb { helloWorld: Hello World!, welcome: Welcome to Flutter, greeting: Hello {name}!, count: {count, plural, zero{No items} one{1 item} other{{count} items}} } // l10n/intl_zh.arb { helloWorld: 你好世界, welcome: 欢迎来到 Flutter, greeting: 你好 {name}, count: {count, plural, zero{没有项目} one{1个项目} other{{count}个项目}} }高级技巧二使用 flutter_localizations创建自定义 LocalizationsDelegateclass AppLocalizations { final Locale locale; AppLocalizations(this.locale); static AppLocalizations? of(BuildContext context) { return Localizations.ofAppLocalizations(context, AppLocalizations); } static const LocalizationsDelegateAppLocalizations delegate _AppLocalizationsDelegate(); MapString, String _localizedStrings {}; Futurevoid load() async { String jsonString await rootBundle.loadString( i18n/${locale.languageCode}.json, ); MapString, dynamic jsonMap json.decode(jsonString); _localizedStrings jsonMap.map((key, value) MapEntry(key, value.toString())); } String translate(String key) { return _localizedStrings[key] ?? key; } } class _AppLocalizationsDelegate extends LocalizationsDelegateAppLocalizations { const _AppLocalizationsDelegate(); override bool isSupported(Locale locale) { return [en, zh, ja].contains(locale.languageCode); } override FutureAppLocalizations load(Locale locale) async { AppLocalizations localizations AppLocalizations(locale); await localizations.load(); return localizations; } override bool shouldReload(_AppLocalizationsDelegate old) false; }配置 MaterialAppMaterialApp( localizationsDelegates: [ AppLocalizations.delegate, GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, ], supportedLocales: [ const Locale(en, US), const Locale(zh, CN), const Locale(ja, JP), ], )高级技巧三动态切换语言创建语言切换服务class LanguageService extends ChangeNotifier { Locale _currentLocale const Locale(en, US); Locale get currentLocale _currentLocale; void setLocale(Locale locale) { _currentLocale locale; notifyListeners(); } }使用 GetX 切换语言// 在 GetX 中配置 GetMaterialApp( locale: Get.deviceLocale, fallbackLocale: const Locale(en, US), translations: MyTranslations(), ) // 切换语言 Get.updateLocale(const Locale(zh, CN));高级技巧四格式化日期和数字日期格式化// 使用 intl 包 final date DateTime.now(); final formatter DateFormat(yyyy-MM-dd HH:mm:ss, zh_CN); print(formatter.format(date)); // 2024-01-15 14:30:00数字格式化final number 1234567.89; final formatter NumberFormat(#,##0.00, zh_CN); print(formatter.format(number)); // 1,234,567.89货币格式化final amount 1234.56; final formatter NumberFormat.currency( locale: zh_CN, symbol: ¥, ); print(formatter.format(amount)); // ¥1,234.56高级技巧五RTL 支持配置 RTLMaterialApp( locale: const Locale(ar), supportedLocales: [ const Locale(ar), ], localizationsDelegates: [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, ], )检测文本方向final direction Directionality.of(context); if (direction TextDirection.rtl) { // RTL 布局 } else { // LTR 布局 }实战案例多语言应用class HomePage extends StatelessWidget { override Widget build(BuildContext context) { final localizations AppLocalizations.of(context); return Scaffold( appBar: AppBar( title: Text(localizations?.translate(welcome) ?? Welcome), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(localizations?.translate(helloWorld) ?? Hello World), SizedBox(height: 20), ElevatedButton( onPressed: () { // 切换语言 final locale Localizations.localeOf(context); if (locale.languageCode en) { Get.updateLocale(const Locale(zh, CN)); } else { Get.updateLocale(const Locale(en, US)); } }, child: Text(localizations?.translate(changeLanguage) ?? Change Language), ), ], ), ), ); } }实战案例动态语言切换class LanguageSelector extends StatelessWidget { final ListMapString, String languages [ {code: en, name: English}, {code: zh, name: 中文}, {code: ja, name: 日本語}, ]; override Widget build(BuildContext context) { return DropdownButtonString( value: Get.locale?.languageCode, items: languages.map((lang) { return DropdownMenuItem( value: lang[code], child: Text(lang[name]!), ); }).toList(), onChanged: (value) { if (value ! null) { Get.updateLocale(Locale(value)); } }, ); } }常见问题与解决方案Q1如何支持地区变体A使用完整的 locale 代码supportedLocales: [ const Locale(en, US), const Locale(en, GB), const Locale(zh, CN), const Locale(zh, TW), ],Q2如何处理复数形式A使用 ICU 消息格式{ items: {count, plural, zero{No items} one{1 item} other{{count} items}} }Q3如何获取设备语言A使用WidgetsBinding.instance.window.localefinal deviceLocale WidgetsBinding.instance.window.locale;最佳实践1. 集中管理翻译资源// 错误硬编码字符串 Text(Hello World) // 正确使用本地化 Text(AppLocalizations.of(context)?.translate(helloWorld) ?? Hello World)2. 使用 ARB 文件{ helloWorld: Hello World!, helloWorld: { description: The classic hello world message } }3. 支持 RTL 语言Directionality( textDirection: TextDirection.rtl, child: Text(مرحبا بالعالم), )总结国际化是构建全球化应用的重要环节。通过本文的学习你应该能够配置基本的国际化支持创建多语言资源文件实现动态语言切换格式化日期、数字和货币支持 RTL 语言掌握这些技巧能够帮助你构建更加友好的全球化应用。