這個 blog 用 Astro content collections——三個語系(en/ja/zh),每篇每語一個 markdown 檔。架設過程踩到兩個值得記下來的坑。一來修法不直覺,二來,拿來當第一篇實戰文剛剛好。
坑一:frontmatter 的 slug 就是 entry 的 id
我給每篇都加了 slug 欄位,想讓同一個故事的三個語言版本共用一個 URL:
lang: "en-us"
slug: "unify-mac-ios-downloads"
build 沒報錯。但只有中文 index 列出了那篇——英文和日文都顯示「還沒有文章」。
原來 glob loader 把 frontmatter 的 slug 欄位當成 entry 的 id。三個檔宣告了同一個 slug,於是它們被併成單一 entry,最後載入的那個(zh-tw,照字母排序)勝出。另外兩個就這樣從 collection 裡消失了——沒有錯誤、沒有警告。
修法:拿掉 slug 欄位。讓 id 就是檔案路徑(en-us/unify-mac-ios-downloads,每語系唯一),URL 的 slug 改從檔名推導:
const slug = post.id.replace(/^[^/]+\//, ''); // 去掉語系資料夾前綴
坑二:getStaticPaths 跑在自己的 scope
id 修好後,下一次 build 失敗得更乾脆:
slugOf is not defined
我把那段小小的 replace 抽成路由檔頂層的一個 helper,然後在 getStaticPaths 裡呼叫它。但 getStaticPaths 很特殊——Astro 會把它抽出來在獨立 scope 執行,所以它看不到同一個 frontmatter 裡其他的頂層 const。那個 helper 對元件的其餘部分存在,唯獨在它裡面不存在。
修法:把推導內聯,或把它定義在 getStaticPaths 裡面:
export async function getStaticPaths() {
const posts = (await getCollection('blog')).filter((p) => p.data.lang === 'zh-tw');
return posts.map((post) => ({
params: { slug: post.id.replace(/^[^/]+\//, '') },
props: { post },
}));
}
兩個都是那種——happy path 下 build 全綠,要等你有超過一篇、或共用 helper 時才咬你一口的坑。現在你知道了——你正在讀的這個 blog,就是修完這兩坑後立刻上線的。