将 Localizable.strings 迁移到 String Catalog(.xcstrings)

更新于 · LocalePilot

从 Xcode 15 开始,Apple 一直在引导所有人使用 String Catalog:一个 JSON 文件包含所有语言,支持复数和设备变体。迁移只需点两下,真正需要你关注的是随后产生的差异。

赶时间?直接在浏览器中把独立的 .strings 文件转换为目录:免费,无需注册,不上传任何内容。

迁移之前

两项预防措施,可以避免大多数意外:

  • 干净的 Git 工作区,并在专门的分支上进行。迁移后值得检查的内容都在差异里,所以你需要一个只包含迁移的差异。
  • 迁移前构建能通过。Xcode 在构建时扫描代码来填充目录。如果项目无法编译,目录就会不完整,而你却不知道原因。

迁移本身

在项目导航器中右键点击 Localizable.strings,然后选择 Migrate to String Catalog。Xcode 会列出涉及的文件和 target;具体措辞可能随 Xcode 版本变化,但思路不变。

fr.lproj/Localizable.strings、de.lproj/Localizable.strings 等文件会被一个包含所有语言的 Localizable.xcstrings 取代。.strings 文件中的注释会成为每个键的注释。

文件长什么样

String Catalog 是 JSON。一个简单的键:

{
  "sourceLanguage" : "en",
  "strings" : {
    "welcome_user" : {
      "comment" : "Home screen greeting",
      "localizations" : {
        "fr" : {
          "stringUnit" : {
            "state" : "translated",
            "value" : "Bon retour, %@ !"
          }
        }
      }
    }
  },
  "version" : "1.0"
}

复数则存放在 variations 下:

"items_left" : {
  "localizations" : {
    "fr" : {
      "variations" : {
        "plural" : {
          "one" : { "stringUnit" : { "state" : "translated", "value" : "%lld article restant" } },
          "other" : { "stringUnit" : { "state" : "translated", "value" : "%lld articles restants" } }
        }
      }
    }
  }
}

注意格式:每个冒号前有一个空格,这是 Xcode 的写法。如果某个工具把文件重写成“标准” JSON,就会产生巨大的差异,却没有改动任何一条译文。

差异中要审阅什么

标记为 Stale 的键。构建后,Xcode 会标记在代码中再也找不到的键。它们不一定是废弃的:运行时动态生成的键(NSLocalizedString(variable, comment: "")、从配置文件读取的键)对扫描是不可见的。删除前请先确认。

从代码中提取的新键。构建可能会添加你从未放进 Localizable.strings 的字符串,典型的是 SwiftUI 的 Text("…") 字面量,它们默认就是可本地化的。这些字符串没有译文,所以 Xcode 显示的各语言完成百分比会下降,这属于正常现象。

状态。每个值都有一个状态:translated、needs_review,或者表示尚未有人翻译的 new。三个月后,你就是靠它来了解哪些内容真正审阅过。

格式说明符。像 Text("\(count) items left") 这样的 SwiftUI 字符串会生成键 %lld items left。如果旧的 .strings 对同一句话使用的是 %d,你现在就会有两个几乎重复的键;保留代码实际使用的那个。

旧版 iOS 怎么办?

不需要 iOS 17。目录是一种源格式:构建时,Xcode 会把它编译成应用包中的常规 .strings 文件。最低部署版本不会改变。

在 Xcode 之外编辑目录

处理少量字符串时,Xcode 的编辑器就够用了。若要审阅整种语言、批量翻译或检查占位符,专门的工具会更方便。LocalePilot 打开 .xcstrings 文件,并按 Xcode 的风格写回:未修改的目录逐字节相同,一次编辑只改动它自己的那几行,编辑过的值会变为 translated 状态。复数和设备变体可以编辑;使用替换(%#@count@)的键和标记为 shouldTranslate: false 的键会原样保留,留给你在 Xcode 中编辑。

常见问题

String Catalog 需要 iOS 17 吗?

不需要。Xcode 在构建时会把目录编译成常规的本地化文件;应用的最低部署版本不会改变。

String Catalog 中的 Stale 是什么意思?

Xcode 在上次构建时没有在你的代码中找到这个键。删除之前,请确认它不是在运行时动态生成的。

如何撤销迁移?

在专门的分支上迁移,并在迁移前先提交一次:在 Git 中回退即可恢复 .strings 文件。