鸿蒙项目实战 - 度量衡本地化 — 技术实现篇

发布时间:2026/10/30 11:26:25

鸿蒙项目实战 - 度量衡本地化 — 技术实现篇 一、业务需求为什么这么设计产品要做一款面向全球的食谱工具核心需求是同一份食材清单在不同国家用户面前自动变成他们习惯的单位体系。中文用户看到「250 克 无盐黄油」英文用户看到「8.8 oz unsalted butter」法文用户看到「250 grammes」注意不是 grams日文用户看到「250 グラム」美国用户看烤箱温度是「347°F」欧洲用户看是「175°C」正式文档用「250 grams」界面标签用「250 g」图表刻度用「250g」。这些诉求背后是两个完全独立的工程问题单位怎么换算业务逻辑与语言无关与换算结果怎么呈现本地化交给 Intl。本应用把两者严格分层本文记录完整技术方案所有代码与UnitConvertPage.ets一一对应。二、总体架构┌─ 语言层LANGS 元数据code/name/flag/system STRINGS 文案表 t() 降级 ├─ 数据层INGREDIENTS 食材表公制基准值 中英文名 TEMPS 温度档位 ├─ 换算层toImperial() 克→盎司、毫升→液量盎司cToF() 摄氏→华氏非线性特例 ├─ 格式化层fmtUnit() 统一入口NumberFormat style:unit unitDisplay 三档 └─ 状态层StorageLink currentLocale State displayIdx / tempIdx分层原则换算层只做纯数学格式化层只做纯呈现。toImperial(250, gram)返回8.81849...它不知道也不关心用户说什么语言fmtUnit(fr_FR, 250, gram, short)返回250 g它不关心这 250 是什么食材。中间没有任何一层把克磅这样的字符串写死在业务代码里。数据流切换语言后发生了什么用户点击 English 徽章 → this.currentLocale en_USStorageLink 写回 AppStoragePersistentStorage 落盘 → isImperial() 变 trueen_US 属于英制体系 → 每条食材nameOf() 改用英文名amountOf() 走 toImperial() 换算 fmtUnit() 英文单位 → 单位显示示例250g/0.24L按英文体系重算 → 烤箱温度摄氏 175 不变华氏由 cToF(175) 计算并按英文格式化 → build() 全树重渲染一次点击、五处联动三、语言层体系元数据与文案表3.1 语言元数据——把体系挂在语言上这是本应用与系列其他应用最大的数据差异LangCfg多了一个system字段interfaceLangCfg{code:string;// locale 代码name:string;// 母语名flag:string;// 国旗 emojisystem:string;// metric | imperial ← 度量衡体系}privateisImperial():boolean{returnthis.curCfg().systemimperial;}privatecurCfg():LangCfg{returnLANGS.find((l:LangCfg)l.codethis.currentLocale)??LANGS[0];}设计决策用system显式标注而非靠US 结尾推断英制——真实产品中英国部分地区混用、日本用公制但用坪tsubo计量面积靠推断必然出错。显式字段 兜底?? LANGS[0]找不到 locale 时按默认语言处理是防御式编码的典型写法。3.2 文案表5 语言functionbuildStrings(pairs:Array[string,string]):Mapstring,string{returnnewMap(pairsasArray[string,string]);}constSTRINGS:Mapstring,Mapstring,string((){constmnewMapstring,Mapstring,string();m.set(zh_CN,buildStrings([[K.title,国际食谱],[K.sub,一份食谱全球通用],[K.recipe,经典曲奇 · 食材清单],[K.convert,单位转换器],[K.oven,烤箱温度换算],[K.temp,温度],[K.weight,重量],[K.volume,容量],[K.display,单位显示方式],[K.metric,公制],[K.imperial,英制]]));m.set(zh_TW,buildStrings([/* 繁體中文國際食譜、單位轉換器 … */]));m.set(en_US,buildStrings([/* English: Global Recipes, Unit converter … */]));m.set(ja_JP,buildStrings([/* 日本語国際レシピ、単位変換 … */]));m.set(fr_FR,buildStrings([/* Français: Recettes du monde, Convertisseur … */]));returnm;})();取用仍走本系列统一的三级降级functiont(code:string,key:string):string{constvSTRINGS.get(code)?.get(key);if(v!undefined)returnv;returnSTRINGS.get(DEFAULT_LOCALE)?.get(key)??key;// 目标 → 默认 → key}注意文案表里不包含任何单位词“克”磅都不在 STRINGS 里——单位词由fmtUnit()从 CLDR 数据生成文案表只管界面通用词标题/按钮/标签。这是本应用与纯文案翻译应用的本质区别单位名称不是翻译出来的是格式化出来的。四、格式化层Intl.NumberFormat style:unit核心 API4.1 核心用法functionfmtUnit(code:string,value:number,unit:string,display:string):string{try{constfmtnewintl.NumberFormat(code,{style:unit,unit:unit,unitDisplay:displayasshort,maximumFractionDigits:1});returnfmt.format(value);}catch(err){return${value.toFixed(1)}${unit};// 兜底格式化失败时回退纯数字 代码}}一个函数输出全部 5 种语言 三档显示 任意单位单位名称、复数形态、空格/连字符规则全部由 CLDRUnicode Common Locale Data Repository数据驱动locale100 kglong8.8 ozshorten_US100 kilograms8.8 ozzh_CN100千克8.8盎司ja_JP100キログラム8.8オンスfr_FR100 kilogrammes8.8 ozzh_TW100公斤8.8盎司三个技术点复数形态自动处理kilograms/grammes的复数、中文/日文无复数形态全部由 CLDR 规则决定代码里一个if都不用写空格规则随 locale英文8.8 oz用窄空格法文8,8 oz数字的小数点都变了8.8vs8,8日文窄格式8.8oz无空格——排版细节全部正确非法 unit 抛错unit传了 CLDR 不支持的代码如拼错的killogram会抛异常try/catch兜底为纯数字 单位代码保证 UI 永不空白。4.2 unitDisplay 三档档位示例en, 0.24 L设计意图long0.24 liters正式/朗读场景单位词完整short0.24 LUI 标签默认平衡信息与空间narrow0.24L图表刻度/极窄空间去掉所有空格本应用把三档做成运行时切换displayIdx用户可实时对比——这也是验证 CLDR 数据完整性最直观的手段。五、换算层公制基准 两个特例5.1 数据统一用公制基准INGREDIENTS表里所有数值都是公制基准值克/毫升英制显示时才换算——这是国际化存储的铁律存储用 SI 基准单位展示层换算。// 换算公制 → 英制functiontoImperial(metric:number,unit:string):number{if(unitgram){returnmetric/28.35;// g → oz}returnmetric/29.5735;// ml → fl oz}如果反过来存储英制、展示换算公制每次新增地区都要改数据而存公制后即使未来加印度英制或缅甸本地单位数据零改动。5.2 温度是唯一非线性特例长度、重量、容量都是目标值 基准值 × 系数的线性关系温度不行functioncToF(c:number):number{returnc*9/532;}175°C → 347°F而非175 × 1.8 315——差 32 度的偏移量必须单独处理。换算层为温度单开函数而不是硬塞进统一的rate表保证了线性换算表可以保持纯乘法的简单性。同理开尔文与摄氏K C 273.15也是偏移型将来扩展同样单开函数。六、状态联动与重渲染StorageLink(STORAGE_LOCALE)currentLocale:stringDEFAULT_LOCALE;StatedisplayIdx:number1;// 0 long / 1 short / 2 narrowStatetempIdx:number1;// 175°C 默认三个状态各自独立、互不覆盖语言切换只改currentLocale显示粒度只改displayIdx温度档只改tempIdx。任一变化 → 相关格式化函数重算 → ArkUI 增量渲染。没有换算结果这个状态——结果永远是即时计算出来的这正是声明式 UI 的优势展示值不落状态、不存缓存天然避免数据与展示不同步的经典 bug。持久化细节aboutToAppear():void{if(!AppStorage.getstring(STORAGE_LOCALE)){AppStorage.setOrCreate(STORAGE_LOCALE,DEFAULT_LOCALE);}PersistentStorage.persistProp(STORAGE_LOCALE,DEFAULT_LOCALE);}先setOrCreate兜底、再persistProp落盘与系列其他应用一致displayIdx/tempIdx不持久化回到默认档位无伤大雅且避免 Preferences 写入过于频繁。七、数据流复盘一次完整交互启动 → aboutToAppearAppStorage 初始化 persistProp → build按 zh_CN 公制渲染食材250克、240毫升、单位 short 档、温度 175°C ⟷ 347°F 用户点击 English → currentLocale en_USStorageLink → AppStorage → 落盘 → curCfg() 返回 imperial 配置isImperial() true → nameOf()无盐黄油 → unsalted butter → amountOf()250/28.358.818… → fmtUnit(en_US, 8.8, ounce, short) → 8.8 oz → 温度fmtF() → fmtUnit(en_US, 347, fahrenheit, short) → 347°F → ArkUI 增量渲染全程无闪烁 用户点击 narrow 档 → displayIdx 2 → 所有 fmtUnit 的 unitDisplay 变 narrow → 250g / 0.24L八、ArkTS 兼容要点unitDisplay: display as shortdisplayNames()返回string[]传给需要字面量联合类型的选项时需要断言arkts-no-any-unknown规范下最常见的写法catch (err)不带类型注解arkts-no-types-in-catch格式化异常统一走兜底分支对象字面量全部显式接口LangCfg/Ingredientnew Map(pairs as Array[string, string])需要断言避免元组类型推断问题ForEachkey 生成器返回稳定唯一值语言用l.code、食材用i.id、温度用${idx}-${c}数值可能有重复页面最外层Scroll()承载Column无scrollable属性scrollBar(BarState.Off)隐藏滚动条保持清爽TEMPS常量数组用[160, 175, 190, 200, 220]直接量ForEach回调里(c: number, idx: number)显式标注参数类型。九、性能与内存fmtUnit()每次调用都new intl.NumberFormat(...)本页单次渲染约 12 次调用5 食材 × 2 体系分支 2 示例 2 温度毫秒级可接受若食材上百条应在模块级按code unit display缓存格式化器实例换算函数是纯数学除法/乘法无状态、无 IO重复计算开销可忽略——不要把换算结果存进状态计算比缓存更便宜且不会过期PersistentStorage只存语言字符串不存对象符合小数据用 Preferences的工程约定页面无定时器、无监听器后台自动销毁无内存泄漏风险。十、小结本应用把度量衡本地化拆成三个互不耦合的层次语言层管体系归属谁用公制、谁用英制、换算层管纯数学线性表 温度特例、格式化层管呈现style:unit输出本地化单位。其中fmtUnit()一个函数通吃语言 × 单位 × 档位三个维度是 CLDR 数据驱动能力的最佳示范。应用深化维度01多语言文案表 三级降级语言层骨架03NumberFormat 货币/汇率金额维度04NumberFormat 数字/百分比数值维度09NumberFormat style:‘unit’单位维度← 本文13货币 数字 日期 单位综合生产级账单给生产环境的三条铁律① 数据永远存 SI 基准单位② 展示永远走Intl style:unit绝不手拼单位字符串③ 默认单位跟随 locale但允许用户手动覆盖并单独持久化——因为用户偏好和系统默认是两回事。
延伸阅读

更多相关文章

2026/10/30 11:54:55

国际食谱 - 度量衡本地化 —鸿蒙实战

一、场景痛点 度量衡是国际化的「隐性差异」:文案没翻译用户能看出来,但单位不对用户往往说不清哪里不对,只会觉得"这 App 不对劲"。 美国用户看到「250 克 无盐黄油」:知道是黄油,但完全无法估计 250 克是多…

2026/10/25 1:50:14

机器学习特征工程实战:类别型特征编码方法全解析与避坑指南

1. 项目概述:为什么我们需要编码类别型特征?在数据科学和机器学习的日常工作中,我们拿到手的数据集里,总少不了像“城市”、“产品类型”、“学历等级”这样的类别型特征。它们用文字描述,直观易懂,但机器可…

2026/10/26 5:56:47

国际食谱 - 度量衡本地化 — ArkTS页面设计

一、设计目标 把「单位换算 本地化呈现」做成一个直观的食谱工具:选语言、看食材用量自动变成当地单位体系、调单位显示档位、换算烤箱温度,结果即时呈现。UI 设计要回答三个问题: 同一份食谱,如何在 5 种语言下都"读得懂&q…

2026/10/30 11:53:47

NModbus4实战拆解:Modbus RTU/TCP主从站调试工具与避坑

简介:NModbus Tools 是一套基于 NModbus4 的 Modbus 通讯辅助工具集,面向工业自动化开发者、PLC 调试人员及正在学习 Modbus 协议的读者,可用于串口与网络通信测试、请求响应报文解析、虚拟从站模拟和常见故障诊断,帮助理解设备间…

2026/10/30 11:53:47

Tecplot CFD后处理教程:云图切片流线与宏脚本批量出图

简介:《Tecplot教程合集》面向工程仿真、流体力学与科学数据可视化领域的学习者,从入门操作到脚本自动化均有覆盖,适合希望系统掌握Tecplot二维/三维绘图、后处理分析与布局定制的初级到中级用户。压缩包共收录143个文件,约119.87…

2026/10/30 11:53:47

AI工具链如何提升设计团队3倍效率

1. 设计团队效率革命:如何用一套AI工具整合三个岗位的工作流 去年接手新项目时,我发现团队里三个设计执行岗每天80%的时间都耗在重复劳动上——平面设计忙着处理基础排版,视觉设计师困在素材搜集,而创意设计师则在反复修改提案。这…

2026/10/30 11:53:47

Spring Boot实战DDD:用ArchUnit enforce分层与上下文边界

简介:本资源是一份面向软件架构师、后端开发工程师及技术团队的领域驱动设计(DDD)入门与实践指南,聚焦业务建模与技术实现的深度对齐,解决复杂业务系统中模型失真、沟通断层与架构腐化等典型问题。内容源自InfoQ中文站…

2026/10/30 11:53:47

经销商惹的祸,总部的企业负面舆情公关该怎么切割

一条门店短视频能在数小时内完成百万级曝光,而总部往往在热搜挂上后才第一次看到它。监测覆盖3000个以上平台的系统发出告警时,评论区已经有人把矛头从“这家店”转向“这个品牌”。问题出在:绝大多数总部把经销商舆情当成“别人的事”&#…

2026/10/30 11:48:46

SK-VQA:知识增强的多模态大模型训练与应用

1. SK-VQA项目概述:当多模态大模型遇上知识增强训练 在知识驱动的视觉问答(KB-VQA)领域,现有模型常面临"知识盲区"困境——当问题涉及训练数据之外的领域知识时,性能会断崖式下跌。这正是我们团队开发SK-VQA…

2026/10/29 17:05:52

全国计算机二级C语言真题PDF高效刷题指南

简介:这份PDF是全国计算机二级C语言考试的试题与答案解析,专为备考二级C语言的考生准备。内容涵盖C语言特点与设计理念、程序基本组成单位、main函数执行流程、函数嵌套调用规则、标识符命名规范、整型实型字符型数据类型、八进制十六进制数合法形式、运…

2026/10/29 17:06:02

requests+BeautifulSoup:爬虫抓取h1标题的入门实战指南

简介:一份以实战为导向的Python爬虫入门资源,面向具备基本编程基础、希望快速掌握网络数据抓取的初学者,可解决从零编写简单爬虫的常见困惑。实例以requests库发送HTTP请求、BeautifulSoup解析HTML文档,完整演示从网页中提取 标题…

2026/10/29 17:06:13

递推最小二乘参数辨识MATLAB编程:二阶系统与渐消记忆实现

简介:这是一篇面向控制系统、信号处理与机器学习等领域学习者的最小二乘参数辨识程序说明文档。资源共包含一个docx格式文档,压缩包大小七十八KB,内容围绕递推最小二乘算法和渐消记忆最小二乘算法展开:先构造一个极点位于单位圆内…

2026/10/30 0:00:46

NCP1342高频准谐振反激控制器:从设计约束到验收的完整方案

简介:NCP1342 高频准谐振反激控制器应用介绍译文文档,面向电源设计工程师、硬件研发人员及开关电源学习者,帮助读者跨越英文原版资料的语言门槛,系统理解这款高集成度 QR Flyback 控制器的设计要点。文档围绕芯片核心特性展开&…

2026/10/30 0:00:46

NCP1342准谐振反激电源设计:从选型到验收的全流程解析

简介:这份文档面向电源设计工程师与开关电源学习者,系统梳理NCP1342高频准谐振反激控制器的应用要点,帮助读者快速理解这款高集成度离线电源变换器控制芯片的工作机制与设计方法。内容涵盖芯片核心特性、引脚功能定义、电气特性参数表、最大额…

2026/10/30 0:00:46

Web系统自动登出机制设计与实现全解析

1. 系统自动登出机制的设计初衷在各类Web应用和后台管理系统中,自动登出功能早已成为标配。这个看似简单的功能背后,实际上涉及用户安全、资源管理和体验优化的多重考量。作为从业十多年的老开发,我见过太多因为登出逻辑设计不当导致的安全事…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑