深色模式與 i18n · Agent

深色模式與 i18n

互動示範(與本規範網站人類閱讀版相同):/docs/dark-mode-i18n

示範對應規則

以下章節與人類版頁面互動示範所涵蓋的規則一致(文字版,供 Agent 複製)。

Dark Mode

核心規則

  • Token 化:semantic token 切換 light / dark value,元件不寫死色(見 19-design-tokens.md)。
  • 不用純黑 #000:用深灰底(本指南網站:#0A0E14 content、#0F141C sidebar),降低眼睛疲勞。
  • 不用純白 #FFFFFF 當文字:用 #E2E8F0 等淺灰,避免螢光感。
  • Elevation 越高越亮(不靠 shadow,用 surface tone 變化;與 styles/globals.css 同步)。
    • base bg:#0A0E14
    • card / surface:#11161F
    • hover surface:#1A212C
    • border:#1E2530
  • 飽和色降 saturation 10–20%(避免螢光感)。
  • 維持 WCAG 4.5:1 對比

Surface tone vs Shadow

Light mode 用 shadow 表示 elevation:

white bg + box-shadow → 視覺浮起

Dark mode 用 brighter surface:

#0F1115 base
↑
#1A1D23 card(變亮)
↑
#252830 modal(再亮)

陰影在深底幾乎看不到,所以靠亮度差別。

Color 調整

  • Primary brand color 可能需要在 dark mode 加亮(淺一階)。
  • Status colors 同樣需要:
    • Success: light #16A34A / dark #22C55E
    • Warning: light #CA8A04 / dark #EAB308
    • Error: light #DC2626 / dark #EF4444

Image & 圖示

  • 圖示在 dark 模式可能要替換版本(black logo → white logo)。
  • 圖片內嵌淺色背景在深底會突兀,提供 dark 版或半透明處理。
  • 僅 SVG 向量圖(禁止 emoji);currentColor 跟隨文字色;本指南用 @untitledui/icons

切換 UI

  • Settings → Appearance → Light / Dark / System。
  • 預設跟隨 system(prefers-color-scheme)。
  • 使用者選擇後持久化(localStorage / cookie / DB)。
  • 切換要全 app 同步,避免半邊 light 半邊 dark。

App shell 語意色

元件與版面禁止寫死 light 色;用 semantic token,在 .dark(或 data-theme="dark")一次切換:

TokenLight 示意Dark 示意用途
sidebar#F9FAFB#1A1D23左側導覽底
content#FFFFFF#0F1115主內容底
text-primary#101828#E6E8EB標題、正文強調
text-secondary#475467#94969C內文、說明
border#E4E7EC#2E323A分隔線
brand-subtle淺藍底深藍底active nav、提示條
  • 深色模式下 sidebar、topbar、prose 內文、表格、inline code 皆須可讀(對比 ≥ 4.5:1)。
  • 勿在 dark 模式保留 hover:bg-white 等 light-only 寫法。

本指南網站:styles/globals.css:root / .dark CSS 變數驅動全站。

過渡動畫

  • 切換時用 transition: background 200ms,避免閃。
  • 或用 no-transition class 切換瞬間,避免奇怪 lerp。

i18n

核心規則

  • 預留文字 30–40% 延伸空間:英文短、德文 / 芬蘭文長 35%。
  • 設計時用 1.5–2 倍英文字串長度檢查 layout。
  • 不寫死寬度 在 button / nav item / label。
  • 不串接字串:用 ICU message format。
  • 支援 RTL(阿拉伯文、希伯來文):mirror layout、icon、padding。
  • 日期、時間、數字、貨幣用 locale-aware formatterIntl.*)。
  • Pluralization 用 CLDR plural rules(zero/one/few/many/other)。
  • 字體要支援目標語系字符集(CJK 字數遠超拉丁字)。

字串長度預留範例

語言相對英文長度
英文100%(baseline)
中文(繁/簡)70–80%(更短)
日文80–100%
德文130–140%(最長)
芬蘭文130%+
法文115–125%
韓文95–105%
阿拉伯文110–130% + RTL

不串接字串

❌ 不好(無法翻譯):

"You have " + n + " items in your cart";

✅ 好:

t("cart.itemCount", { count: n });
// → "You have {count} items"
// 中文 → "您的購物車有 {count} 件商品"

ICU Message Format

{count, plural,
  zero {No items}
  one {# item}
  other {# items}
}

中文沒有單複數,但德文有 6 種 plural form,用 ICU 統一處理。

日期 / 時間 / 數字 / 貨幣

Intl API:

new Intl.DateTimeFormat("zh-TW").format(date);
// → "2026/5/29"
new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(1234.5);
// → "$1,234.50"

不要 hardcode:

  • date.toISOString().slice(0, 10) 然後 display
  • "NT$" + price.toLocaleString()

RTL

支援阿拉伯文、希伯來文時:

  • 整體 layout mirror(sidebar 變右側、breadcrumb 反向)。
  • Icon 方向反轉(← → 互換、回上一頁的箭頭、播放按鈕也反)。
  • 數字、英文、商標不反。
  • CSS 用 direction: rtl + logical properties(padding-inline-start 而非 padding-left)。

字體

  • 不同語系可能需要不同字體:
    • 拉丁文:Inter / Helvetica
    • 中文:PingFang TC / Noto Sans TC
    • 日文:Hiragino / Noto Sans JP
    • 阿拉伯文:Noto Sans Arabic
  • Web font 用 font-display: swap 避免白屏。

翻譯流程

  • 用 i18n library:react-intl / i18next / Lingui
  • 翻譯 key 用語義命名:button.save / cart.empty.title
  • 翻譯放獨立 json / yaml 檔,前端建置時注入。
  • 用翻譯服務 / vendor:Crowdin / Lokalise / Transifex。
  • Context 文字給翻譯者("Used as button on settings page")。

Locale 偵測 / 切換

  • 預設用瀏覽器 navigator.language 或 server Accept-Language header。
  • Settings 提供手動切換(語言下拉)。
  • 切換後立刻套用(不需 reload)。
  • URL 可帶 locale prefix(/zh-tw/settings)SEO / 分享友善。

不要做的事

  • ❌ 用 flag 圖示代表語言(國旗 ≠ 語言)。
  • ❌ 把 "EN" / "中" 字串 hardcode 在按鈕。
  • ❌ 用機器翻譯放上 production(除非標註「machine-translated」)。
  • ❌ 假設 LTR 永遠成立。