引言

在全球化时代,开发支持多语言的应用已成为必备技能。Flutter提供了强大的国际化(Internationalization, i18n)和本地化(Localization)支持,使开发者能够轻松创建支持多种语言和地区的应用。

一、国际化基础概念

1.1 核心术语

术语 含义
国际化(i18n) 设计应用使其能够适应不同语言和地区的过程
本地化(l10n) 为特定语言和地区定制应用的过程
Locale 语言和地区标识符,如zh_CNen_US
ICU Unicode国际化组件,提供日期、数字等格式化

1.2 Flutter国际化支持

Flutter通过以下包提供国际化支持:

  • flutter_localizations - 官方本地化支持
  • intl - 国际化工具库
  • intl_translation - 翻译文件生成工具

二、配置流程

2.1 添加依赖

dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter
  intl: ^0.19.0

dev_dependencies:
  intl_translation: ^0.19.0

2.2 配置MaterialApp

import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:intl/intl.dart';
import 'l10n/app_localizations.dart';

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('en', 'US'),
        Locale('zh', 'CN'),
        Locale('ja', 'JP'),
        Locale('ko', 'KR'),
      ],
      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 HomePage(),
    );
  }
}

三、创建翻译文件

3.1 创建ARB文件

ARB (Application Resource Bundle)是Flutter推荐的翻译文件格式:

// l10n/app_en.arb
{
  "@@locale": "en",
  "hello": "Hello",
  "welcome": "Welcome to our app",
  "greeting": "Hello {name}!",
  "itemsCount": "{count, plural, zero{No items} one{1 item} other{{count} items}}",
  "welcomeMessage": "{gender, select, male{Welcome Mr. {name}} female{Welcome Ms. {name}} other{Welcome {name}}}"
}
// l10n/app_zh.arb
{
  "@@locale": "zh",
  "hello": "你好",
  "welcome": "欢迎使用我们的应用",
  "greeting": "你好 {name}!",
  "itemsCount": "{count, plural, zero{没有项目} one{1个项目} other{{count}个项目}}",
  "welcomeMessage": "{gender, select, male{欢迎 {name} 先生} female{欢迎 {name} 女士} other{欢迎 {name}}}"
}

3.2 生成翻译代码

创建l10n.yaml配置文件:

arb-dir: l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
output-class: AppLocalizations
preferred-supported-locales:
  - en
  - zh

运行生成命令:

flutter pub run intl_translation:generate_from_arb \
  --output-dir=lib/l10n \
  --no-use-deferred-loading \
  lib/l10n/app_localizations.dart \
  l10n/app_en.arb \
  l10n/app_zh.arb

四、使用翻译

4.1 基本用法

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    final loc = AppLocalizations.of(context)!;

    return Scaffold(
      appBar: AppBar(title: Text(loc.welcome)),
      body: Center(
        child: Column(
          children: [
            Text(loc.hello),
            Text(loc.greeting('Flutter')),
            Text(loc.itemsCount(5)),
            Text(loc.welcomeMessage('male', 'John')),
          ],
        ),
      ),
    );
  }
}

4.2 使用BuildContext扩展

extension AppLocalizationsX on BuildContext {
  AppLocalizations get loc => AppLocalizations.of(this)!;
}

// 使用
Text(context.loc.hello)

五、复数和选择器

5.1 复数处理

// app_en.arb
{
  "itemsCount": "{count, plural, zero{No items} one{1 item} other{{count} items}}"
}
// 使用
Text(loc.itemsCount(0))  // No items
Text(loc.itemsCount(1))  // 1 item
Text(loc.itemsCount(5))  // 5 items

5.2 选择器

// app_en.arb
{
  "welcomeMessage": "{gender, select, male{Welcome Mr. {name}} female{Welcome Ms. {name}} other{Welcome {name}}}"
}
Text(loc.welcomeMessage('male', 'John'))    // Welcome Mr. John
Text(loc.welcomeMessage('female', 'Jane'))  // Welcome Ms. Jane
Text(loc.welcomeMessage('other', 'Alex'))   // Welcome Alex

5.3 组合使用

{
  "notification": "{unreadCount, plural, zero{No new notifications} one{1 new notification from {sender}} other{{unreadCount} new notifications from {sender}}}"
}

六、日期和数字格式化

6.1 日期格式化

import 'package:intl/intl.dart';

void formatDates() {
  final now = DateTime.now();
  
  // 中文格式
  print(DateFormat.yMMMd('zh_CN').format(now));  // 2025年5月20日
  print(DateFormat.yMMMEd('zh_CN').format(now)); // 2025年5月20日 星期二
  print(DateFormat.Hm('zh_CN').format(now));     // 14:30
  
  // 英文格式
  print(DateFormat.yMMMd('en_US').format(now));  // May 20, 2025
  print(DateFormat.yMMMEd('en_US').format(now)); // Tue, May 20, 2025
}

6.2 数字格式化

void formatNumbers() {
  final number = 1234567.89;
  
  // 中文格式
  print(NumberFormat.decimalPattern('zh_CN').format(number));  // 1,234,567.89
  print(NumberFormat.currency(locale: 'zh_CN', symbol: '¥').format(number)); // ¥1,234,567.89
  
  // 英文格式
  print(NumberFormat.decimalPattern('en_US').format(number));  // 1,234,567.89
  print(NumberFormat.currency(locale: 'en_US', symbol: '\$').format(number)); // \$1,234,567.89
}

6.3 百分比格式化

void formatPercentages() {
  final percentage = 0.75;
  
  print(NumberFormat.percentPattern('zh_CN').format(percentage)); // 75%
  print(NumberFormat.percentPattern('en_US').format(percentage)); // 75%
}

七、动态切换语言

7.1 使用状态管理

class LocaleProvider extends ChangeNotifier {
  Locale _locale = const Locale('en', 'US');
  
  Locale get locale => _locale;
  
  void setLocale(Locale locale) {
    _locale = locale;
    notifyListeners();
  }
}

7.2 更新MaterialApp

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,
          // ...其他配置
        );
      },
    );
  }
}

7.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支持

8.1 启用RTL

MaterialApp(
  // ...
  supportedLocales: const [
    Locale('ar', 'SA'), // 阿拉伯语
  ],
  // ...
)

8.2 RTL布局适配

Widget build(BuildContext context) {
  return Directionality(
    textDirection: TextDirection.rtl,
    child: const Text('مرحبا'), // 阿拉伯语"你好"
  );
}

8.3 动态方向检测

bool isRTL = Directionality.of(context) == TextDirection.rtl;

九、最佳实践

9.1 翻译文件管理

lib/
└── l10n/
    ├── app_localizations.dart    # 自动生成
    ├── app_localizations_delegate.dart
    └── arb/
        ├── app_en.arb
        ├── app_zh.arb
        ├── app_ja.arb
        └── app_ko.arb

9.2 使用专业翻译工具

推荐使用专业翻译管理平台:

  • Crowdin
  • Lokalise
  • Transifex

9.3 测试多语言环境

void main() {
  testWidgets('Chinese localization', (tester) async {
    await tester.pumpWidget(
      MaterialApp(
        locale: const Locale('zh', 'CN'),
        localizationsDelegates: const [
          AppLocalizations.delegate,
          GlobalMaterialLocalizations.delegate,
          GlobalWidgetsLocalizations.delegate,
        ],
        home: const HomePage(),
      ),
    );
    
    expect(find.text('你好'), findsOneWidget);
  });
}

9.4 性能优化

// 使用const避免重复创建
const supportedLocales = [
  Locale('en', 'US'),
  Locale('zh', 'CN'),
];

十、常见问题

10.1 翻译字符串未更新

解决方案:确保运行了翻译生成命令:

flutter pub run intl_translation:generate_from_arb ...

10.2 Locale未生效

解决方案:检查localeResolutionCallback配置:

localeResolutionCallback: (locale, supportedLocales) {
  return locale ?? supportedLocales.first;
}

10.3 日期格式不正确

解决方案:确保导入了intl包并使用正确的Locale:

import 'package:intl/intl.dart';

DateFormat.yMMMd(Localizations.localeOf(context).toString())

十一、总结

Flutter的国际化支持非常强大,通过合理配置可以轻松创建多语言应用。关键要点:

  1. 使用ARB文件管理翻译内容
  2. 利用intl包进行日期、数字格式化
  3. 支持复数、选择器等复杂场景
  4. 实现动态语言切换
  5. 考虑RTL语言支持

掌握Flutter国际化开发,将使你的应用能够服务全球用户,提升用户体验和产品竞争力。

更多推荐