iT邦幫忙

2026 iThome 鐵人賽

DAY 18
0
Software Development

30天打造一套企業PLM系列 第 18

Day 18:antd/ProTable 實戰陷阱與 i18n

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20260904/20161290BLznVHdKyP.jpg

系列:30 天打造企業級 PLM|面向:前端|素材:前端實碼與踩坑紀錄

問題場景

元件庫替你做了 90% 的事,剩下那 10%,也就是它自作聰明的部分,會花掉你 90% 的時間。Mini-PLM 前端建在 Ant Design 6 加 Pro Components 上,今天集中講幾個最貴的陷阱與 i18n 架構

商業邏輯設計

  • 多語系是業務需求:多地廠區(台灣、大陸、泰國)的操作人員各有語言需求,四語系(en / zh-TW / zh-CN / th)
  • 「user-facing 字串只用英文」的開發紀律:程式裡寫死的字串一律英文、中文只出現在註解。跨國稽核與截圖溝通需要一個共同基準語言,翻譯全部走 i18n 資源檔
  • Modal 規格統一(維護彈窗 90%/1400px、不再新增 Drawer):操作一致性直接影響使用者的訓練成本

核心內容(一):ProTable 的 render 陷阱

ProTable 在 ellipsis: truerender 並用時,render 的第一個參數不是 raw value,是包過 ellipsis 的 ReactElement。直接拿第一參數做字串處理,輕則顯示 [object Object],重則 toFixed is not a function 直接白屏。存活下來的寫法(實碼,AdminManufacturer.tsx):

{
  title: "Notes",
  dataIndex: "notes",
  ellipsis: true,
  render: (_, r) => r.notes || "—",   // ← 第一參數棄用,一律從 record 取 raw
},

團隊紀律固定成一句話:只要欄位有 render,就從 record[dataIndex] 取原始資料,第一參數當作不存在。這條不分 ellipsis 有沒有開,因為別人維護時加一個 ellipsis: true,你的 render 就默默壞掉,而 TypeScript 型別救不了你,第一參數本來就宣告成聯集。

https://ithelp.ithome.com.tw/upload/images/20260904/20161290POGrbW5Sei.png

上半部是最常見的誤區:ellipsis 先把第一參數包成 ReactElement,後續再做字串或數值處理便可能直接出錯。下半部刻意繞過第一參數,從 record 取得原始值,讓顯示邏輯不受 ProTable 包裝行為影響。

核心內容(二):i18n 架構——四語言 × 十命名空間

架構演進:從伺服器端 Resource Bundle 到前端領域命名空間

以前在 Oracle Agile PLM 中,多語系是交由後端處理的。

底層透過 Java EE 的 ResourceBundle.properties 資源檔)在 WebLogic 端解析,帶來幾大痛點:

  1. 切換語系必須整頁重載:切換語言需要向伺服器發送請求,整頁重新渲染,無法做到前端即時無縫切換;
  2. Web/Java Client 雙重分裂:Web Client 的 JSP 與 Java Client 的 Swing 各自吃不同的 .properties 資源檔,維護兩份翻譯檔,經常造成某一端缺字漏翻;
  3. 無命名空間概念:所有翻譯 key 塞在幾個巨大的檔案裡,極易命名衝突。

Mini-PLM 將介面多語系完全移至前端 React 現代 i18n 體系,採用「4 語言 × 10 領域命名空間」結構:

https://ithelp.ithome.com.tw/upload/images/20260904/20161290afdgrgSPCU.png

這次調整不只是把翻譯檔搬到前端,而是重新劃分責任:程式介面的固定文字由 locale 與 namespace 管理;管理員建立的動態欄位則保留在資料層,交給 mp_localized 處理,兩者不共用同一套 key。

語系資源目錄 (locales)/
  en/ zh-TW/ zh-CN/ th/
    common.json  auth.json  status.json  message.json  nav.json
    layout.json  form.json  item.json  workflow.json  admin.json

命名空間按領域切,跟 Day 17 的 store 切片同一個思路:formitemworkflow 對應業務領域,commonmessagenav 對應框架層。取用時明示命名空間,t("form:workflow"),維護者一眼知道去哪個檔案找 key。

實戰規則兩條。新增 key 四語言一起補:缺 key 的語言會顯示 key 本身,等於把工程術語直接端給泰國產線的操作員。CI 還沒擋這件事,目前靠 code review 人肉守,這是 Day 30 工具化清單的一項。動態欄位的名稱不走 i18n:Day 4 的 metadata 欄位名稱(field_name)是管理員輸入的資料,不是程式資源,資料的多語言是另一個題目(mp_localized 表),跟程式 i18n 分開處理。

核心內容(三):antd 6 升級的相容性收斂

系列開頭提過 v1 到 v2 帶著 React 19 加 antd 6 升級。幾個實際的收斂點:ProTable v3 的 API 位移(actionRender 行為變動,Day 16 加減按鈕消失的坑)、gcTime 取代 cacheTime(TanStack Query v5)、useQuery 拿掉 onSuccess/onError 改用 useEffect 監聽。升級的心法是先鎖行為再升版本:E2E(Day 20)是升級的安全網,沒有它,升上去等於閉眼開車。

踩坑記錄:E2E 視角的 antd 陷阱

寫 Playwright 測試(與本系列的截圖腳本)時被 antd 咬過的,Day 20 會再回收:

  • isVisible() 不等待。它是即時判斷,antd 元件還在動畫中就回 false,一律改 waitFor
  • AutoComplete 的 Enter 被攔截,會被解讀成選取建議項而不是送出。先 Escape 關掉建議清單再點按鈕
  • 按鈕的 accessible name 有空格:antd 對兩個中文字的按鈕自動插空格(「登 入」),selector 要寫 getByRole("button", { name: /登\s*入/ })。這個坑在寫本系列截圖腳本時當場又踩一次
  • Segmented 與 Tabs 的文字比對:hasText 是包含比對,「測試表單」會先命中「新測試表單」。精準選取要用 exact 或更特定的 locator

共同教訓:UI 測試面對的不是你的程式,是元件庫渲染後的 DOM。寫 selector 前先開 DevTools 看真實結構,不要憑 API 文件想像。

小結

render 一律從 record 取值,防的是別人日後加 ellipsis;i18n 按領域切命名空間、程式碼英文唯一;升級靠 E2E 鎖行為。跟元件庫相處的要訣大概就是:它幫你省的時間收下,它埋的雷記下來變成紀律。明日 Day 19:SSE 即時通知——「我明明把權限開給他了!」的完整解法。


上一篇
Day 17:前端架構——Zustand 切片與前端快取
系列文
30天打造一套企業PLM18
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言