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:
1 parent
cf42081614
commit
c74815b516
11 files changed
+273
-4
No files matched your search
@@ -0,0 +1,28 @@
|
||||
---
|
||||
// AI 译文标注(用户拍板:文学内容机器翻译须明示)
|
||||
import { t } from '../i18n/ui';
|
||||
interface Props { locale?: 'zh' | 'en' }
|
||||
const { locale = 'en' } = Astro.props;
|
||||
---
|
||||
|
||||
<p class="ai-note" role="note">{t('ai_translated_note', locale)}<span class="ai-tag">TMT</span></p>
|
||||
|
||||
<style>
|
||||
.ai-note {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 12px;
|
||||
color: var(--ink-3);
|
||||
border: 1px dashed var(--line);
|
||||
border-radius: 6px;
|
||||
padding: var(--space-2) var(--space-3);
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
.ai-tag {
|
||||
margin-left: var(--space-2);
|
||||
color: var(--ink-4);
|
||||
font-size: 10px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 3px;
|
||||
padding: 0 4px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,4 +1,6 @@
|
||||
---
|
||||
import { localeOf, localePath } from '../lib/locale';
|
||||
|
||||
// 顶部导航:≤4 项纪律(防 higan 十项复发);当前页 aria-current
|
||||
const nav = [
|
||||
{ href: '/archives/', label: '文章' },
|
||||
@@ -8,6 +10,11 @@ const nav = [
|
||||
const path = Astro.url.pathname;
|
||||
const isCurrent = (href: string) =>
|
||||
path === href || path === href.replace(/\/$/, '') || path.startsWith(href);
|
||||
|
||||
// 语言切换:同路径跨前缀跳转(zh 无前缀 ↔ /en/),预构建双语毫秒级
|
||||
const curLocale = localeOf(path);
|
||||
const switchHref = localePath(path, curLocale === 'zh' ? 'en' : 'zh');
|
||||
const switchLabel = curLocale === 'zh' ? 'EN' : '中';
|
||||
---
|
||||
|
||||
<header class="site-header safe-area">
|
||||
@@ -35,6 +42,10 @@ const isCurrent = (href: string) =>
|
||||
</svg>
|
||||
</button>
|
||||
</li>
|
||||
<!-- 语言切换:与主题钮同为触屏友好的 mono 小钮 -->
|
||||
<li>
|
||||
<a href={switchHref} class="tap nav-link nav-lang" hreflang={curLocale === 'zh' ? 'en' : 'zh'}>{switchLabel}</a>
|
||||
</li>
|
||||
<!-- 触屏等价物:主题可视切换(与键盘 t / ThemeScript 同一存储 key) -->
|
||||
<li>
|
||||
<button class="tap nav-link" type="button" data-theme-toggle aria-label="切换明暗">
|
||||
@@ -97,6 +108,7 @@ const isCurrent = (href: string) =>
|
||||
}
|
||||
.nav-link:hover { color: var(--ink-1); }
|
||||
.nav-link[aria-current='page'] { color: var(--accent); }
|
||||
.nav-lang { font-family: var(--font-mono); font-size: 13px; }
|
||||
@media (max-width: 560px) {
|
||||
.nav-link { font-size: 14px; padding-inline: var(--space-1); }
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user