Flutter 国际化与本地化完全指南
·
Flutter 国际化与本地化完全指南
引言
国际化(Internationalization)和本地化(Localization)是构建全球应用的关键。Flutter 提供了强大的国际化支持。本文将深入探讨如何实现应用的多语言支持。
基础概念回顾
国际化 vs 本地化
- 国际化 (i18n): 设计应用使其能够适应不同语言和地区
- 本地化 (l10n): 为特定语言和地区定制应用
核心组件
- Localizations: 本地化组件
- LocalizationsDelegate: 本地化委托
- GlobalMaterialLocalizations: Material组件本地化
- GlobalWidgetsLocalizations: Widget本地化
高级技巧一:基础配置
添加依赖
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
intl: ^0.18.1
配置 MaterialApp
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
supportedLocales: const [
Locale('zh', 'CN'),
Locale('en', 'US'),
Locale('ja', 'JP'),
],
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
localeResolutionCallback: (locale, supportedLocales) {
for (var supportedLocale in supportedLocales) {
if (supportedLocale.languageCode == locale?.languageCode &&
supportedLocale.countryCode == locale?.countryCode) {
return supportedLocale;
}
}
return supportedLocales.first;
},
home: const HomeScreen(),
);
}
}
高级技巧二:创建本地化类
使用 intl 工具生成
import 'package:flutter/widgets.dart';
import 'package:intl/intl.dart';
class AppLocalizations {
AppLocalizations(this.locale);
final Locale locale;
static AppLocalizations? of(BuildContext context) {
return Localizations.of<AppLocalizations>(context, AppLocalizations);
}
static const LocalizationsDelegate<AppLocalizations> delegate =
_AppLocalizationsDelegate();
Map<String, String> _localizedStrings = {};
Future<void> load() async {
String jsonString = await Future.delayed(
const Duration(milliseconds: 100),
() => '{"hello": "Hello", "welcome": "Welcome"}',
);
// 实际项目中从assets加载
Map<String, dynamic> jsonMap = {};
_localizedStrings = jsonMap.map((key, value) => MapEntry(key, value.toString()));
}
String translate(String key) {
return _localizedStrings[key] ?? key;
}
}
class _AppLocalizationsDelegate
extends LocalizationsDelegate<AppLocalizations> {
const _AppLocalizationsDelegate();
@override
bool isSupported(Locale locale) {
return ['en', 'zh', 'ja'].contains(locale.languageCode);
}
@override
Future<AppLocalizations> load(Locale locale) async {
AppLocalizations localizations = AppLocalizations(locale);
await localizations.load();
return localizations;
}
@override
bool shouldReload(_AppLocalizationsDelegate old) => false;
}
使用 .arb 文件
创建 l10n/intl_en.arb:
{
"@@locale": "en",
"hello": "Hello",
"welcome": "Welcome to our app",
"greeting": "Hello {name}",
"count": "{count} items"
}
创建 l10n/intl_zh.arb:
{
"@@locale": "zh",
"hello": "你好",
"welcome": "欢迎来到我们的应用",
"greeting": "你好 {name}",
"count": "{count} 个项目"
}
高级技巧三:使用生成的代码
配置 pubspec.yaml
flutter:
generate: true
assets:
- lib/l10n/
创建 l10n.yaml
arb-dir: lib/l10n
template-arb-file: intl_en.arb
output-localization-file: app_localizations.dart
使用生成的本地化类
Text(AppLocalizations.of(context)!.hello);
Text(AppLocalizations.of(context)!.greeting('John'));
Text(AppLocalizations.of(context)!.count(5));
高级技巧四:动态切换语言
创建语言切换器
class LanguageProvider with ChangeNotifier {
Locale _locale = const Locale('en');
Locale get locale => _locale;
void setLocale(Locale locale) {
_locale = locale;
notifyListeners();
}
}
使用 Provider
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => LanguageProvider(),
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return Consumer<LanguageProvider>(
builder: (context, provider, child) {
return MaterialApp(
locale: provider.locale,
supportedLocales: const [
Locale('en', 'US'),
Locale('zh', 'CN'),
Locale('ja', 'JP'),
],
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
home: const HomeScreen(),
);
},
);
}
}
创建语言选择器
class LanguageSelector extends StatelessWidget {
const LanguageSelector({super.key});
@override
Widget build(BuildContext context) {
return DropdownButton<Locale>(
value: Provider.of<LanguageProvider>(context).locale,
items: const [
DropdownMenuItem(value: Locale('en', 'US'), child: Text('English')),
DropdownMenuItem(value: Locale('zh', 'CN'), child: Text('中文')),
DropdownMenuItem(value: Locale('ja', 'JP'), child: Text('日本語')),
],
onChanged: (locale) {
if (locale != null) {
Provider.of<LanguageProvider>(context, listen: false).setLocale(locale);
}
},
);
}
}
高级技巧五:格式化日期和数字
日期格式化
String formatDate(DateTime date) {
return DateFormat.yMd(AppLocalizations.of(context)!.localeName).format(date);
}
String formatFullDate(DateTime date) {
return DateFormat.yMMMMd(AppLocalizations.of(context)!.localeName).format(date);
}
数字格式化
String formatNumber(int number) {
return NumberFormat.decimalPattern(AppLocalizations.of(context)!.localeName)
.format(number);
}
String formatCurrency(double amount) {
return NumberFormat.currency(
locale: AppLocalizations.of(context)!.localeName,
symbol: '\$',
).format(amount);
}
实战案例:完整的国际化应用
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
return Scaffold(
appBar: AppBar(
title: Text(l10n.welcome),
actions: const [LanguageSelector()],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(l10n.hello),
const SizedBox(height: 16),
Text(l10n.greeting('Flutter')),
const SizedBox(height: 16),
Text(l10n.count(10)),
const SizedBox(height: 32),
Text('Today: ${formatDate(DateTime.now())}'),
const SizedBox(height: 16),
Text('Number: ${formatNumber(1234567)}'),
const SizedBox(height: 16),
Text('Price: ${formatCurrency(99.99)}'),
],
),
),
);
}
}
实战案例:本地化验证消息
class Validators {
static String? validateEmail(String? value, BuildContext context) {
final l10n = AppLocalizations.of(context)!;
if (value == null || value.isEmpty) {
return l10n.requiredField;
}
if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(value)) {
return l10n.invalidEmail;
}
return null;
}
static String? validatePassword(String? value, BuildContext context) {
final l10n = AppLocalizations.of(context)!;
if (value == null || value.isEmpty) {
return l10n.requiredField;
}
if (value.length < 8) {
return l10n.passwordTooShort;
}
return null;
}
}
实战案例:RTL 支持
Directionality(
textDirection: TextDirection.rtl,
child: Text('سلام'),
)
检测 RTL
bool isRTL(Locale locale) {
return ['ar', 'he', 'fa', 'ur'].contains(locale.languageCode);
}
常见问题与解决方案
Q1:如何处理复数形式?
A:使用 intl 包的 PluralRules:
int count = 5;
String message = Intl.plural(
count,
zero: 'no items',
one: '1 item',
other: '$count items',
);
Q2:如何处理性别?
A:使用 intl 包的 GenderRules:
String message = Intl.gender(
'male',
male: 'He',
female: 'She',
other: 'They',
);
Q3:如何预加载本地化数据?
A:在启动时提前加载:
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// 预加载本地化数据
await AppLocalizations(const Locale('en')).load();
runApp(const MyApp());
}
最佳实践
1. 使用 .arb 文件管理翻译
flutter:
generate: true
assets:
- lib/l10n/
2. 创建翻译管理类
class Translation {
static String get hello => AppLocalizations.of(navigatorKey.currentContext!)!.hello;
static String get welcome => AppLocalizations.of(navigatorKey.currentContext!)!.welcome;
}
3. 使用 context 扩展
extension BuildContextExtension on BuildContext {
AppLocalizations get l10n => AppLocalizations.of(this)!;
}
// 使用
Text(context.l10n.hello);
总结
Flutter 的国际化和本地化功能非常强大。通过本文的学习,你应该能够:
- 配置基本的国际化支持
- 创建和管理翻译文件
- 使用 intl 工具生成代码
- 动态切换语言
- 格式化日期和数字
- 支持 RTL 语言
掌握这些技巧,能够帮助你构建真正全球化的应用。
更多推荐
所有评论(0)