Skip to content

feat: i18n 双语系统 + 150 篇文档翻译 + Hero UX 迭代#281

Merged
longsizhuo merged 20 commits intomainfrom
feat/i18n-and-ux-iteration
Apr 15, 2026
Merged

feat: i18n 双语系统 + 150 篇文档翻译 + Hero UX 迭代#281
longsizhuo merged 20 commits intomainfrom
feat/i18n-and-ux-iteration

Conversation

@longsizhuo
Copy link
Copy Markdown
Member

Summary

  • i18n 系统:中英双语切换(cookie-based + IP/Accept-Language 自动检测中间件),静默 fallback 无横幅;docId + translatedFrom frontmatter 约定让 contributors 脚本跳过翻译版
  • 150 篇文档翻译:5 个 translator subagent 并行产出,覆盖 ai / computer-science / jobs / CommunityShare / Leetcode 全目录;Leetcode 改用 kebab-case 英文 slug
  • 首页 Hero 重构:Top Rank 改 12 列 grid(Top 3 贡献者 8 列 + HotDocsPreview 热门文档 4 列);新增 `/api/analytics/top-docs` ISR 300s
  • UX 一致性:禁用 fumadocs 内置 next-themes(修 /settings 主题闪屏 bug);BrandMark 包 Link、Header 锚点改绝对路径(修 /rank 无路径回主页 bug);DocShareButton / DocHistoryPanel / EditOnGithub / ContributorRow / HotDocsTab / SettingsForm 6 处细节统一
  • Bug 修复:/api/docs/history fumadocs 相对路径归一化;Prisma JSON 字段改内存过滤;多处 MDX 语法错误(<digit 当 JSX tag)

Test plan

  • `pnpm build` 本地通过(305 静态页全部生成)
  • `pnpm test` 通过
  • `pnpm lint:images` 通过
  • Vercel Preview 部署验证
  • /settings 主题切换不再闪屏
  • /rank 点击 BrandMark 回主页
  • cookie locale=en 时随机抽查若干文档展示英文版
  • cookie locale=zh 或未设置时展示中文原文

fumadocs 的 page.file.path 返回相对 app/docs/ 的路径(如 ai/xxx/index.mdx)
而不是仓库根路径,导致前端调 /api/docs/history 时报 400。
在 normalizeDocsPath 里补上 app/docs/ 前缀兼容。
- DocShareButton: 去掉 rounded-md / h-11,改为 font-mono uppercase + border + 翻转色 hover
- EditOnGithub: 同步改为相同风格,图标 h-8→h-4
- DocHistoryPanel: 骨架屏和头像去掉 rounded-full,保持方角报纸风
- ContributorRow: Dialog 硬编码 #111111/#F9F9F7 换成 CSS 变量;POWER LEVEL 标签移动端隐藏
- HotDocsTab: 加 sessionStorage 5 分钟缓存,减少重复打后端
- SettingsForm: AI 提供商从原生 select 改为 segment button group;中文 label 去掉 uppercase tracking-widest
问题:
- fumadocs 的 RootProvider 内置 next-themes,与自己的 ThemeProvider
  同时往 <html class> 写 light/dark,导致进入 /settings 时从黑闪白
- Header 右上角 ThemeToggle 切换主题后,/settings 表单选中态不更新
- Settings 保存时的语言设置写不到文档页能读到的位置

修复:
- RootProvider 加 theme={{ enabled: false }},禁用 fumadocs 内置主题
- SettingsForm 用 useTheme() 读当前主题作为初始值
- 监听 currentTheme 变化,同步到表单选中态
- 切主题按钮立即 setTheme(所见即所得,不用等保存)
- handleSave 保存成功后把 language 写 cookie,供 /docs Server Component 读
- BrandMark 外层包 <Link href="/">,点 logo 回首页
- Header 三个锚点从 #features 改为 /#features 等绝对路径,
  从 /rank 或任意子页点导航能跳回主页对应区块

方案 F(最小修复),比引入 Header 全站 layout 或新组件成本更低。
- 新建 middleware.ts:首次访问 /docs 时根据 IP geo / Accept-Language
  判断默认 locale,写入 cookie;默认 zh(社区主体语言)
- [...slug]/page.tsx: 从 cookie 读 locale,尝试加载 slug.en.mdx / slug.zh.mdx
  翻译版,不存在时静默 fallback 到原文(不展示碍眼横幅)
- docs-i18n-design.md: 完整双语化设计方案(frontmatter 约定、
  contributors 脚本改造、翻译流程、术语词表方向)
- 新建 HotDocsPreview.tsx(async Server Component + ISR 300s)
  fetch 自家 /api/analytics/top-docs?window=7d&limit=5,
  后端或数据挂掉时静默降级,不影响首页其他模块
- 新建 app/api/analytics/top-docs/route.ts:从 analyticsEvent 表
  按 eventData.path 聚合 7d 阅读量,供前端 SSR 使用
- Hero.tsx Leaderboard 区域改 12 列 grid:
  贡献者 Top3 (lg:col-span-8) / 热门文档 Top5 (lg:col-span-4)

SEO 价值:首页 HTML 中包含 5 条热门文章标题+链接,Google 爬虫
可直接建立首页 → 文章的链接关系,帮助长尾关键词索引。
翻译版文件 frontmatter 有 translatedFrom 字段,这类文件由
AI 产出、不应污染 contributor 统计。
在 parseDocFrontmatter 新增 isTranslation 字段,主循环里
检测到翻译版则打印跳过日志并 continue,不拉 commit 历史、
不写入 doc_contributors 表。

影响:generate-leaderboard.mjs 无需改动(它从 DB 聚合,
源头已过滤),DB schema 不变。
覆盖不同场景的 MVP 样本,用于验证翻译 agent 的能力边界:

- ai/reinforcement-learning-overview (zh→en) — AI 术语密集
- ai/compute-platforms-handbook (zh→en) — 代码块保留
- computer-science/01-singly-linked-list (en→zh) — 长文 + ASCII 图
- jobs/bq (zh→en) — 文化语境 / STAR / BQ 行话
- CommunityShare/Geek/cloudflare-r2-sharex (zh→en) — 表格 + 中文 URL 编码

每份翻译版:
- 继承原文 docId(共享同一文档的版本)
- frontmatter 有 translatedFrom/translatedAt 标记,contributors 脚本会跳过
- 代码块 / math / URL / MDX 属性名原样保留
- 仅翻译 title、description、正文文本与 MDX 文本内容

MVP 5 篇总 token ~29.4k,平均 5.9k/篇。
translator-community 产出。双向翻译:
- Geek: git101 / picturecdn / swanlab / raspberry-guide / CommonUsedMarkdown
  / Katex×2 (全部 zh→en),leworldmodel (en→zh)
- RAG: context_engineering_intro / embedding / rag (全部 zh→en)
- Amazing-AI-Tools: perplexity-comet (zh→en),
  prompt-repetition-improves-non-reasoning-llms (en→zh)

跳过:3 个 <Cards> 索引页、1 个 MVP 已翻译文件。
所有翻译版 frontmatter 继承原文 docId,带 translatedFrom 标记
供 contributors 脚本跳过统计。
translator-ai-1 产出。全部 zh→en。覆盖:
- Introduction-of-Multi-agents-system (1)
- MoE (2)
- Multi-agents-system-on-Code-Translation (1)
- agents-todo (2)
- ai-math-basics (9) — 含 calculus / linear-algebra / probability 等子目录
- compute-platforms (1,handbook 已在 MVP 提交)
- foundation-models (7) — lifecycle / datasets / training / finetune 等
- generative-todo (1)

所有翻译版 frontmatter 继承原文 docId,带 translatedFrom: zh
标记供 contributors 脚本跳过统计。
translator-cs-jobs 产出:
- computer-science: 双向翻译
  - data-structures (en→zh, 5 篇): index / array / linked-list
  - frontend (zh→en, 2 篇)
  - cpp_backend (zh→en, 8 篇): mempool / threadpool / 编译系列
  - index.mdx (zh→en)
- jobs (zh→en, 5 篇): event-keynote 2 / interview-prep 3
- all-projects (zh→en, 2 篇): ai-town / multimodal-rl

代码块内变量名和 API 原样保留,仅翻译注释。
frontmatter 继承原文 docId,带 translatedFrom 标记。
translator-leetcode 产出。Leetcode 题解目录特殊处理:
- 原文件名含方括号/中文/空格/_translated 后缀,命名风格混乱
- 翻译版统一改为 kebab-case 英文 slug:
  [146]LRU 缓存_translated.md → 146-lru-cache.en.md
  [121]买卖股票的最佳时期_translated.md → 121-best-time-to-buy-and-sell-stock.en.md

双向翻译:
- zh→en: 35 篇(绝大多数原文是中文题解)
- en→zh: 7 篇(2241 ATM / 2270 Split Array / 3138 Anagram /
  46 全排列 / 9021 TUT / 93 IP / Counting Stars / 等英文原文)

代码块(Python/C++/Java)原样保留,仅译注释。LaTeX 公式保留。
frontmatter 继承原文 docId,带 translatedFrom 标记。
跳过:1 个 <Cards> 索引页。
translator-ai-2 产出 + MDX 语法修复。全部 zh→en。

覆盖:
- llm-basics (12 篇): courses / cuda / deep-learning / embeddings / pytorch / transformer
- methodology (1)
- misc-tools (1)
- model-datasets-platforms (1)
- multimodal (10): courses / llava / mllm / qwenvl / ViT / VAE / VQVAE / RQVAE 等
- recommender-systems (7): 王树森推荐系统笔记全集(粗排/精排/重排/冷启动等)

MDX 语法修复:
- 2 个 recommender-systems 文件的裸 <1000 / <30 / <24 / <8 用反引号包裹
  (MDX 把 <digit 当成 JSX tag 开头导致 build 失败)
- app/api/analytics/top-docs/route.ts: Prisma JSON filter startsWith
  写法错误,改为内存筛选

术语决策(新发现):
- 笔记 → post (Xiaohongshu 场景)
- 粗排/精排/重排 → pre-ranking / full ranking / re-ranking
- 保量 → exposure guarantee
- 融合分数 → fused score
- 老汤模型 → aged model (inline explanation)
- wangshusen_recommend_note_improvement.en.mdx: 补 H1
- wangshusen_recommend_note_retrieval.en.mdx: H5 → H4
- leworldmodel.md / 1545-...en.md: 小修
- generated/site-leaderboard.json: 自动更新
Copilot AI review requested due to automatic review settings April 15, 2026 18:14
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 15, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
involutionhell-github-io Ready Ready Preview, Comment Apr 15, 2026 7:24pm
website-preview Ready Ready Preview, Comment Apr 15, 2026 7:24pm

修 CI lint error: @next/next/no-html-link-for-pages
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a cookie-based bilingual (zh/en) docs experience with auto-detection, adds a large batch of translated documentation, and iterates the homepage Hero + ranking UX by adding “Hot Docs” surfacing and analytics support.

Changes:

  • Add locale auto-detection middleware + cookie-based docs locale selection with silent fallback to original docs.
  • Add Top Docs surfacing: Hero “Hot This Week” preview + /rank Hot Docs tab client caching, plus a new Next API route for top-docs with ISR.
  • Update contributor backfill script to skip translated docs based on translatedFrom frontmatter; add/adjust many translated docs and minor UX component styling tweaks.

Reviewed changes

Copilot reviewed 157 out of 158 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
scripts/backfill-contributors.mjs Skip translation variants (frontmatter translatedFrom) when building contributor stats.
middleware.ts New middleware to set default locale cookie based on geo + Accept-Language (docs-only matcher).
app/docs/[...slug]/page.tsx Load translated doc variant based on locale cookie by appending .en/.zh suffix to last slug segment; silent fallback to original.
app/layout.tsx Disable fumadocs built-in theming to avoid next-themes conflicts / flicker.
app/api/docs/history/route.ts Normalize fumadocs relative doc paths to repo-root app/docs/... for history lookups.
app/api/analytics/top-docs/route.ts New ISR endpoint for top docs based on analytics events in Prisma.
app/components/rank/HotDocsTab.tsx Add sessionStorage TTL cache to avoid repeat fetches within a session.
app/components/HotDocsPreview.tsx New Hero-side “Hot This Week” preview panel fetching top docs.
app/components/Hero.tsx Restructure Hero layout into 12-col grid and include HotDocsPreview.
app/components/Header.tsx Convert in-page anchors to absolute /#... to work from nested routes.
app/components/BrandMark.tsx Make BrandMark clickable (Link to /) to fix navigation from nested routes like /rank.
app/components/EditOnGithub.tsx Style/size adjustments for the edit button to match refreshed UX.
app/components/DocShareButton.tsx Style updates for share button to match refreshed UX.
app/components/DocHistoryPanel.tsx Minor UI consistency tweaks (avatar/skeleton rounding, hover container).
app/docs/computer-science/index.en.mdx Add translated doc (note: currently contains mixed EN+ZH content).
app/docs/** (many new/updated .en/.zh docs) Large-scale doc translation additions across multiple directories with docId + translation frontmatter.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread app/api/analytics/top-docs/route.ts Outdated
export async function GET(req: NextRequest) {
const { searchParams } = new URL(req.url);
const window = searchParams.get("window") ?? "7d";
const limit = Math.min(Number(searchParams.get("limit") ?? "5"), 20);
Comment thread app/api/analytics/top-docs/route.ts Outdated
Comment on lines +30 to +46
// 统计各路径 PV(内存过滤 /docs/ 前缀)
const counts: Record<string, number> = {};
for (const row of rows) {
const data = row.eventData as { path?: string; title?: string } | null;
const path = data?.path;
if (path && path.startsWith("/docs/")) {
counts[path] = (counts[path] ?? 0) + 1;
}
}

const top = Object.entries(counts)
.sort((a, b) => b[1] - a[1])
.slice(0, limit)
.map(([path, views]) => ({ path, views }));

return Response.json(top);
}
Comment on lines +9 to +20
async function fetchTopDocs(): Promise<TopDocDto[]> {
const backendUrl = process.env.BACKEND_URL ?? "http://localhost:8081";
try {
const res = await fetch(
`${backendUrl}/analytics/top-docs?window=7d&limit=5`,
{ next: { revalidate: 300 } },
);
if (!res.ok) return [];
const json = await res.json();
// 后端用 ApiResponse<List<TopDocDto>> 包裹,data 字段存实际数据
return json.data ?? json;
} catch {
Comment thread app/docs/[...slug]/page.tsx
Comment thread middleware.ts Outdated
Comment on lines +22 to +28
const acceptLang = req.headers.get("accept-language") ?? "";

// 默认中文;只有明确英文 Accept-Language 且非中国 IP 才切 en
const isExplicitlyEnglish =
!acceptLang.toLowerCase().startsWith("zh") &&
acceptLang.toLowerCase().startsWith("en") &&
country !== "CN";
Comment thread app/docs/computer-science/index.en.mdx Outdated
- 新增 app/components/DispatchNetwork.tsx:48px 高报纸末版"发行网络"风格横条
- 内容:GitHub / Discord / Zotero 三个入口 + Join CTA
- 移除 page.tsx 对 Features 和 Community 的引用
- 组件文件保留未删,后续可能复用;主页不再渲染

用户原因:Top Rank 之后的 Mission Statement 四格口号和 Community 三卡链接
完全 useless,和 Footer 重复劳动。方案三(极简 bar)被选中。
1. top-docs/route.ts: limit 参数加 Number.isFinite 校验防 NaN
2. top-docs/route.ts: 返回结构统一为 ApiResponse{success,data},补齐 title 字段
   (通过 fumadocs source.getPage 回填,同时保留埋点带来的 title)
3. HotDocsPreview.tsx: 改走同源 /api/analytics/top-docs 走 ISR,
   不再直连 BACKEND_URL,消除 '白做缓存' 问题
4. docs/[...slug]/page.tsx: generateMetadata 也按 locale 取页面,
   避免英文页显示中文 title/description
5. middleware.ts: Accept-Language 解析改为按 q 值排序取首选语言,
   正确处理 'fr-CA,fr;q=0.9,en;q=0.8' 这类多语言 header
6. computer-science/index.en.mdx: 删除正文末尾混入的整段中文
之前 150 篇中文 + 150 篇英文翻译全塞进同一个 /search.json,
构建产物 23MB 触发 FALLBACK_BODY_TOO_LARGE 导致 Vercel 部署失败。

方案:
- 新增 lib/search-index.ts 抽出 pageToIndex 和 isEnglishPage 工具
- /search.zh.json — 中文 / 无语言标的原文 + lang="zh" 翻译版(约 13MB)
- /search.en.json — 仅 lang="en" 翻译版,用 Orama 英文分词(约 11MB)
- layout.tsx 读 locale cookie 动态选 API(默认 zh)
- createSearchAPI 的 indexes 必须是 Dynamic<T>=()=>T[]|Promise<T[]>,不是裸 Promise

下一步扩容:文档数量继续增长可按目录再切片(ai/cs/leetcode 各自独立 json)。
Vercel Fluid CPU 用到 85%,原因是前端 /api/analytics 每次埋点都跑 Next function
做 resolveUserId + Prisma 直写。现改为走 next.config 已有的
/analytics/:path* rewrite 直转 Java,Vercel 只做 edge 代理不跑 function。

前端改动:
- lib/analytics.ts: URL /api/analytics → /analytics/events,header x-satoken → satoken
- 删除 app/api/analytics/route.ts(Prisma 直写逻辑搬到 Java)

配套后端改动(另开 PR):
- AnalyticsController 加 POST /events
- 新增 AnalyticsEventIngestService 用 JdbcTemplate INSERT
- SaTokenConfigure 白名单放行 /analytics/events(匿名也收)

AnalyticsEvent 表保留:GA4 走 top-docs,这张表暂无读取方但
未来自建 dashboard / 登录用户精细追踪可直接复用。
@longsizhuo longsizhuo merged commit d0a420d into main Apr 15, 2026
8 checks passed
@longsizhuo longsizhuo deleted the feat/i18n-and-ux-iteration branch April 15, 2026 19:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants