feat(i18n): /en/ 双语骨架——路由/切换/hreflang/回退/AI 标注

- astro i18n(zh 默认无前缀,en /en/)+ locale.ts 路径工具
- Header 语言切换(同路径跨前缀跳转,预构建毫秒级)
- /en/index + /en/archives/{slug}(postsEn 优先,缺译回退 zh 正文
  + AiTranslatedBadge 虚线标注 TMT);en RSS(标题回退同策略)
- BaseLayout hreflang 双向 + x-default 指向 zh 源
- posts-en 集合(postsSchema.extend locked 润色锁)
- 坑:ts 端点文件误带 --- 开头会以 undefined:2:7 报 Unexpected token

验证: astro build 18 页 exit 0;/en/ 六文章路由+首页+rss 产物断言过;
      zh 侧零回归(11 页不变);hreflang 抽查 dist/index.html

Spec: 2026-10-25_add-blog-i18n
Co-authored-by: GLM-5.3
This commit is contained in:
wait4xandGLM-5.3 committed 2026-10-25 09:20:16 +08:00
1 parent cf42081614
commit c74815b516
11 files changed
+273 -4

No files matched your search

+10
View File
@@ -12,6 +12,7 @@ import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro';
import ThemeScript from '../components/ThemeScript.astro';
import Keyboard from '../components/geek/Keyboard.astro';
import { localeOf, localePath } from '../lib/locale';
import SearchDialog from '../components/SearchDialog.astro';
import BuildInfo from '../components/geek/BuildInfo.astro';
import { ClientRouter } from 'astro:transitions';
@@ -23,6 +24,12 @@ interface Props {
track?: 'reading' | 'tech';
}
const { title, description = 'wait4x 的博客:阅读 · 技术 · 摄影', track = 'tech' } = Astro.props;
// hreflang 双向:zh 无前缀 ↔ /en/;x-default 指向 zh(中文是源语言)
const rawPath = Astro.url.pathname.replace(/\/index\.html$/, '');
const zhPath = localeOf(rawPath) === 'en' ? localePath(rawPath, 'zh') : rawPath;
const enPath = localePath(rawPath, 'en');
const withSlash = (p: string) => (p.endsWith('/') ? p : `${p}/`);
---
<!doctype html>
@@ -37,6 +44,9 @@ const { title, description = 'wait4x 的博客:阅读 · 技术 · 摄影', tr
<title>{title}</title>
<ThemeScript />
<ClientRouter />
<link rel="alternate" hreflang="zh" href={new URL(withSlash(zhPath), Astro.site)} />
<link rel="alternate" hreflang="en" href={new URL(withSlash(enPath), Astro.site)} />
<link rel="alternate" hreflang="x-default" href={new URL(withSlash(zhPath), Astro.site)} />
</head>
<body>
<a class="skip-link" href="#main">跳到正文</a>