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

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: flutter2.2 配置 MaterialAppimport 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.yamlflutter: 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 使用 Providervoid main() { runApp( ChangeNotifierProvider( create: (context) LocaleProvider(), child: const MyApp(), ), ); } class MyApp extends StatelessWidget { const MyApp({super.key}); override Widget build(BuildContext context) { return ConsumerLocaleProvider( 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.ofLocaleProvider(context); return DropdownButtonLocale( 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 WidgetDirectionality( 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, 20237.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/3110.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 ConsumerLocaleProvider( 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 支持- 处理从右到左的语言格式化- 日期、时间、数字的本地化格式化通过合理的国际化设计可以让应用程序更好地服务于全球用户。
http://www.zskr.cn/news/1399249.html

相关文章:

  • ARMv8/ARMv9架构MDCR_EL3寄存器详解与调试实践
  • MemPalace:开源AI记忆系统,实现LLM持久化本地记忆管理
  • 后台静默失效:系统隐形杀手与高可用架构防御实战
  • AI协同开发实战:从架构设计到部署的十四周SaaS平台构建
  • AutoDL远程桌面连接保姆级教程:从VNC Viewer配置到SSH隧道避坑(附进程管理)
  • AI编程工具成本优化实战:Squeezr代理压缩上下文节省70%API开销
  • 告别Thonny!用VSCode+RT-Thread插件玩转合宙ESP32-C3的MicroPython开发(附固件烧录避坑指南)
  • ShotgunWSD 2.0:基于词向量聚类与离群点消除的全局词义消歧算法详解
  • 手把手教你理解Xilinx PCIe IP核的AXI-Stream接口:以PG213文档中的m_axis_cq_tuser为例
  • 企业级实时音视频方案怎么选?自建、SDK集成、全托管三套方案成本对比
  • 别再让远处的模型糊成一片了!Unity/UE4中Mipmap的正确打开方式与性能调优
  • 别再让SkinnedMeshRenderer拖垮你的游戏!Unity骨骼动画性能优化实战(BakeMesh + 动态合批)
  • 避坑指南:Automation Studio变量关联与PCVue数据缩放的那些“坑”
  • AI代码生成五大症结与可持续集成工作流实践
  • 告别鼠标依赖!用Python的keyboard库打造你的专属键盘快捷键(附完整代码)
  • C语言中“\n”是什么意思
  • 别再手动调参了!用MATLAB实现VSS LMS自适应滤波器,让收敛速度和稳态误差自动平衡
  • nnUNetv2训练自定义数据集翻车实录:从mask格式报错到成功跑通2D模型的避坑总结
  • 别再手动改配置了!用Maven Profile一键切换Tomcat和TongWeb 7.0.E.6嵌入式环境
  • AD18/19新手避坑指南:Board Report里这些数据到底什么意思?(附PCB信息完整解读)
  • 倾斜摄影OSGB数据转换全流程详解:从数据下载、整理到3DTiles/S3M/I3S生成
  • 别再乱填了!Modbus Slave模拟器Connection和Slave Definition参数保姆级配置指南
  • 告别玄学调参!用HFSS优化功能自动找到T形波导的最佳隔片位置
  • 信贷风控新范式:从预测到因果推断的实践与挑战
  • SaaS产品定价策略:如何通过9美元订阅计划解决创作者资源排队痛点
  • 手把手教你用tinygrad框架跑通LLaMA模型:一个轻量级AI库的实战入门指南
  • 别再只看衰减了!手把手教你读懂USB3.0线束测试报告(以AVT相机线为例)
  • 别再死记硬背了!用Python画个动图,5分钟搞懂Moore和Mealy状态机的区别
  • RK3588开发板触摸屏调试实录:搞定GT9XX驱动编译与DTS配置的那些坑
  • Python开发新范式:MCP峰会揭示工具链、并发与依赖管理的变革