在 Xcode 中移除未使用的本地化键时,应将 Stale 条目视为待审核项。删除前检查运行时键的构造方式,将必要的动态消息保留为手动管理的条目,并在清理后测试受影响的 Mac app 界面。

本指南适用于维护使用 String Catalog 的 SwiftUI Mac app 的开发者。这里要进行一次目录清理:找出可以移除的消息,同时保留 app 仍会显示的标签和计数消息。下面的决策参考将提取证据与运行时行为区分开来。

过期的本地化键究竟是什么意思?

Xcode 会在构建期间发现可本地化字符串。Apple 说明,提取范围涵盖当前方案和平台。当先前提取的字符串消失时,Xcode 可以自动移除尚未翻译的条目;已有翻译的条目则会保留下来,并标记为 Stale。该状态表示 Xcode 已无法在代码中找到此字符串。Apple 明确指出,只有确认不再需要该字符串后,才能将其删除。发现 String Catalog

实际操作中,找不到源代码引用只是调查的起点。精确搜索 panel.compact 可以找到字面量引用,但无法确定代码是否会将 panel. 与所选模式拼接起来构造该键。应同时检查候选键和生成查询键的代码。

对每个候选项使用下面这份建议审核表。这是根据文档说明的提取范围整理出的决策参考,并非 Xcode 的额外功能。

审核时发现的证据 决策 后续操作
某个已移除界面是唯一使用方;不再有运行时键生成器 删除候选项 移除条目,然后重新构建并检查更改
运行时代码仍可能生成这个确切的键 保留 通过手动管理保留该条目,或将查询改为明确的可本地化引用
该键仍在另一个目标或构建条件中被引用 保留 构建并检查相关配置
另一个表中存在名称相似的键 单独调查 确认每次查询使用的表和资源包
未找到使用方,但键的生成代码不明确 暂缓 先追踪生成逻辑,再决定是否删除

如何在 Xcode 中建立有用的基线?

首先,保存一份可恢复的目录副本,或提交其当前状态。然后打开 app 项目,选择要审核的 Mac 方案。记录哪些目标和条件配置可能向受影响的界面提供消息;这样审核范围就明确了。

在项目导航器中选择目录,然后选取 Product > Build。Apple 说明,构建会根据可本地化的源文本填充并更新 String Catalog。选中目录后,选择 Editor > Assistant,再选择某个键以查看其源位置。也可以从目录跳转回源代码。使用字符串目录本地化和调整文本

如果预期的 Swift 字符串缺失,请检查目标的 Use Compiler to Extract Swift Strings 构建设置是否已启用。Apple 将其列为编译器提取的前提条件。提取不完整的目录不适合作为决定移除内容的可靠基线。本地化你的 SwiftUI app

对每个候选项,记录键、目录、可能的使用方和决定。先搜索完整键,再搜索稳定前缀和相关的本地化封装。检查传入这些封装的模型值和捆绑查询数据。如果其他目标或编译条件会改变包含的源代码,请重新运行相关构建。

不要仅仅因为两个条目显示相同文本就合并处理。Apple 的本地化 API 接受表名,而主 app 以外的资源可能需要不同的资源包。将另一个条目视为替代项之前,先追踪查询所用的表和资源包。为 app 文本做好翻译准备

如何保留动态引用的消息?

Apple 专门针对动态构造键的字符串说明了手动管理方式。选择所需条目,然后使用检查器将其设为手动管理。Xcode 的构建后本地化同步不会更新或移除这类条目。对于缺失的动态条目,Apple 也展示了如何使用目录中的加号按钮添加手动字符串。发现 String Catalog

手动管理需要明确的维护规则。在选择有效键的代码或数据旁记录这些键,并在目录注释中说明用途。之后移除某个受支持模式时,也要检查对应的手动条目;自动提取不会替你完成这项清理。

检查 SwiftUI 是否真的执行了查询

传给 Text 的 String 变量会直接显示,不会进行本地化。若要将变量用作查询键,Apple 说明应从它构造 LocalizedStringKey。直接传给 Text 的字面量会自动走可本地化路径。这些重载可能产生相同的源语言输出,但行为不同。LocalizedStringKey

下面这个完整的演示 app 将运行时选择的键与插值计数分开处理。在一个使用 Xcode 15 或更高版本、最低部署目标为 macOS 13 的临时 SwiftUI Mac app 项目中,用这段代码替换生成的 app 声明。只保留一个 @main 声明。已根据文档中的 API 检查此代码;由于这里没有 Swift 工具链,因此未进行编译。

import SwiftUI

@main
struct CatalogReviewDemoApp: App {
    var body: some Scene {
        WindowGroup {
            CatalogReviewView()
        }
    }
}

struct CatalogReviewView: View {
    @State private var compact = true
    @State private var itemCount = 2

    private var selectedKey: String {
        "panel." + (compact ? "compact" : "expanded")
    }

    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            Toggle("Compact layout", isOn: $compact)

            Text(LocalizedStringKey(selectedKey))

            Text("\(itemCount) items",
                 comment: "Number of items in the sample panel.")

            Button("Add item") {
                itemCount += 1
            }
        }
        .padding()
        .frame(minWidth: 320)
    }
}

将 Localizable.xcstrings 添加到 app 目标。添加 panel.compact 和 panel.expanded 这两个手动管理的条目,源值分别设为 Compact panel 和 Expanded panel。构建项目以提取字面量消息。显式转换为 LocalizedStringKey 会请求运行时查询,但不会向提取器提供所有可能构造出的键的字面量声明。

对于少量、有限的选项,可以考虑将键构造改为各分支中明确的可本地化字面量。对于需要在组件之间传递消息的自定义界面,Apple 推荐使用 LocalizedStringResource;它会携带表、注释和默认值等本地化信息。选择一种能让完整的受支持消息集最易于检查的表示方式。为 app 文本做好翻译准备

为什么变化的计数与变化的键不同?

在演示中,选择布局会改变 app 查询的消息键。更改 itemCount 则会为一条可本地化消息提供不同的参数。SwiftUI 插值会将嵌入的值转换为供本地化使用的格式占位符;运行时渲染时再填入相应的值。本地化你的 SwiftUI app

因此,你不需要为渲染出的 2 items、3 items 和 4 items 标签分别创建键。构建示例,找到提取出的计数消息,按住 Control 键并点按其键,然后选择 Vary by Plural。对于英语源语言本地化,使用 Xcode 显示的整数占位符,为 One 输入 %lld item,为 Other 输入 %lld items。Apple 说明了这种计数消息工作流程,以及为其他受支持的本地化添加适当复数变体的方法。使用字符串目录本地化和调整文本

清理期间,应将计数消息作为完整资源检查,包括其各种变体。源代码中没有出现 4 items 这样的渲染文本,并不能说明底层的插值消息是否在使用。同样,保留计数模板也不会因为它和无关的动态键出现在同一面板中,就让该动态键一并得到保留。

清理时究竟应移除哪些条目?

以审核 24 个候选项为例。这些数量仅用于构造示例,并非实际项目的测量结果。

审核结果 条目数 操作
唯一使用方属于已移除的面板 9 确认面板及其查询数据均已移除后,再删除
运行时选择仍可访问这些键 8 保留并记录手动管理情况
引用属于另一个目标或条件构建 5 保留并验证相应构建范围
键的来源仍未确定 2 暂时保留,等待调查
已审核总数 24 9 个删除候选项

这项计算让清理决策可供审查:审核 24 个条目并不意味着要删除 24 个。对 9 个删除候选项中的每一个,都应简要说明已移除的使用方,以及检查过的运行时查询路径。对于 8 个动态条目,记录允许使用的键集合,以便下次审核时重复验证。

只移除已确认的候选项,然后检查目录差异。确认更改只包含预期的键及其相关翻译,没有误删无关的复数变体或设备变体。重新构建相关配置。如果被删除的键又通过提取出现,请先重新检查其使用方,再次决定是否删除。

如何在 Mac 上验证保留的消息?

使用 Product > Scheme > Edit Scheme,选择 Run,然后打开 Options。在 App Language 中选择一种现有的受支持本地化,关闭编辑器,然后运行 Mac 目标。Apple 说明了如何使用这些方案控件测试本地化构建。对 app 支持的本地化重复此操作。运行 app 时测试本地化

尝试每种允许的动态选择,包括演示中的两种布局。确认 app 显示的是预期目录值,而不是标识符。对于计数消息,检查 0、1、2 和 12 等示例值;如果受支持的本地化需要其他复数类别,也要检查它们。只需更改示例中的初始计数,就能测试这些值。

保留的消息显示不正确时,请先检查确切的键、表、资源包和 Text 初始化方式,再修改目录。Apple 较新的生成符号工作流程也能让手动资源更易于引用:Xcode 26 引入了 String Catalog 符号生成,可通过 Generate String Catalog Symbols 为现有项目启用。这是一种可选的重构方式,未被引用的手动资源并不会因此就被证明为未使用。跟着代码探索 Xcode 本地化

最后保留一组精简且理由清晰的删除项,并记录保留的动态消息列表。理想的结果是目录中每个剩余条目都有明确的使用方,同时运行时选择的键和格式化计数消息仍能在 Mac 界面中正常工作。

来源