Communitygithub.com

Nelsutro/agent-skills

生產級國際化與在地化解決方案,適用於網頁應用。用於新增多語言支援、翻譯 UI、萃取硬編碼字串、處理複數/性別/ICU 訊息、語言路由、RTL 版面、在地化日期/數字/貨幣、hreflang/SEO,或驗證翻譯目錄。提供框架無關的指引,涵蓋 React、Next.js、Vue、Svelte、Astro、Remix 及原生 JS,並附有 next-intl、react-i18next、FormatJS 與 Lingui 範例。

agent-skills 是什麼?

這項技能提供一套完整、生產級國際化(i18n)與在地化(l10n)的準則與實作模式,適用於任何網頁開發堆疊。其核心哲學是將國際化視為架構設計,而非最終的翻譯流程,因此重點放在避免常見反模式,例如串接翻譯片段、使用英文文字作為鍵值,或是在格式化日期、數字及貨幣時硬編碼格式。 此技能支援所有主流框架(React、Next.js、Vue、Svelte、Astro、Remix)及原生 JavaScript,並提供特定參考文件。其中包含明確的逐步工作流程:審計現有程式碼、決定架構(語言策略、目錄格式、ICU 訊息語法)、外部化字串、設定語言路由與渲染、處理語言敏感值、為擴充與 RTL 設計,以及設置 CI 中的翻譯品質檢查。 內建非妥協事項清單,確保正確性:禁止串接翻譯、禁止使用英文文字作為鍵值、禁止硬編碼格式、語言標籤需使用 BCP 47、伺服器端語言狀態需為請求作用域,並將翻譯視為不可信任的輸入。此外也涵蓋詳細的複數規則(包含所有語言特定分類)、性別與語法一致性的 select 子句、rich text 標籤插值、SEO(hreflang、sitemap、在地化 meta 資料),以及緩存注意事項。 提供的 SKILL.md 檔案可作為開發者與 AI 代理的完整參考文件,結構化為目錄、非妥協事項、工作流程、關鍵決策表、複查清單與反模式列表。其目標是協助團隊從專案初期就導入穩固的 i18n 基礎,避免後續昂貴的重構。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Nelsutro/agent-skills/tree/HEAD/web-i18n

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

Web i18n

國際化是架構問題,不是專案尾聲的翻譯工作。目標是讓 UI 在任何語言環境下都能正確呈現,無需特殊處理:不串接句子、不硬編碼格式、不因德語長 35% 或阿拉伯語改變方向而版面崩壞。

何時使用此技能

當使用者要求以下事項時使用:

  • 新增語言、語言切換器或語言路由
  • 翻譯應用程式、萃取字串或建立翻譯工作流程
  • 修正複數、性別或「1 個項目 / 2 個項目」的錯誤
  • 根據語言環境格式化日期、數字、貨幣、相對時間或列表
  • 支援 RTL(阿拉伯語、希伯來語、波斯語、烏爾都語)
  • 新增 hreflang、在地化 metadata 或多語言站點地圖
  • 審查現有 i18n 設定、遺漏的鍵值或翻譯偏差
  • 在 next-intl、react-i18next、FormatJS、Lingui 或 TMS 之間選擇

相關參考文件在 references/ 中,腳本在 scripts/ 中。僅讀取任務所需的內容。路徑相對於此技能目錄(代理執行時期會在技能載入時列印該目錄)。

非妥協事項

  1. 絕對不要串接翻譯片段來建立句子。 應翻譯完整訊息並使用佔位符:使用 "{count} 則新訊息",而非 t('new') + ' ' + count + ' ' + t('messages')。
  2. 絕對不要使用英文文字作為鍵值。 鍵值應是穩定的識別碼(inbox.unreadCount),這樣文字修改才不會破壞查詢或使所有翻譯失效。
  3. 每個使用者可見的字串都必須外部化,包括 aria-label、alt、title、placeholder、驗證錯誤、空狀態、toast、電子郵件、推播通知和 PDF 輸出。
  4. 絕對不要硬編碼格式。 不要使用 toFixed(2) + '$'、手動 MM/DD/YYYY、英文月份名稱或假設 24h。請使用 Intl。
  5. 語言環境是 BCP 47 標籤,不是語言。pt-BR 與 pt-PT、zh-Hans 與 zh-Hant、es-MX 與 es-ES 是不同的產品。請儲存完整標籤。
  6. 伺服器上的語言狀態必須為請求作用域。 絕不在 SSR 期間將當前語言環境放入模組層級的可變變數中;並發請求會導致語言互相洩漏。
  7. 若翻譯來自 TMS、社群或 CMS,則視為不可信任的輸入。 在未經清理的情況下,絕不將它們傳遞給 innerHTML/dangerouslySetInnerHTML。

工作流程

1. 在修改程式碼前先審計

  • 在提出任何建議前,先檢測堆疊、路由器和現有的 i18n 函式庫。
  • 尋找硬編碼字串:
    node scripts/scan-hardcoded.mjs src
    
    將輸出視為候選項目,而非定論:跳過測試 fixture、常數、URL 和開發者導向的日誌。
  • 盤點當前提供的每個語言環境以及目錄存放位置。
  • 優先找出風險最高的區域:驗證、結帳、錯誤、電子郵件和法律文字。

2. 決定架構(寫下來)

在撰寫程式碼前回答這些問題:

決策預設值備註
語言策略路徑前綴 /es/SEO 與快取最佳。僅在有充分理由時才使用子網域/國碼頂級域。
預設語言環境原始語言不要在未提供覆蓋選項的情況下,自動將首次造訪者重新導向到猜測的語言環境。
語言偵測URL → cookie → Accept-Language → 預設值URL 優先,這樣共享連結永遠不會改變語言。
目錄格式每個語言環境每個命名空間一個 JSON延遲載入命名空間;永遠不要將所有語言環境發送到客戶端。
訊息語法ICU MessageFormat可跨函式庫和 TMS 移植;支援複數/選擇/序數。
鍵值風格feature.component.element穩定、可搜尋、無英文文字。
翻譯來源TMS 或儲存庫,不可兩者並存選擇一個;在 CI 中單向同步。
後備語言環境鏈 es-MX → es → en在開發環境中記錄後備情況以找出缺口。

3. 外部化並結構化訊息

  • 將字串替換為語義鍵值,並將文案移至基礎語言環境目錄。
  • 每個完整句子使用一個鍵值。如果句子包含連結或粗體字,請使用富文本插值而非拆分句子。
  • 保持佔位符命名({userName}),絕對不要使用位置序號({0})。
  • 除非函式庫明確支援型別標籤,否則不要在目錄中放入 HTML。
  • 按產品表面分組,而非按頁面:checkout.json、auth.json、errors.json。

關於複數、選擇、序數和富文本規則,請參閱 references/icu.md。

4. 設定語言路由與渲染

  • 從路由推導語言環境,驗證其是否在支援的列表中,對於未知語言環境回傳 404,而非靜默地使用後備。
  • 提供一個語言切換器,保留當前路徑和查詢字串。
  • 在伺服器渲染的文件上設定 <html lang="..."> 和 <html dir="...">。
  • 僅載入當前語言環境的目錄以及預設後備目錄。
  • 確保伺服器和客戶端渲染使用相同的目錄;避免重複或分歧的翻譯邏輯。

特定框架的設定與陷阱:references/frameworks.md。 路由、偵測、hreflang 和站點地圖:references/routing-seo.md。

5. 處理語言環境敏感值

使用 Intl(或框架的包裝器),絕對不要手動格式化:

new Intl.NumberFormat(locale, { style: 'currency', currency }).format(amount)
new Intl.DateTimeFormat(locale, { dateStyle: 'medium', timeZone }).format(date)
new Intl.RelativeTimeFormat(locale, { numeric: 'auto' }).format(-3, 'day')
new Intl.ListFormat(locale, { type: 'conjunction' }).format(items)
new Intl.PluralRules(locale).select(count)
new Intl.Collator(locale).compare(a, b)
  • 將時間戳儲存為 UTC;在使用者的時區呈現,而非伺服器的時區。
  • 貨幣與語言環境不同:法國使用者可能使用美元付款。
  • 使用 Intl.Collator 進行排序/搜尋,不要使用 < 或未指定語言環境的 localeCompare()。
  • 大小寫轉換:使用 toLocaleUpperCase(locale),因為土耳其語的 i 會變成 İ。

6. 為擴充與 RTL 設計

  • 假設文字長度增加 +30–40%(德語、芬蘭語、俄語),不要使用固定寬度的按鈕或單行標籤。
  • 讓容器可以成長,允許換行,並使用偽語言環境進行測試:
    node scripts/pseudo-localize.mjs locales/en.json --out locales/en-XA.json
    
  • 使用邏輯 CSS 屬性(margin-inline-start、padding-inline、inset-inline)而非 left/right。
  • 鏡像方向性圖示(箭頭、V 形、進度條),而非標誌、時鐘、媒體控制項或勾選標記。
  • 將使用者產生的內聯內容包裝在 <bdi> 或 dir="auto" 中。

完整的 RTL 檢查清單:references/rtl.md。

7. 在 CI 中設定翻譯品質檢查

至少,當目錄結構無效時要讓建置失敗:

node scripts/check-locales.mjs locales --base en

它會檢查缺少/多餘的鍵值、空值、佔位符不匹配以及損壞的 ICU 語法。如果有的話,可以透過函式庫的 CLI 加入 ICU 編譯:

npx formatjs compile-folder --ast locales/en locales/compiled/en

為複數類別、日期/數字輸出(使用固定時區)和 RTL 渲染加入測試。詳見 references/testing.md。

關鍵決策與陷阱

函式庫選擇

需求選擇
Next.js App Router、伺服器元件next-intl
React SPA、成熟生態系、多種外掛react-i18next
ICU 優先、框架無關、CLI 萃取FormatJS (react-intl)
編譯時目錄、小型執行時期、型別安全Lingui
Vuevue-i18n
Sveltesvelte-i18n 或 paraglide
Astro內建 i18n 路由 + 你的訊息函式庫
原生 / 最小需求Intl + 小型目錄載入器

不要在已有 i18n 函式庫的應用程式中引入第二個。請擴展現有設定。

複數規則

複數類別因語言而異。英文有 one/other;阿拉伯文有 zero/one/two/few/many/other;日文只有 other。永遠不要寫 count === 1 ? ... : ...。

"inbox.unread": "{count, plural, =0 {沒有未讀訊息} one {# 則未讀訊息} other {# 則未讀訊息}}"

性別與語法一致性

在性別變數上使用 select,並保持變數的值為語言中立(female、male、neutral、unknown),而非在地化的詞彙:

"profile.greeting": "{gender, select, female {歡迎,{name}} male {歡迎,{name}} other {歡迎,{name}}}"

如果語言需要在形容詞、名詞或冠詞上保持一致,請將語法性別/格作為引數暴露出來,而不是駭入字串。

富文本

不要將 "閱讀我們的 " + link + " 條款" 拆分為三個鍵值。請使用標籤插值:

"signup.terms": "我同意<link>服務條款</link>"
t.rich('signup.terms', { link: (chunks) => <a href="/terms">{chunks}</a> })

SEO

  • 每個語言環境一個可索引的 URL;永遠不要在相同 URL 背後提供機器翻譯的內容。
  • 自我參照的 hreflang 加上 x-default;使用絕對 URL;包含每個語言環境。
  • 在地化 <title>、meta 描述、OG 標籤和結構化資料。
  • 不要在地化頁面上同時使用 hreflang 與 noindex。

詳情請參閱 references/routing-seo.md。

快取

  • 路徑中包含語言環境可使 CDN 快取變得簡單。如果透過標頭協商,請傳送 Vary: Accept-Language,並預期會出現快取碎片化。
  • 不要在共享的 CDN 回應中快取個人化的語言偏好。
  • 保持 Content-Language 與渲染的語言環境一致。

複查清單

在宣告 i18n 完成之前:

  • 元件、電子郵件或錯誤中沒有硬編碼的使用者可見字串
  • 沒有句子串接;每則訊息都是完整的可翻譯單元
  • 鍵值是語義化且穩定的;沒有使用英文文字作為鍵值
  • 每個佔位符都存在於每個語言環境中;ICU 語法編譯通過
  • 複數使用 Intl.PluralRules/ICU,而非 count === 1
  • 性別/一致性透過 select 處理,而非使用 "/a" 後綴
  • 日期、數字、貨幣、相對時間使用 Intl 並指定語言環境/時區
  • <html lang> 和 dir 在首次繪製(SSR)時正確,而非在 effect 中設定
  • 語言切換器保留路徑/查詢,並適用於深層連結
  • 未知的語言環境回傳 404,而非靜默使用後備
  • 版面能承受 +35% 的文字擴充和 RTL
  • hreflang、canonical、站點地圖和在地化 metadata 正確
  • 目錄驗證在 CI 中執行
  • 後備行為是有意且可觀察的

反模式

  • t('hello') + ', ' + name — 破碎的句子
  • key={englishText} — 將翻譯文字作為鍵值
  • count === 1 ? 'item' : 'items' — 僅限英文的複數邏輯
  • new Date().toLocaleDateString() 未指定語言環境或時區
  • value.toFixed(2) 用於金錢
  • 在元件中零散使用 locale === 'ar' && { direction: 'rtl' },而非在文件層級設定 dir
  • 在 useEffect 內設定 <html lang> — 會造成閃爍並破壞爬蟲
  • 將所有語言環境載入到初始 bundle 中
  • 根據 IP 自動重新導向,且無任何覆蓋方式
  • 翻譯專有名詞、產品名稱、程式碼識別碼或 URL
  • 機器翻譯字串後未經審查即發佈
  • 假設單一目錄檔案能擴充到數百個鍵值以上

相關技能