ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

HarmonyOS 7 ResourceManager:多语言键闭包与字号溢出验收

HarmonyOS 7 ResourceManager:多语言键闭包与字号溢出验收 一、德语按钮没丢翻译却还是没通过这轮验收LocaleGate是我们给 Release 包加的一道本地化预检。事情起因很小版本7.2.072031的确认支付页在中文和英文设备上都正常切到德语后底部按钮只剩“Zahlung bestä…”。资源文件里确实有完整译文ResourceManager 也成功取到了字符串所以常规的“缺少 key 检查”给出全绿结果。接着把系统字体调到 1.3×阿拉伯语订阅页的金额说明又压住了右侧图标。这个问题更麻烦key 存在、占位符数量相同、页面也没有崩溃只是最终截图不符合交付要求。继续靠人工切语言、调字号、逐页截图既慢也很难保证每次版本都检查同一组状态。本次 Demo 使用项目LocaleGate、页面ReleaseAuditPage、任务I18N-2142时间 21:42。目标语言为zh_CN / en_US / de_DE / ar_SA检查CheckoutConfirmPage与SubscriptionPage字体倍率为 1.0× 和 1.3×总计 16 张验收快照。初次扫描得到missing3、signatureMismatch1、overflow7、screenshots9/16修复后四项分别为0、0、0、16/16状态PASS。二、把“本地化完整”拆成三层证据这次复盘后我不再用一个绿色对勾概括本地化。真正能进入提审包的证据至少有三层。第一层是资源闭包基准语言的 146 个 key在另外三个 locale 中都存在不能依赖运行时回退掩盖遗漏。第二层是参数签名%{public}s、数量占位符和 plural/select 分支要一致防止德语译文存在却在格式化时失败。第三层是页面可见性字符串拿得到不代表在 360vp、RTL 和 1.3× 字体下完整显示。项目因此没有继续往string.json检查脚本里塞条件而是拆成独立管线LocaleGate/ ├── entry/src/main/ets/pages/ReleaseAuditPage.ets ├── entry/src/main/ets/pages/CheckoutConfirmPage.ets ├── entry/src/main/ets/pages/SubscriptionPage.ets ├── entry/src/main/ets/audit/TextProbe.ets ├── tools/locale/collect-resources.ts ├── tools/locale/compare-signatures.ts ├── tools/locale/run-snapshot-matrix.ts └── reports/locale-report.json工具脚本负责资源静态闭包ArkUI 测试页负责实际布局ReleaseAuditPage只读取报告并展示最终门禁。这样 CI 失败时能直接指出是 key、参数还是像素层问题而不是留下一句“德语有问题”。三、第一道检查是闭包不是“能回退就算成功”第一段代码解决资源回退掩盖缺失的问题。脚本以zh_CN为基准读取四套resources/*/element/string.json把 key 与占位符签名规范化。缺少项、额外项和签名不一致分别记录任何一项非 0 都不能进入截图阶段。// tools/locale/compare-signatures.tstypeResourceMapMapstring,stringfunctionsignature(text:string):string[]{consttokenstext.match(/%\{public\}[sdf]|\{\w,\s*(plural|select),/g)??[]returntokens.map(tokentoken.replace(/\s/g,)).sort()}exportfunctioncompare(base:ResourceMap,target:ResourceMap):LocaleDiff{constmissing[...base.keys()].filter(key!target.has(key))constextra[...target.keys()].filter(key!base.has(key))constmismatch[...base.keys()].filter(key{if(!target.has(key))returnfalsereturnsignature(base.get(key)!).join(|)!signature(target.get(key)!).join(|)})return{missing,extra,mismatch}}这里没有把 extra 一律当错误。灰度功能可能先在某个 locale 出现所以报告会展示 extra但门禁只对缺失和签名不一致失败。当前项目的明确约束是Release 分支新增基准 key 时其他 locale 必须同批补齐不能让 ResourceManager 回退到中文后悄悄上线。初次报告里de_DE缺少subscription_trial_hint与payment_network_retryar_SA缺少account_export_noticepayment_total_label的德语译文丢了%{public}s。这些都不是截图肉眼最容易发现的问题但会在不同数据条件下变成运行时错误。四、溢出检查要用实际字号和约束宽度第二段代码解决“译文完整却被 UI 截断”。TextProbe在测试页中拿到容器实际宽度再用同字体、同字号计算文本需要的宽度。只要组件配置maxLines(1)测量值超过可用宽度 1 vp 就记为 overflow多行文本则同时检查行数与高度。// TextProbe.ets页面探针核心片段import{measure}fromkit.ArkUIexportfunctioninspectSingleLine(input:ProbeInput):ProbeResult{constsizemeasure.measureTextSize({textContent:input.text,fontSize:input.fontSize*input.fontScale,fontWeight:input.fontWeight,constraintWidth:input.availableWidth})constoverflowsize.widthinput.availableWidth1return{locale:input.locale,page:input.page,componentId:input.componentId,requiredWidth:Number(size.width.toFixed(1)),availableWidth:input.availableWidth,fontScale:input.fontScale,overflow}}测量必须发生在页面完成布局之后。过早读取宽度会拿到 0连续切换语言时旧页面的onAreaChange也可能晚到。测试控制器因此给每个 locale/fontScale 组合分配 generation只有当前 generation 的探针结果能写入报告。这一步也没有试图“自动缩小字号”。支付确认按钮的文案如果过长优先允许按钮增高或调整布局强行把德语缩到 11 fp 虽然能过截图却破坏可读性。阿拉伯语还需要 RTL 实际布局不能只拿字符串长度估算。五、16 张截图必须来自同一组可复现状态初版自动化只负责打开页面结果每次金额、网络状态和倒计时都不同截图无法比较。最终 runner 给两个页面注入固定 Fixture订单ORD-2142、金额¥ 268.00、试用期 7 天、网络状态ONLINE。每张图都带 locale、fontScale、page 和 caseId。// tools/locale/run-snapshot-matrix.tsconstlocales[zh_CN,en_US,de_DE,ar_SA]constscales[1.0,1.3]constpages[CheckoutConfirmPage,SubscriptionPage]for(constlocaleoflocales){for(constfontScaleofscales){for(constpageofpages){constcaseId${locale}-${fontScale}-${page}awaitdevice.applyLocale(locale)awaitdevice.applyFontScale(fontScale)awaitapp.openAuditCase(page,I18N-2142,caseId)constprobesawaitapp.waitForStableLayout(caseId,300)awaitreport.add(caseId,probes,awaitdevice.capture(caseId))}}}awaitreport.assert({missing:0,signatureMismatch:0,overflow:0,shots:16})waitForStableLayout不是固定 sleep 300 ms而是要求 300 ms 内组件矩形和探针结果不再变化动画、字体加载或异步金额更新都会重新计时。执行结束后 runner 恢复设备原 locale 和字体倍率避免把测试环境污染到下一条用例。截图只是证据之一。runner 同时保存组件 ID、需要宽度、可用宽度和最终矩形以后按钮主题变化导致 4 vp 偏移时不需要仅靠像素差猜原因。任务结束、失败或用户停止时都在finally中恢复环境并关闭测试 Ability。六、从 7 个溢出点定位真正的布局责任初次运行的关键日志如下21:42:04.118 LocaleGate I18N-2142 RESOURCE_SCAN keys146 locales4 missing3 mismatch1 21:42:11.604 LocaleGate I18N-2142 OVERFLOW localede_DE scale1.3 idconfirmPay required214.6vp available176vp 21:42:14.227 LocaleGate I18N-2142 OVERFLOW localear_SA scale1.3 idtrialHint required198.2vp available184vp 21:42:29.903 LocaleGate I18N-2142 FAILED overflow7 screenshots9/167 个问题并不是 7 次改文案。confirmPay的责任在按钮布局中文设计稿把左右 padding 写死修复后改为允许两行并保持最小高度。trialHint的责任在 RTL 容器图标仍固定在右侧修复为按布局方向调整顺序。另有 2 个问题来自字符串签名错误补齐参数后测量宽度自然变化。修复过程中最有价值的是“不允许跳过失败 locale”。旧脚本遇到资源格式化异常会继续下一项最后只少几张截图新脚本把 16 张视为完整矩阵只要不是 16/16 就无法 PASS。这样screenshots9/16不会被误解成“已经抽查了 9 张”。七、最终门禁资源 146/146截图 16/16第二次运行的ReleaseAuditPage显示版本7.2.072031、任务I18N-2142、4 个 locale、2 个页面、2 档字号。资源键146/146missing 0signature mismatch 0overflow 0截图 16/16最终状态PASS耗时 31.8 秒。手机页没有把 16 张缩略图全部塞进一屏而是展示矩阵摘要zh_CN 4/4、en_US 4/4、de_DE 4/4、ar_SA 4/4。点击某行才能查看对应截图和组件报告。这样运行图仍是一个真实审核页面不会变成九宫格或宣传海报。底部按钮是“查看报告”和“导出证据”。处于 FAILED 时“生成提审包”按钮禁用PASS 后才允许继续。报告文件以 taskId 和版本号命名重复执行同一版本会创建新 runId不覆盖上一轮便于确认修复前后差异。八、边界自动化能挡住确定性问题不能替代语言审校LocaleGate 能发现缺 key、参数签名不一致、确定性截断和矩阵缺图却判断不了德语是否自然、阿拉伯语是否符合语境也无法自动证明按钮文案是否准确。语言质量仍需要译审自动化只负责把“结构完整”和“画面可见”变成可重复证据。截图门禁也不应无限扩大。本文固定 360vp、1.0×/1.3× 和两个关键页面是因为它们正好覆盖当前提审风险若产品支持更多窗口尺寸应按风险增加 profile而不是一次枚举所有像素宽度。测试矩阵越大越需要明确每个 profile 为什么存在。这次真正收口的不是德语按钮那 38.6 vp 的差值而是发布前的判断方式。ResourceManager 提供正确字符串ArkUI 负责真实布局自动化 runner 固定状态并留证三层结果同时通过版本才从“看起来翻译完了”变成“有证据可以交付”。
RELATED READING

延伸阅读

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