ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

HarmonyOS 7 Hvigor:多语言资源键集漂移与RTL门禁

HarmonyOS 7 Hvigor:多语言资源键集漂移与RTL门禁 一、英文没报错不代表资源是完整的这次问题出在提审前最后一轮阿拉伯语截图。LocaleSealLab 的中文、英文页面看起来都正常切到ar_SA后订单页右侧按钮突然显示成英文金额说明里的数字顺序也不对。更麻烦的是构建过程没有失败ResourceManager 找不到区域资源时会回退到 base页面仍然能打开缺失就这样被“正常运行”掩盖了。我们随后把 312 个 base 文案键与zh_CN、en_US、ar_SA三套资源做了矩阵对账第一次得到 9 个缺失键、4 处占位符错配、2 处复数规则遗漏和 1 处 RTL 方向问题。它们分散在三个功能分支里靠人工翻目录很容易漏。我把这次门禁任务定为 I18N-1812页面叫 LocaleAuditPage目标不是检查翻译好不好而是确保每个运行分支都有结构完整、参数安全的资源。这里先定一个边界工具不会判断“退款处理中”翻成什么最地道它只判断键是否存在、占位符类型和数量是否一致、复数资源是否覆盖当前语言规则以及 RTL 页面在运行时是否真的镜像。语义质量仍需语言审核但工程结构不能再依赖截图阶段碰运气。二、把三套 JSON 变成一张可比较的资源矩阵工程里新建tools/locale-audit不侵入业务组件。LocaleMatrix.ts负责读取resources/base/element/string.json与限定目录PlaceholderLint.ts分析%s、%d和位置参数RuntimeLocaleProbe.ets在模拟器里切换区域并验证实际解析结果LocaleAuditPage.ets只展示报告。Hvigor 的 release 任务在打包前调用这套脚本debug 构建则可以手动运行。第一段代码解决键集漂移。它以 base 为主集合同时找出目标语言缺失键和反向多出的孤儿键。比较前先规范化 JSON 结构而不是直接比较文件文本否则字段排序和格式化会制造无意义差异。// tools/locale-audit/LocaleMatrix.tstypeLocaleMapMapstring,stringexportinterfaceLocaleDiff{locale:string;missing:string[];orphan:string[]}exportfunctioncollectLocaleMatrix(base:LocaleMap,locales:Mapstring,LocaleMap):LocaleDiff[]{constbaseKeysnewSet(base.keys())return[...locales.entries()].map(([locale,values]){constlocaleKeysnewSet(values.keys())return{locale,missing:[...baseKeys].filter((key)!localeKeys.has(key)).sort(),orphan:[...localeKeys].filter((key)!baseKeys.has(key)).sort()}})}我们没有允许“缺失就回退”为通过。对用户可见的 release 页面只要目标语言声明支持缺失就是失败测试页面和仅调试使用的键可以放入带到期版本的豁免清单。孤儿键同样要处理因为它通常说明 base 中的键已改名而某个语言包还保留旧版本。豁免必须写明页面、原因和删除版本不能只给一个永久白名单。三、占位符错配比缺少一句翻译更危险最严重的一处英文资源把订单 %1$s 将在 %2$d 分钟后关闭写成了Order %1$s closes in %2$s minutes。键存在、页面也能打开直到格式化参数走到数字分支才暴露问题。另一个阿拉伯语资源交换了金额和币种位置却没有使用显式位置参数导致代码侧传参顺序无法安全调整。第二段代码将占位符解析成“位置 类型”的签名不比较具体文案。无位置编号的占位符按出现顺序编号同一键在所有语言中的签名必须与 base 一致。%%会先被剔除避免把百分号当参数。// tools/locale-audit/PlaceholderLint.tsconstTOKEN/%(?:(\d)\$)?([sdif])/gexportfunctioncomparePlaceholders(key:string,base:string,translated:string):string[]{constsignature(text:string):string[]{letseq0return[...text.replaceAll(%%,).matchAll(TOKEN)].map((m)${m[1]??seq}:${m[2]})}constexpectedsignature(base)constactualsignature(translated)returnexpected.join(|)actual.join(|)?[]:[${key}: expected${expected.join(,)}but got${actual.join(,)}]}脚本只在构建进程中持有文件内容执行结束立即释放不生成带完整文案的持久缓存。重复调用时以文件哈希作为输入身份同一哈希已经通过便复用结果但规则版本变化会强制重跑。这样本地保存触发多次构建也不会并发写坏报告。四、复数和 RTL 必须进入运行时探针静态 JSON 对账解决不了两个问题。第一复数规则并不是“单数、复数”两档阿拉伯语的类别更多第二资源文件正确不代表组件布局真的随 RTL 镜像。于是门禁增加运行时探针依次切换三种区域调用 ResourceManager 读取指定键再打开订单卡片采集方向、对齐和可见文本摘要。// RuntimeLocaleProbe.etsexportasyncfunctionrunRuntimeProbe(context:UIAbilityContext,locale:string):PromiseProbeResult{constrmcontext.resourceManagerconstorderId20261003-1812consttitleawaitrm.getStringByName(order_expire,orderId,15)constzeroawaitrm.getPluralStringByName(cart_items,0,0)consttwoawaitrm.getPluralStringByName(cart_items,2,2)constdirectionlocalear_SA?RTL:LTRhilog.info(0x1812,LocaleSeal,taskI18N-1812 locale${locale}direction${direction}probePASS)return{locale,direction,values:[title,zero,two],passed:true}}探针在专用测试 Ability 内运行切换区域后重建页面不在业务进程里热改全局语言。每个区域完成后销毁页面、取消布局监听再进入下一轮避免上一个方向的状态残留。若中途进入后台当前区域标记为 ABORTED恢复后从该区域重新开始不沿用半份快照。RTL 检查也没有粗暴要求所有 Row 反转。金额、订单号和时间戳可保持自身书写方向镜像的是导航层级、边距、图标和阅读顺序。报告因此同时记录容器方向与文本方向一处异常最终定位到开发者把margin.left写死而不是使用 start/end 语义。DevEco Studio 中能看到完整项目树、占位符比较代码、右侧三语言门禁页面和底部 HiLog。日志统一使用任务 I18N-1812keys312 locales3 checks936最终行是stateRELEASE_READY missing0 placeholder0 plural0 rtl0 pass936/936。这些数字也写入导出的报告任何一处对不上都不允许 release 产物晋级。五、从 16 个问题到一份可复查的证据第一次扫描的 9 个缺失键中4 个是新功能没有同步到ar_SA3 个是重命名后留下的旧键2 个属于错误豁免。4 处占位符错配全部改成显式位置参数2 处复数资源补齐类别那处 RTL 问题改用逻辑方向边距。重新运行后三种区域、312 个键形成 936 项检查全部通过任务进入 RELEASE_READY。手机结果页刻意不展示大量翻译文本而是给出最有价值的结构证据语言矩阵、问题归零、运行探针和 936/936 进度。按钮“注入占位符错配”用于演示失败路径点击后英文资源的签名被测试层替换状态立即变为 BLOCKED“导出上架报告”只在 RELEASE_READY 可用。注入只存在内存不修改资源源文件退出页面自动清理。六、门禁要能被维护而不是成为新的包袱规则接入 Hvigor 后我们保留了三条维护约束。其一base 删除键时必须同步清理各区域孤儿键其二新增支持语言要先进入 warning 模式翻译交付完成后再切换 blocking其三规则升级要写版本号旧报告不能冒充新规则结果。报告采用临时文件写完再原子替换避免构建被取消时留下一个看似完整的 JSON。工具的资源释放也要和页面一样严谨。文件句柄在单文件解析后关闭运行探针的订阅在每个区域结束时解绑导出任务使用 generation 丢弃迟到结果。连续点击扫描按钮只保留最新一轮旧轮次即使完成也不能覆盖当前状态。这次处理完以后多语言审核不再是“把系统切成英文看一眼”。我们把原本隐藏在回退机制里的结构问题拆成键集、占位符、复数和方向四类每一类都有静态证据或运行证据。翻译质量依旧需要人但工程完整性终于可以在上架之前自动回答。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进