開發紀錄
三語網站為什麼讓網址成為語言來源
從繁中、英文與日文路由的實際分工,說明網址如何同時決定頁面語言、文案、metadata 和語言切換的下一步。
- 多語網站
- Astro
- 路由
- 資訊架構
一個多語網站不一定要先問瀏覽器偏好,也不一定要把語言藏在一個全域狀態裡。對目前這個網站來說,網址本身就是語言來源:繁中使用 /,英文使用 /en,日文使用 /ja。
這個選擇看起來像路由命名,實際上會一路影響頁面該讀哪份文案、html lang 要輸出什麼、metadata 要使用哪種語言,以及語言切換時下一個連結是否真的存在。主要判斷是:如果語言是公開網址的一部分,就讓建置和連結都從同一個 locale 來源推導,不要再用另一套瀏覽器狀態把它覆寫。
先分清楚預設語言和其他語言
目前的語言集合是三個值:zh-TW、en、ja。繁中是預設語言,所以固定頁直接使用原本的路徑;英文和日文則在前面加上語言前綴:
繁中:/about
英文:/en/about
日文:/ja/about
程式裡的 localizedPath() 只做這件事:收到一個固定頁面和語言,回傳對應網址。它不負責偵測瀏覽器、不讀取 cookie,也不決定使用者偏好哪一種語言。這種分工很小,但它讓網址產生規則只有一份。
網址生成函式也保留了預設語言的例外。zh-TW 不需要 /zh-TW,所以 /、/notes、/about 這些路徑就是繁中版本;英文和日文才加上前綴。若把三種語言都一律加前綴,便會改變現有繁中網址,也會讓舊連結和 canonical 規則多一層遷移問題。
固定頁面的語言來自 route props
固定頁面不是打開之後才由瀏覽器把一個空殼切換成不同語言。英文和日文的動態路由會在建置時列出固定頁面集合,再把 lang 和頁面責任傳給同一組 Astro 元件。
因此,同一個 /en/about 和 /ja/about 雖然共用 AboutPage,它們仍然是兩個實際輸出的頁面。元件拿到的 lang 會決定:
- 標題和段落從哪個語言字典讀取。
- 導覽列要連到哪一組固定頁。
html元素的lang屬性要輸出什麼。- 頁面描述、Open Graph 文案和結構化資料使用哪一種語言。
這裡的「共用元件」不等於「共用一份頁面狀態」。它只是共用頁面結構。真正的語言值仍然由 route props 傳進來,建置時就已經確定。
連結也要使用同一個語言來源
主導覽的連結不是把目前網址字串直接切一段再拼回去,而是把目前頁面和目標語言交給同一個路徑函式。例如在英文的 Notes 頁切換到日文,目標應該是 /ja/notes;在繁中的產品頁切換到英文,目標則是 /en/products/daily-order。
這讓語言切換保持原生連結的特性:
- 可以用瀏覽器開新分頁。
- JavaScript 失效時仍然能切換。
- 連結本身可以被爬蟲和輔助工具理解。
- 每個語言頁都能有自己的 canonical 和
hreflang。
這裡不需要另外建立一個「目前語言」的 client store。頁面已經從網址知道自己是哪種語言,語言切換只要產生另一個真實網址即可。
固定頁和內容頁不是同一種多語問題
固定頁可以預先知道三種語言都有對應頁面,因為路由和文案字典都由網站自己控制。但筆記文章不一樣:某篇中文文章可能還沒有英文或日文版本。
所以文章頁的語言連結不能只對照全域語言清單。文章版型會根據同一個 stableId 找到目前實際存在、而且符合正式發布條件的翻譯,再產生對應連結。沒有翻譯的語言,不會被硬拼成一個看似存在的文章網址。
可以把兩種情況分開看:
| 內容類型 | 語言網址怎麼產生 | 沒有翻譯時的處理 |
|---|---|---|
| 固定頁 | 由固定路由和語言集合建置 | 每種支援語言都有對應頁面 |
| 筆記文章 | 由實際存在的文章和 stableId 建置 |
不生成假的翻譯文章頁 |
這也是為什麼「語言網站都有英文頁」和「每篇文章都有英文翻譯」不能混成同一句話。前者是固定頁路由契約,後者是內容供給狀態。
網址決定語言,不代表所有內容都已翻譯
目前英文和日文的筆記列表可以正常存在,即使它們暫時沒有正式文章。列表頁會顯示該語言自己的空狀態,不會把繁中文章複製過去,也不會把讀者重導回繁中首頁。
這個空狀態很重要,因為它把兩件事分開:
- 這個語言的網站入口已經存在。
- 這個語言目前有多少可公開閱讀的文章。
如果沒有文章,頁面就說沒有文章;如果有文章,文章路由才會出現。網址可以先建立內容入口,但不能替內容本身補一個不存在的翻譯。
這種做法刻意不處理什麼
讓網址成為語言來源,不代表目前已經解決所有多語問題。它刻意不做幾件事:
- 不根據瀏覽器語言自動把讀者送到另一個 locale。
- 不把不存在的翻譯當成原文的替代頁。
- 不因為路由有
/en或/ja就宣稱該語言內容已完整。 - 不把語言切換做成只能靠 JavaScript 才能工作的按鈕。
- 不用一個全域語言狀態取代每一頁自身的 URL 和 metadata。
未來如果加入真正的自動導向、語言偏好記憶或更多內容翻譯,仍然要先確認它們是否會和公開網址、canonical、歷史連結互相衝突。這些都不是目前路由契約已經證明的功能。
寫多語頁面時可以先檢查什麼
- 先決定哪一種語言是預設網址,不要在所有語言前面都加前綴。
- 讓固定頁路由、導覽連結、
lang和 metadata 共用同一組 locale。 - 把固定頁翻譯和文章翻譯分開建模。
- 文章只有在翻譯檔案與發布狀態都成立時,才建立語言連結。
- 沒有內容時顯示真實空狀態,不用假文章維持畫面完整。
- 用產物檢查確認實際 URL、canonical 和語言標記,而不是只看元件檔案裡有沒有
lang變數。
目前這個網站採用的不是「先做一套語言切換,再把所有內容塞進去」,而是讓公開網址、頁面資料和實際存在的內容互相對得上。固定頁可以共用結構,文章可以各自等待翻譯;兩者都從網址開始,但不必假裝它們有一樣的內容狀態。
紀錄範圍
本文整理的是目前三語固定頁、導覽連結和筆記文章路由的實作契約。它說明網址如何成為語言來源,以及固定頁和翻譯文章之間的差異;沒有宣稱自動語言偵測、翻譯完整度、搜尋流量或讀者偏好已經被量測。