Files
pangolin/web/website/src/layouts/Site.astro
T
wangjia f0d574bc44 feat(website/i18n): 6 语(加 日/韩/俄/西)+ 默认英文
- strings.ts: [zh,en] 二元组 → Record<Lang,string> 六语对象(157 条,ja/ko/ru/es
  据英文翻译,无红线词,scan-redline 过);createT 回退英文;PRICES 补 4 语(复用
  USD 价)
- 路由:默认英文移到 /(原 zh);中文挪 /zh/;新增 /ja//ko//ru//es/;删冗余 /en/
- Site.astro:html lang / canonical / hreflang(6+x-default)/ og:locale 全部改
  为按语言查表(HTML_LANG/OG_LOCALE/langPath)
- Header.jsx:langseg 2 项 → 原生 select 6 语下拉(横排会挤);+ .langsel 样式
- astro build 通过,6 页生成;/ 英文、/zh/ 中文、/ja/ 日语标题均已本地化

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-07 13:29:35 +08:00

98 lines
3.7 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
/**
* Site.astro — 官网整页布局(构建期单显一种语言)。
* 两条路由复用:/ → lang="zh"/en/ → lang="en"(铁律 6 单显不并排)。
*
* 样式来源顺序(全部自托管,无第三方 CDN):
* @fontsource/* → 自托管字体 @font-face(替代原型的 Google Fonts CDN
* tokens.gen.css → 由 design/colors_and_type.css 生成(唯一令牌真相源)
* website.css → 逐字迁自 ui_kits/website
* site-extra.css → 仅承载「原型内联 style= → class」的等价工具类
*/
import '@fontsource/sora/500.css';
import '@fontsource/sora/600.css';
import '@fontsource/sora/700.css';
import '@fontsource/manrope/400.css';
import '@fontsource/manrope/500.css';
import '@fontsource/manrope/600.css';
import '@fontsource/manrope/700.css';
import '@fontsource/noto-sans-sc/400.css';
import '@fontsource/noto-sans-sc/500.css';
import '@fontsource/noto-sans-sc/700.css';
import '@fontsource/jetbrains-mono/400.css';
import '@fontsource/jetbrains-mono/500.css';
import '../styles/tokens.gen.css';
import '../styles/website.css';
import '../styles/site-extra.css';
import { createT, type Lang } from '../i18n/strings';
import Header from '../components/Header.jsx';
import AnnouncementBar from '../components/AnnouncementBar.jsx';
import Hero from '../components/Hero.astro';
import Features from '../components/Features.astro';
import WhySignup from '../components/WhySignup.astro';
import Pricing from '../components/Pricing.astro';
import Download from '../components/Download.astro';
import Docs from '../components/Docs.astro';
// Blog 区暂隐藏(占位内容未就绪);有真实文章后恢复 import + <Blog/> + 导航/页脚链接。
import CtaBand from '../components/CtaBand.astro';
import Footer from '../components/Footer.astro';
interface Props { lang: Lang }
const { lang } = Astro.props;
const t = createT(lang);
// 语言 → <html lang> / og:locale / 路由前缀(默认 en = /,其余 /<lang>/)。
const HTML_LANG: Record<Lang, string> = { zh: 'zh-CN', en: 'en', ja: 'ja', ko: 'ko', ru: 'ru', es: 'es' };
const OG_LOCALE: Record<Lang, string> = { zh: 'zh_CN', en: 'en_US', ja: 'ja_JP', ko: 'ko_KR', ru: 'ru_RU', es: 'es_ES' };
const ALL_LANGS: Lang[] = ['en', 'zh', 'ja', 'ko', 'ru', 'es'];
const langPath = (l: Lang) => (l === 'en' ? '/' : `/${l}/`);
const headerT = {
product: t('nav.product'),
pricing: t('nav.pricing'),
download: t('nav.download'),
docs: t('nav.docs'),
blog: t('nav.blog'),
login: t('nav.login'),
get: t('nav.get'),
suBtn: t('su.btn'),
};
---
<!doctype html>
<html lang={HTML_LANG[lang]}>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{t('meta.title')}</title>
<meta name="description" content={t('meta.desc')} />
<meta name="robots" content="index,follow" />
<link rel="canonical" href={new URL(langPath(lang), Astro.site).href} />
{ALL_LANGS.map((l) => (
<link rel="alternate" hreflang={HTML_LANG[l]} href={new URL(langPath(l), Astro.site).href} />
))}
<link rel="alternate" hreflang="x-default" href={new URL('/', Astro.site).href} />
<meta name="theme-color" content="#B96A3D" />
<meta property="og:type" content="website" />
<meta property="og:title" content={t('meta.title')} />
<meta property="og:description" content={t('meta.desc')} />
<meta property="og:locale" content={OG_LOCALE[lang]} />
</head>
<body>
<AnnouncementBar client:idle text={t('ann.txt')} cta={t('ann.cta')} />
<Header client:load lang={lang} t={headerT} />
<a id="top"></a>
<Hero t={t} />
<Features t={t} />
<WhySignup t={t} />
<Pricing t={t} lang={lang} />
<Download t={t} />
<Docs t={t} />
<CtaBand t={t} />
<Footer t={t} />
</body>
</html>