帮助中心 —— Markdown 文章、搜索、国际化
帮助中心是一个小型静态站点,其硬性要求是:每个文章都必须可达——从侧边栏、从搜索、从尚未包含该文章的翻译语言区域均可访问。本文由实际部署过该模块的工程师编写;早期版本是一个营销网站的帮助中心。以下模块将此要求作为已验证的属性:侧边栏显示每个分类的每篇文章,搜索对任何输入形式进行排名,相关链接要么解析成功,要么校验失败。加载器、搜索和标签套件覆盖了所有这些;相关记录位于 references/provenance.md。
何时使用
构建或扩展基于 Next.js App Router 的帮助中心、文档部分或知识库,其文章为仓库中的 Markdown 文件,可带或不带多种语言区域设置。
何时不使用
- 博客。 带有日期、作者、封面图和本地化 slug 的博文属于不同的内容模型;请保留宿主系统的博客模块。
- 从代码生成的文档(OpenAPI、TypeDoc)——使用它们自带的生成器,并链接到输出。
- 基于 CMS 的帮助站点,由编辑人员在运行时发布——索引契约仍然适用,但加载器、静态参数和校验方式不同;请参阅 extensions.md 中的 CMS 说明。
- 恰好是 Markdown 的营销页面。 重用宿主系统的渲染器;该模块的核心是围绕大量文章的导航、搜索和区域设置模型。
架构
content/help/<[locale]/><category>/<slug>.md
│ gray-matter + 规范化
▼
getHelpIndex(locale) ── React cache():每个请求只解析一次 ──┐
categories · articles (sorted) · byKey · tags · byTag · skipped │
│ │
├─► pages: landing / category / tag / article ── metadata, JSON-LD, canonical/hreflang
├─► HelpShell ── toSummary → 导航树(侧边栏 + 抽屉式导航)
│ ── toSearchDoc → HelpSearch(客户端,标记化前缀搜索)
├─► helpSitemapEntries()
└─► validate:help(CI)── 未解析的相关链接、排序平局、无效日期、无效 slug、标签漂移
所有内容都从索引读取。没有其他东西会接触文件系统。
关键事实
- 文件系统就是模式。 文件夹 = 分类,文件名 = slug,frontmatter 仅仅是元数据。frontmatter 中的
slug/category是针对路径进行验证的,永远不会被使用。 - 只有摘要会传递到客户端。
toSummary/toSearchDoc会剥离正文;将HelpArticle传递给客户端组件会在每个页面中传输整个语料库。 - 未翻译的页面是重复的。 它会使用默认语言区域渲染,保持其在导航中的位置,显示提示信息,规范 URL 指向默认语言区域的 URL,并且不会出现在 hreflang 和站点地图中。
- 运行时宽容;CI 不宽容。 错误的引用会被丢弃,缺失的顺序排在最后——而
validate:help会让构建失败,以便作者知晓。 - 搜索设计为客户端搜索。 每篇文章约 1 KB,已经为了导航而加载到页面中,在用户按下第一个按键时就显示结果。全文搜索是扩展功能。
硬性规则
永远不要限制可折叠列表的高度。
max-h-*+overflow-hidden组合会裁剪任何不匹配的内容且不会报告错误。请使用hidden或真正的可折叠原语,以便列表渲染每个链接或全部不渲染。
永远不要仅根据
order排序。 平局会回退到readdir顺序,这在不同的文件系统之间是不同的。请通过标题、然后按 slug 来打破平局。
永远不要在不验证的情况下解析
related。 被删除的引用在页面上不可见;验证器是作者了解某个 slug 已被重命名的唯一途径。
永远不要搜索未修剪的查询,也永远不要只搜索标题。 修剪、分词、忽略变音符、包含标签和标题、按字段排名。
永远不要硬编码 chrome 字符串到具有 i18n 系统的宿主系统中。所有字符串都通过
HelpStrings处理,以便页眉、页脚和提示信息同步切换语言区域。
永远不要按其拼写来键控标签。 大小写、连字符和复数形式在不同作者之间会有所不同。请按
tagSlug分组,以便所有变体最终都落在一个页面上,并让验证器报告差异。
快速开始
- 填写接缝契约并确定分类 ID — adaptation.md。
- 选择内容布局并编写配置 — content-model.md。
- 复制加载器和 frontmatter 解析器;立即在实际内容上运行验证器 — content-loader.md。
- 将字符串和路径连接到宿主系统的 i18n — i18n.md。
- 添加三个页面、SEO 辅助函数和站点地图条目 — routes.md — 然后添加标签页面和链接芯片 — tags.md。
- 构建外壳、导航树和抽屉式导航 — ui.md — 然后构建面包屑导航、卡片、列表和渲染器(使用宿主系统的原语)— ui-content.md。
- 添加搜索及其 hook;将零结果 hook 连接到分析系统 — search.md。
- 运行附带的测试,然后检查行为契约:最长的分类是否完全可达、带尾随空格的查询、带变音符的查询、未翻译页面的规范 URL。
参考目录
| 场景 | 触发关键词 | 参考文档 |
|---|---|---|
| 将此模块集成到现有应用中 | adapt, seam, rename categories, host probe, integrate, dependencies | adaptation.md |
| Frontmatter、文件夹、slug、验证 | frontmatter, slug, category, order, related, updatedAt, featured, validate, CI | content-model.md |
| 读取文件到索引中 | loader, gray-matter, cache, fallback, headings, sort, index, fs | content-loader.md |
| 搜索框与排名 | search, combobox, ranking, tokenize, diacritics, prefix, keyboard, no results | search.md |
| 标签、标签页、芯片、标签云 | tag, tags, tag page, chip, tag slug, spelling, plural, browse by tag | tags.md |
| 语言区域、字符串、日期、规范 URL | i18n, locale, translation, fallback, hreflang, strings, plural, Intl | i18n.md |
| 页面、元数据、JSON-LD、站点地图 | route, page.tsx, generateStaticParams, dynamicParams, notFound, canonical, TechArticle, sitemap | routes.md |
| 外壳、页眉、侧边栏、抽屉式导航 | layout, sidebar, mobile menu, drawer, collapsible, sticky, HelpShell | ui.md |
| 面包屑导航、卡片、列表、渲染器、样式 hook | breadcrumb, category card, article list, react-markdown, data-help, styling | ui-content.md |
| 超越已发布模块的功能 | full-text, Pagefind, TOC, feedback, git dates, MDX, CMS, redirects | extensions.md |
| 模板与早期实现不同的原因 | provenance, audit, ledger, kept deliberately, fixing the earlier implementation | provenance.md |