i18n 都是怎麼做的

Category: Web

i18nLocalizationSoftware Engineering
4 min read

i18n (Internationalization) 是一項工程,目的是讓 app 能支援不同語系這件事。除了語言之外,常常還要考慮不同國家間的差異,包括單位(貨幣、溫度、距離等)、時間與日期表示法、寫作系統(例如文法、斷句、阿拉伯文的 rtl)。支援的語言多,能接觸到的使用者就多了(或者也可能是反過來的順序)

從底層就要支援 Unicode

不只在前端顯示,包含資料傳到後端、資料庫、CMS、response 或錯誤訊息等都要能處理 Unicode,不能有其中一環爆炸

Locale

Locale (地區) 是由 Language Code(ISO 639 定義,例如 en、zh)加上可選的 Country Code(ISO 3166 定義,例如 uk、us、tw)組成

產品的 prototype 可以先做英文或習慣的語言,再根據預計要打的市場、使用者的反饋資料等,決定加上其他語言、地區,甚至貨幣、日期、地址、稅率、電話、法律條款等。重要的原則是先做需要的,預留未來可能擴充的,不過度設計不知道需不需要的

誰應該進來做 i18n?

幾乎所有人,包含前後端、QA、PM、Designer

  • 前後端:建好 UI 用的翻譯檔、locale 的 metadata、溝通 API 格式(header 帶 Accept-Language 或 request 帶 locale 等)
  • PM:決定產品第一版要支援哪些語言或國家、日期貨幣等要不要支援、哪些功能要在地化、維護某個需求的成本估算
  • Designer(UI/UX):Component 或 Layout 能否容納不同翻譯的長度
  • QA:文字是否有被截斷、特殊字元是否正常、有沒有亂碼、翻譯有沒有漏掉等

如果是再大一點的東西,甚至 Legal、Data、Marketing、CSM 什麼的都要加入,最終會發現 i18n 是一個跨團隊的產品能力,要解決的是一個工程問題,並不是單一個團隊的孤立工作

前端通常怎麼做 i18n?

建立翻譯檔

最簡單的架構就是把 app 裡面的文字抽出來,變成 key/string 的 pair,再根據 Locale 選對應文字

通常不是寫 <button>Submit</button>,而是 <button>{t("button.submit")}</button>,並建立翻譯檔像是

{
  "button.submit": "Submit",
  "button.cancel": "Cancel",
  ...
}

之類的

決定 key 叫什麼會是個問題,跟寫程式時決定變數名字一樣,通常就是用結構化/巢狀路徑命名。至於是誰應該決定 key 的名字?一樣,大家一起開會決定吧

以某個語言作為 Single Source of Truth

當多個翻譯檔已經建好了,後續的維護可以用這個原則

假設以英文為準好了,新增翻譯時僅須加上英文翻譯檔的 key 與翻譯字串。其他語言的翻譯檔則不用動,或是僅需新增 key

這樣做的原因,是如果 CI/CD 之類的流程有翻譯管理平台 / 自動翻譯機器人之類的,會有以下情境

  • 新增按鈕,翻譯平台或機器人會看到「哎呀,zh-TW.json 裡面這個 key 的內容居然跟 en.json 長得一樣(或它是空的)!看來這東西還沒翻譯過,得送去翻譯」
  • 改動英文翻譯,翻譯平台或機器人會看到「哎呀,en.json 裡面這個 key 的內容更新了,但中文還是舊的對應,看來需要重新翻譯」

這樣做可以確保幾件事:

  • 缺翻譯的 fallback
  • 人工翻譯不被翻譯平台或機器人蓋掉
  • 工程師僅需維護一個翻譯檔案

i18n Library

有許多現成 Library 把前端的 i18n 工作處理好,常見的有 i18next、react-i18next、next-translate 之類的,選擇的話就是看你的需求、App 框架等來選

Library 處理的功能:

  • 文字翻譯:提供一個像 t('key') 的函式,輸入 key 就會根據目前選擇的語系吐出對應的文字
  • 語系切換:提供動態切換語系的 API(例如 i18n.changeLanguage('zh-TW')),切換後畫面通常能自動或手動更新,讓 i18n 根據 locale 決定 UI 應該顯示什麼
  • 變數代換:t('welcome', { name: 'Alex' }) -> 「歡迎回來,Alex!」
  • 複數處理:根據數字決定要用單數還複數形式,並且不同語言間的複數可以很複雜
  • 格式化:處理不同國家地區的數字、日期、時間、貨幣格式
  • Fallback:缺 key 時不至於使程式崩潰

Testing and Performance

當 i18n 的架構和維護流程都上軌道後,支援的語系跟檔案越來越多就會需要

  • Unit Test:針對 Pluralization、日期、時間、貨幣格式化等邏輯進行測試
  • Static Analysis:在 CI/CD 階段自動比對:檔案間 key 有沒有完全對齊?有沒有 JSON 裡找不到的 Key?有沒有沒用到的冗 key?
  • Lazy Loading:初次載入或切換語系時才透過非同步去下載對應語系的 JSON 檔案
  • 快取與分割:將共用或高頻使用的語系先載入,較少用到的語系或大型頁面的翻譯字典則採用動態載入與快取策略,在「省流量」與「流暢度」之間取得平衡

Reference

About W3C internationalization