在一次版本衝刺中,開發分支新增了十多條介面文案。封存作業順利完成,但測試人員切換語言後,卻看到內容回退成英文,部分按鈕甚至顯示空白。問題不在編譯器,而是流水線從未將在地化完整性列為可能失敗的檢查項目。對於長期運行的雲端 Mac,最有效的做法不是等人逐一操作所有介面,而是在每次建置前直接檢查 .xcstrings。
先定義把關機制要攔截哪些問題
String Catalog 是 JSON 檔案,但「能夠解析」不代表「可以交付」。實用的把關機制至少應檢查三類問題:專案要求的目標語言不存在、翻譯狀態不是 translated,以及最終字串為空。複數形式、裝置差異等內容還會出現在 variations 之下,因此只讀取第一層 stringUnit 會造成漏報。
應先將驗收範圍寫入儲存庫,而不是留在建置節點的暫時設定中。假設專案的來源語言是英文,目前要求支援簡體中文、日文與法文,可以將目標語言當作指令碼參數傳入。
| 檢查項目 | 失敗條件 | 處理方式 |
|---|---|---|
| 目標語言 | localizations 中沒有對應鍵 |
阻止建置 |
| 翻譯狀態 | 任一葉節點不是 translated |
回傳具體鍵與語言 |
| 字串內容 | value 為空或只有空白 |
阻止建置 |
| 不參與翻譯 | shouldTranslate 為 false |
明確略過 |
把關機制只判斷目錄中的在地化資源是否完整,不能取代針對介面截斷、動態參數與語意準確性的人工驗收。
撰寫不依賴外部套件的檢查器
將指令碼儲存為 Scripts/check_xcstrings.py。它只依賴 macOS 內建的 Python 執行環境;如果團隊固定使用獨立的 Python 路徑,應在建置設定中明確指定,以免互動式 shell 與 CI 的 PATH 不一致。
#!/usr/bin/env python3
import argparse
import json
import pathlib
import sys
def units(node):
found = []
if isinstance(node, dict):
unit = node.get("stringUnit")
if isinstance(unit, dict):
found.append(unit)
for key, value in node.items():
if key != "stringUnit":
found.extend(units(value))
elif isinstance(node, list):
for value in node:
found.extend(units(value))
return found
parser = argparse.ArgumentParser()
parser.add_argument("root")
parser.add_argument("--locale", action="append", required=True)
args = parser.parse_args()
failures = []
files = sorted(pathlib.Path(args.root).rglob("*.xcstrings"))
if not files:
failures.append("no .xcstrings files found")
for path in files:
with path.open(encoding="utf-8") as handle:
catalog = json.load(handle)
for key, entry in catalog.get("strings", {}).items():
if entry.get("shouldTranslate") is False:
continue
localizations = entry.get("localizations", {})
for locale in args.locale:
localized = localizations.get(locale)
if localized is None:
failures.append(f"{path}:{key}:{locale}:missing locale")
continue
leaves = units(localized)
if not leaves:
failures.append(f"{path}:{key}:{locale}:missing stringUnit")
continue
for index, unit in enumerate(leaves):
state = unit.get("state")
value = unit.get("value", "")
if state != "translated":
failures.append(
f"{path}:{key}:{locale}:{index}:state={state}"
)
if not value.strip():
failures.append(
f"{path}:{key}:{locale}:{index}:empty value"
)
for failure in failures:
print(failure, file=sys.stderr)
sys.exit(1 if failures else 0)
在儲存庫根目錄執行:
python3 Scripts/check_xcstrings.py . \
--locale zh-Hans \
--locale ja \
--locale fr
指令碼以非零狀態結束後,任何常見的工作流程編排器都能中止後續步驟。輸出內容包含檔案、字串鍵、語言與葉節點序號,修復人員不必先翻閱完整的建置記錄。
在編譯前接入雲端 Mac 流程
建議將檢查安排在相依套件解析之後、xcodebuild build 或 archive 之前。相依項目準備階段可以確認儲存庫內容已完整寫入磁碟,而提前檢查也能避免對明顯不合格的提交執行編譯、測試與封存。
set -euo pipefail
mkdir -p build/reports
python3 Scripts/check_xcstrings.py Sources \
--locale zh-Hans \
--locale ja \
--locale fr \
2> build/reports/localization-errors.txt
xcodebuild \
-project App.xcodeproj \
-scheme App \
-configuration Release \
-destination 'generic/platform=iOS' \
build
set -o pipefail 非常重要。如果之後將輸出傳給 tee,缺少這項設定時,流水線可能只看到最後一個命令成功,因而忽略檢查器的失敗狀態。報告目錄也應在工作開始時重新建立,避免雲端 Mac 上次執行留下的檔案被誤認為本次結果。
透過匯出結果增加一層人工複核
直接解析 .xcstrings 適合作為自動化把關機制,但發布前仍應匯出在地化套件,確認開發語言、註解與上下文是否完整。可以針對目標語言執行:
rm -rf build/xcloc
mkdir -p build/xcloc
xcodebuild -exportLocalizations \
-project App.xcodeproj \
-localizationPath build/xcloc \
-exportLanguage zh-Hans
xcodebuild -exportLocalizations \
-project App.xcodeproj \
-localizationPath build/xcloc \
-exportLanguage ja
匯出失敗時,不要直接重試並覆蓋現場。應先保留完整的標準錯誤輸出,再確認 scheme、專案路徑與目標語言識別碼。語言代碼必須與 Catalog 中的鍵完全一致,例如 zh-Hans 與 zh-Hant 是兩個獨立目標,不能以模糊的前綴取代。
匯出的 .xcloc 適合交由在地化負責人抽查,但不建議將建置產物提交到主分支。儲存庫應保存來源 .xcstrings、檢查指令碼與目標語言清單;匯出目錄則應作為單次工作的產物。
排除常見誤報並完成驗收
第一類誤報來自「不需要翻譯」的技術字串。不要在指令碼中任意依照鍵名前綴忽略,而應在 Catalog 中明確設定 shouldTranslate: false,讓規則跟隨資源本身。第二類來自複數分支只翻譯了 one,卻遺漏 other;遞迴讀取每個 stringUnit,即可精確找出這類問題。
第三類情況是開發人員新增語言後,忘記更新 CI 參數。目標語言清單最好只有單一來源,例如專案指令碼中的陣列或流水線變數,避免多個工作各自維護一份。第四類則是空格、換行或預留位置被誤認為有效翻譯。目前的指令碼會拒絕純空白內容;針對 %@、%d 等格式參數,也可以增加來源字串與目標字串的預留位置集合比對,但應先涵蓋跳脫字元與位置參數,再啟用阻斷。
合併前依照以下順序驗收:
- 刻意刪除一個目標語言,確認工作失敗。
- 將一條翻譯改成空格,確認報告指出具體鍵。
- 建立一個未完成的複數分支,確認遞迴檢查能夠發現。
- 還原資源後重新執行,確認舊報告不會影響結果。
- 最後執行 Release 設定的建置,驗證把關機制與正式工作使用同一份簽出內容。
最後建立的不是一次性的翻譯掃描,而是一項能在雲端 Mac 上穩定重現的工程約束:資源不完整時及早失敗,只有修復資源後才進入編譯、測試與封存。
常見問題
String Catalog 檢查應放在建置流程的哪個階段?
應放在相依項目準備完成之後、編譯與封存之前。發現問題便立即停止,避免把時間消耗在無法交付的封存工作。
只檢查 state 是否為 translated 就足夠嗎?
不夠。還要確認目標語言存在、翻譯內容不是空值,並遞迴檢查複數等 variations 分支中的每一個 stringUnit。
讓下一次建置在雲端 Mac 上執行。
比較三種 Apple Silicon 設定,從五個海外節點中選擇適合目前工作流程的區域。