尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

React Native for OpenHarmony 三方库 expo-localization 57.0.2 适配实战:地区格式与响应式日历

React Native for OpenHarmony 三方库 expo-localization 57.0.2 适配实战:地区格式与响应式日历 React Native for OpenHarmony 三方库 expo-localization 57.0.2 适配实战读取语言、地区和日历设置本文把一个看似“返回几个字符串”的库拆开讲清楚系统语言和系统地区不是一回事日历与时区也不是固定常量Hook 还涉及订阅和卸载。所有结果均来自最终精简包和鸿蒙真机不把 mock 结果冒充设备结果。适配仓库oh-react-native/expo-localization交付分支main适配 TAG57.0.2-ohos-1.0.0受测提交27f1372368e7023c8a326964a1b29a897d710e49上游基线9e5319c0f821a27b7924841903abae50e2b41790对应 57.0.2 发布源码配套源码expo-localization一、这个库解决什么问题国际化不是把界面翻译成几种语言就结束。应用还要知道用户偏好的语言顺序、系统地区、货币符号、千分位和小数点、温度单位、第一天是星期几、当前日历和时区。expo-localization 把这些信息统一成 JavaScript API公开的四个入口是 getLocales、getCalendars、useLocales 和 useCalendars。前两个适合一次性读取后两个适合页面长期观察系统设置变化。语言与地区必须分开理解。用户可能偏好加拿大英语但系统地区仍是英国语言标签里的地区是语言偏好上下文系统 locale 的地区是格式化和货币上下文。适配时把两者都写成同一个国家代码会在真实用户环境中产生错误金额和日期格式。本次实现还保留 languageRegionCode、languageCurrencyCode 等 57.0.2 字段不能按老版本的简化字段表硬编码。编码前检查了 oh-react-native 组织、CPF-RN 组织、归档清单和活动中心 manifest覆盖去 scope 名称与 rntpc 前缀没有命中同上游的有效适配。上游 MIT 许可证和 Unicode CLDR 许可证按来源保留。图 1真机首选语言顺序与系统地区。语言列表是用户偏好不是设备支持语言全集。二、验证环境和交付边界受测环境为 React Native 0.84.1、React 19.2.3、RNOH 0.84.3、DevEco Studio 26.0.0 Release、HarmonyOS SDK 26.0.0系统 OpenHarmony-7.0.0.105。精简仓库保留 src/index.ts、ExpoLocalizationTurboModule.ts、TurboModule Spec、harmony/expo_localization/Index.ets、ExpoLocalizationPackage.cpp、LocaleData.cpp、ExpoLocalizationPackage.ets、HAR、双语 README、测试、spec.json 和许可证。完整宿主、签名 HAP、设置前后 JSON、日志和截图不进入公开库文章中的图片是公开发布后的真机截图。这类库属于“原生读取 JS Hook”混合实现。语言和日历查询需要 ArkTS 读取系统配置货币、符号和分隔符依赖 C ICUHook 还要管理系统观察器。仅通过 JS mock 能证明类型和失败契约不能证明手机真的切换了时制后 Hook 会更新。三、从 API 到系统的调用链React 组件 - src/index.ts 的 getLocales/getCalendars 或 Hook - ExpoLocalizationTurboModule Spec - C Package 与 JSI/ICU - ArkTS ExpoLocalizationPackage - HarmonyOS locale、calendar、timeZone 和设置观察getLocales 返回语言数组每项包含 languageTag、languageCode、languageRegionCode、textDirection、digitGroupingSeparator、decimalSeparator、measurementSystem、currencyCode 和 currencySymbol 等字段。getCalendars 返回 calendar、timeZone、firstWeekday、hourCycle、uses24hourClock、languageTag 等字段。getCalendars 的系统首日枚举从周一为 1而公开 API 约定周日为 1所以适配中使用 state.firstWeekday % 7 1并对 1 到 7 之外的值返回 null。系统地区和语言地区分别计算。语言标签缺少地区时才使用系统地区补全货币按 locale 的语义读取不把设备地区的货币强行覆盖到所有首选语言。ICU 查询还覆盖阿拉伯数字分隔符与非法 BCP-47 标签避免只用中文环境写出“看起来正常”的实现。图 2七组 ICU 查询返回货币代码、符号和分隔符三个非法标签被拒绝。四、具体改了哪些源码src/index.ts 保留 Expo 的公开函数和 Hook业务侧不需要换 API。ExpoLocalizationTurboModule.ts 负责把 JS 调用转到同名 TurboModule并把原生结果转换为 57.0.2 的字段形状。harmony/expo_localization/Index.ets 和 ExpoLocalizationPackage.ets 注册 Package 工厂保证模块名在 JS、ArkTS、C 三处一致。LocaleData.cpp 使用 ICU 处理 locale、货币、数字符号与非法标签不把 ArkTS 的默认显示字符串当成 ICU 结果。ArkTS 读取系统语言、地区、日历、时区和 12/24 小时制首个 Hook 订阅观察器最后一个 Hook 卸载时移除。HAR 随库发布CMake、oh-package.json5 和自动链接配置保留避免消费方只能拿到 JS 而缺少原生实现。两个 Hook 使用 useSyncExternalStore 共享观察结果。快照没有改变时保留相同对象身份减少无意义重渲染初始化查询和后续事件存在先后竞争所以卸载后不再 setState较晚到达的旧快照也不能覆盖新事件。图 3日历、时区、首日、原时制和首选语言由库结果与独立探针交叉核对。五、用实际 tgz 接入 RNOH 宿主最终验证使用与文首 TAG 对应的 expo-localization-57.0.2.tgz。把 tgz 放入独立宿主后执行export RNOH_HOST$HOME/rnoh-qa export EVIDENCE_DIR$RNOH_HOST/evidence/expo-localization mkdir -p $EVIDENCE_DIR cd $RNOH_HOST npm install $HOME/Downloads/expo-localization-57.0.2.tgz --save-exact ./node_modules/.bin/react-native link-harmony cd harmony ohpm install --all cd .. ./node_modules/.bin/react-native bundle-harmony --dev false --sourcemap-output $EVIDENCE_DIR/bundle.map hvigorw assembleHap --mode module -p productdefault --no-daemon等待 npm 完整结束后再运行宿主自己的 CLI不要用 npx 让工具临时下载另一套 RN。link-harmony 生成 HAR 与 Package 的自动链接ohpm 安装鸿蒙依赖bundle-harmony 生成资源 bundlehvigorw 负责 HAP。只有 source map 指向安装包中的 expo-localization 文件才能证明本轮真机确实测到了这个 TAG。只读语言信息不需要新增系统写入权限。安装最终 HAP 后可用下面的命令固定设备、保持屏幕常亮并保存一张现场截图hdc list targets export DEVICE_ID$(hdc list targets | awk NF {print $1; exit}) hdc -t $DEVICE_ID install $RNOH_HOST/output/tested.hap hdc -t $DEVICE_ID shell power-shell wakeup hdc -t $DEVICE_ID shell power-shell timeout -o 2147483647 hdc -t $DEVICE_ID shell aa start -a EntryAbility -b com.example.rnqa hdc -t $DEVICE_ID shell uitest screenCap -p /data/local/tmp/localization.png hdc -t $DEVICE_ID file recv /data/local/tmp/localization.png $EVIDENCE_DIR/localization.png验证结束后恢复原屏幕超时设备常亮不是库功能也不能写进用户的生产配置。六、业务侧完整调用示例当前版本的 getLocales 和 getCalendars 是同步 getter调用时直接返回数组不需要 await。它们读取的是模块维护的最新快照需要随着系统设置变化刷新页面时再使用下面的 Hook。import * as Localization from expo-localization; export function readLocale() { const locales Localization.getLocales(); const calendars Localization.getCalendars(); return { languageTag: locales[0]?.languageTag, region: locales[0]?.regionCode, timeZone: calendars[0]?.timeZone, hourCycle: calendars[0]?.hourCycle, }; }在组件中观察变化const locales Localization.useLocales(); const calendars Localization.useCalendars(); return ( Text {locales[0]?.languageTag} / {calendars[0]?.timeZone} /Text );Hook 应该由 React 组件调用不能在普通工具函数里直接调用。组件卸载时由库内部解除观察如果业务自己封装订阅也必须在 useEffect 的清理函数中取消。系统变化是异步的后台调度可能延迟不应把 500ms 观察周期理解成绝对实时承诺。七、真机验证如何做验证宿主先读取系统原始设置再显示库的 getLocales 与 getCalendars。首轮结果为简体中文、中国、Asia/Shanghai 和 12 小时制。随后打开系统设置将时制切换为 24 小时等待页面收到真实变化再卸载 Hook最后恢复原来的 12 小时制并重新挂载检查卸载期间没有旧回调继续更新页面。图 4两个 Hook 的初始结果与同步 getter 一致设置不变时没有重复提交。图 5系统设置应用实际切换到 24 小时制Calendar Hook 更新随后卸载两个 Hook事件计数停止。图 6恢复原 12 小时制后重新挂载读到当前系统值卸载阶段没有组件更新。本轮还测试了七组 ICU 查询、非法标签、日历字段、Hook 挂载/卸载、重挂载、设置恢复和两个不同进程的离线 bundle 冷启动。–dev false 不是 release HAP 标志本轮使用签名 debug HAP发布时应重新生成自己的 HAP 和截图。八、真实问题和边界第一版曾把首日星期直接返回鸿蒙枚举中文环境看不出问题换到星期日开周的用户就会错一天最终用完整七天契约测试和真实字段核对修复。第二个问题是独立 C 小程序虽能交叉编译却不能在手机临时目录执行最后改为在签名宿主内调用 ICU才真正覆盖 JSI 和动态库接线。首轮日志采集过晚丢失了前半段输出run-02 改为从受测宿主 PID 启动前持续采集。语言、地区、温度单位变化以及系统查询失败由 mock 覆盖真机重点覆盖语言/地区读取、时制变化、Hook 生命周期和冷启动。没有覆盖其他 ROM、长期后台功耗、平板和多用户配置。不要把一次设备结果扩大成所有 HarmonyOS 版本都完全相同。九、参考链接上游 expo-localization 文档适配仓库首页main 分支57.0.2-ohos-1.0.0 TAGOpenHarmony 国际化与语言区域 APIUnicode CLDR 项目欢迎加入 RN for OpenHarmony 社区。
返回列表