一个版本临近封板时,翻译供应方通常会交回若干 .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-unit 的 source 与 target,规范化占位符后比较多重集合。
| 检查项 | 阻断条件 | 处理方式 |
|---|---|---|
| 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 配置、四种租用周期与四个在售节点,再按任务需要完成部署。