ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

HarmonyOS NEXT应用国际化实践:会议随记Pro的i18n架构全解析

HarmonyOS NEXT应用国际化实践:会议随记Pro的i18n架构全解析 1. v1.1 版本到底改了些什么一次“会议随记 Pro”的国际化大扫除先说结论会议随记 Pro 从立项到 v1.0 上线一直只支持中文界面。v1.1 我花了大半个月时间把整个应用推倒重看了一遍核心就一件事——在 HarmonyOS NEXT 上把国际化i18n做扎实。这不是一次简单的“把按钮文字翻译成英文”的工作而是从资源文件组织、运行时切换、格式化差异到导出内容的全链路改造。为什么一个会议记录工具要这么认真搞国际化因为会议随记 Pro 的定位从一开始就不是“一个小众的本地工具”。公司晨会、客户访谈、头脑风暴、远程协作文档整理这些场景天然是混语言环境设备可能是英文系统参会人可能是外籍同事导出的会议纪要可能要发给海外团队。如果应用只顾中文用户换个系统语言就面临满屏英文按钮、中文内容混排、日期格式混乱的窘境体验直接崩掉。这版 v1.1 我做的事可以拆成四块第一重建资源目录和全部文案 key形成一个可持续扩展的多语言资源体系目前内置简体中文和英文两套语言包第二做了应用内运行时语言切换用户可以在设置页手动覆盖系统语言不需要重启设备就全量刷新第三把日期、时间、数字、参会人排序这些“藏在角落里的硬编码”全部改为本地化感知第四让导出 PDF 和分享摘要也跟随语言设置否则界面全英文、导出的会议纪要却还是中文跟没做一样。这篇文章会把这套架构的完整思路、具体文件摆法、踩过的坑全部写出来。如果你也在做 HarmonyOS NEXT 原生应用的国际化或者只是想让自己的 App 具备“加语言包像加配置一样轻松”的能力这篇应该对你有用。2. 资源文件怎么摆决定了你以后加语言包有多轻松2.1 三套资源目录与 key 命名规范HarmonyOS NEXT 的资源体系里字符串资源默认放在resources目录下通过element/string.json管理。这里的核心思路是把默认语言放在base里再按语言限定词建目录放翻译版本。会议随记 Pro 目前的目录结构长这样resources/ ├── base/ │ └── element/ │ ├── string.json │ ├── color.json │ └── float.json ├── en_US/ │ └── element/ │ └── string.json └── zh_CN/ └── element/ └── string.jsonbase 是兜底资源zh_CN 和 en_US 是两份完整翻译。这么做的好处是后续加日语、韩语只需要新增一个ja_JP目录把 string.json 翻译一遍构建系统会自动根据设备语言选择最匹配的资源完全不用改业务代码。string.json 内部的组织方式也很关键。我踩过一版“平铺所有 key”的坑——两百多个 key 堆在一个文件里改一个文案要找半天翻译表格也没法读。v1.1 改成按模块分区注释{ string: [ { name: app_name, value: 会议随记 Pro }, { name: meeting_create_title, value: 新建会议记录 }, { name: meeting_duration_format, value: %1$d 小时 %2$d 分钟 }, { name: common_save, value: 保存 } ] }key 命名我定了三条规则团队一起维护也不会乱前缀按模块meeting_、agenda_、action_、export_、setting_、common_中间是语义create_title、duration_format、save不要用“中文拼音 key”不要用 UI 位置做 key比如button_1、label_22.2 占位符和长文案的处理策略会议记录里有大量“动态内容拼接静态文案”的场景比如“会议时长1 小时 30 分钟”“共 12 条行动项”。英文和中文的语序完全不同绝不能做字符串拼接。正确姿势是用占位符。HarmonyOS 的资源字符串支持格式化占位符我统一使用%1$d、%2$s这种带索引的写法而不是裸%s。带索引的好处是翻译时可以把占位符挪到目标语言语法习惯的位置// 中文 { name: meeting_duration_format, value: 会议时长%1$d 小时 %2$d 分钟 }// 英文 { name: meeting_duration_format, value: Meeting duration: %1$d h %2$d min }代码侧通过$r(app.string.meeting_duration_format)引用资源再填入参数。这样中文、英文各写各的语法互不干扰。长文案是另一个被低估的坑。会议模板里有一段“会议目的”的默认引导文案中文版 20 个字英文版 35 个词视觉宽度差了快一倍。如果你把这段文案硬塞进固定高度的卡片必然出现截断。我的处理是涉及长文案的地方全部改用可拉伸布局用constraintSize约束最小宽度而不是固定高度同时在 QA 阶段把系统语言切到英文逐个页面检查是否存在溢出。2.3 用代码检查脚本守住资源文件质量多语言资源文件最怕两个问题一是 key 漏翻译二是 key 拼错导致运行时白屏。HarmonyOS 构建期只检查 JSON 合法性不会自动发现“zh_CN 有 201 个 keyen_US 只有 198 个”这类问题。我写了一个简单的 Node 脚本在 CI 阶段自动对比各语言包的 key 集合const fs require(fs) const path require(path) const zh JSON.parse(fs.readFileSync(resources/zh_CN/element/string.json, utf8)) const en JSON.parse(fs.readFileSync(resources/en_US/element/string.json, utf8)) const zhKeys new Set(zh.string.map(item item.name)) const enKeys new Set(en.string.map(item item.name)) const missingInEn [...zhKeys].filter(key !enKeys.has(key)) const missingInZh [...enKeys].filter(key !zhKeys.has(key)) if (missingInEn.length missingInZh.length 0) { console.error(资源 key 不一致) console.error(缺少英文翻译, missingInEn) console.error(缺少中文翻译, missingInZh) process.exit(1) }这个脚本实测下来帮了大忙。翻译是分批拿到的经常出现这周英文包没齐、下周中文包被误改的情况没有脚本把关漏翻译的 key 会一直悄悄存在直到用户在某个边缘页面看到一串裸 key。HarmonyOS NEXT 对未命中的资源 key 处理是很敏感的一旦拼错轻则显示 key 字符串重则页面报错所以机械化的校验比人工 review 可靠得多。3. 运行时语言切换从“跟着系统走”到“会议场景自己说了算”3.1 切换策略的选择为什么不直接跟随系统绝大多数 App 的国际化都是“跟随系统”——系统语言切到英文应用自动变英文。这确实是最省事的实现方式HarmonyOS NEXT 的资源机制天然支持。但会议随记 Pro 的场景比较特殊开会时经常是“人不动、设备动”同一个会议室里不同的投屏设备、不同的平板可能跑着不同的系统语言。如果会议记录工具完全跟随系统用户拿英文系统的设备开中文会议标题是英文纪要正文中文导出混排非常别扭。所以 v1.1 我选了“系统语言为默认、应用内可手动覆盖”的策略。核心逻辑是未设置时跟随系统语言和区域用户在设置页切到中文或英文后覆盖系统设置手动选择的结果持久化下次启动依然生效手动切换后当前界面栈整体刷新不要求重启这套方案在 HarmonyOS NEXT 上的实现思路是语言偏好写入轻量级偏好数据库Preferences应用启动时读取偏好并决定用哪套资源再配合页面重建机制让所有界面刷新。这种做法在原生应用里是标准的“用户显式覆盖”模式好处是可控坏处是要处理刷新链路的边界情况。3.2 状态同步与页面刷新最容易翻车的环节如果说资源文件是基础那“切换语言后页面怎么平滑刷新”就是整个 i18n 改造中真正的技术难点。我在这个环节反复折腾了三天核心原因是页面状态和语言资源的绑定关系没有设计好。踩坑过程最初我把当前语言存到了一个全局单例变量里。设置页切换语言时直接调用router.replaceUrl跳回首页想用重新入栈的方式逼迫页面重建。结果发现返回栈里的其他页面还是旧语言用户在二级页面点返回看到的又是上一门语言界面语言“忽中忽英”非常糟糕。最终我采用的干净方案是切换语言后重置整个页面路由栈把入口页面作为第一个页面重新入栈同时所有页面通过onPageShow重新拉取当前语言下的资源。对用户来说切完语言就像打开了一次 App所有页面都是新语言。这个体验最接近系统级的语言切换也不会出现返回栈混语言的问题。状态管理同步上我做了三件事用一个 Observable 的 AppState 对象承载当前语言 code页面里所有涉及语言的文本都从它派生页面级记住“自己是在哪个语言版本下构建的”onPageShow时如果语言 code 变了重建该页面的文本和列表事件类数据会议记录内容本身绝不参与翻译只有界面壳和系统生成文案参与翻译避免语言切换把用户真实数据改掉3.3 持久化与语言包加载语言偏好的持久化我用的是 Preferences字段就一个app_language取值system、zh_CN、en_US。为什么用这个三态设计因为用户手动选择了语言之后如果系统语言又变了不能强制顶掉用户的选择这时候“恢复跟随系统”就得有一个显式的选项。设成三态后设置页上就是一个单选列表最后一项是“跟随系统”逻辑非常直接。还有一个细节值得留意启动时读偏好的顺序。我一开始把读偏好放在了主页面aboutToAppear生命周期里导致首页第一屏会闪一下系统语言的文案。后来移到EntryAbility的窗口创建阶段去读保证第一帧渲染出来的就是正确语言。HarmonyOS NEXT 的启动链路里资源语言决定越早做越好拖到页面生命周期再处理多少会有一帧错位。4. 会议记录里最磨人的格式化细节日期、时间、数字和名字4.1 日期时间在不同地区习惯下怎么显示会议记录应用逃不开时间。会议日期、开始时间、结束时间、创建时间、导出报告头部的时间戳——“12月5日”和“Dec 5”只是最基础的区别。完整来做还要看地区的文化习惯中文环境习惯“2025年12月5日”英文环境习惯“Dec 5, 2025”或“5 December 2025”24 小时制与 12 小时制也不同。这个地方我交过学费第一版直接拼接year 年 month 月等到做英文包时发现这套逻辑完全没法复用。v1.1 全部改成通过系统 i18n SDK 的时区和日期格式化能力来输出具体做法是一律以 ISO 8601 格式存储时间戳显示时再格式化显示格式按“用户当前语言 当前区域”动态决定24 小时制 / 12 小时制交给系统区域规则处理不写死格式参考表实测场景zh_CN 显示en_US 显示会议日期2025年12月5日Dec 5, 2025创建时间2025/12/05 14:3012/5/25 2:30 PM时长1小时30分钟1 h 30 min粒度更细的周内日期本周五 14:30Fri at 2:30 PM还有个很隐蔽的坑周起点。中文习惯周一是一周的开始英文环境很多人习惯周日是开始。会议记录里如果显示“本周会议列表”周起点不同会影响列表分组。这个问题在数据量小的时候不明显但一旦按周汇总会议跨语言用户会看到完全不同的“本周”范围。v1.1 里我选择暂时跟随系统区域规则没有做二次覆盖但这个点已经写进了下一版的 TODO。4.2 数字、百分比和货币的坑会议纪要里经常有“3/5 位参会人已完成待办”“项目完成度达到 86.5%”“预算 ¥12,000”。数字的表示方式在不同语言环境下差异不小千分位分隔符有些地区用逗号、有些用空格小数点有些用点、有些用逗号。如果只做翻译不处理数字格式化用户读起来会很别扭。这块的工程实现要注意不要在资源文件里写死“86.5%”这种带格式的文本应该存数字类型数据在渲染时根据当前 locale 做格式化。会议随记 Pro 现在的做法是使用系统 i18n 提供的数字格式化能力按当前语言和区域生成显示文本。比如中文环境显示“86.5%”英文环境显示“86.5%”看起来一样但换到德语环境按当地规则就会是“86,5%”——虽然 v1.1 没上德语但架构上已经支持。货币符号更是个大坑尤其是“¥”这个符号。中文环境下它默认指人民币但国际用户看到 ¥ 可能以为是日元。会议随记 Pro 的导出报告里涉及预算字段现在的策略是金额只存储数字和币种代码比如 CNY、USD显示时按当前 locale 决定符号和格式。没有币种代码的裸金额不渲染符号只显示数字从源头上避免歧义。4.3 排序规则与参会人名单的显示参会人名单是个让人头疼的功能。中文姓名按拼音排序还是按笔画排序英文姓名按姓的字母序还是名的字母序东西方姓名的书写顺序本来就不一样。在 v1.1 我处理的是相对简单的英文排序和中文拼音排序的统一策略存储时保留用户的原始输入顺序显示时提供“按名字 A-Z”“按添加顺序”两个选项默认按添加顺序。这块最想提醒的是不要自己做字符串的localeCompare之外的花活尤其在多语言姓名混排的名单里。不同语言字符集混在一起时排序结果很容易让用户疑惑。一个加急处理的小原则是如果排序规则做不严谨就退回“按添加时间”这是所有语言环境都不会出错的方案。会议记录的应用场景里稳定性和可预期性比花哨的排序重要得多。5. 导出报告同样是国际化模板、PDF 与剪贴板5.1 会议摘要导出的文案逻辑不能只做界面翻译很多 App 做国际化只做界面一导出 PDF 就露馅。会议随记 Pro 的导出内容是这套 i18n 架构里我花心思最多的地方。会议摘要导出会生成一段结构化的文本会议主题、时间、参会人、讨论要点、行动项。“会议主题”是用户输入的内容天然不需要翻译但“参会人”“行动项”“会议纪要”这些标签是系统文案必须跟随语言。更麻烦的是整段摘要导出的模板结构中英文的阅读顺序差异很大。我的做法是把导出模板抽象成一个多段结构的生成器而不是一段静态文本。代码层面区分开三块用户录入的内容会议标题、笔记正文、待办文本——原样导出不做任何转换系统标签“标题”“参会人”“行动项”“备注”——所有标签走资源文件结构拼接逻辑段落顺序、分隔符、标题层级——独立的模板代码每个语言包可微调比如中文惯用“会议主题xxx”这种冒号分隔英文惯用“Meeting Topic: xxx”看起来差不多但换到标题层级中文导出喜欢“一、二、三”英文就是“1. 2. 3.”。如果代码里把标题号的生成逻辑写死了英文环境导出就全是“一、二、三”很出戏。v1.1 的导出模板把标题编号也纳入资源体系那一段确实费了些功夫。5.2 剪贴板分享与文件名本地化另一个容易忽略的是复制到剪贴板的“分享摘要”和导出 PDF 的文件名。之前文件名是“会议纪要_20251205.pdf”英文环境下用户拿到这个文件文件名完全看不懂。v1.1 改成了“Meeting_Notes_20251205.pdf”和“会议纪要_20251205.pdf”两套由当前语言决定默认文件名。剪贴板分享的文案还要考虑目标受众。用户复制一段会议摘要到即时通讯软件收件人可能来自另一个语言环境这部分我保持了“跟随当前界面语言”的策略。这种选择不完全符合“受众导向”但产品上更易于解释你界面看什么语言复制出去就是什么语言。等之后模板体系成熟可以让用户在选择分享时临时指定导出语言那才是终态方案。6. 发布前我做的兼容性检查与真实用户反馈6.1 安装包体积与资源裁剪加了两套语言资源后安装包体积会不会膨胀答案是会但可控。我统计了一下v1.0 只有一个 string.json约 4KBv1.1 三分资源文件加中文英文两份翻译约 13KB。这个量级对安装包整体来说几乎可以忽略。如果你做的是图片资源多语言比如多语言引导图、音频资源多语言那才要考虑按需下载。会议随记 Pro 目前没有这类资源所以体积不是压力。不过资源裁剪有一个反直觉的注意点HarmonyOS NEXT 的资源合并是按限定词目录处理的如果你把某些大文件误放到 non-base 限定词目录里而少放了 base 兜底某些语言环境下会出现资源缺失。我的检查脚本里顺手加了一个规则所有非 base 目录下的资源base 目录必须存在同名 key。这样兜底永远存在某门语言没翻译完至少不会崩。6.2 回归测试清单切语言以后挨个页面走一遍语言切换类的回归测试最大的问题是“以为改了全局就都变了”实际上总有漏网之鱼。我列了一个检查清单发布前人工跑了两遍系统语言切英文 应用内语言切中文界面中文系统组件比如输入法候选区英文输入体验是否正常应用内语言切英文后重启 App设置项是否保留会不会跳回中文从设置页切语言后返回上一页返回栈有没有残留旧语言页面英文环境下新建会议标题输入中文混排渲染是否正常是否溢出导出 PDF文件名、内容结构、日期时间、编号层级是否全部跟随语言剪贴板分享摘要复制出来的文本是否完整、标签是否对应横竖屏切换 语言切换同时发生布局是否错乱系统日期格式改成“年/月/日”等非常规格式应用显示是否跟随表格里的最后一项是我在实测中发现的一个真实问题我自己的测试机系统日期格式是“12/5/25”切到英文语言后会议列表页的时间显示却没有变排查半天才发现代码里对日期的格式化走了“语言”而漏了“区域”。语言和区域是两个维度某用户可能系统语言是英文但区域选的是中国大陆日期格式应按中国大陆的习惯显示。之前的实现只取了Language没取Region导致区域信息完全不影响显示。这个 bug 直接暴露了我对 i18n 和 l10n本地化两个概念的理解还有盲区。6.3 一个小教训语言切换后返回栈的构建顺序最后分享一个真实的用户反馈。有用户在论坛留言说“我在设置页把语言切成英文后点返回键一下就回到了旧语言的首页。”我一开始不理解后来复现才发现问题语言切换是通过clear()路由栈重建实现的但设置页的重建时机和返回手势产生了竞态。用户快速按返回键时系统还在执行旧栈的清理动画返回键触发的是旧栈的行为。这次的修复也很简单不算是高深技巧。在设置项保存语言之后增加一个小延迟再执行栈重建同时给界面加一个短暂的 loading 遮罩阻止用户在重建过程中做任何返回操作。从用户视角看就是“切语言需要等半秒”体感合理换来了绝对稳定的栈状态。这也是我为什么一直说i18n 从来不是一个“资源文件翻译”的活儿它是和 UI 生命周期、路由栈、系统区域规则深度耦合的工程问题。回到这次的 v1.1真正让我觉得成就感拉满的不是完成了中英文翻译而是这套架构确定下来之后下一个语言包接入的工作量已经收敛到几乎只剩翻译文本和跑一遍检查脚本。对我个人来说这大概才是“HarmonyOS NEXT 原生国际化最佳实践”这句话的真实分量——不是某个文档里的标准答案而是自己的应用在真实设备上跑过崩溃、惊出一身冷汗之后沉淀下来的那套属于自己的流程。
返回列表