From 1e99a3139725993ec565d1074fafbc7f50141336 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Wed, 6 May 2026 15:38:23 +0000
Subject: [PATCH] =?UTF-8?q?chore(seo):=20redirects=20=E5=8D=95=E8=B7=B3=20?=
=?UTF-8?q?+=20root=20metadata=20hreflang=EF=BC=8Ci18n=20=E6=AE=B5?=
=?UTF-8?q?=E5=8C=96=E6=94=B6=E5=B0=BE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
i18n PR (#330) 之后存在两个 SEO 残留问题,本 PR 一并修:
1. next.config.mjs redirects 全部 destination 加 /zh/ 前缀
现状:destination 都是 /docs/...(不带 locale),用户访问老 URL 流程:
/docs/CommunityShare/RAG/rag
→ 301 /docs/learn/ai/foundation-models/rag/rag
→ 308 /zh/docs/learn/ai/foundation-models/rag/rag (next-intl middleware)
两跳 redirect 对 SEO PageRank 不友好(Google 推荐单跳)。把 destination
直接指向带 /zh/ 前缀的 canonical URL,搜索引擎单跳到位。
改 46 条规则的 destination,source 保持不带 locale(匹配老外链)。
英语用户由站内 切到 /en/...,cookie 同步偏好。
2. root layout metadata hreflang
alternates.canonical 之前写死 /,i18n 段化后根路径会被 middleware
redirect,搜索引擎拿到的 canonical 是个 redirect 目标,不准。
改为:
- canonical: /zh(默认 locale 首页)
- languages: zh-CN / en-US / x-default 三向声明
注:每个具体 [locale]/page 的 generateMetadata 会覆盖 root metadata 的
languages,给到该 page 各 locale 的具体 URL。这里 root 只作 fallback。
---
app/layout.tsx | 12 +++++++-
next.config.mjs | 75 +++++++++++++++++++++++++++----------------------
2 files changed, 52 insertions(+), 35 deletions(-)
diff --git a/app/layout.tsx b/app/layout.tsx
index 3dfe554..b8b3dfe 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -53,8 +53,18 @@ export const metadata: Metadata = {
creator: "longsizhuo",
publisher: "Involution Hell",
category: "Technology",
+ // alternates 是 fallback,被 [locale] 段下的 generateMetadata 覆盖。
+ // 默认 canonical 指 /zh(默认 locale 首页),不指 / 因为根路径会被
+ // next-intl middleware 308 redirect,搜索引擎索引到 /zh 更直接。
+ // languages 同时声明 hreflang,让 root metadata 应用到不在 [locale]
+ // 下的路径(如 /sitemap.xml 详情页 fallback 时)也能正确给出语言关系。
alternates: {
- canonical: "/",
+ canonical: "/zh",
+ languages: {
+ "zh-CN": "/zh",
+ "en-US": "/en",
+ "x-default": "/zh",
+ },
},
robots: {
index: true,
diff --git a/next.config.mjs b/next.config.mjs
index 79863fe..c7d8e31 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -37,124 +37,131 @@ const config = {
async redirects() {
// Option C IA 大重组:按读者意图分 learn / career / community / projects 四大顶层区。
// 顺序敏感——Next.js 首匹配命中,特殊文件级 + cpp_backend 老名字必须排在 wildcard 前。
+ //
+ // 2026-05 i18n URL 段化(PR #330):所有 destination 都带 /zh/ 前缀
+ // (默认 locale)。不带前缀的话会先 301 到 /docs/...,next-intl
+ // middleware 再 308 到 /zh/docs/...,对 SEO 是两跳 redirect。直接
+ // destination 写带前缀的 canonical URL 让搜索引擎和用户都单跳到位。
+ // 英语用户由站内 切到 /en/docs/...,cookie 同步偏好。
+ // source 保持不带 locale 形式(匹配老 URL)。
return [
// ============= 特殊路径(必须在 wildcard 之前) =============
// CommunityShare/RAG → learn/ai/foundation-models/rag (RAG 文件归 ai 主题)
{
source: "/docs/CommunityShare/RAG/rag",
- destination: "/docs/learn/ai/foundation-models/rag/rag",
+ destination: "/zh/docs/learn/ai/foundation-models/rag/rag",
statusCode: 301,
},
{
source: "/docs/CommunityShare/RAG/embedding",
- destination: "/docs/learn/ai/foundation-models/rag/embedding",
+ destination: "/zh/docs/learn/ai/foundation-models/rag/embedding",
statusCode: 301,
},
{
// 文件名顺手规范化:context_engineering_intro → context-engineering-intro
source: "/docs/CommunityShare/RAG/context_engineering_intro",
destination:
- "/docs/learn/ai/foundation-models/rag/context-engineering-intro",
+ "/zh/docs/learn/ai/foundation-models/rag/context-engineering-intro",
statusCode: 301,
},
// CommunityShare/Geek/leworldmodel → community/papers(paper summary 归社区论文)
{
source: "/docs/CommunityShare/Geek/leworldmodel",
- destination: "/docs/community/papers/leworldmodel",
+ destination: "/zh/docs/community/papers/leworldmodel",
statusCode: 301,
},
// CommunityShare/Amazing-AI-Tools 下两篇分家:tool review 归 tools,paper 归 papers
{
source: "/docs/CommunityShare/Amazing-AI-Tools/perplexity-comet",
- destination: "/docs/community/tools/perplexity-comet",
+ destination: "/zh/docs/community/tools/perplexity-comet",
statusCode: 301,
},
{
source:
- "/docs/CommunityShare/Amazing-AI-Tools/prompt-repetition-improves-non-reasoning-llms",
+ "/zh/docs/CommunityShare/Amazing-AI-Tools/prompt-repetition-improves-non-reasoning-llms",
destination:
- "/docs/community/papers/prompt-repetition-improves-non-reasoning-llms",
+ "/zh/docs/community/papers/prompt-repetition-improves-non-reasoning-llms",
statusCode: 301,
},
// PPO 强化学习主题 → learn/ai/reinforcement-learning
{
source:
- "/docs/CommunityShare/Personal-Study-Notes/Reinforcement-Learning/ppo",
- destination: "/docs/learn/ai/reinforcement-learning/ppo",
+ "/zh/docs/CommunityShare/Personal-Study-Notes/Reinforcement-Learning/ppo",
+ destination: "/zh/docs/learn/ai/reinforcement-learning/ppo",
statusCode: 301,
},
// swanlab 之前 test run 已移到 ai/misc-tools/(main commit d6d0a3d),现改到 community/tools
{
source: "/docs/ai/misc-tools/swanlab",
- destination: "/docs/community/tools/swanlab",
+ destination: "/zh/docs/community/tools/swanlab",
statusCode: 301,
},
// cpp_backend 老命名(下划线 / 大驼峰)→ learn/cs/cpp-backend/ (kebab-case)
{
source: "/docs/computer-science/cpp_backend/mempool_simple",
- destination: "/docs/learn/cs/cpp-backend/mempool-simple",
+ destination: "/zh/docs/learn/cs/cpp-backend/mempool-simple",
statusCode: 301,
},
{
source:
- "/docs/computer-science/cpp_backend/Handwritten_pool_components/1_Handwritten_threadpool",
+ "/zh/docs/computer-science/cpp_backend/Handwritten_pool_components/1_Handwritten_threadpool",
destination:
- "/docs/learn/cs/cpp-backend/handwritten-pool-components/1-handwritten-threadpool",
+ "/zh/docs/learn/cs/cpp-backend/handwritten-pool-components/1-handwritten-threadpool",
statusCode: 301,
},
{
source:
- "/docs/computer-science/cpp_backend/Handwritten_pool_components/2_Handwritten_mempool1",
+ "/zh/docs/computer-science/cpp_backend/Handwritten_pool_components/2_Handwritten_mempool1",
destination:
- "/docs/learn/cs/cpp-backend/handwritten-pool-components/2-handwritten-mempool1",
+ "/zh/docs/learn/cs/cpp-backend/handwritten-pool-components/2-handwritten-mempool1",
statusCode: 301,
},
{
source: "/docs/computer-science/cpp_backend/easy_compile/1_cpp_libs",
- destination: "/docs/learn/cs/cpp-backend/easy-compile/1-cpp-libs",
+ destination: "/zh/docs/learn/cs/cpp-backend/easy-compile/1-cpp-libs",
statusCode: 301,
},
{
source: "/docs/computer-science/cpp_backend/easy_compile/2_base_gcc",
- destination: "/docs/learn/cs/cpp-backend/easy-compile/2-base-gcc",
+ destination: "/zh/docs/learn/cs/cpp-backend/easy-compile/2-base-gcc",
statusCode: 301,
},
{
source: "/docs/computer-science/cpp_backend/easy_compile/3_Make",
- destination: "/docs/learn/cs/cpp-backend/easy-compile/3-make",
+ destination: "/zh/docs/learn/cs/cpp-backend/easy-compile/3-make",
statusCode: 301,
},
{
source: "/docs/computer-science/cpp_backend/easy_compile/4_CMake",
- destination: "/docs/learn/cs/cpp-backend/easy-compile/4-cmake",
+ destination: "/zh/docs/learn/cs/cpp-backend/easy-compile/4-cmake",
statusCode: 301,
},
{
source: "/docs/computer-science/cpp_backend/easy_compile/5_vcpkg",
- destination: "/docs/learn/cs/cpp-backend/easy-compile/5-vcpkg",
+ destination: "/zh/docs/learn/cs/cpp-backend/easy-compile/5-vcpkg",
statusCode: 301,
},
// all-projects/ai-town → projects/ai-town (顶层化)
{
source: "/docs/all-projects/ai-town",
- destination: "/docs/projects/ai-town",
+ destination: "/zh/docs/projects/ai-town",
statusCode: 301,
},
// all-projects 裸路径本身也要兜底,防止指 /docs/all-projects 直接 404
{
source: "/docs/all-projects",
- destination: "/docs/projects",
+ destination: "/zh/docs/projects",
statusCode: 301,
},
// CommunityShare / Amazing-AI-Tools index 顶层
{
source: "/docs/CommunityShare",
- destination: "/docs/community",
+ destination: "/zh/docs/community",
statusCode: 301,
},
{
source: "/docs/CommunityShare/Amazing-AI-Tools",
- destination: "/docs/community/tools",
+ destination: "/zh/docs/community/tools",
statusCode: 301,
},
@@ -162,29 +169,29 @@ const config = {
// 学科主目录:ai → learn/ai, computer-science → learn/cs
{
source: "/docs/ai/:path*",
- destination: "/docs/learn/ai/:path*",
+ destination: "/zh/docs/learn/ai/:path*",
statusCode: 301,
},
{
source: "/docs/computer-science/:path*",
- destination: "/docs/learn/cs/:path*",
+ destination: "/zh/docs/learn/cs/:path*",
statusCode: 301,
},
// 求职场景 jobs/{interview-prep,event-keynote} → career/{interview-prep,events}
{
source: "/docs/jobs/interview-prep/:path*",
- destination: "/docs/career/interview-prep/:path*",
+ destination: "/zh/docs/career/interview-prep/:path*",
statusCode: 301,
},
{
source: "/docs/jobs/event-keynote/:path*",
- destination: "/docs/career/events/:path*",
+ destination: "/zh/docs/career/events/:path*",
statusCode: 301,
},
// 项目 all-projects → projects
{
source: "/docs/all-projects/:path*",
- destination: "/docs/projects/:path*",
+ destination: "/zh/docs/projects/:path*",
statusCode: 301,
},
// CommunityShare 分家:其他按主题归 community/*
@@ -193,27 +200,27 @@ const config = {
// 生成的 slug 映射做字面匹配,单跳 301 到正确拼音 URL。
{
source: "/docs/CommunityShare/Language/:path*",
- destination: "/docs/community/language/:path*",
+ destination: "/zh/docs/community/language/:path*",
statusCode: 301,
},
{
source: "/docs/CommunityShare/Life/:path*",
- destination: "/docs/community/life/:path*",
+ destination: "/zh/docs/community/life/:path*",
statusCode: 301,
},
{
source: "/docs/CommunityShare/MentalHealth/:path*",
- destination: "/docs/community/mental-health/:path*",
+ destination: "/zh/docs/community/mental-health/:path*",
statusCode: 301,
},
{
source: "/docs/CommunityShare/Geek/:path*",
- destination: "/docs/community/dev-tips/:path*",
+ destination: "/zh/docs/community/dev-tips/:path*",
statusCode: 301,
},
{
source: "/docs/CommunityShare/Amazing-AI-Tools/:path*",
- destination: "/docs/community/tools/:path*",
+ destination: "/zh/docs/community/tools/:path*",
statusCode: 301,
},
];