/** 站点 SEO 按 locale 构造(唯一配置源仍在此,喂给 @app/seo-geo)。 */ import type { PageSeoInput, SiteSeoConfig } from "@app/seo-geo"; import { type Locale, t } from "@app/i18n"; const rawSiteUrl = import.meta.env.PUBLIC_SITE_URL?.replace(/\/+$/, ""); const siteUrl = rawSiteUrl && rawSiteUrl.length > 0 ? rawSiteUrl : "http://localhost:4321"; /** 无 locale 的公开路径(basePath);受保护页面不进 llms/sitemap 列表。 */ const PUBLIC_PATHS = ["/"] as const; function pathForLocale(locale: Locale, basePath: string): string { if (basePath === "/") return locale === "zh-CN" ? "/zh-CN" : "/en"; return locale === "zh-CN" ? `/zh-CN${basePath}` : `/en${basePath}`; } export function getSiteSeo(locale: Locale): SiteSeoConfig { const name = t(locale, "meta.brand"); const isZh = locale === "zh-CN"; const description = isZh ? "Astro + tRPC + Drizzle monorepo 应用脚手架,开箱即用的分层基线。" : "Astro + tRPC + Drizzle monorepo application scaffold with a ready vertical baseline."; return { siteUrl, name, description, locale: isZh ? "zh-CN" : "en", defaultImage: "/og-default.png", organization: { name, url: siteUrl }, llms: { summary: isZh ? "这是可复用的应用脚手架模板:Astro SSR + React islands + tRPC + Drizzle/Turso,附带鉴权与用户垂直示例。" : "Reusable application scaffold: Astro SSR + React islands + tRPC + Drizzle/Turso, with auth and a user vertical example.", details: [ isZh ? "分层:types → models → db → dao → trpc → web。业务实体按 user 切片扩展,禁止跨层。" : "Layers: types → models → db → dao → trpc → web. Extend domains via the user slice; never skip layers.", isZh ? "不绑定具体产品业务;works 等领域逻辑应在业务仓库中添加。" : "No product domain baked in; add domains such as works in your product repo.", ], pages: PUBLIC_PATHS.map((basePath) => ({ path: pathForLocale(locale, basePath), title: basePath === "/" ? `${name}${isZh ? "首页" : " home"}` : name, description, })), discouraged: isZh ? ["/dashboard、/settings 等需登录页面", "/auth/* 等认证页", "/api/* 接口响应"] : [ "/dashboard, /settings and other app pages", "/auth/* and auth pages", "/api/* responses", ], }, }; } /** 兼容旧引用:默认中文。 */ export const siteSeo = getSiteSeo("zh-CN"); export function pageSeo(input: PageSeoInput): PageSeoInput { return input; }