云端 Mac 用 XLIFF 验收 Xcode 本地化导入

云端 Mac 用 XLIFF 验收 Xcode 本地化导入

一个版本临近封板时,翻译供应方通常会交回若干 .xcloc.xliff 文件。直接在开发者的日常工作区双击导入,短期看很快,实际却容易把过期翻译键、错误目标语言和占位符损坏一起写入工程。更稳妥的做法,是在云端 Mac 上建立一次性验收目录:先从当前提交导出基线,再预检交付物,最后导入、审查差异并执行构建。

把验收环境固定下来

验收节点应从干净提交开始。不要复用已经执行过本地化导入的工作区,也不要在包含未提交修改的目录里测试。可以为每次交付创建临时分支或独立 worktree:

git fetch origin
git worktree add ../localization-review origin/main
cd ../localization-review
git switch -c review/localization-batch
xcodebuild -version
git status --short

xcodebuild -version、提交 SHA、目标 Scheme 和交付批次写入日志。若团队使用多个 Xcode 版本,还应显式设置 DEVELOPER_DIR,避免交互式终端与自动任务调用不同的工具链。

export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer"
xcode-select -p
git rev-parse HEAD

本地化验收的基准不是“文件能打开”,而是同一提交、同一 Xcode 工具链和同一组导出参数能够得到可解释的结果。

在 GPUMini 的云端 Mac 上执行时,先在控制台确认当前可选配置,再选择用于验收的节点。该任务通常不依赖高并发,但导入后若要完整编译大型工程,内存与磁盘余量仍需纳入计划。

从当前工程导出基线

对于 .xcodeproj,可用项目参数导出;使用 Workspace 的工程则改用 -workspace,并提供可共享的 Scheme。以下示例导出简体中文、日语和法语,重复执行 -exportLanguage 即可增加语言:

mkdir -p Artifacts/Baseline
xcodebuild \
  -project ExampleApp.xcodeproj \
  -scheme ExampleApp \
  -exportLocalizations \
  -localizationPath Artifacts/Baseline \
  -exportLanguage zh-Hans \
  -exportLanguage ja \
  -exportLanguage fr

导出完成后先保存文件清单,而不是立刻覆盖收到的译文:

find Artifacts/Baseline -type f -print0 \
  | sort -z \
  | xargs -0 shasum -a 256 > Artifacts/baseline.sha256

基线回答三个问题:当前工程有哪些可翻译单元、源语言内容是什么、Xcode 认可哪些目标语言。若交付文件包含基线中不存在的键,常见原因是翻译基于旧提交;若基线新增大量未翻译单元,则说明交付范围可能漏掉了近期功能。

导入前检查结构与占位符

XLIFF 本质上是 XML。首先用系统工具检查格式,再提取语言属性和翻译单元数量。不要用正则表达式改写 XML;预检可读取,修复应回到本地化工具或经过审查的脚本。

find Incoming -name "*.xliff" -print0 |
while IFS= read -r -d '' file; do
  echo "Checking $file"
  xmllint --noout "$file"
  xmllint --xpath \
    'string(/*[local-name()="xliff"]/*[local-name()="file"][1]/@target-language)' \
    "$file"
  echo
done

占位符是最值得阻断的问题。%@%d、位置参数以及 String Catalog 生成的变量必须在源文和译文中保持语义对应。简单地统计百分号不够,因为 %%、格式宽度和位置参数含义不同。建议用 XML 解析器分别读取每个 trans-unitsourcetarget,规范化占位符后比较多重集合。

检查项 阻断条件 处理方式
XML 结构 xmllint 返回非零 退回交付文件
目标语言 与文件批次不一致 更正语言映射后重导
翻译单元 出现未知 ID 核对基线提交
占位符 类型或数量不一致 修订译文后重检
空译文 关键界面目标为空 确认是否允许回退

对于用户可见的格式串,还应抽查换行符、转义字符和复数分支。占位符顺序可以因语言改变,但使用位置参数时必须保持索引有效。

在隔离目录导入并审查差异

预检通过后,再逐个导入 .xcloc 包。每次导入后立即记录状态,便于定位是哪一份交付物引入了异常:

mkdir -p Artifacts/Logs
for package in Incoming/*.xcloc; do
  name="$(basename "$package" .xcloc)"
  xcodebuild \
    -project ExampleApp.xcodeproj \
    -importLocalizations \
    -localizationPath "$package" \
    2>&1 | tee "Artifacts/Logs/import-${name}.log"
  test "${PIPESTATUS[0]}" -eq 0 || exit 1
  git status --short
done

导入成功不等于验收完成。先用 git diff --stat 判断影响范围,再逐文件阅读差异。正常变化应集中在 String Catalog、字符串资源或对应语言目录;若出现 Scheme、构建设置、签名配置或无关项目文件变化,应暂停并查明来源。

git diff --stat
git diff --check
git diff -- '*.xcstrings' '*.strings' '*.stringsdict' 'project.pbxproj'

git diff --check 能发现部分尾随空格和冲突痕迹,但不能判断翻译语义。建议生成一份“新增、删除、修改翻译单元”的摘要,随导入日志一起作为流水线产物保存。

用构建和复导出完成闭环

最后执行目标 Scheme 的干净构建。若工程包含多个应用目标或扩展,应覆盖实际发布路径,而不是只构建最小库目标。

set -o pipefail
xcodebuild \
  -project ExampleApp.xcodeproj \
  -scheme ExampleApp \
  -configuration Release \
  -destination 'generic/platform=iOS Simulator' \
  clean build |
  tee Artifacts/Logs/localization-build.log

构建通过后,再从导入后的工程导出一次 XLIFF,与基线比较。复导出可以发现某些译文没有真正落入工程、语言代码映射错误,或导入后仍存在未翻译单元。比较时应忽略纯排序和工具生成元数据,只关注翻译单元的源文、目标文本、状态与备注。

一条可维护的验收流水线应明确保存四类证据:工具链与提交信息、预检报告、导入差异摘要、构建日志。失败时不要自动清理工作目录,先保留现场;成功后则删除临时 worktree,避免下一批交付继承旧状态。

最终门禁可以归纳为:XML 可解析、目标语言正确、占位符集合一致、未知翻译单元为零、差异范围符合预期、目标 Scheme 构建成功。这样,云端 Mac 承担的是可重复的本地化验收节点,而不是另一个只能靠人工记忆维护的导入环境。

常见问题

导入 XLIFF 前为什么要先导出一份基线?

基线能固定当前工程可识别的翻译单元、源文和目标语言。收到译文后可据此发现过期键、未知键及错误语言,而不是直接让工程文件承担试错成本。

XLIFF 导入成功是否代表本地化可以发布?

不代表。导入成功只说明文件可被 Xcode 处理,还应检查占位符、复数规则、工程差异,并至少执行一次目标 Scheme 的干净构建。

验收任务应该在主工作区直接执行吗?

不建议。应在临时分支或独立工作目录中导入,保存导入日志和差异摘要,确认结果后再合并需要的资源变更。

独享物理节点

为开发与构建任务选择一台云端 Mac

比较两档 M4 配置、四种租用周期与四个在售节点,再按任务需要完成部署。

选择配置并下单