iT邦幫忙

2026 iThome 鐵人賽

DAY 22
0
Modern Web

一個活了六年的 Vue 2 後台,升級到 Vue 3 的那兩年系列 第 22

Day 22|語系從 4 個變成 11 個,維護成本反而下降了

  • 分享至 

  • xImage
  •  

模組五|重構期的品質與節奏(Day 22–26)

我在一個團隊裡,把一套後台管理系統從 Vue 2 升級到 Vue 3:舊系統當時還在線上服務使用者,新系統是我們按業務功能一塊一塊搬出來的那一版。兩套都要支援多國語言。

舊系統有四份語言檔。我把它們的行數加起來:

15,606 行。

四個純物件字面量的檔案(本質上就是一份「代號對文案」的清單,只是寫成 .js 程式碼),一萬五千行。而每新增一句文案,你要手動同步四個檔案。

新系統的語系從 4 個變成 11 個,但維護成本下降了。今天講這件事怎麼做到的,以及一個我們沒修掉、反而變嚴重的問題。

舊流程有多荒謬

先講翻譯是怎麼進到那四個檔案裡的。

專案根目錄有一支 322 行的腳本,它做的事是這樣:

  1. 把整個語言檔目錄複製一份
  2. 把複製出來的 .js 檔改成 .mjs
  3. 用動態載入把它們執行起來,拿到裡面的物件
  4. 攤平成表格
  5. 匯出成 Excel

然後把那個 Excel 寄給翻譯人員,等他們填完寄回來,再手動貼回程式碼。

我第一次讀懂這支腳本的時候的感覺是:這不是工程,這是儀式。

每一步都有它的理由:因為語言檔是 .js 而不是資料格式,所以要執行才拿得到內容;因為要執行,所以要轉成模組格式;因為翻譯人員不會用 git,所以要 Excel。

每一個「因為」都合理,加起來就變成一條沒有人想維護的流水線。

而且它有個致命的性質:它是單向的。你可以把程式碼變成 Excel,但 Excel 回來之後,要靠人貼回去。貼漏一個,那個語系就永遠少一句。

新流程:三種模式加一個狀態檔

新系統的翻譯腳本大約 670 行,串接翻譯服務。但真正讓它可用的不是「會自動翻譯」,是這三件事:

一、三種模式

腳本靠一個參數決定這次要處理哪些內容:

--mode=missing     # 只補「有中文、但某個語系還沒有」的
--mode=bootstrap   # 全新語系,整包翻一次
--mode=changed     # 只處理中文有變動的

第三個最重要。因為日常開發的情況幾乎都是「改了幾句中文」,而不是「新增一個語系」。

如果只有全量模式,那每次都要重翻一萬多句:慢、貴,而且會蓋掉人工修過的翻譯。

二、每個模式都有預演

任何一種模式後面都可以再加一個參數,讓腳本只印出它打算做什麼、不真的動到檔案:

--mode=changed --dry-run

先看會動到哪些 key(key 就是每一句文案在程式裡的代號,程式碼引用的是代號,實際顯示哪個字由語系檔決定),確認沒問題再真的跑。

這件事看起來很基本,但它決定了大家敢不敢用。一個會直接改動十一個語系檔、又不能預覽的腳本,沒有人會在下班前跑它。

三、一個狀態檔

我只往還空著的那幾畦撒種,補過的那幾畦一粒都不碰

這是我覺得最關鍵的設計。專案裡有一份記錄「哪些 key 在哪個語系翻到什麼程度」的狀態檔。

有了它,腳本才知道:

  • 這句中文改過了 → 對應的十個語系翻譯要重跑
  • 這個 key 是新的 → 十個語系都要補
  • 這個翻譯是人工修過的 → 不要覆蓋

沒有狀態,你就只能全量重跑。而全量重跑會蓋掉人工修正,那正是大家不敢用自動化的原因。

這件事的通則

舊流程和新流程的差別,不在「有沒有用 AI 翻譯」,在於:

舊的是「人執行一套流程」,新的是「腳本執行,人做決定」。

而讓這個轉變成立的是那三樣東西:可以只做增量、可以預演、有狀態可以記憶。

缺任何一個,自動化都會退回人工,因為不敢用。

我覺得這可以推廣成一條判準:

會被人工重複執行的流程,遲早會出錯。而把它自動化的前提,是它必須「可增量、可預演、有記憶」。

順帶一提,語系從 4 個變成 11 個之後,人反而不是流程中的瓶頸了。這才是真正的收益:不是省下打字時間,是新增一個語系從「一個專案」變成「一個指令」。

但有一件事,我們沒修掉

講完成功的部分,講失敗的。

舊系統的 i18n key(i18n 是 internationalization「國際化」的縮寫,把中間 18 個字母省略成數字,講的就是「同一套介面切換多國語言」這件事)有一個很嚴重的反模式:把整句中文的拼音當成 key。

大概是這種形狀(我改寫成中性的例子):

{
  zuiGaoLeiJiJinEBuNengXiaoYuZuiDiLeiJiJinE: '最高累計金額不能小於最低累計金額',
  quSheZhiQiShuHouSanQiZuiGaoJinEZuoWeiJiZhun: '取設定期數後三期最高金額作為基準',
}

我數了舊系統:超過 30 個字元的 key 有 123 個。

這種命名的問題很明顯:

  1. 文案一改,key 就對不上了——但你不會想改 key(要動所有引用處),所以最後變成「key 說的是舊文案,值是新文案」
  2. 完全無法從 key 判斷它用在哪裡——zuiGao... 是哪一頁的?不知道
  3. 同一句話在不同頁面,會產生兩個一模一樣的 key

正確的做法應該是描述「位置與用途」,例如 orderSearch.validation.amountRange

然後我去數了新系統

舊地方門口那塊絆腳的石頭,我特地挖起來,在新的地方埋成一整排

457 個。

不是 123。是 457。

而且新系統的語系檔總行數比舊系統的單一語系檔還少,也就是說,這個反模式的密度變高了三倍以上。

我看到這個數字的時候,比看到任何舊系統的問題都更不舒服。因為這代表:

我們把舊系統最糟糕的一個習慣,原封不動地搬進了新系統,而且做得更徹底。

為什麼會這樣

我事後回想,原因大概有三個,而且每一個都很好理解:

一、搬遷的時候,key 是跟著頁面一起搬的。 你在重寫一個頁面,順手把它的 i18n key 複製過來。這是阻力最小的路徑。重新命名代表你要決定新名字、要確認沒有衝突、要處理其他頁面也在用的情況。

二、沒有人負責 i18n 的命名規範。 API 命名有人管(Day 15 那份對照表,一份明文規定每支 API 該叫什麼名字的文件),元件命名有人管,但 i18n key 沒有。它被當成「文案的附屬品」,而不是程式碼。

三、它不會痛。 這是最關鍵的。爛的 key 不會導致 bug、不會讓建置失敗、不會在 review 時被抓到,它只會在三年後讓某個人多花二十分鐘。

而 Day 20 講過:能落地的規則,特徵是「違反時一眼看得出來」。 i18n key 剛好相反。

代價

一、自動翻譯的品質需要人工複查。 機器翻譯在後台系統的專業術語上會出錯,而錯的翻譯不會有人回報,因為使用那個語系的人,通常不會來跟你說「這個詞怪怪的」。我們的做法是關鍵頁面人工確認,其餘接受。

二、狀態檔本身變成一個要維護的東西。 它如果跟實際檔案不同步(例如有人手動改了語系檔沒跑腳本),腳本的判斷就會錯。

三、那 457 個 key 現在更難改了。 因為它們散在 11 個語系裡,改一個 key 要同步 11 個檔案。我們把一個 4 倍的問題,變成了 11 倍。

帶走什麼

  1. 自動化的前提是「可增量、可預演、有記憶」。 缺一個,大家就不敢用,然後退回人工。
  2. 判斷一個流程該不該自動化,看它是不是「人在執行流程」而不是「人在做決定」。
  3. 不會痛的問題,不會被修。 i18n key 的命名沒有任何即時反饋,所以它連續兩代都沒有被處理。
  4. 重寫不會自動修掉舊習慣,尤其是那些「複製過來最省事」的習慣。 你必須明確地把它列進遷移清單,否則它一定會跟著搬家。
  5. 在擴大規模之前,先修掉會被放大的問題。 我們把語系從 4 個增加到 11 個,同時也把 key 的爛命名放大了三倍。

明天 Day 23 講一個看起來很難看的數字:重構期間,四成的改動是在修 bug。我會論證為什麼這不是品質問題——以及什麼情況下你才真的該擔心。


上一篇
Day 21|一個叫「表單項」的 hook,寫完之後零次使用
下一篇
Day 23|將近一半的改動是在修 bug,而我認為這是好消息
系列文
一個活了六年的 Vue 2 後台,升級到 Vue 3 的那兩年25
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言