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

Flutter - 国际化

环境

Flutter 3.29
macOS Sequoia 15.4.1
Xcode 16.3

配置

安装依赖包

默认情况下,Flutter只提供美式英语的本地化,可以通过flutter_localizations这个package来实现国际化。

创建flutter工程后执行

¥ flutter pub add flutter_localizations --sdk=flutter
¥ flutter pub add intl:any

执行后的pubspec.yaml文件的效果

dependencies:flutter:sdk: fluttercupertino_icons: ^1.0.8flutter_localizations:sdk: flutterintl: any

安装对应的package

¥ flutter pub get 

2025-05-22 11.28.32.png

pubspec.yaml文件

在 Flutter 项目的根目录中添加一个新的 yaml 文件,命名为 l10n.yaml,其内容如下:

arb-dir: lib/common/localization/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart 
  1. 将 应用资源包 (.arb) 的输入路径指定为 ${FLUTTER_PROJECT}/lib/common/localization/l10n。
  2. 将英文的语言模板设定为 app_en.arb。
  3. 指定 Flutter 生成本地化内容到 app_localizations.dart 文件。

要在pubspec.yaml 文件中,设置generate为true,执行命令时才会生成对应的app_localizations.dart文件

flutter:generate: true

指定这个是为了生成项目时能自动生成对应的.dart代码文件

${FLUTTER_PROJECT}/lib/common/localization/l10n 添加 app_en.arb 模板文件

{"helloWorld": "Hello World!"
}

在同一目录下添加中文的模板文件 app_zh.arb

{"helloWorld":"你好,世界"
}

执行命令生成.dart文件

¥ flutter run

运行报错

Synthetic package output (package:flutter_gen) is deprecated: https://flutter.dev/to/flutter-gen-deprecation. In a future release, synthetic-package will
default to `false` and will later be removed entirely.
Generating synthetic localizations package failed with 1 error:Error: Attempted to generate localizations code without having the flutter: generate flag turned on.
Check pubspec.yaml and ensure that flutter: generate: true has been added and rebuild the project. Otherwise, the localizations source code will not be
importable.

有两个问题,一个是使用flutter_gen生成synthetic package 的方式已经过期,推荐使用在 Flutter 项目中指定的目录下生成国际化资源代码文件。该标志默认为 true,还有一个报错是因为pubspec.yaml没有设置允许自动生成代码

解决方案: l10n.yaml文件中追加设置不使用Synthetic package的方式生成国际化资源文件

synthetic-package: false

解决方案: pubspec.yaml设置允许自动生成代码

flutter:generate: true

重新运行 flutter run

报错消失,并在l10n目录下生成对应的.dart代码文件

2025-05-22 14.16.28.png

打开 app_localizations.dart

import 'dart:async';import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:intl/intl.dart' as intl;import 'app_localizations_en.dart';
import 'app_localizations_zh.dart';abstract class AppLocalizations {AppLocalizations(String locale) : localeName = intl.Intl.canonicalizedLocale(locale.toString());final String localeName;static AppLocalizations? of(BuildContext context) {return Localizations.of<AppLocalizations>(context, AppLocalizations);}static const LocalizationsDelegate<AppLocalizations> delegate = _AppLocalizationsDelegate();/// 国际化代理对象的集合/// 代理中实现支持哪些语言,是否重新加载static const List<LocalizationsDelegate<dynamic>> localizationsDelegates = <LocalizationsDelegate<dynamic>>[delegate,/// Material风格的组件的国际化GlobalMaterialLocalizations.delegate,/// iOS风格的组件的国际化GlobalCupertinoLocalizations.delegate,/// Widget组件的国际化GlobalWidgetsLocalizations.delegate,];/// A list of this localizations delegate's supported locales./// 支持的语言列表static const List<Locale> supportedLocales = <Locale>[Locale('en'),Locale('zh')];/// 国际化的词条String get helloWorld;
}/// 自定义的词条的代理对象
class _AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {const _AppLocalizationsDelegate();Future<AppLocalizations> load(Locale locale) {/// 同步加载资源return SynchronousFuture<AppLocalizations>(lookupAppLocalizations(locale));}bool isSupported(Locale locale) => <String>['en', 'zh'].contains(locale.languageCode);/// Returns true if the resources for this delegate should be loaded again by calling the [load] method./// 调用load方法后是否要重新加载bool shouldReload(_AppLocalizationsDelegate old) => false;
}AppLocalizations lookupAppLocalizations(Locale locale) {// Lookup logic when only language code is specified.switch (locale.languageCode) {case 'en': return AppLocalizationsEn();case 'zh': return AppLocalizationsZh();}throw FlutterError('AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ''an issue with the localizations generation tool. Please file an issue ''on GitHub with a reproducible sample app and the gen-l10n configuration ''that was used.');
}

使用

场景: 系统的Widgets

使用系统的组件,比如日历

Widget build(BuildContext context) {return MaterialApp(title: "localization示例",/// 1. 设置代理对象列表/// 一般开发都会有业务相关的词条,会创建词条,根据上面的代码可以传AppLocalizations.localizationsDelegates/// 没有其它自定义词条时也可以/// GlobalMaterialLocalizations.delegate,/// GlobalWidgetsLocalizations.delegate,/// GlobalCupertinoLocalizations.delegate,localizationsDelegates: AppLocalizations.localizationsDelegates,/// 2. 设置支持语言supportedLocales: [Locale('en'), Locale('zh')],theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),),home: MyHomePage(title: 'home page'),locale: Locale("zh"), /// 设置应用是中文还是英文,默认跟随系统);}
...
children: <Widget>[CalendarDatePicker(initialDate: DateTime.now(),firstDate: DateTime(2020, 3, 30),lastDate: DateTime(2025, 6, 31),onDateChanged: (value) => {logger.d(value.toString())},),
],
...

2025-05-22 16.14.00.png

locale参数传Locale("en") 再次运行

2025-05-22 17.00.22.png

场景: 自定义

实现业务过程中相关的自定义的词条

...
return Scaffold(appBar: AppBar(backgroundColor: Theme.of(context).colorScheme.inversePrimary,title: Text(AppLocalizations.of(context)!.helloWorld),),
...

效果同上,中文时显示你好,世界,英文时显示Hello World

场景: 切换语言

添加get依赖

get: 4.7.2
Widget build(BuildContext context) {/// 1.MaterialApp => GetMaterialAppreturn GetMaterialApp(title: "localization示例",localizationsDelegates: AppLocalizations.localizationsDelegates,supportedLocales: [Locale('en'), Locale('zh')],theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),),home: MyHomePage(title: 'home page'),locale: Locale("en"),);}
/// 使用updateLocale来设置语言
if (Get.locale?.languageCode == "zh") {Get.updateLocale(Locale('en'));
} else {Get.updateLocale(Locale('zh'));
}

202505221709.gif

场景: 占位符,复数和选项

当需要再词条中接收变量时可以使用占位符

"hello": "Hello {userName}"
"@hello": {"description": "A message with a single parameter","placeholders": {"userName": {"type": "String","example": "Bob"}}
}
 "hello": "你好 {userName}","@hello": {"description": "A message with a single parameter","placeholders": {"userName": {"type": "String","example": "张三"}}}
...
Text(AppLocalizations.of(context)!.hello("Programmer")),
...

20250523082627.gif

你还可以使用数字占位符来指定多个值。不同的语言有不同的单词复数化形式。该语法还支持指定单词的复数化形式。

"{countPlaceholder, plural, =0{message0} =1{message1} =2{message2} few{messageFew} many{messageMany} other{messageOther}}"

countPlaceholder: 变量名
plural: 固定搭配
=0{message0} =1{message1}…: 参数0时显示message0,参数1时显示message1

app_en.arb

"people": "{count, plural, =0{no people} =1{person} other{people} }",
"@people": {"description": "A plural people","placeholders": {"count": {"type": "num","format": "compact"}}
}

app_zh.arb

 "people": "{count, plural, =0{没有人} =1{一个人} other{人群}}",
"@people": {"description": "A plural people","placeholders": {"count": {"type": "num","format": "compact"}}
}

other的情况必须填写,不然会报

[app_en.arb:people] ICU Syntax Error: Plural expressions must have an “other” case.
{count, plural, =0{no people} =1{person} few{some people} }
^
[app_zh.arb:people] ICU Syntax Error: Plural expressions must have an “other” case.
{count, plural, =0{没有人} =1{一个人} few{一些人} }
^
Found syntax errors.

添加词条后可以在.arb文件中右键然后选择Generate Localizations 生成国际化的.dart文件

2025-05-23 08.40.56.png

...
Text(AppLocalizations.of(context)!.people(0)),
Text(AppLocalizations.of(context)!.people(1)),
Text(AppLocalizations.of(context)!.people(2)),
...

20250523084809.gif

也可以使用String占位符来表示选择一个值,类似词条使用case语句的感觉

"{selectPlaceholder, select, case{message} ... other{messageOther}}"

selectPlaceholder: 变量名
select: 固定搭配
case…other: 情况1,情况2,其它情况

"pronoun": "{gender, select, male{he} other{she}}",
"@pronoun": {"description": "A gendered message","placeholders": {"gender": {"type": "String"}}
}
"pronoun": "{gender, select, male{他} other{她}}",
"@pronoun": {"description": "A gendered message","placeholders": {"gender": {"type": "String"}}
}
Text(AppLocalizations.of(context)!.pronoun('male')),
Text(AppLocalizations.of(context)!.pronoun('female')),

20250523085334.gif

其它

避免语法解析

有时候符号(例如{})也可能是普通文本的部分,如果不想被解析为一种语法,可以在l10n.yaml中设置use-escaping

use-escaping: true

启用后,解析器会忽略使用一对单引号包括的文字,如果在文字中又想使用单个单引号,需要使用成对的单引号进行转义。

配置l10n.yaml文件

通过l10n.yaml文件,可以配置gen-l10n工具,指定以下内容

  • 所有输入文件的位置
  • 所有输出文件的创建位置
  • 为本地化代理设置自定义的类名

可以在命令行运行 flutter gen-10n --help 获取命令的具体使用

详见Flutter 应用里的国际化

参考

  1. Flutter 应用里的国际化
  2. Localized messages are generated into source, not a synthetic package.
http://www.xdnf.cn/news/608275.html

相关文章:

  • Flutter 3.32 升级要点全解析
  • ros2 humble安装ros-humble-tf2-tools
  • 布丁扫描高级会员版 v3.5.2.2| 安卓智能扫描 APP OCR文字识别小助手
  • 数字人交互系统哪家强?品牌技术对比!
  • JavaScript进阶(十二)
  • 【AS32X601驱动系列教程】GPIO_点亮LED详解
  • 在bash中,如何打开特定文件,使用特定字符串替换特定字符串?请编写代码
  • 哈希表的实现(上)
  • mac将自己网络暴露到公网
  • ROS云课三分钟-cmake gcc g++ 默认版本和升级-250523
  • 前后端联调实战指南:Axios拦截器、CORS与JWT身份验证全解析
  • 提示词工程框架——CO-STAR 框架实战
  • 江科大DMA直接存储器访问hal库实现
  • 深度剖析 MCP SDK 最新版:Streamable HTTP 模式
  • 学习黑客Nmap 是什么?
  • 数据结构 -- 交换排序(冒泡排序和快速排序)
  • 信息安全管理与评估赛项参考答案-模块1网络平台搭建
  • 【软件测试】第三章·软件测试基本方法(基于需求的测试方法)
  • Trae+12306 MCP,10分钟搭建行程可视化助手
  • Gmsh 代码深度解析与应用实例
  • 【开源项目1】基于机器学习木马查杀引擎项目
  • 1.3 线性系统的时域分析法
  • kafka速度快的原理
  • 【时时三省】(C语言基础)对被调用函数的声明和函数原型
  • [Datagear] [SQL]实现分组统计同时带汇总行的两种方式对比分析
  • AI架构师的新工具箱:ChatGPT、Copilot、AutoML、模型服务平台
  • NtfsLookupAttributeByName函数分析之和Scb->AttributeName的关系
  • vim快速移动光标
  • 多路径传输(比如 MPTCP)控制实时突发
  • 动态规划经典三题_完全平方数