适配仓库: https://atomgit.com/oh-flutter/locale_plus
适配分支:
feat/ohos_locale_plus_2.0.0受测提交:
1387b3d8b3eadbb782c77c679a6446e50e9ebca0
一、最终效果与适配目标
国际化页面不只需要 zh_CN。金额输入要知道小数和分组符,日历要知道周首日,时间页需要 12/24 小时制、AM/PM 文本、IANA 时区及 UTC 偏移。locale_plus 2.0.0 已提供 13 个 Dart 查询接口,本次适配目标是保持接口和可空返回值不变,在 OHOS 上从系统国际化能力读取真实数据。

图 1:CHZ-AL00 / HarmonyOS 7.0.0.105 上读取到 zh、CN、Asia/Shanghai、28800 秒偏移、中文时段符号和公制偏好。
| 验证点 | 实测结果 | 证据 |
|---|---|---|
| 语言与地区 | zh / CN,二者独立读取 |
图 1、图 6 |
| 时间信息 | Asia/Shanghai、UTC 偏移 28800 秒、周首日 7 |
图 1、图 6 |
| 格式偏好 | 小数点、分组符、AM/PM、日期模式、12/24 小时制均返回 | 图 1 |
| 自动化与构建 | 15 Dart + 3 Widget + 15 ArkTS,共 33 项及 HAP 通过 | 图 4、图 5 |
| 验证边界 | 未修改系统语言、地区、历法或时区;其他 locale 由自动化覆盖 | 图 6 |
成果速览
| 项目 | 内容 |
|---|---|
| 上游基线 | TAG v2.0.0,提交 9897037d81007432306825c3fb860c204efb853c,MIT |
| 适配分支 | feat/ohos_locale_plus_2.0.0 |
| 适配提交 | 1387b3d8b3eadbb782c77c679a6446e50e9ebca0 |
| 新增 OHOS 能力 | 13 项语言、地区、数字、日期、时区与单位偏好查询 |
| 保持不变的接口 | LocalePlus 全部公开方法与 locale_plus 通道 |
| 真机结论 | 13 项基础读取通过;系统设置切换与其他设备未覆盖 |
二、实测环境与选库核对
| 组件 | 实测版本 |
|---|---|
| Flutter OH | 3.41.10-ohos-1.0.1 |
| Dart | 3.11.5 |
| DevEco Studio | 26.0.0 Release |
| HarmonyOS SDK | API 26;示例兼容 API 18 |
| 真机 | CHZ-AL00,HarmonyOS 7.0.0.105 |
| 插件 | locale_plus 2.0.0 |
环境搭建参考 Flutter OH 环境搭建指南。本文使用已完整回归的稳定版 Flutter OH;3.44.9+ohos-0.0.1-canary1 是预览标签,不冒充本次实测版本。
2026 年 9 月 11 日,我核对 pub.dev 最新版 2.0.0、上游 TAG 与发布包,并对目标组织仓库做同名排查。没有发现可直接使用的 OHOS 实现后,才创建本次仓库。发布包 SHA 与上游来源均记录在适配批次证据中。
三、从上游基线建立 OHOS 分支
shell
git clone https://atomgit.com/oh-flutter/locale_plus.git
cd locale_plus
git switch -c feat/ohos_locale_plus_2.0.0 v2.0.0
flutter create --template=plugin --platforms=ohos --no-pub .
我保留原 Dart、Android、iOS 和 Web 代码,只新增 OHOS HAR、示例宿主、双语说明与测试。模板生成后把插件类改为 LocalePlusPlugin,通道继续使用 locale_plus。

图 2:AtomGit origin、适配分支、完整 HEAD 与工作区状态。
四、先梳理 13 个接口的语义
公开接口都返回可空值,原生系统数据缺失时不应自行编造:
| 接口组 | API | 返回语义 |
|---|---|---|
| locale | getLanguageCode()、getRegionCode() |
基础语言码和地区码 |
| 数字 | getDecimalSeparator()、getGroupingSeparator() |
当前 locale 的数字符号 |
| 时区 | getTimeZoneIdentifier()、getSecondsFromGMT() |
IANA 标识和当前偏移秒数 |
| 时间/日期 | is24HourTime()、getAmSymbol()、getPmSymbol()、getFirstDayOfWeek()、getDateFormatPattern() |
系统格式偏好 |
| 其他 | usesMetricSystem()、isUsingSamsungKeyboard() |
单位策略与上游平台特定判断 |
语言和地区必须分开读。例如系统语言可能是 zh-Hans,基础语言码应解析为 zh;地区则来自独立的系统 region。UTC 偏移必须按"当前时刻"计算,才能包含夏令时,而不能写死时区的标准偏移。
五、用 LocalizationKit 实现格式查询
LocalePlusPlugin.ets 通过 i18n.System、i18n.getTimeZone() 和 systemDateTime 取得基础数据;数字、时段和日期格式通过 Intl 的 formatToParts() 拆解:
typescript
case 'getRegionCode':
return i18n.System.getSystemRegion();
case 'getLanguageCode':
return new Intl.Locale(i18n.System.getSystemLanguage()).language;
case 'getTimeZoneIdentifier':
return systemDateTime.getTimezoneSync();
case 'getSecondsFromGMT':
return i18n.getTimeZone().getOffset(Date.now()) / 1000;
日期模式不能通过替换格式化后的数字猜测。实现用一个固定 UTC 日期拆出 year、month、day 和 literal,再按照字段宽度生成 yyyy/M/d、dd/MM/yyyy 等模式。字面量中如果有字母,需要加引号,防止被 intl.DateFormat 当成模式字符。
getFirstDayOfWeek() 保持系统返回的 ISO 1 到 7,不再把星期日改成 1。公制判断沿用上游 Android 的地区策略:US、LR、MM 返回 false,其他地区返回 true;它是地区规则,不是用户可单独修改的单位设置。Samsung 键盘是其他平台特定能力,OHOS 明确返回 false。

图 3:13 个方法的路由,以及语言、地区、时区和格式数据来源。
该插件只读取公开系统 locale 数据,无需新增权限,也不会修改系统设置。Engine 解绑后清除 handler;系统异常返回 locale_query_failed,未知方法返回 notImplemented。
六、测试和 HAP 构建
shell
flutter pub get
flutter analyze
flutter test
node --test ohos/test/locale_plus.test.cjs
cd example
flutter analyze
flutter test
flutter build hap --debug --no-codesign
15 项 Dart、3 项 Widget 和 15 项 ArkTS,共 33 项通过。原生测试覆盖英语、德语、法语、阿拉伯语、印地语数字符号,正负及半小时时区偏移,中英文 AM/PM,四种日期顺序,地区公制规则、错误恢复和 Engine 重绑。

图 4:静态分析、33 项功能测试和示例回归结果。

图 5:无签名 HAP 元数据及远程 Git 依赖锁定口径。
七、固定提交接入与真机验证
yaml
dependencies:
locale_plus:
git:
url: https://atomgit.com/oh-flutter/locale_plus.git
ref: 1387b3d8b3eadbb782c77c679a6446e50e9ebca0
隔离宿主的 pubspec.lock 解析到同一 SHA,签名 HAP 构建和覆盖安装通过。真机逐项读取 13 个接口,并用 Dart 本地时区偏移对照 28800 秒,全部通过。页面结果包括 zh、CN、Asia/Shanghai、小数点、分组符、中文上午/下午、12 小时制、周首日 7、yyyy/M/d 和公制。

图 6:13 项真机读取结论及未修改系统设置的验证边界。
本轮没有切换语言、地区、历法和时区,因此不能声称所有组合都已真机验收。自动化覆盖不同 locale 的算法分支,但它和真实设备切换是两类证据。
应用内真实截图:刷新后的格式信息

图 7:页面上半段展示语言、地区、时区、UTC 偏移及数字分隔符。

图 8:刷新后第 2 次读取,日期模式、周首日、24 小时制和公制偏好仍保持一致。
八、FAQ
Q1:为什么语言码和地区码不能从同一个字符串截取
- 现象: 脚本假定 locale 总是
language_REGION,遇到脚本码或缺少地区时解析错误。 - 原因: 系统语言和地区是独立设置,语言标签也可能包含 script。
- 解决方法: 分别读取 system language 与 system region,语言再交给
Intl.Locale解析。 - 验证结果: 自动化用
zh-Hans与GB的组合确认返回zh/GB。
Q2:UTC 偏移为什么不能写成时区常量
- 现象: 夏令时地区在不同日期返回值会变化。
- 原因: IANA 时区标识不等于固定偏移,偏移与当前时间有关。
- 解决方法: 调用
getOffset(Date.now())并从毫秒换算为秒。 - 验证结果: 测试覆盖负偏移、零偏移和半小时偏移,真机与 Dart 当前偏移一致。
Q3:usesMetricSystem 是否读取用户偏好
- 现象: 调用方以为它代表系统设置页中的自定义单位。
- 原因: 上游 API 按地区规则推断,OHOS 适配保持同一语义。
- 解决方法: 将它用于默认值;用户明确选择的单位仍应保存在业务配置中。
- 验证结果: US/LR/MM 与 CN/GB 的规则由 ArkTS 测试覆盖。
九、总结
locale_plus 2.0.0 已在 OHOS 上补齐全部 13 项系统 locale 查询,并保留可空返回值和原通道契约。33 项自动化、HAP 构建和 API 26 真机基础读取通过,且没有申请权限或修改系统设置。
正式项目应锁定受测 SHA,把这些结果当作格式默认值,并为系统数据缺失保留降级显示。语言与地区切换、其他历法和更多设备仍需按业务覆盖。
十、参考链接
欢迎加入CPF-Flutter 鸿蒙社区:https://atomgit.com/CPF-Flutter