Project

General

Profile

Actions

Requirement #363

closed
RA SA

[NFR-B.2-支援多國語系] 支援多國語系

Requirement #363: [NFR-B.2-支援多國語系] 支援多國語系

Added by Redmine Admin 9 days ago. Updated 7 days ago.

Status:
Closed
Priority:
P2-Normal
Assignee:
Start date:
10/17/2026
Due date:
% Done:

50%

Estimated time:
Story Points:

Description

需求基本資料

項目 內容
分組 B.2(BRD-003 §3)
小節 支援多國語系(NFR 項數 1)
對應架構設計 SDD-007 §7
驗證方式 語系切換測試、資源檔覆蓋率
主責 FE
WBS 階段 Phase-4 領域服務 R2/R3 與參數極大化
Owner SA/SD Agent

逐項(1 項)符合度回應見建議書附錄 B;本議題以小節層級追蹤歸屬與驗證。


Files

RA Updated by Redmine Admin 9 days ago Actions #1

落地狀態評定:In Progress

項目 內容
分組 B.2(BRD-003 §3)
小節 支援多國語系(NFR 項數 1)
對應架構設計 SDD-007 §7
驗證方式 語系切換測試、資源檔覆蓋率
主責 FE

判定依據

  • 設計依據:SDD-007 §7
  • 逐節驗收方式之執行證據待補登(TRP-002)——設計已備 ≠ 已驗證

★ 評定規則見 GOV-008 §16/GOV-004 §5;可重跑 ops/redmine/req_assess.py。

RA Updated by Redmine Admin 9 days ago Actions #2

落地狀態評定:In Progress

項目 內容
分組 B.2(BRD-003 §3)
小節 支援多國語系(NFR 項數 1)
對應架構設計 SDD-007 §7
驗證方式 語系切換測試、資源檔覆蓋率
主責 FE

判定依據

  • 設計依據:SDD-007 §7
  • 逐節驗收方式之執行證據待補登(TRP-002)——設計已備 ≠ 已驗證

★ 評定規則見 GOV-008 §16/GOV-004 §5;可重跑 ops/redmine/req_assess.py。

RA Updated by Redmine Admin 7 days ago Actions #3

註:本則為 #A-318 當時之快照(部分數字為歷史值);最新狀態見同議題 #A-323 之 Journal。

前端多國語系(i18n)推進記錄(2026-10-18)

一、框架與錯誤訊息

  • #A-307 導入 i18next + react-i18next(npm audit 0 vulnerabilities;peer 為 react>=16.8,未升 React 19)。
    語言解析順序:localStorage → 瀏覽器語系 → 預設 zh-TW;只接受已支援語言碼(避免缺檔時靜默退回)。
  • #A-312/#A-313 CR-060 前端錯誤訊息多語化:
    • 10 個具名錯誤改為 ApiError(messageKey + code + status);錯誤碼不翻譯(契約)。
    • 66 個 fallback 全數改為 errors.action.* 語系鍵。
    • 順手收斂 10 份重複 的 toErrorMessage/errorText 為單一 api/errors.ts。

二、頁面內文外譯(已完成 8 個)

  1. 營運總覽(DashboardConsole)
  2. 應用程式外殼(App.tsx;並移除已無人使用之 label/title 死資料)
  3. 分期作業台(InstallmentConsole)
  4. 批次作業監控(BatchConsole)
  5. 授權檢核明細(AuthorizationPolicyConsole)
  6. 歸戶 360(AccountOverviewConsole)
  7. 卡片生命週期(CardLifecycleConsole)
  8. 額度控管(LimitConsole)

三、量測(可稽核,非估算)

指標 起始 現況
語系鍵 70 557
待遷移檔案 32 31
待遷移行數 約 1674 約 885(完成約 47%)
  • 量測方式:tools/verify_nfr_ext.py 掃描 src/**/*.tsx 與 src/**/*.ts,排除程式註解(/* */、//、*、{/* */)。
  • ⚠️ 過程中修正三次量測誤判(註解被當成介面文案),並將掃描範圍由僅 .tsx 擴及 .ts——
    數字曾因此「變差」(29 檔/1209 行 → 50 檔/1275 行),那是把原本看不到的債務掀出來,非工作倒退。

四、如實揭露(未完成項)

  • 頁面內文仍有 31 檔/約 885 行待遷移(含 CardApplicationConsole、StipConsole 等)。
  • 後端錯誤訊息未在地化(P3):toErrorMessage 會優先採用後端訊息,故實際畫面仍可能顯示中文;此屬後端契約變更,已於 CR-060 列為 P3。
  • api/*.ts 仍餘部分 label 對映(如 RECEIVED: '受理'、SINGLE: '單店'),屬頁面標籤遷移,隨各頁處理。

五、提交

b1b3be7 → 26c908b → 0ee062e → 31cce20 → c8d1dec → 095b8f9 → 49b639b → 40231f5 → 5e1aa56 → 5f65adf → fc8414d → a21e14d

RA Updated by Redmine Admin 7 days ago Actions #4

     1. #A-323 i18n 內文外譯全部完成(2026-10-18)

        1. 結果(單一真實來源:`tools/i18n_pending.py`,與 `verify_nfr_ext.py::check_fe_i18n` 同邏輯)
項目 完成前 完成後
待遷移檔案 26 檔(18 頁 + 8 個 .ts) 0 檔
待遷移行數 761 行 0 行
語系鍵(leaf) 659 1297(zh-TW 與 en-US 同鍵)

官方檢核器輸出:待遷移 0 檔/約 0 行(跑檢核器,非自評)。

        1. 真實渲染驗證(不是「能編譯就算過」)

VITE_AUTH_ENABLED=false npm run build → vite preview → headless Chrome --dump-dom
(英文以注入 localStorage 觸發;Chrome --lang 不會改變 navigator.language)

逐頁比對渲染後文字:18 頁全部 0 個中文殘留(唯一允許的中文是語言切換器的自稱「繁體中文」——
選項以各語系自稱呈現,否則切到英文後無從辨識哪個是中文)。

證據:docs/08-evidence/N4_i18n_批次外譯_DOM驗證_20261018.md

        1. 併修的三個真缺陷(非美化)
  1. useApiErrorMessage 身分不穩定:每次 render 回傳新函式,而多頁把它放進
    useCallback([apiErr]) 依賴 → useEffect 每輪重跑 → 無限重新查詢
    (畫面看似正常,實際持續打後端)。改 useCallback([t]) 固定身分。

  2. API 層以中文字串當語系鍵:bff.ts/gateway.ts 之 getJson(..., '查詢健康狀態失敗') →
    英文模式顯示中文(t() 查無鍵時原樣回傳)。改 6 個 errors.action.* 鍵。

  3. 分頁標題盲區:index.html 的 <title> 為靜態中文,而 i18n 掃描只掃 .ts/.tsx →
    英文模式分頁標題仍為中文、量測看不到。改由 App.tsx 以 document.title 隨語系設定。

         1. 新增工具(避免重造)
    
  • tools/i18n_add_keys.py:新增語系鍵唯一入口——深層合併、既有鍵不覆寫(值不同即報錯)、
    兩語系鍵集合強制一致(缺一邊會靜默退回中文=假綠)。

  • tools/i18n_pending.py:待遷移量測(全量/單檔)。

          1. 如實揭露(這些不是「i18n 未完成」)
    
  • CR-060 P3 後端錯誤訊息在地化:未開始(需另立 CR)。ApiError.serverMessage 目前
    優先顯示後端原文且不翻譯,故後端回中文時畫面仍為中文。

  • 數字/日期仍固定 toLocaleString('zh-TW')(千分位/日期在 en-US 差異有限,但非 locale-aware)。

  • 開發診斷訊息(authClient/pkce/main.tsx)固定英文:僅進 console、不顯示於介面。

          1. 驗證陷阱(留給後人)
    
  • --user-data-dir 會保留 localStorage:同一 Chrome profile 重跑會一直得到英文,
    曾誤判「繁中也變英文」。驗繁中必須換新 profile。

  • 行尾註解(code(); // 中文)仍會被量測計入 → 移到獨立行;不為此放寬規則(寧可多報,不可漏報)。

  • Redmine 6 的 Setting.text_formatting 是 common_mark(不是 Textile):
    用 h5./|_. |/@code@ 會被原樣顯示。本則曾誤用 Textile,已改寫為 CommonMark。

          1. 三層留痕
    
  • Wiki(完整敘事):[[ExecutionLog-A-323]]

  • 附件:N4_i18n_批次外譯_DOM驗證_20261018.md(issue #363)

RA Updated by Redmine Admin 7 days ago Actions #5

  • Status changed from In Progress to Closed

#A-325 P3 後端錯誤訊息在地化完成(2026-10-18)

CR-060 留下的最後一段:前端已全外譯(#A-323),但只要錯誤來自後端,畫面仍是中文。

做法(規格先行:CR-061)
項目 內容
決策 D1 伺服器端在地化(訊息屬 API 契約,B2B/curl 同受益);D2 語言來源 Accept-Language;D3 文件為單一來源;D4 未登錄→回退原訊息;D5 details 不在地化;D6 錯誤碼不翻譯
訊息表 SDD-006 §3.4 新增**「訊息(EN)」欄**(138 列/133 碼),並統一 5 個重複碼措辭(一碼一訊息)
產生器 tools/gen_error_i18n.py:--check 驗「bundle 漂移/覆蓋率/缺 EN」,任一不過即非 0
bundle common-spring/i18n/messages*.properties(zh-TW/en,各 133 鍵 error.<CODE>)
機制 ErrorMessageLocalizer + MessageSource + LocaleResolver(預設 zh-TW、支援 [zh-TW, en])
前端 authFetch 顯式送 Accept-Language(使用者切換器選的語言勝過瀏覽器預設)
驗證(可失敗,逐項實測)
  • gen_error_i18n.py --check → 133 碼/使用 133/未登錄 0,bundle 一致
  • 單元 7 例(真實 bundle:en/zh/en-US/fr→zh/未登錄回退;回退:空 bundle、code 空白)
  • MockMvc 語言整合 4 例(en/zh-TW/未帶→zh-TW/同請求兩語言訊息確實不同)
  • 真機 E2E(bff-web 實際執行):
請求 回應
Accept-Language: en CC-PLT-4010 Not authenticated
Accept-Language: zh-TW CC-PLT-4010 未認證
Accept-Language: fr(未支援) 未認證(回預設)
未帶標頭 未認證(預設 zh-TW)
GET /bff-api/v1/bff/nope + en/zh CC-PLT-4040 Resource or path not found/資源或路徑不存在
  • 真實瀏覽器(vite preview + headless --dump-dom):同一頁 /system/batch,
    en-US 顯示 Not authenticated、zh-TW 顯示 未認證 → 前端未為此做任何翻譯,證明訊息語言真的來自後端。
兩個原 CR 沒寫到的坑(已修)
  1. 401/403 不經 GlobalExceptionHandler——由 SecurityAutoConfiguration 的
    entry point/access denied handler 直接寫回應;只改 handler 的話,最常見的兩種錯誤永遠是中文。
  2. @WebMvcTest 切片因缺 bean 起不來——改建構子注入後 rewards-service 8 個測試轉紅;
    改為「無參數建構子 + @Autowired(required=false) setter」(切片走 passthrough,行為與在地化前一致)。
如實揭露(未完成項)
  • details[] 未在地化(欄位名/規則說明仍中文;前端目前未呈現)→ 後續另立。
  • 166 處動態組合訊息(如 field + " 不得為空")在地化後由通用措辭取代;欄位資訊仍在 details[]。
  • 日誌維持中文(維運語料,刻意不隨客戶語言變)。
  • api-gateway(WebFlux)未掃描 common.spring,其錯誤不走本機制(現況即此,非本批退步)。

證據:docs/08-evidence/CR-061_後端錯誤訊息在地化_E2E_20261018.md(含 CR-061 §9 執行記錄)。

Actions

Also available in: PDF Atom