國際化 (i18n)
基於 @nuxtjs/i18n 建構的多語言支援,內建簡體中文、繁體中文、英文三語言,翻譯檔案扁平化管理,AI 友好。
啟用 i18n
// nuxt.config.ts
swiftConfig: {
i18n: {
enabled: true,
defaultLocale: 'zh-CN',
locales: ['zh-CN', 'zh-TW', 'en'],
}
}
swiftConfig.i18n.locales 只做一件事:把語言碼映射成 @nuxtjs/i18n 的 { code, language } 用於產生路由前綴(/en/、/zh-TW/)。它不註冊翻譯檔案 —— 裡面沒有 file 欄位。
翻譯檔案靠 nuxt.config.ts 頂層的 i18n 區塊接上(sw config set framework.i18n.enabled true 會自動解開 starter 裡這段註解):
// nuxt.config.ts —— 與 swiftConfig 平級的頂層 i18n
i18n: {
langDir: 'locales', // 相對 i18n/ 目錄
locales: [
{ code: 'zh-CN', language: 'zh-CN', name: '简体中文', file: 'zh-CN.json' },
{ code: 'zh-TW', language: 'zh-TW', name: '繁體中文', file: 'zh-TW.json' },
{ code: 'en', language: 'en', name: 'English', file: 'en.json' },
],
}
頂層 i18n.locales 會覆寫 framework 給的預設值,所以兩處的語言碼要保持一致。只配 swiftConfig.i18n.locales、不配頂層 file 的後果是:路由前綴正常、$t() 卻拿不到任何翻譯(原樣吐 key)。策略 / 瀏覽器偵測 / bundle 等其餘項由 framework 提供預設值,不必重複寫。
framework 內建文案的多語言行為
framework 元件自身也有文案(分頁器的「共 N 筆」、feed 的「N 秒後可關閉廣告」、對話框的
「關閉 / 取消 / 確認」,以及約 33 條 aria-label)。這些會自動跟隨你的目前語言,業務零改動。
- 啟用 i18n 時:跟隨使用者目前選擇的語言(不是建置期的
defaultLocale)—— 使用者點SwLangSwitch切換,framework 文案當場跟著變 - 未啟用 i18n 時:一律中文,零額外開銷
- 內建
zh-CN/zh-TW/en三語;選了其它語言(如ja)時回落中文,不會出現空白 - 語言碼按主碼歸一:
zh-Hant/zh-HK/zh-MO都歸繁體,en-US/en-GB都歸英文
<!-- 你什麼都不用做:切到 en 後分頁器自己就是英文 -->
<SwPagination v-model:page="page" :total="total" show-total />
<!-- zh-CN → 「共 100 条」/ zh-TW → 「共 100 筆」/ en → 「100 items」 -->
已有 slot / prop 逃生艙的元件不受影響——SwModal 的 cancelText / confirmText、
SwGallery 的 #empty / #finished 等你傳了就用你的,沒傳才走內建詞條。
翻譯檔案結構
翻譯檔案位於 i18n/locales/ 目錄,採用扁平化 JSON 格式:
// i18n/locales/zh-CN.json
{
"首页": "首页",
"快速上手": "快速上手",
"立即开始": "立即开始",
"首页SEO描述": "Swift Web 框架的官方文档站,涵盖组件库和开发规范"
}
// i18n/locales/en.json
{
"首页": "Home",
"快速上手": "Quick Start",
"立即开始": "Get Started",
"首页SEO描述": "Official docs for Swift Web framework, covering components and standards"
}
在頁面中使用
<script setup lang="ts">
const { t } = useI18n()
useSeo({
title: () => t('首页'),
description: () => t('首页SEO描述'),
})
</script>
<template>
<h1>{{ t('快速上手') }}</h1>
<button>{{ t('立即开始') }}</button>
</template>
語言切換
<template>
<!-- 內建切換器:僅 i18n 啟用且 locales ≥ 2 時渲染(自 gate,否則佔 0 空間);
支援 token(--sw-lang-switch-*)/ 具名 icon slot / default 作用域 slot 三檔定制 -->
<SwLangSwitch />
</template>
需要完全自定義 UI 時再手寫(注意用 <option :selected> 而非 <select :value>——後者是非法 HTML,SSR 直出會選錯項):
<script setup lang="ts">
const { locale, setLocale, locales } = useI18n()
</script>
<template>
<select @change="setLocale(($event.target as HTMLSelectElement).value)">
<option v-for="loc in locales" :key="loc.code" :value="loc.code" :selected="loc.code === locale">
{{ loc.name }}
</option>
</select>
</template>
翻譯 Key 規範
- 短文案(≤50字):直接用中文原文作為 key,如
t('立即开始') - 長文案(>50字):用簡短中文描述作 key,全文放在 JSON value 裡,如
t('首页SEO描述') - 禁止將長句直接作為 key,否則翻譯檔案難以閱讀
URL 本地化
@nuxtjs/i18n 預設使用路徑前綴策略:
- zh-CN(預設語言):
example.com/(無前綴) - zh-TW:
example.com/zh-TW/ - en:
example.com/en/
canonical 標籤由 useSeo() 統一注入;多語言站(locales ≥ 2、prefix 策略)的 sitemap 自動按 locale 拆分並輸出 hreflang 互鏈(零設定,詳見 SEO)。
瀏覽器語言自動偵測在可快取頁上不生效
@nuxtjs/i18n 的 detectBrowserLanguage(按 Accept-Language / Cookie 把訪客帶到對應語言前綴)在 ISR / 可快取頁上結構性失效:nitro 的快取包裝只向渲染透傳 cache.varies 白名單裡的請求頭(host / x-forwarded-proto),Accept-Language 與 Cookie 根本到不了偵測邏輯。這不是設定問題,把它們加進 varies 也不是出路——varies 會進快取 key,按 Cookie 分裂等於命中率歸零。
實際行為與取捨:
- 直達可快取頁(含首頁)不會發生語言自動跳轉,所有訪客拿到預設語言的快取副本。這是確定性行為——也因此偵測重定向不可能被快取廣播,無正確性風險。
- 訪客換語言靠 SwLangSwitch,路徑前綴(
/en/...)是語言的持久載體。 - framework 會把可快取頁渲染期寫出的 Set-Cookie(含 i18n 的偵測標記 cookie)從回應剝離:可快取回應帶 Set-Cookie 會進共享快取副本回放,還會讓未配
proxy_ignore_headers的 nginx / CDN 拒絕快取整頁(排障判據見部署手冊)。dev 模式下剝離伴隨 console 告警說明剝了哪個 cookie;需要按使用者寫 Cookie 請在客戶端寫,或把該路由改為不可快取。
html lang 自動跟隨語言
多語言站(locales ≥ 2)下,framework 透過內建執行時插件把 <html lang> 接管為當前語言(取 locale 的 language 欄位,兜底 code):
- 訪問
/en/xxx→<html lang="en">,SSR 首幀即正確 - 站內切語言(
SwLangSwitch)→lang即時跟隨 - 單語言站 / i18n 關閉時保持建置期靜態值(
i18n.defaultLocale),零行為變化
applier 零配置,也不要在業務側再用 useHead({ htmlAttrs: { lang } }) 雙寫 —— htmlAttrs 所有權在 framework,雙寫會互相覆蓋。