欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。
Flutter国际化(i18n)实现详解
Flutter的国际化(Internationalization,简称i18n)是开发多语言应用的关键技术,它涉及多语言支持、区域设置和文本方向等内容。完整的国际化方案需要考虑语言切换、日期/数字格式化、复数处理等多方面因素。以下是实现国际化的详细方法和代码示例。
1. 添加依赖
在pubspec.yaml中添加必要的国际化相关依赖:
yaml
dependencies:
flutter:
sdk: flutter
# Flutter官方提供的本地化支持
flutter_localizations:
sdk: flutter
# 提供国际化工具和格式化功能
intl: ^0.18.1
# 可选:简化arb文件生成
intl_utils: ^2.3.0
dev_dependencies:
# 用于生成本地化代码
intl_translation: ^0.18.2
运行flutter pub get安装依赖后,建议重启IDE以确保代码生成器正常工作。
2. 配置MaterialApp
在应用的根Widget(通常是MaterialApp)中配置本地化代理和支持的语言:
dart
import 'package:flutter_localizations/flutter_localizations.dart';
MaterialApp(
title: '国际化示例',
// 必须配置的本地化代理
localizationsDelegates: [
// 提供Material组件的本地化字符串
GlobalMaterialLocalizations.delegate,
// 提供基础Widget的本地化(如文本方向)
GlobalWidgetsLocalizations.delegate,
// iOS风格组件的本地化
GlobalCupertinoLocalizations.delegate,
// 添加我们自定义的本地化代理
AppLocalizations.delegate,
],
// 应用支持的语言列表
supportedLocales: [
const Locale('en', 'US'), // 英语(美国)
const Locale('zh', 'CN'), // 中文(简体)
const Locale('es', 'ES'), // 西班牙语
const Locale('fr', 'FR'), // 法语
// 可以只指定语言代码,不指定国家代码
const Locale('ja'), // 日语
],
// 当系统语言不在supportedLocales中时使用的备选语言
localeResolutionCallback: (locale, supportedLocales) {
// 检查是否支持系统语言
for (var supportedLocale in supportedLocales) {
if (supportedLocale.languageCode == locale?.languageCode) {
return supportedLocale;
}
}
// 默认返回英语
return const Locale('en', 'US');
},
home: MyHomePage(),
)
3. 创建arb资源文件
ARB(Application Resource Bundle)是Google推荐的国际化资源文件格式。在项目根目录创建l10n文件夹(l10n是"localization"的缩写),然后添加语言资源文件:
intl_en.arb(英语资源)
json
{
"@@locale": "en",
"helloWorld": "Hello World!",
"@helloWorld": {
"description": "Common greeting text",
"type": "text",
"placeholders": {}
},
"welcomeMessage": "Welcome, {name}!",
"@welcomeMessage": {
"description": "Personalized welcome message",
"type": "text",
"placeholders": {
"name": {
"type": "String",
"example": "John"
}
}
}
}
intl_zh.arb(中文资源)
json
{
"@@locale": "zh",
"helloWorld": "你好,世界!",
"welcomeMessage": "欢迎,{name}!"
}
intl_es.arb(西班牙语资源)
json
{
"@@locale": "es",
"helloWorld": "¡Hola Mundo!",
"welcomeMessage": "¡Bienvenido, {name}!"
}
4. 生成本地化类
使用以下命令生成Dart本地化代码:
- 首先从Dart代码中提取需要国际化的字符串到arb文件:
bash
flutter pub run intl_translation:extract_to_arb --output-dir=lib/l10n lib/localizations.dart
- 然后根据arb文件生成本地化类:
bash
flutter pub run intl_translation:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/localizations.dart lib/l10n/intl_*.arb
这些命令会生成以下文件:
messages_all.dart:包含所有语言的映射messages_xx.dart:各语言的实现文件intl_messages.dart:基础消息类
5. 实现本地化代理
创建localizations.dart文件实现自定义本地化:
dart
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:intl/message_lookup_by_library.dart';
import 'messages_all.dart';
class AppLocalizations {
// 单例模式
static AppLocalizations? _current;
static AppLocalizations get current {
assert(_current != null, 'No instance of AppLocalizations loaded');
return _current!;
}
static Future<AppLocalizations> load(Locale locale) {
final name = (locale.countryCode?.isEmpty ?? true)
? locale.languageCode
: '${locale.languageCode}_${locale.countryCode}';
// 设置Intl默认语言环境
Intl.defaultLocale = name;
return initializeMessages(name).then((_) {
Intl.defaultLocale = name;
_current = AppLocalizations();
return _current!;
});
}
static AppLocalizations of(BuildContext context) {
return Localizations.of<AppLocalizations>(context, AppLocalizations) ?? current;
}
// 定义本地化字符串getter方法
String get helloWorld => Intl.message(
'Hello World',
name: 'helloWorld',
desc: 'Common greeting text',
);
String welcomeMessage(String name) => Intl.message(
'Welcome, $name!',
name: 'welcomeMessage',
desc: 'Personalized welcome message',
args: [name],
);
}
// 本地化代理类
class AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
const AppLocalizationsDelegate();
@override
bool isSupported(Locale locale) {
return ['en', 'zh', 'es', 'fr', 'ja'].contains(locale.languageCode);
}
@override
Future<AppLocalizations> load(Locale locale) {
return AppLocalizations.load(locale);
}
@override
bool shouldReload(AppLocalizationsDelegate old) => false;
}
6. 使用本地化文本
在Widget中使用本地化字符串:
dart
Column(
children: [
Text(AppLocalizations.of(context).helloWorld),
Text(AppLocalizations.of(context).welcomeMessage('张三')),
// 使用带参数的本地化字符串
Text(
AppLocalizations.of(context).itemCount(5),
style: Theme.of(context).textTheme.headline6,
),
],
)
7. 动态切换语言
实现语言切换功能需要管理应用状态:
dart
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Locale _locale = const Locale('en', 'US');
void _changeLanguage(Locale locale) {
setState(() {
_locale = locale;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
locale: _locale,
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: LanguageSwitcherPage(
onChangeLanguage: _changeLanguage,
),
);
}
}
class LanguageSwitcherPage extends StatelessWidget {
final ValueChanged<Locale> onChangeLanguage;
const LanguageSwitcherPage({required this.onChangeLanguage});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(AppLocalizations.of(context).helloWorld)),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () => onChangeLanguage(const Locale('en', 'US')),
child: Text('English'),
),
ElevatedButton(
onPressed: () => onChangeLanguage(const Locale('zh', 'CN')),
child: Text('中文'),
),
// 显示当前语言环境
Text(
'Current locale: ${Localizations.localeOf(context).toString()}',
style: TextStyle(fontSize: 16),
),
],
),
),
);
}
}
8. 高级特性实现
处理复数形式
在arb文件中定义复数规则:
json
{
"itemCount": "{count,plural, =0{No items}=1{1 item}other{{count} items}}",
"@itemCount": {
"description": "Plural message example",
"placeholders": {
"count": {}
}
}
}
生成对应的Dart方法:
dart
String itemCount(int count) => Intl.plural(
count,
zero: 'No items',
one: '1 item',
other: '$count items',
name: 'itemCount',
args: [count],
examples: const {'count': 2},
);
处理性别相关文本
json
{
"greeting": "{gender,select, male{Hello sir} female{Hello madam} other{Hello}}",
"@greeting": {
"description": "Gender-specific greeting",
"placeholders": {
"gender": {}
}
}
}
对应的Dart方法:
dart
String greeting(String gender) => Intl.gender(
gender,
male: 'Hello sir',
female: 'Hello madam',
other: 'Hello',
name: 'greeting',
args: [gender],
);
9. 日期与数字格式化
使用intl包进行区域敏感的格式化:
dart
// 日期格式化
final now = DateTime.now();
final dateFormat = DateFormat.yMMMMd(Localizations.localeOf(context).toString()).format(now);
final timeFormat = DateFormat.Hms(Localizations.localeOf(context).toString()).format(now);
// 数字格式化
final number = 1234567.89;
final numberFormat = NumberFormat.decimalPattern(Localizations.localeOf(context).toString()).format(number);
final currencyFormat = NumberFormat.currency(
locale: Localizations.localeOf(context).toString(),
symbol: '', // 可以自定义货币符号
).format(number);
// 在UI中使用
Column(
children: [
Text('当前日期: $dateFormat'),
Text('当前时间: $timeFormat'),
Text('格式化数字: $numberFormat'),
Text('货币格式: $currencyFormat'),
],
)
10. 文本方向(RTL)处理
对于从右向左(RTL)的语言如阿拉伯语、希伯来语等,需要特殊处理:
- 在
supportedLocales中添加RTL语言:
dart
supportedLocales: [
const Locale('en', 'US'), // LTR
const Locale('ar', 'SA'), // RTL
// ...
],
- 自动检测文本方向:
dart
// 获取当前文本方向
TextDirection getCurrentTextDirection(BuildContext context) {
return Directionality.of(context);
}
// 根据语言自动设置方向
TextDirection getTextDirectionForLocale(Locale locale) {
switch (locale.languageCode) {
case 'ar':
case 'he':
return TextDirection.rtl;
default:
return TextDirection.ltr;
}
}
- 在Widget中使用:
dart
Directionality(
textDirection: getTextDirectionForLocale(Localizations.localeOf(context)),
child: Text(AppLocalizations.of(context).helloWorld),
)
11. 测试与验证
为确保国际化实现正确,应该:
- 添加单元测试验证本地化加载:
dart
test('Test English localization', () async {
await AppLocalizations.load(const Locale('en', 'US'));
expect(AppLocalizations.current.helloWorld, 'Hello World!');
});
test('Test Chinese localization', () async {
await AppLocalizations.load(const Locale('zh', 'CN'));
expect(AppLocalizations.current.helloWorld, '你好,世界!');
});
- 使用不同语言环境运行应用:
bash
flutter run --dart-define=FLUTTER_LOCALE=zh_CN
- 验证UI布局在RTL语言下的表现。
12. 最佳实践
-
分离业务逻辑与本地化:不要在业务逻辑中直接使用本地化字符串
-
保持arb文件整洁:
- 为每个字符串添加描述
- 使用一致的命名约定
- 分组相关字符串
-
考虑语言长度差异:某些语言的翻译可能比原文长很多,确保UI有足够空间
-
定期更新翻译:建立翻译更新流程,使用专业翻译服务或社区协作
-
提供翻译上下文:在arb文件的description中提供足够的使用场景说明
-
处理缺失翻译:实现回退机制,当某种语言缺少翻译时使用默认语言
通过以上完整实现,Flutter应用可以获得完善的国际化支持,包括多语言切换、复数处理、性别相关文本、日期/数字格式化和RTL支持等功能,为全球用户提供本地化的使用体验。欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。