i18n 與 OCR 配置流程

返回:文件索引 / README

本文區分兩套用途不同的語言資源:

  • i18n/<locale>/LC_MESSAGES/ok.po:GUI/gettext 翻譯。
  • assets/lang/<module>.json:任務 OCR 匹配器、本地化業務文本,以及少量多語言 data-only 資料。

兩者不能互相替代。

1. 執行鏈路

flowchart TD
    A[executor.locale 或 task.locale] --> B[BaseEfTask.runtime_locale]
    A --> C[get_lang_accessor]
    C --> D[规范化为活动 locale]
    D --> E[assets/lang/module.json]
    E --> F[选择 key 下的 locale 节点]
    F --> G[self.lang.module.key]
    G --> H[ocr / wait_ocr / 业务比较]
    I[assets/ocr_fix/ocr_text_fix.json] --> J[install_ocr_text_fix_patch]
    J --> K[扩展 OCR match 参数]
    K --> H

入口 main.py/main_debug.py 在匯入並啟動 ok 應用前呼叫 install_startup_patches()。OCR 補丁對 TaskExecutor.__init__ 和 OCR.fix_match_regex 安裝一次性 monkey patch。

2. 活動 locale

活動 OCR locale 由 src/data/lang/__init__.py 的 ACTIVE_LOCALES_CONFIG 明確控制:

ACTIVE_LOCALES_CONFIG = {
    "zh_CN": True,
    "zh_TW": True,
    "en_US": False,
    "ja_JP": False,
    "ko_KR": False,
    "es_ES": False,
}

因此當前 SUPPORTED_LOCALES == ("zh_CN", "zh_TW")。JSON 和 gettext 目錄中存在英語、日語、韓語、西班牙語內容,不代表這些語言已啟用為任務 OCR locale。

locale 來源和規範化規則:

  1. 有 executor 時讀取 self.executor.locale;否則讀取 self.locale。
  2. 支援 Enum、帶 name 屬性/方法的 locale 物件和字串。
  3. - 轉為 _,並對活動 locale 做大小寫寬容匹配。
  4. 非活動、未知或空 locale 回退 zh_CN。

BaseEfTask.runtime_locale 只暴露提取到的原始字串;真正的活動 locale 規範化發生在 LangAccessor 中。

3. 統一 JSON schema

資源路徑是單檔案:

assets/lang/<module>.json

不存在執行時使用的 assets/lang/<module>/<locale>.json 目錄結構。統一檔案以業務 key 為第一層,以 locale 為第二層:

{
  "k_confirm": {
    "zh_CN": {"string": "确认"},
    "zh_TW": {"string": "確認"},
    "en_US": {"string": "Confirm"}
  },
  "k_number": {
    "zh_CN": {"pattern": "^\\d+$"},
    "zh_TW": {"pattern": "^\\d+$"}
  },
  "k_accept": {
    "zh_CN": {"terms": ["接取", "接受"]},
    "zh_TW": {"terms": ["接取", "接受"]}
  }
}

每個 locale 節點應只使用一種值:

節點 訪問結果 用途
{"string": "确认"} str 固定文本或業務顯示文本
{"pattern": "^\\d+$"} 編譯後的 re.Pattern 正則 OCR 匹配
{"terms": ["A", "B"]} list 多個候選值

如果 locale 節點本身不是字典,則原樣返回。直接屬性解析檢查順序為 string、pattern、terms;build_matcher 檢查順序為 pattern、string、terms。不要在同一個 locale 節點混放這些欄位。

載入一個 key 時的值回退順序:

  1. 當前規範化 locale。
  2. 當前為 zh_TW 時仍回退 zh_TW,否則回退 zh_CN。
  3. 該 key 下第一個可用 locale 值。

模組檔案不存在、JSON 讀取失敗或 key 不存在時返回空模組/None;不會按舊目錄結構尋找其它檔案。

4. 程式碼訪問

result = self.wait_ocr(
    match=self.lang.DeliveryTask.k_ae8fb114,
    box=self.box.bottom,
    time_out=5,
)

self.wait_click_ocr(
    match=self.lang.daily_battle_mixin.k_b56d9ac6,
    box=self.box.bottom_right,
    time_out=5,
)

安全讀取並提供程式碼級 fallback:

from src.data.lang import get_lang_module_value

matcher = get_lang_module_value(
    self.lang,
    "DeliveryTask",
    "k_ae8fb114",
    fallback="确认",
)

業務資料仍以中文 canonical key 儲存時,使用現有工具轉換:

  • src/data/world_map_utils.py:get_world_map_matcher、get_world_map_text、is_world_map_text。
  • src/data/characters_utils.py:get_localized_name_by_canonical、get_contact_list_with_feature_list。

scripts/i18n/official_prefix_overrides.json 按語言模組、關卡類別分組,顯式列出適用的 key 和中文字首。只有配置的 key 按“字首·該 key 的中文核心名”查詢完整官方標題,其他 key 不受影響。當前幹員/武器養成使用“協議空間”,能量淤積點使用普通和重度兩個字首,危境再現使用“危境再現”;危境預演仍走圖片識別,未配置字首。

同步以中文完整標題精確查詢官方文本鍵,再通過同一個鍵讀取其他語言,不用英文反查。已有 OCR 核心名稱能匹配這些官方標題時保持原值;需修正或補齊時從對應標題提取核心名稱,保留官方縮寫。完整標題或某語言的字首變體缺失時保留已有資源,不回退到獨立地名或物品名。選關繼續通過現有 get_world_map_matcher 讀取,中文名稱及配置鍵保持不變。

5. OCR 混淆補丁

配置檔案:

assets/ocr_fix/ocr_text_fix.json

schema 是完整文本的 OCR 错误文本 -> 正确文本:

{
  "乾員聯絡": "幹員聯絡"
}

當前補丁 不會替換 OCR 輸出文本,也不會寫入 TaskExecutor.text_fix。實際行為是:

  1. 只讀取長度相同的錯誤/正確文本對。
  2. 對每個不同字元構建 正确字符 -> OCR 错误字符,例如 幹 -> 乾。
  3. 同一正確字元對映到多個不同錯字時,保留先前對映並跳過沖突。
  4. 在框架原有 OCR.fix_match_regex 處理後,擴充套件呼叫方的 match。

不同 match 型別的行為:

輸入 補丁行為
str 生成原文和混淆變體,最多 4 個;有多個時返回列表
re.Pattern 只擴充套件安全的字面字元;字元類內追加錯字,保留 flags
list 遞迴擴充套件並攤平結果
其它 原樣返回

正則轉義、量詞、分組和其它結構標記不會被重寫;混淆字元本身是正則元字元時跳過。編譯或處理失敗會返回原始 match。這是一層匹配相容,不是 OCR 結果標準化,因此業務程式碼讀取 box.name 時仍可能看到原始誤識文本。

舊 src/data/ocr_normalize_map.py 機制已不存在。若需要業務輸出標準化,應在明確的業務解析層實現,不能假定全域性補丁已改寫文本。

6. 新增 OCR 文本

  1. 確定模組名,通常與使用它的任務類或 Python 模組一致,例如 DeliveryTask、login_mixin。
  2. 編輯 assets/lang/<module>.json,新增頂層 key。
  3. 至少為當前活動 locale zh_CN、zh_TW 增加同名節點。
  4. 節點只選 string、pattern、terms 之一。
  5. 在程式碼中引用 self.lang.<module>.<key>。
  6. 執行語言引用測試並實測 OCR 區域。
uv run --locked python -X utf8 -m unittest tests.TestCheckLang -v

TestCheckLang 使用 AST 掃描 self.lang.<module>.<key>,同時覆蓋語義 key 和舊 k_* key:

  • 模組檔案不存在,或引用的 key 缺少任一當前活動 locale:失敗。活動 locale 來自 get_supported_locales(),當前為 zh_CN、zh_TW。
  • 全部 lang JSON 的節點必須只含一種合法型別,值非空且型別正確,pattern 必須能編譯為 Python 正則。
  • 動態屬性訪問不屬於這類靜態引用的覆蓋範圍;OCR 區域、實際匹配效果和專有名詞仍需人工核驗。新增 key 優先使用語義名稱,已有 k_* key 無需強制改名。

7. GUI gettext

GUI 文本使用:

i18n/<locale>/LC_MESSAGES/ok.po

當前倉庫有 zh_CN、zh_TW、en_US、ja_JP、ko_KR、es_ES catalog。相關驗證:

uv run --locked python -X utf8 -m unittest tests.TestGuiI18n -v
uv run --locked python -X utf8 -m unittest tests.TestPoLocaleConsistency -v

TestPoLocaleConsistency 檢查 catalog 重複/空翻譯、佔位符一致性、部分語言不應複製英文 fallback,以及已知執行時汙染 msgid。它不決定 OCR SUPPORTED_LOCALES。

8. 工具狀態

scripts/i18n/ 下的可用工具:

  • sync_*.py:官方譯名同步進 lang JSON 與 ok.po(world_map / map_mark / wiki_item / character / official_i18n 各資料來源一個指令碼)。
  • gen_lang_stubs.py:掃描 OCR/業務 lang JSON 生成 src/data/lang/_lang_typed.py 型別提示存根;DATA_ONLY_MODULES 中的模組(當前為 effect_names、yingtuo_stages)被顯式排除,不暴露為 self.lang OCR 模組。
  • lang_fill_missing.py:缺失語言節點補全與審計(--dry-run 冪等)。
  • restore_empty_po_entries.py:從任意 git ref 的歷史 po 恢復被清空的翻譯。

針對舊 assets/lang/<module>/<locale>.json 目錄 schema 的批次翻譯與遷移工具已隨 schema 切換刪除。

當前可靠流程是編輯統一 JSON,使用 TestCheckLang 校驗引用和節點,再人工複核實際匹配與遊戲專有名詞。更新後執行 uv run --locked python scripts/i18n/gen_lang_stubs.py,確認生成 diff 中每一項都對應本次資源改動。已跟蹤的完整業務文本維護六種核心 locale,並保留額外 locale;本地生成檔案保持原路徑和忽略狀態。

9. 排查清單

語言節點未生效時依次檢查:

  1. 檔案是否為 assets/lang/<module>.json。
  2. 頂層 key 和程式碼屬性是否完全一致。
  3. 執行時 locale 是否在 ACTIVE_LOCALES_CONFIG 中啟用。
  4. locale 節點是否只包含一個合法型別欄位。
  5. 正則字串是否為有效 Python 正則。
  6. 重新執行 TestCheckLang 並生成型別存根,確認語義 key、舊 key 和生成 diff 與資源改動一致;動態引用另行人工檢查。

OCR 穩定誤識時,先確認是否只是匹配問題。只有等長字元混淆適合加入 ocr_text_fix.json;長度變化、詞序變化或僅某一業務成立的糾錯應在語言 pattern 或業務解析中處理。

在 GitHub 查看來源 ↗ · 頁面產生時間: 2026年10月23日