news 2026/10/19 7:56:29

swiftui-expert-skill - localization

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
swiftui-expert-skill - localization

SwiftUI 本地化参考

面向用户的文本指导:Text、Button、Label、导航/工具栏标题、alert 以及携带可本地化字符串的类型。关于单个Text上更窄的"verbatim 与本地化"决策,请参阅references/text-patterns.md。

目录

  • SwiftUI 自动本地化字符串字面量
  • String Catalogs
  • Swift 包和框架的 Bundle
  • 本地化变量和自定义类型
  • 非视图类型的 LocalizedStringResource
  • 插值 vs 拼接
  • 大小写
  • 格式化日期、数字和货币
  • 为本地化设计布局
  • 读取当前语言环境
  • SwiftUI 视图之外的 String(localized:)
  • 给翻译人员的注释

SwiftUI 自动本地化字符串字面量

接受LocalizedStringKey的初始化器(Text、Button、Label、.navigationTitle、alert 标题等)会自动将字符串字面量视为本地化键。不要将字面量包裹在NSLocalizedString、String(localized:)或LocalizedStringResource中——那会急切地解析字符串并忽略\.locale覆盖。

// 避免:双重工作,且急切解析Text(String(localized:"start_workout"))// 优先:直接传递字面量Text("start_workout")

不透明的键("start_workout")和自然语言字符串("Start Workout")都可以作为键——选择项目已经使用的约定。只有当你想让字面量不参与本地化时才使用Text(verbatim:)(例如插值运行时值的调试标签)。当参数已经是String变量时,Text(value)会调用StringProtocol重载并自行跳过本地化。

String Catalogs

大多数项目通过 String Catalogs(.xcstrings)进行本地化。每次构建会将代码中的新键同步到 catalog 中,但 catalog 文件必须已经存在——Xcode 不会自动创建。如果项目已经使用.strings/.stringsdict,请在那里添加新字符串,而不是迁移。使用tableName:将字符串组路由到特定 catalog。

Text("Explore",tableName:"Navigation",comment:"Tab bar item title for the Explore screen.")

Swift 包和框架的 Bundle

应用、应用扩展和 XPC 服务是它们自己的主 bundle,因此可以省略bundle。框架和 Swift 包需要显式的bundle:——没有它,SwiftUI 会在Bundle.main中查找字符串,查找会静默失败,字符串在运行时以未本地化形式出现。

// 避免(在框架/包内):搜索应用的 catalogText("Save to Favorites")// 优先:#bundle 解析到当前目标的 bundleText("Save to Favorites",bundle:#bundle,comment:"Button to bookmark a recipe.")

#bundle是首选形式;Bundle.module和Bundle(for:)仍然有效,但属于较老的模式。

本地化变量和自定义类型

传递给Text的String变量会运行StringProtocol重载,并且不会被本地化。将其包裹在LocalizedStringKey(_:)中也没有帮助——Xcode 无法从运行时值提取字面量,因此不会进入 catalog。要本地化从已知集合中选择的值,请用暴露LocalizedStringResource的类型对其进行建模:

enumCategory{caseappetizers,mains,dessertsvarname:LocalizedStringResource{switchself{case.appetizers:"Appetizers"case.mains:"Mains"case.desserts:"Desserts"}}}Text(category.name)

当视图或模型暴露面向用户的文本时,将属性类型设为LocalizedStringKey或LocalizedStringResource而不是String。每个接受本地化文本的 SwiftUI 视图都接受两者,因此在显示处延迟解析不花费任何成本,并保留 locale/bundle 上下文。

非视图类型的 LocalizedStringResource

当非视图类型携带面向用户的文本时——模型对象、提示、排队通知——使用LocalizedStringResource而不是String。它将解析延迟到显示时间,因此会遵循值实际渲染时生效的 locale,而不是创建时的 locale。

// 避免:在创建时解析,无法在另一种 locale 中重新渲染structTip{letheadline:String}lettip=Tip(headline:String(localized:"Tip of the Day"))// 优先:解析延迟到显示时间structTip{letheadline:LocalizedStringResource}lettip=Tip(headline:"Tip of the Day")

在设计新类型或更改面向用户的文本时应用此做法——不要作为无关编辑的一部分扫荡现有的String属性。

插值 vs 拼接

字符串插值保留LocalizedStringKey并在 catalog 中生成格式字符串(例如"Welcome, %@")。使用+拼接会产生普通String,不会被本地化。绝不要将分别本地化的片段拼成句子——词序在不同语言中各不相同。

// 避免:+ 产生 String;句子组装破坏词序Text("Error: "+statusMessage)Text(String(localized:"Created by"))+Text(" ")+Text(authorName)// 优先:一个可让翻译人员重新排列的插值字符串Text("Error:\(statusMessage)")Text("Created by\(authorName)")

大小写

将所需的大小写烧录到字符串中,而不是在运行时通过.textCase(_:)、.localizedUppercase或.localizedCapitalized转换。运行时转换会强制每种翻译都使用相同的大小写,让翻译人员没有余地按语言调整。

// 避免Text("Section Header").textCase(.uppercase)// 优先Text("SECTION HEADER")

这适用于本地化字符串;用户输入的文本应原样显示。如果转换不可避免,优先使用.localizedUppercase/.localizedCapitalized,它们会遵循用户的 locale。

格式化日期、数字和货币

使用Text的format:参数或.formatted(),而不是带硬编码格式字符串的DateFormatter/NumberFormatter。格式样式会适应用户的 locale;硬编码的格式字符串不会。

// 避免letf=DateFormatter();f.dateFormat="MM/dd/yyyy"Text(f.string(from:workout.date))Text("$\(product.price,specifier:"%.2f")")// 优先Text(workout.date,format:.dateTime.month().day().year())Text(product.price,format:.currency(code:store.currencyCode))

字段组件(.month()、.day())选择显示哪些字段;顺序由 locale 决定。对于列表,Array.formatted()会插入 locale 正确的分隔符和连接词,而不是joined(separator:)。当DateFormatter确实不可避免时,使用setLocalizedDateFormatFromTemplate(_:)而不是直接赋值dateFormat。

为本地化设计布局

  • 使用.leading/.trailing而不是.left/.right——它们会为从右到左的 locale 翻转。
  • 不要为文本硬编码 frame 宽度/高度;翻译长度不一,脚本高度不一。当布局可能放不下更长的翻译时,使用ViewThatFits。
  • 使用文本样式(.body、.headline)而不是固定的点大小,以便行高适应每种脚本。
// 优先Text(recipe.title).frame(maxWidth:.infinity,alignment:.leading)ViewThatFits{HStack{actionButtons}VStack{actionButtons}}

读取当前语言环境

在视图中使用@Environment(\.locale)进行依赖 locale 的逻辑,而不是Locale.current——环境值会尊重预览覆盖和逐视图注入。

SwiftUI 视图之外的 String(localized:)

当你在视图之外需要本地化的String时,使用String(localized:),而不是NSLocalizedString。不要在NSLocalizedString内部插值——Xcode 在构建时从字面量提取键,无法提取插值。String(localized:)支持插值(它提取格式字符串并将值视为运行时参数),并且优于String(format:)——后者无论 locale 如何总是将数字渲染为 0–9。

// 优先lettitle=String(localized:"activity_summary",comment:"Dashboard header")

给翻译人员的注释

添加描述 UI 元素及其用途的comment:,特别是对模棱两可的字符串。对于插值字符串,按位置描述每个占位符——翻译人员看不到 Swift 变量名。注释可以放在调用处,也可以放在 String Catalog 中每个字符串的 Comment 字段中;每个字符串保持单一信息来源。

// 避免:"Edit" 可能是名词或动词Text("Edit")// 优先Text("Edit",comment:"Toolbar button that enters editing mode for the list.")Text("Completed\(count)of\(total)",comment:"Progress label — first variable is finished items, second is the total.")

汇总清单

  • 字符串字面量直接传递给Text/Button/Label(不包裹在NSLocalizedString/String(localized:)中)
  • 框架和 Swift 包内面向用户的字符串使用bundle: #bundle
  • 模型/非视图类型上面向用户的文本类型为LocalizedStringResource,而不是String
  • 动态字符串使用插值(而不是+);不从片段组装句子
  • 大小写烧录到字符串中,而不是通过.textCase应用
  • 日期/数字/货币使用带 locale 感知样式的format:/.formatted()
  • 使用.leading/.trailing(而不是.left/.right);不硬编码文本 frame 尺寸
  • 视图中的 locale 逻辑使用@Environment(\.locale)
  • 为模棱两可的字符串和插值占位符提供comment:
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 20:42:17

自动化与脚本实战:语言选型到测试运维全指南

自动化与脚本,这两个词放在一起,多少有点江湖气。外行听起来像是黑客电影里的暗号,内行知道这就是把重复劳动交给机器的基本盘。我干了这么多年自动化相关的工作,从最早的按键精灵到现在的 pytest、Ansible、Playwright&#xff0…

作者头像 李华
网站建设 2026/10/19 7:34:19

扭转光子晶体准BIC的COMSOL仿真:实现远场偏振连续调控

如果你近期在跟光学超表面较劲,那BIC(连续谱束缚态)这三个字母大概怎么都躲不开。它最迷人的地方在于:一个本该向远场辐射的模式,因为结构对称性或动量匹配条件,被硬生生“锁”在结构内部,Q因子…

作者头像 李华