feat: language switch links to same post in other language
Deploy / build (push) Successful in 10s

- 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
This commit is contained in:
hoelee committed 2026-09-06 06:50:37 +08:00
1 parent 677fc7cb20
commit 6b56c1f4db
3 files changed
+20 -3

No files matched your search

+3
View File
@@ -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(),
}),
});
+8 -3
View File
@@ -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' : '中文';
<a href="/categories/">categories</a>
<a href="/about/">about</a>
<a href="/rss.xml" title="RSS feed">rss</a>
<a class="lang-switch" href={switchHref} aria-label={isZh ? 'Switch to English' : '切换到中文'}>{switchLabel}</a>
<a class="lang-switch" href={resolvedSwitchHref} aria-label={isZh ? 'Switch to English' : '切换到中文'}>{switchLabel}</a>
<button class="theme-toggle" id="theme-toggle" aria-label="Toggle theme">◐</button>
</nav>
</header>
+9
View File
@@ -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;
---
<BaseLayout
@@ -45,6 +53,7 @@ const related = all
lang={lang}
ogImage={post.data.ogImage ?? '/og-default.png'}
type="article"
switchHref={switchHref}
article={{
publishedTime: post.data.pubDate.toISOString(),
modifiedTime: post.data.updatedDate?.toISOString(),