@@ -144,8 +195,35 @@ export async function generateMetadata({ params }: Param): Promise {
notFound();
}
+ // 规范化 slug → canonical 路径。用户访问 /docs/ai/rl(原文)或 /docs/ai/rl.en(翻译版)
+ // 都统一指向原始 slug,避免两个 URL 竞争同一份内容的 PageRank。
+ const slugPath = (slug ?? []).join("/");
+ const canonical = slugPath ? `/docs/${slugPath}` : "/docs";
+
+ // hreflang:告诉搜索引擎该文档有哪些语言版本。
+ // 翻译版文件命名是 `.en.mdx` / `.zh.mdx`,URL 靠 cookie 切换,
+ // 两种语言走同一 canonical URL,因此 hreflang 都指向自己。
+ const languages: Record = {
+ "zh-CN": canonical,
+ "en-US": canonical,
+ "x-default": canonical,
+ };
+
return {
title: page.data.title,
description: page.data.description,
+ alternates: { canonical, languages },
+ openGraph: {
+ type: "article",
+ title: page.data.title,
+ description: page.data.description,
+ url: canonical,
+ locale: locale === "en" ? "en_US" : "zh_CN",
+ },
+ twitter: {
+ card: "summary_large_image",
+ title: page.data.title,
+ description: page.data.description,
+ },
};
}
diff --git a/app/layout.tsx b/app/layout.tsx
index 98786f21..203029d8 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -67,15 +67,16 @@ export const metadata: Metadata = {
canonical: "/",
},
robots: {
+ // nocache 会抑制 rich snippet / cached page,对 SEO 反而不利;移除
index: true,
follow: true,
- nocache: true,
googleBot: {
index: true,
follow: true,
- "max-image-preview": "standard",
- "max-snippet": 160,
- "max-video-preview": 0,
+ // 允许摘要长度,不要限制过短(160 char → -1 让 Google 自行判断)
+ "max-image-preview": "large",
+ "max-snippet": -1,
+ "max-video-preview": -1,
},
},
formatDetection: {
@@ -187,6 +188,33 @@ export default async function RootLayout({
type="image/png"
fetchPriority="high"
/>
+ {/*
+ WebSite + SearchAction 结构化数据:Google 搜索结果下方可能直接显示站内搜索框
+ (Sitelinks Search Box)。target 指向我们的搜索页带 query 参数;
+ search-input 占位符必须叫 "search_term_string"(Google 硬约定)。
+ */}
+
{/* 结构化数据:英文主名 + 中文 alternateName */}