Flutter 国际化与本地化:intl、ARB 与多语言应用

Flutter 国际化与本地化完整实践:Locale 与本地化流程、ARB 文件与 gen-l10n 代码生成、intl 日期/数字/货币格式化、RTL 布局支持、动态语言切换与持久化,以及多语言内容管理的最佳实践。

开篇:好的国际化不是"翻译字符串"

当你的应用准备走向海外市场,“国际化(i18n)“和"本地化(l10n)“就不仅仅是把按钮文字翻译成英文那么简单。日期在各国书写习惯不同、金额需要按地区货币符号与小数位显示、阿拉伯语和希伯来语需要 RTL 布局、时区与复数规则千差万别——真正的本地化是"让每种语言的用户都感觉应用是原生的”。

Flutter 提供了一套完整的国际化基础设施:flutter_localizations 提供组件内置文案(如日期选择器、对话框),intl 提供日期/数字/货币的格式化能力,ARB 文件 + gen-l10n 把翻译声明式地接入代码。本章将系统讲解从零构建多语言应用的完整流程。


一、国际化基础与 Locale

1.1 Locale 是什么

Locale 是语言与地区的组合标识,格式为 语言代码-地区代码,如 zh-CN、en-US、ja-JP。Flutter 通过它决定显示哪种语言与格式。

const localeZh = Locale('zh', 'CN'); // 简体中文(中国)
const localeEn = Locale('en');        // 英语
const localeAr = Locale('ar');        // 阿拉伯语(触发 RTL)

1.2 配置 flutter_localizations

国际化第一步是在 pubspec.yaml 与 MaterialApp 中启用本地化支持:

# pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter
  intl: any
  intl_translation: any # 可选,用于某些工具链
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';

MaterialApp(
  localizationsDelegates: const [
    GlobalMaterialLocalizations.delegate, // Material 组件内置文案
    GlobalWidgetsLocalizations.delegate,  // Widget 层(如文本方向)
    GlobalCupertinoLocalizations.delegate,// Cupertino 组件内置文案
  ],
  supportedLocales: const [
    Locale('zh', 'CN'),
    Locale('en'),
    Locale('ar'),
  ],
  locale: const Locale('zh', 'CN'), // 手动指定;不指定则跟随系统
  home: const HomePage(),
)
Delegate提供的内容
GlobalMaterialLocalizations日期选择器、对话框、菜单等组件文案
GlobalWidgetsLocalizations文本方向(LTR/RTL)
GlobalCupertinoLocalizationsiOS 风格组件文案

一句话:flutter_localizations 负责"组件内置文案"的本地化,intl 负责"业务数据格式"的本地化,两者配合才算完整的国际化。


二、ARB 文件与 gen-l10n

2.1 ARB 文件格式

ARB(Application Resource Bundle)是 Flutter 官方推荐的翻译文件格式,本质是 JSON。每个 locale 一个文件:

// lib/l10n/app_zh.arb
{
  "@@locale": "zh",
  "appTitle": "我的应用",
  "@appTitle": {
    "description": "应用标题"
  },
  "welcome": "欢迎,{name}!",
  "@welcome": {
    "description": "带参数的问候语",
    "placeholders": {
      "name": { "type": "String" }
    }
  },
  "itemCount": "{count, plural, =0{没有项目} =1{1 个项目} other{{count} 个项目}}",
  "@itemCount": {
    "placeholders": {
      "count": { "type": "int" }
    }
  }
}
// lib/l10n/app_en.arb
{
  "@@locale": "en",
  "appTitle": "My App",
  "welcome": "Welcome, {name}!",
  "itemCount": "{count, plural, =0{No items} =1{1 item} other{{count} items}}"
}

2.2 配置 gen-l10n 生成代码

在 pubspec.yaml 中启用代码生成:

flutter:
  generate: true # 启用 gen-l10n

  l10n:
    arb-dir: lib/l10n          # ARB 文件目录
    template-arb-file: app_zh.arb
    output-localization-file: app_localizations.dart
    output-class: AppLocalizations
    nullable-getter: false

运行 flutter gen-l10n(或 flutter pub run build_runner build)后,会生成 AppLocalizations 类,代码中即可安全访问翻译:

// 在 build 中获取本地化实例
AppLocalizations l10n = AppLocalizations.of(context)!;

Text(l10n.appTitle),                    // "我的应用"
Text(l10n.welcome('张三')),              // "欢迎,张三!"
Text(l10n.itemCount(3)),                // "3 个项目"

2.3 手动配置 delegate(可选)

如果不用代码生成,也可手动编写 delegate。但推荐始终使用 gen-l10n——它生成编译期安全的访问器、自动处理复数与占位符:

方式优点缺点
gen-l10n(推荐)编译期安全、自动复数/占位符需要 codegen
手写 delegate无额外工具易错、繁琐

一句话:ARB 是"翻译的源文件”,gen-l10n 把它变成编译期安全的 Dart 代码——l10n.welcome('张三') 这样调用,拼错 key 直接编译报错。


三、intl 格式化:日期、数字、货币

3.1 日期格式化

不同地区日期写法差异极大,DateFormat 按 locale 自动适配:

import 'package:intl/intl.dart';

final zhDate = DateFormat.yMMMd('zh').format(DateTime.now());
// 例如 2026年9月26日

final enDate = DateFormat.yMMMd('en').format(DateTime.now());
// 例如 Sep 26, 2026

// 自定义格式
final custom = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
// 2026-09-26 10:00

3.2 数字与货币格式化

final nf = NumberFormat('#,##0.00', 'en_US');
print(nf.format(1234567.891)); // 1,234,567.89

// 货币:按地区自动带符号与小数位
final usd = NumberFormat.currency(locale: 'en_US', symbol: r'$');
print(usd.format(19.99)); // $19.99

final cny = NumberFormat.currency(locale: 'zh', symbol: '¥');
print(cny.format(19.99)); // ¥19.99

final euro = NumberFormat.currency(locale: 'de', name: 'EUR');
print(euro.format(19.99)); // 19,99 €(注意小数逗号)

3.3 格式化能力对照

类能力地区差异示例
DateFormat日期时间yyyy/MM/dd(中)vs MM/dd/yyyy(美)
NumberFormat数字千分位/小数1,234.5 vs 1.234,5
NumberFormat.currency货币符号$ / ¥ / € / £
Intl.plural复数规则英文 2 种、阿拉伯语 6 种

一句话:绝不要手动拼接日期/数字/货币——intl 按 locale 自动处理千分位、小数位、货币符号和复数规则,交给它最省心。


四、RTL 布局支持

4.1 RTL 语言的挑战

阿拉伯语、希伯来语、波斯语使用从右到左(RTL)的书写方向。Flutter 通过 Directionality 自动适配,多数内置组件开箱即用:

// 语言切换为阿拉伯语后,Row/Column 的方向会自动翻转
Row(
  children: [
    Icon(Icons.arrow_back), // 在 RTL 下自动镜像为向前
    Text('返回'),
  ],
)

// 手动指定方向
Directionality(
  textDirection: TextDirection.rtl,
  child: Row(children: [...]), // 强制 RTL
)

4.2 需要适配的常见点

场景问题解决方案
图标箭头方向固定用 Icons.arrow_forward 的镜像语义或 Directionality 包裹
Padding/Alignment硬编码左/右用 start/end 替代 left/right
自定义绘制文本锚点错误TextPainter.textDirection 跟随上下文
数字与混排数字方向默认按内容自动处理,无需干预
// 用逻辑方向而非物理方向
Padding(
  padding: const EdgeInsetsDirectional.only(
    start: 16,  // RTL 下自动成为右侧
    end: 8,
  ),
  child: Row(
    children: [
      // Row 的 children 顺序在 RTL 下自动反转
      Text('标签'),
      const Spacer(),
      Text('值'),
    ],
  ),
)

一句话:RTL 适配的黄金法则是"用逻辑方向(start/end)代替物理方向(left/right)"——Flutter 会在 RTL 语言下自动完成镜像。


五、动态语言切换与持久化

5.1 运行时切换语言

MaterialApp 的 locale 是响应式的,只需在状态管理中改变它:

class LocaleController extends ChangeNotifier {
  Locale _locale = const Locale('zh', 'CN');
  Locale get locale => _locale;

  Future<void> setLocale(Locale locale) async {
    _locale = locale;
    notifyListeners();
    final prefs = await SharedPreferences.getInstance();
    await prefs.setString('locale', locale.toString()); // 持久化
  }
}

// 在 MaterialApp 中绑定
ListenableBuilder(
  listenable: localeController,
  builder: (context, _) => MaterialApp(
    locale: localeController.locale,
    supportedLocales: const [Locale('zh', 'CN'), Locale('en')],
    // ...delegates
  ),
)

5.2 语言选择器

DropdownButton<Locale>(
  value: currentLocale,
  items: const [
    DropdownMenuItem(value: Locale('zh', 'CN'), child: Text('简体中文')),
    DropdownMenuItem(value: Locale('en'), child: Text('English')),
    DropdownMenuItem(value: Locale('ja'), child: Text('日本語')),
  ],
  onChanged: (locale) => context.read<LocaleController>().setLocale(locale!),
)

5.3 方案对比

方案跟随系统用户手动选择持久化
locale: null✅❌无需
手动 + SharedPreferences可回退✅✅
本地化包(如 easy_localization)✅✅✅

一句话:动态语言切换 = “可变的 locale + 状态管理”——把 locale 存进控制器并持久化,切换即生效、重启即恢复。


六、多语言内容管理最佳实践

6.1 翻译流程与角色分工

环节责任方产出物
文案提取开发者新增 key 到 ARB
翻译翻译人员/本地化平台各 locale ARB
审核产品/QA语境截图、术语表
构建CI 流水线自动 gen-l10n

6.2 使用 Flutter Intl 插件提升效率

flutter-intl 插件(VS Code)支持从编辑器直接创建/编辑 ARB,并提供预览:

# .vscode/settings.json(示例)
{
  "flutter-intl.arbDir": "lib/l10n",
  "flutter-intl.outputDir": "lib/l10n",
  "flutter-intl.enabled": true
}

6.3 关键实践清单

  • Key 语义化:用 button.save、error.timeout 等层级命名,而非 text_001
  • 占位符类型化:ARB 中声明 type: String/int,保证生成代码类型安全
  • 复数必须处理:用 ICU 复数语法 {count, plural, ...} 而非字符串拼接
  • 翻译语境注释:用 @key 的 description 字段给翻译者说明语境
// 好的做法:把文案集中调用,避免字符串散落
class AppStrings {
  static String appTitle(BuildContext context) =>
      AppLocalizations.of(context)!.appTitle;
}

一句话:多语言内容管理的核心是"一个 key 源(ARB)+ 一条 CI 流水线”——key 语义化、类型安全、复数正确,翻译效率与代码质量就能双赢。


七、测试与 CI 集成

7.1 本地化单元测试

import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_localizations/flutter_localizations.dart';

void main() {
  testWidgets('英文环境渲染正确', (tester) async {
    await tester.pumpWidget(
      MaterialApp(
        locale: const Locale('en'),
        supportedLocales: const [Locale('en')],
        localizationsDelegates: GlobalMaterialLocalizations.delegates,
        home: const HomePage(),
      ),
    );
    expect(find.text('My App'), findsOneWidget);
  });
}

7.2 自动化验证清单

检查项方法
所有 locale 均有对应 ARBCI 脚本校验文件存在性
无缺 keyflutter gen-l10n 报错即失败
文本溢出各语言渲染截图 + Golden 测试
RTL 布局用 locale: ar 跑 Widget 测试

一句话:本地化的测试分两层——单元层验证"文案正确取到",集成层用真实 locale 渲染验证"布局不溢出"。


八、总结

Flutter 国际化与本地化是一个覆盖配置、生成、格式化、布局与运维的完整工程:

环节工具/机制核心要点
基础设施flutter_localizations组件内置文案 + 文本方向
翻译源ARB 文件语义化 key + 类型化占位符
代码生成gen-l10n编译期安全的访问器
格式本地化intl日期/数字/货币自动适配
布局适配DirectionalityRTL 用逻辑方向
动态切换Locale + 持久化用户可控、重启恢复
质量保障CI + Golden 测试缺 key 即失败、溢出即发现

一句话:国际化不是"翻译一下就行",而是"本地化思维贯穿架构"——从 ARB 到 gen-l10n,从 intl 到 RTL,从动态切换再到 CI 保障,环环相扣才能让应用真正走向全球。


相关阅读

  • https://plumephp.com/flutter-architecture-patterns/ — 架构模式(i18n 在大型项目的组织)
  • https://plumephp.com/flutter-state-management/ — 状态管理(locale 作为全局状态)
  • https://plumephp.com/flutter-form-validation/ — 表单验证(本地化后的表单校验文案)

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「Flutter」更多文章

  1. Flutter 表单与输入验证:Form、Validator 与自定义控件
  2. Flutter 渲染引擎与框架内部:Widget 树、Element 树与渲染管线
  3. Flutter 无障碍可访问性:语义、屏幕阅读器与导航