iT邦幫忙

2026 iThome 鐵人賽

DAY 12
0

前言

多語言(i18n)跟深色模式加自訂主題色,這兩件表面功夫都不難,但我各做錯過一次。

多語言:只有三個地方帶語言

這個產品有一條規則我從第一天就定下來:內容永遠不翻譯

你的知識庫裡是中文就是中文,是英文就是英文,混著也可以。系統不碰它,一個字都不動。這聽起來理所當然,但如果不先講清楚,很容易在某個地方手滑,例如把使用者的標題丟去正規化,或者在搜尋時做語言判斷。

需要語言的只有三個地方:

  1. 介面文字:按鈕、選單、提示,放在字典檔裡
  2. 模版預設內容:新工作區起始頁的語言,註冊時依瀏覽器的語言決定
  3. agent 的系統提示詞:跟著工作區的語言走,而且知識庫的規則頁可以覆寫

第三點是我後來想清楚的。agent 用什麼語言寫頁面,由這座知識庫的設定決定,跟介面語言無關。你可能用英文介面但想要中文的知識庫。而且如果使用者在規則頁裡寫「一律用英文撰寫」,那應該蓋過設定。

錯誤訊息也要雙語。我的做法:錯誤物件本身帶兩種語言,到 API 或 MCP 那一層才依當下的語言選一個:

throw new NoteError('FORBIDDEN', {
  'zh-TW': `raw/ 為唯讀來源層,不可更新:${path}`,
  en: `raw/ is the read-only source layer and cannot be updated: ${path}`,
});

出處:src/notes.ts:235

這樣「決定講哪種語言」只發生在最外層一個地方,中間所有函式都不需要知道語言是什麼。

深色模式有三種狀態,不是兩種

這是我做錯過的地方。使用者的主題偏好有三種:

  1. 明確選了淺色
  2. 明確選了深色
  3. 沒有選,跟著系統

第三種是預設值,也是最多人的狀態。我第一版只用一個 dark class 切換,跟隨系統的使用者就永遠拿到淺色。

正確的組合是這樣:

:root { --ink: #22313A; --paper: #FDFEFD; /* 淺色的完整定義 */ }

/* 跟隨系統且沒有明確選淺色時,用深色 */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --ink: #E4ECE8; --paper: #1D2422; }
}

/* 明確選了深色,蓋過系統 */
:root[data-theme="dark"] { --ink: #E4ECE8; --paper: #1D2422; }

節錄並簡化自 web/src/styles.css:6-37,實際一組是十幾個變數。

三段缺一不可。第一段是完整的淺色定義,後面兩段只重新定義變數。所有顏色都必須先在第一段出現過,只寫在媒體查詢裡的顏色,在「未指定」狀態下會是空的。

主題還要在 HTML 解析的最前面就套用,用一小段內嵌腳本從 localStorage 讀出來塞進 data-theme。不這樣做的話,使用者會先看到一閃的白色再變深色。

一個色相控制整組顏色

做完深色模式之後,加自訂主題色就簡單了,因為顏色已經全部走變數。

我用 OKLCH,它的三個分量是亮度、彩度、色相。好處是改色相不會改變亮度,所以對比度自動維持,換成靛藍或赭紅,文字跟背景的對比不會突然變糟。

:root[data-accent] {
  --celadon:      oklch(52% 0.085 var(--accent-h));
  --celadon-deep: oklch(42% 0.09  var(--accent-h));
  --celadon-mist: oklch(94% 0.025 var(--accent-h));
  /* 圖表用的六階也從同一個色相重算 */
  --ramp-1: oklch(94% 0.025 var(--accent-h)); /* … */ --ramp-6: oklch(42% 0.09 var(--accent-h));
}

出處:web/src/styles.css:38-52

我第一次做就漏了這個:三種狀態會在每一層重複出現一次。自訂色相不是只寫上面這一段就好,深色那一組也要重算,所以實際上有三塊:[data-accent][data-accent][data-theme="dark"],以及媒體查詢裡的 [data-accent]:not([data-theme="light"])

而且深色那一組只換色相還不夠,六階的亮度要倒過來。淺色的 --ramp-1 是 94%(最淡),深色的 --ramp-1 是 28%(最深):淺底上要淡就得亮,深底上要淡就得暗。這件事沒有自動化的捷徑,只能兩組各寫一次。

設定頁給六個預設色加一條 0 到 359 的色相拉桿,存在 localStorage。圖譜的 canvas 跟 Mermaid 圖不吃 CSS 變數,所以它們在每次繪製時去讀 computed style,並且監聽主題變更事件重畫。

如果用 HSL 做同一件事,換色相會讓亮度亂跑,藍色跟黃色在同樣的 L 值下看起來差很多,對比度就毀了。這就是選 OKLCH 的理由。

小結

語言跟色相是同一種東西:一個只在最外層決定一次、中間全部不必知道的值。錯誤訊息帶著兩種語言一路走到 API 那一層才選一個,六階顏色全部從一個色相算出來,所以加一種語言或一組配色,都不必回頭改中間任何一支函式。


上一篇
Day 11 - 用 Canvas 視覺化圖譜
下一篇
Day 13 - 抓取網頁來源,匯入知識庫
系列文
為你自己蓋一座會複利的知識庫——WikiBrain14
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言