# N4 i18n 內文外譯完成與真實渲染驗證（`#A-323`，2026-10-18）

> 目的：以**真實瀏覽器渲染**（非僅「能編譯」）證明 `en-US` 模式下頁面**無中文殘留**，
> 並如實列出**量測看不到的缺口**與其處置。對應 NFR「支援多國語系」（B.2）。

## 1. 範圍與結果（單一真實來源：`tools/i18n_pending.py`）

| 項目 | 起始（`#A-322` 收尾） | 本批完成後 |
|---|---|---|
| 待遷移檔案 | 26 檔（18 頁 ＋ 8 個 `.ts`） | **0 檔** |
| 待遷移行數 | 761 行 | **0 行** |
| 語系鍵（leaf） | 659 | **1297**（zh-TW 與 en-US 皆同） |
| 語系區塊 | 17 | **34** |

量測指令（與 `verify_nfr_ext.py::check_fe_i18n` 同一邏輯）：

```bash
cd cardcore && python3 tools/i18n_pending.py        # 全量
python3 tools/i18n_pending.py frontend/src/pages/X.tsx   # 單檔（逐頁驗收用）
```

輸出：`---- 檔案 0／行 0`。

## 2. 真實渲染驗證（`vite preview` ＋ headless Chrome `--dump-dom`）

- 建置：`VITE_AUTH_ENABLED=false npm run build`（`dist/` 已 gitignore）
- 英文觸發：**注入 `localStorage.setItem('cardcore.lang','en-US')` 進 `dist/index.html`**
  （★ Chrome `--lang` **不會**改變 `navigator.language`，只設旗標驗不到英文路徑）
- 逐頁 `--dump-dom`（`--virtual-time-budget=6000`），移除 `<script>` 與標籤後，
  比對**渲染後文字**中的中日韓字元

| # | 路由 | 中文殘留（排除語系自稱） | 分頁標題（`document.title`） |
|---|---|---|---|
| 1 | `/card/token` | 0 | BEAM Card Core — Virtual Card & Token Management |
| 2 | `/limit-adjustments` | 0 | BEAM Card Core — Limit Adjustment Request |
| 3 | `/system/reports` | 0 | BEAM Card Core — Reports |
| 4 | `/switch/auth/reversal` | 0 | BEAM Card Core — Reversal Mechanism |
| 5 | `/system/audit` | 0 | BEAM Card Core — Audit Trail Query |
| 6 | `/auth/clearing` | 0 | BEAM Card Core — Clearing & Settlement |
| 7 | `/auth/dispute` | 0 | BEAM Card Core — Dispute Handling |
| 8 | `/system/migration` | 0 | BEAM Card Core — Data Migration |
| 9 | `/payments` | 0 | BEAM Card Core — Payment Console |
| 10 | `/system/endpoints` | 0 | BEAM Card Core — Endpoint & Connection Management |
| 11 | `/authorizations` | 0 | BEAM Card Core — Authorization Console |
| 12 | `/merchant/settlements` | 0 | BEAM Card Core — Merchant & Settlement |
| 13 | `/system/split-ratio` | 0 | BEAM Card Core — Traffic Split Ratio |
| 14 | `/parties` | 0 | BEAM Card Core — Cardholder / Account Lookup |
| 15 | `/switch/auth/state` | 0 | BEAM Card Core — Transaction State Machine View |
| 16 | `/postings` | 0 | BEAM Card Core — Posting Console |
| 17 | `/statements` | 0 | BEAM Card Core — Statement Console |
| 18 | `/card-applications` | 0 | BEAM Card Core — Card Application Console |

**唯一允許的中文**：語言切換器的「繁體中文」——選項以**各語系自稱**呈現，
否則切到英文後使用者無從辨識哪個是中文（`#A-307` 既定決策，非殘留）。

繁中模式抽查（全新 Chrome profile，避免沿用已存的 `localStorage`）：
`/card/token` 標題為 `BEAM 信用卡核心 — 虛擬卡與 Token 管理`。

★ **驗證陷阱（本批實測）**：`--user-data-dir` 會**保留 `localStorage`**，
以同一 profile 重跑會一直得到英文——要驗繁中必須換新 profile 或清除。

## 3. 本批併修的三個真缺陷（非「順手美化」）

| # | 缺陷 | 影響 | 處置 |
|---|---|---|---|
| 1 | `useApiErrorMessage` 每次 render 回傳**新函式**，而多頁把它放進 `useCallback([apiErr])` 依賴 | `useEffect` 每輪重跑 → **無限重新查詢**（畫面看似正常，實則持續打後端） | 改 `useCallback([t])` 固定身分 |
| 2 | `bff.ts`／`gateway.ts` 以**中文字串當語系鍵**（`getJson(..., '查詢健康狀態失敗')`） | 英文模式顯示中文；且 `t()` 查無此鍵 → 原樣回傳中文 | 改為 `errors.action.*` 鍵（6 個） |
| 3 | `index.html` 的 `<title>` 為**靜態中文**，且 i18n 掃描只掃 `.ts`／`.tsx` | 英文模式**分頁標題仍為中文**，量測看不到 | `App.tsx` 以 `document.title = brand — nav.<path>.title` 動態設定，隨語系切換 |

## 4. 如實揭露（未完成／不在本批範圍）

1. **CR-060 P3：後端錯誤訊息在地化——未開始**（需另立 CR）。
   `ApiError.serverMessage` **優先顯示後端原文且不翻譯**，故後端回中文時畫面仍為中文。
   本批已把前端 fallback 全部語系化，但**後端訊息這一層未動**。
2. **開發診斷訊息固定英文**（`authClient`／`pkce`／`main.tsx` 之 `throw`／`console.error`）：
   僅進 console／日誌、**不顯示於介面**，故不進 i18n（已於程式內註明）。
3. **行尾註解仍會被量測計入**（`code(); // 中文`）：本批把 2 處此類註解移至獨立行。
   `tools/i18n_pending.py` 已載明此保守限制；**不建議**為此放寬規則（寧可多報）。
4. **數字／日期格式**仍以 `toLocaleString('zh-TW')` 固定地區（千分位與日期格式在
   en-US 下差異有限，但**不是**真正的 locale-aware 格式）——屬另一層缺口，未在本批處理。
