当前位置: 首页 > news >正文

Flutter 应用国际化 (i18n) 与本地化 (l10n) 完整指南


Flutter 国际化 (i18n) 完全指南:从入门到精通

在现代移动应用开发中,支持多语言是触达全球用户的基本要求。Flutter 提供了强大且灵活的国际化 (i18n) 和本地化 (l10n) 支持。本文将带你从零开始,一步步深入掌握在 Flutter 中实现国际化的几种主流方法。

目录

  1. 核心概念
  2. 准备工作:添加依赖
  3. 方法一:使用 flutter_localizations 基础方案
  4. 方法二:使用 intl 包和 ARB 文件(推荐)
  5. 方法三:使用 easy_localization 第三方库
  6. 动态切换语言
  7. 处理语言环境相关的数据
  8. 最佳实践与总结

核心概念

· 国际化 (Internationalization, i18n): 指在应用设计和开发过程中,使其能够轻松适配不同语言和地区的流程。它是在开发阶段完成的。
· 本地化 (Localization, l10n): 指为国际化的应用添加特定语言环境(Locale)的翻译和格式的过程。它是在国际化之后进行的。
· Locale: 是一个用于标识用户语言和地区偏好的对象,例如 en-US(美国英语)、zh-CN(简体中文)、zh-TW(繁体中文)。

Flutter 中的国际化主要涉及:

  1. 为文本(字符串)提供多种语言的翻译。
  2. 格式化地区相关的数据,如日期、时间、数字和货币。
  3. 根据语言环境(如 LTR 或 RTL)调整布局。

准备工作:添加依赖

首先,在你的 pubspec.yaml 文件中添加必要的依赖。我们将使用官方推荐的 intl 包。

dependencies:flutter:sdk: flutterflutter_localizations: # 提供内置组件的本地化资源和基础类sdk: flutterintl: ^0.18.1 # 用于高级国际化功能,如消息翻译、日期/数字格式化dev_dependencies:flutter_test:sdk: flutterflutter_lints: ^2.0.0build_runner: ^2.4.0 # 用于生成代码intl_translation: ^0.9.0 # 用于从 ARB 文件提取和生成本地化代码

运行 flutter pub get 来安装这些依赖。

方法一:使用 flutter_localizations 基础方案

这种方法适用于简单的应用,手动管理所有字符串。

  1. 配置 MaterialApp/CupertinoApp

在 lib/main.dart 中,配置你的主 Widget 以支持国际化。

import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',// 支持的语言列表supportedLocales: const [Locale('en', ''), // 英语Locale('zh', 'CN'), // 简体中文Locale('es', ''), // 西班牙语],// 本地化代理,用于加载翻译和设置特定的本地化功能localizationsDelegates: const [// 提供默认的 Flutter 控件本地化字符串(如按钮文本)GlobalMaterialLocalizations.delegate,GlobalCupertinoLocalizations.delegate,GlobalWidgetsLocalizations.delegate,// 稍后我们会添加自己的代理// AppLocalizations.delegate,],// 当语言环境不在 supportedLocales 中时,回退到哪个语言localeResolutionCallback: (locale, supportedLocales) {for (var supportedLocale in supportedLocales) {if (supportedLocale.languageCode == locale?.languageCode) {return supportedLocale;}}return supportedLocales.first; // 回退到第一个支持的语言},home: const MyHomePage(),);}
}
  1. 创建自定义本地化类

创建一个文件 lib/l10n/app_localizations.dart。

import 'dart:async';
import 'package:flutter/material.dart';class AppLocalizations {final Locale locale;AppLocalizations(this.locale);static AppLocalizations of(BuildContext context) {return Localizations.of<AppLocalizations>(context, AppLocalizations)!;}static const LocalizationsDelegate<AppLocalizations> delegate =_AppLocalizationsDelegate();// 静态变量存储翻译Mapstatic Map<String, Map<String, String>> _localizedStrings = {'en': {'title': 'Hello World!','message': 'Welcome to my Flutter app.',},'zh_CN': {'title': '你好,世界!','message': '欢迎使用我的 Flutter 应用。',},'es': {'title': '¡Hola Mundo!','message': 'Bienvenido a mi aplicación Flutter.',},};// 获取翻译的方法String translate(String key) {return _localizedStrings[locale.toString()]![key] ?? '** $key not found **';}// 也可以使用 getter 方法,使代码更清晰String get title => translate('title');String get message => translate('message');
}// 本地化代理,负责加载具体的本地化资源
class _AppLocalizationsDelegateextends LocalizationsDelegate<AppLocalizations> {const _AppLocalizationsDelegate();bool isSupported(Locale locale) {// 支持的语言列表return ['en', 'zh', 'es'].contains(locale.languageCode);}Future<AppLocalizations> load(Locale locale) async {// 这里通常是异步加载资源的地方(例如从JSON文件)// 我们这个例子是同步的return SynchronousFuture<AppLocalizations>(AppLocalizations(locale));}bool shouldReload(_AppLocalizationsDelegate old) => false;
}
  1. 使用翻译

在 Widget 中,使用 AppLocalizations.of(context) 来获取翻译后的文本。

// lib/pages/my_home_page.dart
import 'package:flutter/material.dart';
import '../l10n/app_localizations.dart';class MyHomePage extends StatelessWidget {const MyHomePage({super.key});Widget build(BuildContext context) {var loc = AppLocalizations.of(context);return Scaffold(appBar: AppBar(title: Text(loc.title), // 使用 getter),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[Text(loc.message), // 使用 getterText(loc.translate('title')), // 或者使用 translate 方法],),),);}
}

优点:简单直接,无需代码生成。 缺点:手动管理所有键值对,容易出错,难以维护大量翻译。

方法二:使用 intl 包和 ARB 文件(推荐)

这是 Google 官方推荐的方法,它使用 .arb (Application Resource Bundle) 文件来管理翻译,并通过代码生成来自动创建本地化类。

  1. 项目结构

首先,创建以下目录结构:

lib/l10n/intl_*.arb          # ARB 翻译文件app_localizations.dart # 生成的代码会在这里
  1. 创建 ARB 文件

lib/l10n/intl_en.arb (主资源文件,必须)

{"@@locale": "en","title": "Hello World!","@title": {"description": "The title of the app on the home page"},"message": "Welcome to {appName}","@message": {"description": "A welcome message","placeholders": {"appName": {"type": "String","example": "My Flutter App"}}},"buttonText": "Click Me"
}

lib/l10n/intl_zh_CN.arb

{"@@locale": "zh_CN","title": "你好,世界!","message": "欢迎使用 {appName}","buttonText": "点击我"
}

lib/l10n/intl_es.arb

{"@@locale": "es","title": "¡Hola Mundo!","message": "Bienvenido a {appName}","buttonText": "Haz Clic"
}
  1. 提取和生成代码

在项目根目录运行以下命令,从代码中提取需要翻译的字符串到 ARB 文件(如果还没有的话): flutter pub run intl_translation:extract_to_arb --output-dir=lib/l10n lib/l10n/app_localizations.dart

然后,根据 ARB 文件生成 Dart 代码: flutter pub run intl_translation:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/l10n/app_localizations.dart lib/l10n/intl_*.arb

这将在 lib/l10n 目录下生成一系列 messages_*.dart 文件和一个 app_localizations.dart 文件。

  1. 使用生成的代码

生成的 app_localizations.dart 文件包含了所有逻辑。现在你可以在 Widget 中这样使用:

import '../l10n/app_localizations.dart';Text(AppLocalizations.of(context)!.title),
Text(AppLocalizations.of(context)!.message('My Awesome App')),
Text(AppLocalizations.of(context)!.buttonText),

优点:翻译与代码分离,易于管理和协作(可与翻译平台集成);支持带参数的文本;代码自动生成,减少错误。 缺点:需要设置构建步骤。

方法三:使用 easy_localization 第三方库

对于追求快速开发和简单配置的开发者,easy_localization 是一个极佳的选择。

  1. 添加依赖
dependencies:flutter:sdk: fluttereasy_localization: ^3.0.3dev_dependencies:flutter_test:sdk: flutterbuild_runner: ^2.4.0easy_localization_generator: ^3.0.0 # 用于代码生成(可选但推荐)
  1. 创建资源文件

在项目根目录创建 assets/translations 文件夹,并添加 JSON 或 CSV 文件。

assets/translations/en.jsonzh-CN.jsones.json

en.json

{"title": "Hello World!","message": "Welcome to {appName}","buttonText": "Click Me"
}

zh-CN.json

{"title": "你好,世界!","message": "欢迎使用 {appName}","buttonText": "点击我"
}
  1. 配置 pubspec.yaml

声明资源文件。

flutter:assets:- assets/translations/
  1. 配置 Main App
import 'package:easy_localization/easy_localization.dart';void main() async {WidgetsFlutterBinding.ensureInitialized(); // 需要先初始化await EasyLocalization.ensureInitialized(); // 初始化 EasyLocalizationrunApp(EasyLocalization(supportedLocales: const [Locale('en'), Locale('zh', 'CN'), Locale('es')],path: 'assets/translations', // 资源文件路径fallbackLocale: const Locale('en'), // 回退语言child: const MyApp(), // 你的应用),);
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return MaterialApp(localizationsDelegates: context.localizationDelegates,supportedLocales: context.supportedLocales,locale: context.locale,home: const MyHomePage(),);}
}
  1. 使用翻译
Text('title'.tr()), // 简单文本
Text('message'.tr(args: ['My App'])), // 带参数的文本
// 或者使用生成代码(如果用了 easy_localization_generator)
Text(LocaleKeys.title.tr()),

优点:设置简单,API 非常简洁,功能强大(支持复数、性别等)。 缺点:依赖第三方库。

动态切换语言

无论使用哪种方法,动态切换语言的逻辑是相似的。通常使用 Provider 或 Riverpod 来管理状态。

// 一个简单的 Provider 例子
import 'package:flutter/material.dart';class LocaleProvider with ChangeNotifier {Locale? _locale;Locale? get locale => _locale;void setLocale(Locale newLocale) {_locale = newLocale;notifyListeners();}void clearLocale() {_locale = null;notifyListeners();}
}// 在 MaterialApp 中使用
return MaterialApp(locale: context.watch<LocaleProvider>().locale, // 来自 Provider...
);// 在设置页面切换语言
ListTile(title: Text('English'),onTap: () {context.read<LocaleProvider>().setLocale(Locale('en'));Navigator.pop(context);},
),
ListTile(title: Text('简体中文'),onTap: () {context.read<LocaleProvider>().setLocale(Locale('zh', 'CN'));Navigator.pop(context);},
),

处理语言环境相关的数据

使用 intl 包来格式化数据。

import 'package:intl/intl.dart';String formatDate(DateTime date, BuildContext context) {return DateFormat.yMMMMd( Localizations.localeOf(context).toString() ).format(date);
}String formatCurrency(double amount, BuildContext context) {return NumberFormat.currency(locale: Localizations.localeOf(context).toString(),symbol: '' // 可能需要根据货币调整).format(amount);
}// 在 Widget 中使用
Text( formatDate(DateTime.now(), context) ),
Text( formatCurrency(29.99, context) ),

最佳实践与总结

  1. 选择合适的方法:
    · 小型项目/原型:easy_localization 最快。
    · 中大型项目/团队协作:官方 intl + ARB 文件最规范,易于维护。
    · 简单演示:手动管理 Map 也可以。
  2. 键名要有意义:使用类似 homePageWelcomeMessage 的键名,而不是 msg1。
  3. 提供上下文描述:在 ARB 文件中使用 @key 的 description 字段,帮助翻译者理解上下文。
  4. 处理文本方向:注意 RTL (Right-to-Left) 语言(如阿拉伯语、希伯来语)的布局适配。Directionality Widget 可以帮你。
  5. 不要拼接字符串:类似 ‘Hello ’ + name 的拼接在其他语言中语序可能不同,务必使用带参数的翻译。
  6. 测试:务必在不同语言环境下测试你的应用,检查布局是否错乱,翻译是否完整。

总结:Flutter 提供了从简单到复杂的多种国际化方案。intl + ARB 的组合是官方推荐的“黄金标准”,平衡了功能性和可维护性。而 easy_localization 则为开发者提供了快速实现的捷径。


文章转载自:

http://rotOYBo5.yqwsd.cn
http://I0iOhvZC.yqwsd.cn
http://tjPtLocn.yqwsd.cn
http://vUNmMyl6.yqwsd.cn
http://pgVArAee.yqwsd.cn
http://KNYM9M74.yqwsd.cn
http://5UbZ75Vu.yqwsd.cn
http://kbGgELvd.yqwsd.cn
http://DnSV0rwP.yqwsd.cn
http://Di3BDVdu.yqwsd.cn
http://sAN8E4F2.yqwsd.cn
http://IaU7GAKr.yqwsd.cn
http://Yfh8dThJ.yqwsd.cn
http://d7YcnSur.yqwsd.cn
http://gsCT6fZy.yqwsd.cn
http://nrY312SG.yqwsd.cn
http://97p9AlH8.yqwsd.cn
http://er2ZyHhn.yqwsd.cn
http://HPFrSBGB.yqwsd.cn
http://wRdwIma2.yqwsd.cn
http://RqUhkWid.yqwsd.cn
http://meUE8guf.yqwsd.cn
http://yOAmeveZ.yqwsd.cn
http://r9bhqgIu.yqwsd.cn
http://1PkhPrE1.yqwsd.cn
http://HHickb3R.yqwsd.cn
http://8nZhbi4A.yqwsd.cn
http://H5P8si8a.yqwsd.cn
http://j29rDKVj.yqwsd.cn
http://zMhH2WtZ.yqwsd.cn
http://www.dtcms.com/a/379990.html

相关文章:

  • 第 5 篇:深入浅出学 Java 语言(JDK8 版)—— 精通类与对象进阶,掌握 Java 面向对象核心能力
  • Gin-Vue-Admin学习笔记
  • Golang關於信件的
  • The 2024 ICPC Asia East Continent Online Contest (I)
  • 【数所有因子和快速新解/范围亲密数/分解因式怎么去掉重复项】2022-10-31
  • SQL语句执行时间太慢,有什么优化措施?以及衍生的相关问题
  • 【论文阅读】Language-Guided Image Tokenization for Generation
  • PHP:从入门到实战的全方位指南
  • 经典动态规划题解
  • 商城购物系统自动化测试报告
  • [工作表控件20] 拼音排序功能:中文数据高效检索实战指南
  • 9120 部 TMDb 高分电影数据集 | 7 列全维度指标 (评分 / 热度 / 剧情)+API 权威源 | 电影趋势分析 / 推荐系统 / NLP 建模用
  • 【Java】多态
  • LeetCode热题 438.找到字符中所有字母异位词 (滑动窗口)
  • 解决 N1 ARMBIAN Prometheus 服务启动失败问题
  • Linux 正则表达式详解(基础 + 扩展 + 实操)
  • 01.【Linux系统编程】Linux初识(Linux内核版本、基础指令、理论知识、shell命令及运行原理)
  • MATLAB 的无人机 PID 控制及智能 PID 控制器设计的仿真
  • D007 django+neo4j三维知识图谱医疗问答系统|3D+2D双知识图谱可视化+问答+寻医问药系统
  • 5G单兵图传 5G单兵 单兵图传 无线图传 无线图传方案 无人机图传解决方案 指挥中心大屏一目了然
  • npm / yarn / pnpm 包管理器对比与最佳实践(含国内镜像源配置与缓存优化)
  • 运维安全06 - 服务安全
  • nestjs(node.js) 跟 java 关于return 的JSON 数据转换
  • RabbitMQ---面试题
  • npm ERR! code CERT_HAS_EXPIRED
  • Windows、Linux 系统 nodejs 和 npm 版本更新及错误修复
  • 网站漏洞扫描要怎么处理?
  • 无线通信模块撑油库安全:传液位信号,简布线与后期维护工作
  • ruoyi-vue(十四)——前端框架及package.json,vite.config.js, main.js文件介绍
  • 【计算机网络 | 第15篇】动态主机配置协议