From 6b56c1f4db5b2dd96c68ede5f90af45c6954a1db Mon Sep 17 00:00:00 2001 From: hoelee Date: Sun, 6 Sep 2026 06:50:37 +0800 Subject: [PATCH] feat: language switch links to same post in other language - Add translation field to schema + auto-match by zh/ prefix - BaseLayout accepts per-page switchHref; post page resolves translation link - Falls back to section landing (/ or /zh/) when no translation exists --- src/content.config.ts | 3 +++ src/layouts/BaseLayout.astro | 11 ++++++++--- src/pages/posts/[...slug].astro | 9 +++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/content.config.ts b/src/content.config.ts index c94a30a..c35b6ba 100644 --- a/src/content.config.ts +++ b/src/content.config.ts @@ -21,6 +21,9 @@ const posts = defineCollection({ // lang is NOT set in frontmatter anymore — it's derived from the folder: // posts/hello-world.md -> en (flat) // posts/zh/hello-world.md -> zh (subfolder) + // Explicit link to the other-language version (its full slug, e.g. "zh/how-i-host-this-blog"). + // When absent, the language switch falls back to the section landing page (/ or /zh/). + translation: z.string().optional(), ogImage: z.string().optional(), }), }); diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro index 47d84d5..8e82335 100644 --- a/src/layouts/BaseLayout.astro +++ b/src/layouts/BaseLayout.astro @@ -10,6 +10,10 @@ interface Props { lang?: string; ogImage?: string; type?: 'website' | 'article'; + // Optional: direct URL of this page's translation (the other-language version). + // When provided, the language switch links to it; otherwise it falls back to + // the section landing page (/ for EN, /zh/ for ZH). + switchHref?: string; article?: { publishedTime: string; modifiedTime?: string; @@ -24,6 +28,7 @@ const { lang = SITE.lang, ogImage = '/og-default.png', type = 'website', + switchHref, article, } = Astro.props; @@ -31,9 +36,9 @@ const canonical = new URL(Astro.url.pathname, SITE.url).href; const ogImageUrl = new URL(ogImage, SITE.url).href; // Language switcher: opposite of current page lang. -// English pages link to /zh/ (Chinese landing); Chinese pages link back to /. +// If a per-page translation link is provided, use it; else fall back to the section landing. const isZh = lang === 'zh'; -const switchHref = isZh ? '/' : '/zh/'; +const resolvedSwitchHref = switchHref ?? (isZh ? '/' : '/zh/'); const switchLabel = isZh ? 'EN' : '中文'; --- @@ -127,7 +132,7 @@ const switchLabel = isZh ? 'EN' : '中文'; categories about rss - {switchLabel} + {switchLabel} diff --git a/src/pages/posts/[...slug].astro b/src/pages/posts/[...slug].astro index 2ff06ad..41615e2 100644 --- a/src/pages/posts/[...slug].astro +++ b/src/pages/posts/[...slug].astro @@ -37,6 +37,14 @@ const related = all .sort((a, b) => b.score - a.score) .slice(0, 3) .map((r) => r.post); + +// Translation link for the language switch: +// 1. Explicit `translation` field wins. +// 2. Else auto-match the other-language slug (strip/add the `zh/` prefix). +const otherLangSlug = lang === 'en' ? `zh/${post.slug}` : post.slug.replace(/^zh\//, ''); +const translation = post.data.translation + ?? (all.some((p) => p.slug === otherLangSlug) ? otherLangSlug : undefined); +const switchHref = translation ? `/posts/${translation}/` : undefined; ---