Flutter 国际化与本地化实战指南
·
Flutter 国际化与本地化实战指南
一、国际化概述
国际化(Internationalization,简称i18n)是指应用程序能够支持多种语言和地区的能力。本地化(Localization,简称l10n)则是为特定地区或语言调整应用程序的过程。
Flutter 提供了完整的国际化支持,主要通过以下包实现:
flutter_localizations- 官方本地化支持intl- 国际化工具库
二、配置国际化环境
2.1 添加依赖
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
intl: ^0.18.1
dev_dependencies:
flutter_test:
sdk: flutter
2.2 配置 MaterialApp
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:intl/intl.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter i18n Demo',
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [
Locale('en', 'US'), // 英语
Locale('zh', 'CN'), // 中文简体
Locale('ja', 'JP'), // 日语
Locale('ko', 'KR'), // 韩语
],
home: const HomePage(),
);
}
}
三、创建国际化资源文件
3.1 创建 arb 文件
创建 lib/l10n 目录,并添加以下文件:
app_en.arb - 英语资源
{
"@@locale": "en",
"helloWorld": "Hello World",
"welcome": "Welcome to our app",
"greeting": "Hello {name}",
"counter": "{count, plural, =0{No items} =1{One item} other{{count} items}}",
"date": "{date, date, short}",
"time": "{time, time, short}"
}
app_zh.arb - 中文资源
{
"@@locale": "zh",
"helloWorld": "你好世界",
"welcome": "欢迎使用我们的应用",
"greeting": "你好 {name}",
"counter": "{count, plural, =0{没有项目} =1{一个项目} other{{count} 个项目}}",
"date": "{date, date, short}",
"time": "{time, time, short}"
}
app_ja.arb - 日语资源
{
"@@locale": "ja",
"helloWorld": "こんにちは世界",
"welcome": "アプリへようこそ",
"greeting": "こんにちは {name}",
"counter": "{count, plural, =0{項目なし} =1{1つの項目} other{{count} 項目}}",
"date": "{date, date, short}",
"time": "{time, time, short}"
}
3.2 配置 pubspec.yaml
flutter:
generate: true
assets:
- lib/l10n/
3.3 生成代码
运行以下命令生成国际化代码:
flutter pub get
flutter pub run intl_utils:generate
四、使用国际化字符串
4.1 基础用法
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
return Scaffold(
appBar: AppBar(title: Text(l10n.helloWorld)),
body: Center(
child: Column(
children: [
Text(l10n.welcome),
Text(l10n.greeting(name: 'John')),
],
),
),
);
}
}
4.2 复数处理
Text(l10n.counter(count: 0)); // 没有项目
Text(l10n.counter(count: 1)); // 一个项目
Text(l10n.counter(count: 5)); // 5 个项目
4.3 日期和时间格式化
Text(l10n.date(date: DateTime.now()));
Text(l10n.time(time: DateTime.now()));
五、动态切换语言
5.1 创建语言状态管理
class LocaleProvider extends ChangeNotifier {
Locale _locale = const Locale('en');
Locale get locale => _locale;
void setLocale(Locale locale) {
_locale = locale;
notifyListeners();
}
}
5.2 使用 Provider
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => LocaleProvider(),
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return Consumer<LocaleProvider>(
builder: (context, provider, child) {
return MaterialApp(
locale: provider.locale,
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [
Locale('en', 'US'),
Locale('zh', 'CN'),
Locale('ja', 'JP'),
],
home: const HomePage(),
);
},
);
}
}
5.3 创建语言选择器
class LanguageSelector extends StatelessWidget {
const LanguageSelector({super.key});
@override
Widget build(BuildContext context) {
final provider = Provider.of<LocaleProvider>(context);
return DropdownButton<Locale>(
value: provider.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.setLocale(locale);
}
},
);
}
}
六、处理 RTL 语言
6.1 配置 RTL 支持
MaterialApp(
...
supportedLocales: const [
Locale('ar', 'SA'), // 阿拉伯语
Locale('he', 'IL'), // 希伯来语
],
);
6.2 使用 Directionality Widget
Directionality(
textDirection: TextDirection.rtl,
child: Text('مرحبا بالعالم'), // 阿拉伯语
);
6.3 自适应布局
Row(
textDirection: Directionality.of(context),
children: [
Text(l10n.name),
Text(l10n.value),
],
);
七、日期时间格式化
7.1 基础格式化
import 'package:intl/intl.dart';
final now = DateTime.now();
// 格式化日期
print(DateFormat.yMd().format(now)); // 12/31/2023
print(DateFormat('yyyy-MM-dd').format(now)); // 2023-12-31
// 格式化时间
print(DateFormat.jm().format(now)); // 11:59 PM
print(DateFormat.Hms().format(now)); // 23:59:59
// 完整日期时间
print(DateFormat.yMMMd().format(now)); // Dec 31, 2023
print(DateFormat.yMMMEd().format(now)); // Sun, Dec 31, 2023
7.2 本地化日期时间
// 根据当前语言环境格式化
print(DateFormat.yMd(Localizations.localeOf(context).languageCode).format(now));
八、数字格式化
8.1 基础用法
import 'package:intl/intl.dart';
final number = 1234567.89;
print(NumberFormat().format(number)); // 1,234,567.89
print(NumberFormat.currency().format(number)); // $1,234,567.89
print(NumberFormat.percent().format(0.75)); // 75%
8.2 本地化数字
final format = NumberFormat.decimalPattern(Localizations.localeOf(context).languageCode);
print(format.format(number));
九、复数规则
9.1 基础复数
{
"items": "{count, plural, =0{no items} =1{one item} other{{count} items}}"
}
9.2 复杂复数规则
{
"apples": "{count, plural, zero{no apples} one{one apple} two{two apples} few{few apples} many{many apples} other{{count} apples}}"
}
十、处理地区差异
10.1 地区特定格式
// 美国格式
print(DateFormat.yMd('en_US').format(now)); // 12/31/2023
// 欧洲格式
print(DateFormat.yMd('de_DE').format(now)); // 31.12.2023
// 中国格式
print(DateFormat.yMd('zh_CN').format(now)); // 2023/12/31
10.2 货币格式
// 美元
print(NumberFormat.currency(locale: 'en_US').format(100)); // $100.00
// 欧元
print(NumberFormat.currency(locale: 'de_DE').format(100)); // 100,00 €
// 人民币
print(NumberFormat.currency(locale: 'zh_CN', symbol: '¥').format(100)); // ¥100.00
十一、测试国际化
11.1 单元测试
test('English localization', () {
final l10n = AppLocalizationsEn();
expect(l10n.helloWorld, 'Hello World');
expect(l10n.greeting(name: 'Test'), 'Hello Test');
});
test('Chinese localization', () {
final l10n = AppLocalizationsZh();
expect(l10n.helloWorld, '你好世界');
expect(l10n.greeting(name: '测试'), '你好 测试');
});
11.2 Widget 测试
testWidgets('Localized text displays correctly', (tester) async {
await tester.pumpWidget(
MaterialApp(
locale: const Locale('zh'),
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
home: const HomePage(),
),
);
expect(find.text('你好世界'), findsOneWidget);
});
十二、最佳实践
12.1 组织翻译文件
lib/
├── l10n/
│ ├── app_en.arb
│ ├── app_zh.arb
│ ├── app_ja.arb
│ └── app_ko.arb
├── main.dart
└── ...
12.2 使用一致的命名规范
{
"homeTitle": "首页",
"homeSubtitle": "欢迎回来",
"btnSubmit": "提交",
"btnCancel": "取消",
"errorNetwork": "网络错误",
"successSave": "保存成功"
}
12.3 避免硬编码字符串
// 错误
Text('Hello World');
// 正确
Text(l10n.helloWorld);
12.4 使用翻译管理工具
- Lokalise - 专业翻译管理平台
- Transifex - 开源翻译管理
- Crowdin - 企业级翻译管理
十三、实战案例:多语言应用
class InternationalizedApp extends StatelessWidget {
const InternationalizedApp({super.key});
@override
Widget build(BuildContext context) {
return Consumer<LocaleProvider>(
builder: (context, provider, child) {
return MaterialApp(
locale: provider.locale,
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: const [
Locale('en', 'US'),
Locale('zh', 'CN'),
Locale('ja', 'JP'),
Locale('ko', 'KR'),
],
home: const HomePage(),
);
},
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
return Scaffold(
appBar: AppBar(
title: Text(l10n.homeTitle),
actions: const [LanguageSelector()],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(l10n.welcome),
const SizedBox(height: 20),
Text(l10n.greeting(name: '用户')),
const SizedBox(height: 20),
Text(l10n.counter(count: 5)),
const SizedBox(height: 20),
Text(l10n.date(date: DateTime.now())),
],
),
),
);
}
}
十四、总结
Flutter 国际化涉及多个方面:
- 配置环境 - 添加依赖和配置 MaterialApp
- 创建资源文件 - 使用 ARB 格式管理翻译
- 生成代码 - 使用 intl 工具生成本地化类
- 使用翻译 - 在 Widget 中引用本地化字符串
- 动态切换 - 实现语言切换功能
- RTL 支持 - 处理从右到左的语言
- 格式化 - 日期、时间、数字的本地化格式化
通过合理的国际化设计,可以让应用程序更好地服务于全球用户。
更多推荐
所有评论(0)