useI18n 優化

WHY: 為什麼需要這次優化/重構?

背景: 在我們的 NextJS 專案中,我們使用 react-i18next 函式庫來處理多國語系 (i18n)。因為為了保持與 PM 維護的翻譯檔有一致的分類,因此前端專案裡也有多個命名空間 (namespace) 的翻譯檔,而透過 useTranslation 使用方式帶來了幾個明顯的開發痛點。

挑戰:

  1. 程式碼冗長且重複: 當一個頁面需要來自不同翻譯檔案的文字時,就必須多次呼叫 useTranslation Hook,導致元件頂部充滿重複的 Hook 調用,顯得非常冗長。

    // 舊有方式,顯得冗長
    const { t: commonT } = useTranslation('common');
    const { t: unitT } = useTranslation('unit');
    
    
  2. Namespace 容易出錯: 翻譯的 namespace 是以字串形式傳入,這意味著開發者在指定時如果發生 Typo,無法及時發現。

  3. 命名不一致且難以追溯: 團隊對於翻譯用的 t 函式沒有統一的命名規範,常出現 t, commonT, amT 等各種縮寫,讓其他人在閱讀程式碼時,難以快速追溯某個翻譯詞彙是來自哪個檔案,降低了可維護性。

目標: 因此,我的目標是建立一個集中式、型別安全且命名規範的 i18n 解決方案,以提升程式碼品質與開發者體驗。


HOW: 您是如何分析並執行這次任務的?

分析與方案設計: 在分析完上述痛點後,我為新的 Custom Hook 設定了幾個關鍵的設計要求:

  1. 單次調用: Hook 必須能接收一個包含多個 namespace 的陣列,並一次性返回所有對應的 t 函式。

  2. 型別安全: 利用 TypeScript,將所有可用的 namespace 統一定義在一個 const 陣列中,並以此產生一個嚴格的聯合型別 (Union Type)。這樣任何不在列表中的 namespace 都會引發編譯錯誤。

  3. 規範化命名: 回傳的物件中,t 函式的 key 必須是根據傳入的 namespace 自動產生的(例如 common -> commonT),從而強制達成命名一致性。

執行過程: 在確立了設計目標並利用 TypeScript 進行可行性分析後,我著手設計並實作這個 Custom Hook。過程中,我有效地與 AI 協作,快速驗證型別推導 (Type Inference) 的複雜邏輯,加速了開發進程。


WHAT: 最終達成了什麼具體成果?

成功建立了一個名為 useI18n 的 Custom Hook,徹底解決了原有的所有問題,並帶來了以下具體成果:

  1. 提升開發體驗與程式碼簡潔性: 開發者現在只需要呼叫一次 Hook,就能取得所有需要的翻譯函式,程式碼變得更加乾淨、易讀。

    TypeScript

    // 優化前
    const { t: commonT } = useTranslation('common');
    const { t: unitT } = useTranslation('unit');
    
    // 優化後
    const { commonT, unitT } = useI18n(['common', 'unit']);
    
    
  2. 實現完全的型別安全: 所有的 namespace 都被集中管理。如果傳入一個不存在的 namespace,TypeScript 會在開發階段立即報錯,有效避免了執行階段的錯誤。

    1. namespace 的 autocomplete

      image 35.png

    2. 提示錯誤的 namespace

      image 36.png

  3. 強制命名規範,提高可追溯性: 新 Hook 的回傳型別會自動產生標準化的命名 (commonT, unitT 等),根絕了命名混亂的問題。現在任何人都能輕易地從函式名稱追溯到其對應的 namespace。

    image 37.png