跳至主要內容

開發紀錄

三語網站為什麼讓網址成為語言來源

從繁中、英文與日文路由的實際分工,說明網址如何同時決定頁面語言、文案、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 建置 不生成假的翻譯文章頁

這也是為什麼「語言網站都有英文頁」和「每篇文章都有英文翻譯」不能混成同一句話。前者是固定頁路由契約,後者是內容供給狀態。

網址決定語言,不代表所有內容都已翻譯

目前英文和日文的筆記列表可以正常存在,即使它們暫時沒有正式文章。列表頁會顯示該語言自己的空狀態,不會把繁中文章複製過去,也不會把讀者重導回繁中首頁。

這個空狀態很重要,因為它把兩件事分開:

  1. 這個語言的網站入口已經存在。
  2. 這個語言目前有多少可公開閱讀的文章。

如果沒有文章,頁面就說沒有文章;如果有文章,文章路由才會出現。網址可以先建立內容入口,但不能替內容本身補一個不存在的翻譯。

這種做法刻意不處理什麼

讓網址成為語言來源,不代表目前已經解決所有多語問題。它刻意不做幾件事:

  • 不根據瀏覽器語言自動把讀者送到另一個 locale。
  • 不把不存在的翻譯當成原文的替代頁。
  • 不因為路由有 /en 或 /ja 就宣稱該語言內容已完整。
  • 不把語言切換做成只能靠 JavaScript 才能工作的按鈕。
  • 不用一個全域語言狀態取代每一頁自身的 URL 和 metadata。

未來如果加入真正的自動導向、語言偏好記憶或更多內容翻譯,仍然要先確認它們是否會和公開網址、canonical、歷史連結互相衝突。這些都不是目前路由契約已經證明的功能。

寫多語頁面時可以先檢查什麼

  1. 先決定哪一種語言是預設網址,不要在所有語言前面都加前綴。
  2. 讓固定頁路由、導覽連結、lang 和 metadata 共用同一組 locale。
  3. 把固定頁翻譯和文章翻譯分開建模。
  4. 文章只有在翻譯檔案與發布狀態都成立時,才建立語言連結。
  5. 沒有內容時顯示真實空狀態,不用假文章維持畫面完整。
  6. 用產物檢查確認實際 URL、canonical 和語言標記,而不是只看元件檔案裡有沒有 lang 變數。

目前這個網站採用的不是「先做一套語言切換,再把所有內容塞進去」,而是讓公開網址、頁面資料和實際存在的內容互相對得上。固定頁可以共用結構,文章可以各自等待翻譯;兩者都從網址開始,但不必假裝它們有一樣的內容狀態。

紀錄範圍

本文整理的是目前三語固定頁、導覽連結和筆記文章路由的實作契約。它說明網址如何成為語言來源,以及固定頁和翻譯文章之間的差異;沒有宣稱自動語言偵測、翻譯完整度、搜尋流量或讀者偏好已經被量測。