fix(seo): 修复排行榜链接全 404 + 补段化前 .en/.zh 旧 URL 的 301#348
Conversation
排行榜每条链接都 404:generate-leaderboard 从文件路径手拼 URL,没跟上 2026-05 的 i18n 段化——少 /zh|/en 前缀、保留 .en/.zh 后缀和 /index、leetcode 没拼音化。重写 buildCanonicalDocUrl 产出真实 canonical:按后缀选 locale 前缀、 去 /index、leetcode 按题号指向英文版 /en(中文翻译文件名经 fumadocs 解析后 slug 不可预测,英文 ASCII slug 一定 200),并过滤内容树里已不存在的孤儿 docId 死链。dev server 实测 153/153 链接全部 200(原 0/153)。 GSC 上百条 .en/.zh 后缀旧 URL(最大一类 404):在 next.config redirects 补 301 (必须放这而非 proxy.ts——proxy matcher 排除带点路径,.en/.zh 后缀全带点碰不到 中间件)。含 .en/.zh 后缀剥离、/index 剥离、裸 /computer-science 映射、no-locale /docs 兜底。六类重定向 curl 实测均单跳 301 到正确 canonical。 脚本因导入 lib/leetcode-slug.ts 改用 node 跑 .mts(tsx 在 Node 24 下解析 .ts 具名导出报错),prebuild 同步改。
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Pull request overview
该 PR 针对 2026-05 i18n 段化迁移后遗留的 SEO 问题,修复排行榜(/rank)中大量文档链接 404,并为段化前的 .en/.zh 旧 URL 补齐 301 重定向规则,以减少 GSC 报告的 404。
Changes:
- 重写排行榜生成脚本的文档 URL 生成逻辑:按 locale 段前缀产出 canonical
/zh|/en/docs/...,并对 leetcode 做题号→英文 ASCII slug 归一。 - 在
next.config.mjs增加.en/.zh后缀旧 URL 到新段化 URL 的 301 重定向规则,并补充更老的/computer-science/*映射。 - 调整
prebuild脚本执行方式并更新生成的generated/site-leaderboard.json,补充 i18n 路由文档说明。
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| scripts/generate-leaderboard.mts | 生成排行榜数据时改用 canonical docs URL,并对 leetcode URL 进行特殊处理与死链过滤 |
| package.json | 更新 prebuild 脚本链路以运行新的 leaderboard 生成脚本 |
| next.config.mjs | 增加 .en/.zh 旧 URL 301 规则、补充更老路径映射与兜底重定向 |
| generated/site-leaderboard.json | 更新生成产物,使 contributedDocs 链接指向新 canonical 路径 |
| dev_docs/i18n_url_routing.md | 补充旧 URL 301 与排行榜 canonical 链接策略文档 |
Comments suppressed due to low confidence (1)
scripts/generate-leaderboard.mts:83
filename === "index"时这里强制返回/${DEFAULT_LOCALE},会把index.en.mdx这类英文目录页也指向/zh/docs/...,与“按后缀选 locale 前缀”的逻辑不一致,且会导致排行榜/热榜里英文 leetcode 目录页链接落到中文页。建议改为使用解析出来的locale(即/${locale}/docs/${dirRoot}),保持.en/.zh与 URL 前缀一致。
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| // ============= 兜底:no-locale /docs 一律送默认 locale ============= | ||
| // 段化后 canonical 必带 locale 前缀,任何裸 /docs/... 都是旧链接(外链 / | ||
| // GSC / 热榜 DB 旧路径)。必须排在所有具体规则之后,让特化规则先命中。 | ||
| // 目的地带 /zh 前缀,不会再被本规则匹配,无重定向环。 | ||
| { | ||
| source: "/docs/:path*", | ||
| destination: "/zh/docs/:path*", | ||
| statusCode: 301, | ||
| }, |
CR(Copilot):next.config 的 `/docs/:path*` → `/zh/docs/:path*` 兜底是 301 (永久),抢在 next-intl middleware 之前把所有无前缀 /docs 链接(Hero/Footer 站内链接 + 外链)强制钉到 zh,英文用户再也协商不到 /en 且被浏览器/Google 缓存。删掉,无前缀路径交回 next-intl 按 Accept-Language/cookie 协商(307 单跳)。 dotted 中文 leetcode 旧 URL(46.全排列 / 1234. 替换… 这类带点的)此前被 proxy.ts matcher 的 .*\..* 排除、进不了 slug-map 301,只能硬 404。三处改动: - matcher 给 leetcode 开口:(?!.*[Ll]eetcode).*\..*,带点的 leetcode 路径放进中间件 - slug-map value 改成完整 canonical URL:按题号优先指向英文页 /en(.en.md 一定 200),无英文兄弟才退回 zh 拼音;并加 byNumber 兜住「中文文件已改名成英文、 旧中文 URL 仍在 GSC」的题(46.全排列→46-permutations) - 题号兜底仅对含中文的 slug 生效,避免英文 canonical slug 自我重定向成环 / 把合法 /zh 英文命名页打到 /en leetcode canonical 逻辑抽到 lib/leetcode-slug.ts(buildLeetcodeAsciiSlugByNumber / leetcodeCanonicalUrl),proxy.ts、slug-map 脚本、排行榜生成共用同一真源。 dev 实测:46.全排列 / [213]打家劫舍 / 93复原Ip地址 / 1234.替换 / 2894.分类 全部 301→200;/en/46-permutations、/zh/46-permutations 等 canonical 页不被劫持无环; 排行榜仍 153/153;sitemap/robots/普通路由无回归。 Co-authored-by: copilot-pull-request-reviewer[bot] <198982749+Copilot@users.noreply.github.com>
回应 CR + 补 dotted 中文 leetcode(commit 0fbd243)CR 修复(@copilot 指出的 no-locale 兜底 301)确认是真问题,已删。那条 顺带补完 dotted 中文 leetcode 旧 URL
leetcode canonical 逻辑抽到 验证
已知非本 PR 范围
|
eslint no-control-regex 把 /[^\x00-\x7f]/ 里的 \x00 当非法控制字符报错,CI lint 挂掉。改用等价的 /[^ -~]/(可打印 ASCII 取反),对文件名/slug 语义一致,slug-map 重新生成无 diff。
背景
Google Search Console 报告 212 条 404 + 排行榜(/rank)所有文档链接点进去全是 404。两者根因都是 2026-05 i18n 段化迁移 后 URL 方案变了(locale 从文件后缀
.en/.zh变成 URL 段前缀/en、/zh),但有两处没跟上。改动
1. 排行榜链接全 404(线上 bug)
scripts/generate-leaderboard.{mjs→mts}从后端 docId 反查.source文件路径手拼 URL,产出的是/docs/learn/cs/.../01-static-array.zh这种——缺/zh|/en前缀、带.zh/.en后缀、带/index、leetcode 没拼音化,每条都 404。buildCanonicalDocUrl:按后缀选 locale 前缀、去/index、leetcode 按题号指向英文版/en/docs/.../<ascii-slug>(中文翻译文件名经 fumadocs i18n 解析后真实 slug 不可预测,英文 ASCII slug 一定能解析且只在/en渲染)。/docs/<cuid>)。2. GSC 上百条
.en/.zh后缀旧 URL(最大一类 404)在
next.config.mjsredirects()补 301。必须放 next.config 而非 proxy.ts——proxy 的 matcher 排除了.*\..*,.en/.zh后缀全带点,根本不进中间件;next.config redirects 跑在路由层不受影响。.en/.zh后缀剥离 + locale 前缀、/index剥离、裸/computer-science/*映射、no-locale/docs/*兜底。3. 脚本运行方式
.mjs → .mts(因导入lib/leetcode-slug.ts),prebuild 改用node跑(tsx 在 Node 24 下解析.ts具名导出报错;与兄弟脚本generate-leetcode-slug-map.mts一致)。验证
pnpm build通过(exit 0),路由表 + sitemap 正常。dev_docs/i18n_url_routing.md新增的「段化前旧 URL 的 301」「排行榜链接 canonical」两节。已知残留(非本 PR 范围)
/u/{id}与代码块假路径的 404 是正确行为,无需修。