mirror of
https://github.com/jacobinwwey/obsidian-NotEMD.git
synced 2026-07-22 05:48:27 +00:00
fix(docs-site): gate language scope and geo output
This commit is contained in:
parent
dc5302d2ef
commit
a2ecbcd866
17 changed files with 591 additions and 46 deletions
3
.github/workflows/deploy-docs.yml
vendored
3
.github/workflows/deploy-docs.yml
vendored
|
|
@ -41,6 +41,9 @@ jobs:
|
|||
env:
|
||||
NODE_ENV: production
|
||||
|
||||
- name: Audit website build
|
||||
run: npm run audit:build
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
# NotEMD GEO Roadmap
|
||||
|
||||
**Created:** 2026-06-12
|
||||
**Updated:** 2026-06-17
|
||||
**Status:** Phase 1-3 shipped; Phase 4 GitHub Pages reliability, language truth, and AI retrieval entry point complete in this batch.
|
||||
**Updated:** 2026-06-19
|
||||
**Status:** Phase 1-3 shipped; Phase 4 GitHub Pages reliability, language truth, AI retrieval entry point, and build-output gate complete in this batch.
|
||||
**Scope:** Post-deployment GEO optimization for AI search engine visibility, documentation truth, and GitHub Pages reliability.
|
||||
|
||||
---
|
||||
|
||||
## Current Truth Snapshot
|
||||
|
||||
The website is a Docusaurus site under `website/`, deployed by `.github/workflows/deploy-docs.yml` on pushes to `main` that touch `website/**` or the workflow file. The workflow already uses `npm ci` and `npm run build` from the `website` working directory.
|
||||
The website is a Docusaurus site under `website/`, deployed by `.github/workflows/deploy-docs.yml` on pushes to `main` that touch `website/**` or the workflow file. The workflow uses `npm ci`, `npm run build`, and `npm run audit:build` from the `website` working directory before uploading the Pages artifact.
|
||||
|
||||
The current public language surface is deliberately narrow:
|
||||
|
||||
|
|
@ -20,6 +20,28 @@ The current public language surface is deliberately narrow:
|
|||
| Other locales | Not published | Do not add empty locale folders to `i18n.locales` |
|
||||
| Plugin UI i18n | Separate runtime feature | Do not confuse runtime UI language support with website documentation coverage |
|
||||
|
||||
## 2026-06-19 Pages / Language Gate Follow-Up
|
||||
|
||||
### Findings
|
||||
|
||||
| Finding | Evidence | Impact |
|
||||
|---|---|---|
|
||||
| zh-CN homepage still linked untranslated fallback docs | Built `website/build/zh-CN/index.html` contained `/obsidian-NotEMD/zh-CN/docs/intro`, `/zh-CN/docs/getting-started/quick-start`, `/zh-CN/docs/providers/overview`, and `/zh-CN/docs/pillar-ai-knowledge` | Users could still enter English fallback pages through the Chinese homepage even though crawlers were protected by noindex/sitemap filtering |
|
||||
| Navbar/footer docs link localized into fallback docs | Docusaurus `docSidebar` / `to` links produced zh-CN docs routes for untranslated pages | UI language truth diverged from crawler truth |
|
||||
| Published zh-CN scope was duplicated | FAQ exception lived separately in sitemap filtering, DocItem noindex logic, and homepage links | Future translation promotion could easily update one surface and miss another |
|
||||
| Deploy workflow lacked a build-output audit | `.github/workflows/deploy-docs.yml` ran `npm run build` only | GitHub Pages could publish route drift that source review did not catch |
|
||||
|
||||
### Fixes Landed In This Follow-Up
|
||||
|
||||
| Fix | Files | Result |
|
||||
|---|---|---|
|
||||
| Add shared published-language scope | `website/src/lib/publishedLanguageScope.js` | zh-CN published ids/paths have one owner |
|
||||
| Route zh-CN homepage untranslated docs to canonical English URLs | `website/src/pages/index.js` | zh-CN homepage keeps FAQ localized and sends other critical doc links to English canonical pages |
|
||||
| Keep sitemap and noindex on the same rule | `website/docusaurus.config.js`, `website/src/theme/DocItem/Layout/index.js` | Sitemap filtering and fallback robots metadata consume the same published scope |
|
||||
| Add built-output audit | `website/scripts/audit-build.cjs`, `website/package.json` | `npm run audit:build` checks real generated pages, sitemaps, and `llms.txt` |
|
||||
| Gate GitHub Pages deploy | `.github/workflows/deploy-docs.yml` | Pages upload is blocked unless build output satisfies the language/GEO contract |
|
||||
| Record maintainer workflow | `docs/maintainer/github-pages-language-geo-workflow.*` | Future locale promotion has a concrete owner and command path |
|
||||
|
||||
## 2026-06-17 Pages / GEO Audit
|
||||
|
||||
### Findings
|
||||
|
|
@ -113,8 +135,10 @@ Phase 4 exists because the previous roadmap optimized schema/content while leavi
|
|||
| Root broken markdown links removed | Fixed | No `/obsidian-NotEMD/` root broken-link warning in `npm run build` |
|
||||
| Published language policy matches code | Fixed | README + roadmap state `en` plus partial `zh-CN` |
|
||||
| Untranslated zh-CN fallback docs fenced | Fixed | zh-CN sitemap keeps only root + FAQ; fallback docs emit `noindex,follow` |
|
||||
| zh-CN homepage does not route users into fallback docs | Fixed | `npm run audit:build` rejects `/zh-CN/docs/intro`, quick-start, provider overview, and pillar fallback links |
|
||||
| GitHub Pages deploy has built-output language/GEO gate | Fixed | `.github/workflows/deploy-docs.yml` runs `npm run audit:build` after build |
|
||||
| AI retrieval entry point exists | Fixed | `website/build/llms.txt` |
|
||||
| Test files excluded from this commit | Required | Commit file list must not include `src/tests/**` |
|
||||
| Generated outputs excluded from source commit | Required | `website/build` and Slidev export artifacts remain ignored/uncommitted |
|
||||
|
||||
## Better GEO Strategy
|
||||
|
||||
|
|
@ -125,7 +149,8 @@ The next effective GEO strategy is truth-first and route-first, not locale-volum
|
|||
3. **Use `llms.txt` as the compact answer-engine map.** It should point to canonical docs, explain language scope, and discourage answers based on generated exports or stale issue text.
|
||||
4. **Keep schema accurate before making it richer.** Person, Organization, WebPage, TechArticle, FAQPage, citations, and concepts are useful only if URLs resolve and claims match source pages.
|
||||
5. **Consolidate thin provider pages before adding more.** Stub-like provider pages dilute crawl quality; expand them with real setup, request semantics, and troubleshooting or merge them into stronger overview pages.
|
||||
6. **Measure after deploy, then tune.** Re-run Google indexing checks and AI visibility tests only after the fixed Pages build is deployed.
|
||||
6. **Gate the built output, not only the source.** `npm run audit:build` must pass before Pages upload because Docusaurus locale behavior can change the final route graph.
|
||||
7. **Measure after deploy, then tune.** Re-run Google indexing checks and AI visibility tests only after the fixed Pages build is deployed.
|
||||
|
||||
### Critical Path Translation Plan
|
||||
|
||||
|
|
@ -151,19 +176,21 @@ Until this set is translated, zh-CN should stay explicitly partial.
|
|||
| GitHub Pages root route missing | Fixed | Root homepage now exists for default and zh-CN locale |
|
||||
| Docusaurus v4 markdown config breakage | Fixed | Broken markdown hook moved under `markdown.hooks` |
|
||||
| Untranslated zh-CN fallback docs dilute language quality | Controlled | Exclude fallback docs from sitemap and emit `noindex,follow` until translated |
|
||||
| zh-CN UI links users into fallback docs | Controlled | Homepage, navbar, and footer docs entry points now route untranslated critical docs to canonical English URLs; audit blocks regressions |
|
||||
| Thin provider pages dilute crawl budget | Open | Expand or consolidate provider pages before adding more |
|
||||
| Mermaid bundle size on non-diagram pages | Accepted | Docusaurus/theme tradeoff; not the current GEO blocker |
|
||||
| Hand-maintained `llms.txt` can drift | Open | Update it when canonical docs, language status, or provider docs change |
|
||||
| Test or generated export files accidentally entering main | Controlled in this batch | Stage production/docs files only; leave tests and generated exports out of the commit |
|
||||
| Hand-maintained `llms.txt` can drift | Controlled | `npm run audit:build` checks the language-scope markers; update content when canonical docs, language status, or provider docs change |
|
||||
| Test or generated export files accidentally entering main | Controlled in this batch | Stage production/docs/test contract files only; leave generated exports out of the commit |
|
||||
|
||||
## Success Metrics
|
||||
|
||||
| Timeframe | Metric | Target | Current |
|
||||
|-----------|--------|--------|---------|
|
||||
| Immediate | Docusaurus build | No root broken-link or deprecated markdown-hook warnings | Verified 2026-06-17 |
|
||||
| Immediate | Root pages | `index.html` and `zh-CN/index.html` exist | Verified 2026-06-17 |
|
||||
| Immediate | zh-CN fallback fence | Sitemap excludes untranslated docs; fallback docs noindex | Verified 2026-06-17 |
|
||||
| Immediate | AI retrieval map | `llms.txt` exists in build output | Verified 2026-06-17 |
|
||||
| Immediate | Docusaurus build | No root broken-link or deprecated markdown-hook warnings | Verified 2026-06-19 |
|
||||
| Immediate | Root pages | `index.html` and `zh-CN/index.html` exist | Verified 2026-06-19 |
|
||||
| Immediate | zh-CN fallback fence | Sitemap excludes untranslated docs; fallback docs noindex | Verified 2026-06-19 by `npm run audit:build` |
|
||||
| Immediate | zh-CN homepage language routes | Untranslated critical docs route to canonical English URLs; FAQ stays localized | Verified 2026-06-19 by `npm run audit:build` |
|
||||
| Immediate | AI retrieval map | `llms.txt` exists in build output and records language scope | Verified 2026-06-19 by `npm run audit:build` |
|
||||
| 2 weeks post-deploy | Docusaurus site indexed | Google `site:` returns results | Unknown |
|
||||
| 2 weeks post-deploy | First AI citation | 1+ ChatGPT/Perplexity/GLM mention | 0 baseline |
|
||||
| 1 month post-deploy | Citation rate | 5-10% for core keywords | 0 baseline |
|
||||
|
|
@ -189,7 +216,7 @@ Until this set is translated, zh-CN should stay explicitly partial.
|
|||
|
||||
| Date | Action |
|
||||
|------|--------|
|
||||
| 2026-06-17 | Verify fixed website build, root pages, zh-CN root, and `llms.txt` output |
|
||||
| 2026-06-19 | Verify fixed website build, root pages, zh-CN root, canonical English fallback routing, zh-CN sitemap/noindex fence, and `llms.txt` output through `npm run audit:build` |
|
||||
| After deploy | Submit sitemap and inspect canonical root/locale root in Search Console |
|
||||
| 2026-07-01 | Retest GLM baseline in EN + ZH after the fixed Pages deployment |
|
||||
| 2026-08-01 | Second retest. If citations remain 0, audit indexed pages and provider-page thinness |
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ This directory contains repository-level documentation for maintainers and contr
|
|||
## User And Maintainer Entry Points
|
||||
|
||||
- [Language Hub](./i18n/README.md)
|
||||
- [GitHub Pages Language And GEO Workflow](./maintainer/github-pages-language-geo-workflow.md)
|
||||
- [Chapter Split + TOC Extraction](./chapter-split-toc.md)
|
||||
- [Release Workflow](./maintainer/release-workflow.md)
|
||||
- [Release Notes 1.8.2](./releases/1.8.2.md)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
## 用户与维护者入口
|
||||
|
||||
- [语言中心](./i18n/README_zh.md)
|
||||
- [GitHub Pages 语言与 GEO 工作流](./maintainer/github-pages-language-geo-workflow.zh-CN.md)
|
||||
- [章节拆分 + TOC 提取](./chapter-split-toc.zh-CN.md)
|
||||
- [发布流程](./maintainer/release-workflow.zh-CN.md)
|
||||
- [1.8.2 发布说明](./releases/1.8.2.zh-CN.md)
|
||||
|
|
|
|||
|
|
@ -627,6 +627,33 @@ Next direction:
|
|||
6. any upstream Slidev skill PR should stay generic: full references, built-in/configured theme preference, closed frontmatter, readable transforms for large diagrams/tables/code, and browser-sampled build verification. NoteMD vault paths, local fork paths, layout audit internals, and the `architecture.zh-CN.md` fixture should remain project-local.
|
||||
7. standalone acceptance should now be treated as a tracked maintainer evidence package, not as an implicit chat summary: if the strict report or archived output path changes, update `docs/maintainer/slidev-standalone-acceptance-2026-06-18.*` or create a new dated acceptance file in the same shape.
|
||||
|
||||
### Batch G: gate GitHub Pages language and GEO output
|
||||
|
||||
Priority: `P0/P1`
|
||||
|
||||
Background:
|
||||
|
||||
1. the 2026-06-17 Pages pass fixed missing root pages, stale language claims, sitemap filtering, and `llms.txt`, but it still relied mostly on source review plus `npm run build`;
|
||||
2. the real generated zh-CN homepage still exposed `/obsidian-NotEMD/zh-CN/docs/intro`, `/zh-CN/docs/getting-started/quick-start`, `/zh-CN/docs/providers/overview`, and `/zh-CN/docs/pillar-ai-knowledge` as user-facing links even though those pages are untranslated fallback docs;
|
||||
3. that means crawler protection was better than user-facing language truth: `noindex,follow` and sitemap filtering cannot compensate for a Chinese homepage that routes users into English fallback pages.
|
||||
|
||||
Current landed status:
|
||||
|
||||
1. `website/src/lib/publishedLanguageScope.js` now owns the published zh-CN doc scope: currently only `faq`;
|
||||
2. `website/docusaurus.config.js` consumes that scope for sitemap filtering and uses canonical English URLs for the navbar/footer docs entry where untranslated zh-CN docs must not be implied;
|
||||
3. `website/src/theme/DocItem/Layout/index.js` consumes the same scope for fallback-doc `noindex,follow`;
|
||||
4. `website/src/pages/index.js` consumes the same scope so zh-CN homepage links FAQ to `/zh-CN/docs/faq` and routes untranslated critical docs to canonical English URLs;
|
||||
5. `website/scripts/audit-build.cjs` now audits the built output for root pages, canonical/WebPage JSON-LD URLs, zh-CN homepage route truth, noindex fallback behavior, sitemap language scope, and `llms.txt` language markers;
|
||||
6. `website/package.json` exposes `npm run audit:build`, and `.github/workflows/deploy-docs.yml` runs it after `npm run build` before uploading the Pages artifact;
|
||||
7. `docs/maintainer/github-pages-language-geo-workflow.*`, `website/README.md`, and `GEO_ROADMAP.md` now describe the deploy gate and the promotion rule for future zh-CN translations.
|
||||
|
||||
Next direction:
|
||||
|
||||
1. do not add locales as GEO surface area until the critical path is translated and reviewed;
|
||||
2. promote future zh-CN docs by updating the translation and `publishedLanguageScope.js` in the same batch, then running `npm --prefix website run build && npm --prefix website run audit:build`;
|
||||
3. after the route/language gate stays stable, add a provider-page quality audit for thin pages; this is a content-depth problem, not a locale-routing problem;
|
||||
4. keep `website/build` and generated Slidev export artifacts out of source commits.
|
||||
|
||||
## 6. Documentation Sync Rule
|
||||
|
||||
Any future change that updates the provider-settings/model-discovery lane must re-check, at minimum:
|
||||
|
|
@ -650,6 +677,18 @@ Any future change that updates the Slidev export lane must re-check, at minimum:
|
|||
8. `src/ui/NotemdSidebarView.ts`
|
||||
9. `package.json`
|
||||
|
||||
Any future change that updates the GitHub Pages / GEO / website language lane must re-check, at minimum:
|
||||
|
||||
1. `GEO_ROADMAP.md`
|
||||
2. `website/README.md`
|
||||
3. `website/docusaurus.config.js`
|
||||
4. `website/src/lib/publishedLanguageScope.js`
|
||||
5. `website/src/pages/index.js`
|
||||
6. `website/src/theme/DocItem/Layout/index.js`
|
||||
7. `website/static/llms.txt`
|
||||
8. `website/scripts/audit-build.cjs`
|
||||
9. `.github/workflows/deploy-docs.yml`
|
||||
|
||||
## 7. Verification Gate
|
||||
|
||||
Any update that changes the truth claims in this document should still finish with:
|
||||
|
|
@ -668,6 +707,11 @@ If the update touches Slidev export, also run:
|
|||
|
||||
After the rendered-layout gate lands, Slidev export closure must also include a non-empty layout-audit report for `docs/architecture.zh-CN.md` with zero `overflow` and zero `unreadable-scale` findings.
|
||||
|
||||
If the update touches GitHub Pages / GEO / website language scope, also run:
|
||||
|
||||
1. `npm --prefix website run build`
|
||||
2. `npm --prefix website run audit:build`
|
||||
|
||||
## 8. Bottom Line
|
||||
|
||||
Current `main` no longer needs another “did the provider settings lane really land?” argument.
|
||||
|
|
@ -679,4 +723,5 @@ The real current questions are now:
|
|||
3. can Stage-C local-KB / file-selection / chapter-split work deepen mixed-corpus quality evidence instead of relitigating feature existence;
|
||||
4. can the widened bounded provider discovery surface remain shared-core, lightweight, and honest as a maintenance lane rather than becoming the excuse for broader architectural claims;
|
||||
5. can Slidev export keep using a real UI-equivalent workflow proof and add rendered-layout quality gates instead of regressing to “the CLI can build, so the buttons must work” as weak evidence;
|
||||
6. can current truth documents keep tracking the real shipped branch boundary quickly enough that future sessions do not regress back to `1.9.0/1.9.1`-era wording.
|
||||
6. can GitHub Pages keep route, sitemap, canonical, `llms.txt`, and visible language entry points aligned through a build-output gate instead of source-only review;
|
||||
7. can current truth documents keep tracking the real shipped branch boundary quickly enough that future sessions do not regress back to `1.9.0/1.9.1`-era wording.
|
||||
|
|
|
|||
|
|
@ -683,13 +683,18 @@ GitHub Pages 这条线当前最大的结构风险不是缺少更多页面,而
|
|||
2. `website/static/llms.txt` 已提供 canonical answer-engine map;
|
||||
3. Docusaurus markdown link hook 已迁到当前配置位置;
|
||||
4. 未翻译的 zh-CN fallback docs 已从 sitemap 排除,并标记 `noindex,follow`;
|
||||
5. website README 和 GEO roadmap 已同步真实语言边界。
|
||||
5. `website/src/lib/publishedLanguageScope.js` 现在统一拥有已发布 zh-CN doc scope,目前只有 `faq`;
|
||||
6. `website/src/pages/index.js` 会把 zh-CN 首页上未翻译 critical docs 的入口导向 canonical 英文 URL,只把 FAQ 保持在 `/zh-CN/docs/faq`;
|
||||
7. `website/docusaurus.config.js` 与 `website/src/theme/DocItem/Layout/index.js` 复用同一份发布范围,分别用于 sitemap filtering 与 fallback doc 的 `noindex,follow`;
|
||||
8. `website/scripts/audit-build.cjs` 会审计 build 产物中的 root pages、canonical/WebPage JSON-LD URL、zh-CN 首页路由、noindex fallback、sitemap 语言范围与 `llms.txt` 语言声明;
|
||||
9. `website/package.json` 暴露 `npm run audit:build`,`.github/workflows/deploy-docs.yml` 在 `npm run build` 后、上传 Pages artifact 前运行该审计;
|
||||
10. `docs/maintainer/github-pages-language-geo-workflow.*`、website README 与 GEO roadmap 已同步真实语言边界和部署 gate。
|
||||
|
||||
下一步:
|
||||
|
||||
1. 把 zh-CN critical path 补齐到 homepage、FAQ、intro、installation、quick-start、configuration、provider overview 与 pillar page;
|
||||
2. 给 `website` 增加轻量 smoke gate,检查 `build/index.html`、`build/zh-CN/index.html`、`build/llms.txt` 和 sitemap 是否存在;
|
||||
3. 扩写或合并 provider thin pages,优先补真实 provider setup、endpoint/header 语义、model discovery 与 troubleshooting;
|
||||
1. 按 intro、installation、quick-start、configuration、provider overview、pillar page 的顺序补齐 zh-CN critical path;homepage 与 FAQ 已是当前已发布范围;
|
||||
2. 每个页面从 fallback 晋升为已发布页面时,必须在同批更新翻译与 `publishedLanguageScope.js`,并运行 `npm --prefix website run build && npm --prefix website run audit:build`;
|
||||
3. 扩写或合并 provider thin pages,优先补真实 provider setup、endpoint/header 语义、model discovery 与 troubleshooting;这是内容质量问题,不应混进 locale route gate;
|
||||
4. fixed Pages 部署后再进行 Search Console 与 AI visibility 复测,不要用本地未部署结果过早判断 GEO 成败。
|
||||
|
||||
### Batch G:把 Slidev export 从一次性 smoke 提升为真实工作流门禁
|
||||
|
|
@ -747,9 +752,12 @@ GitHub Pages 这条线当前最大的结构风险不是缺少更多页面,而
|
|||
1. `GEO_ROADMAP.md`
|
||||
2. `website/README.md`
|
||||
3. `website/docusaurus.config.js`
|
||||
4. `website/src/pages/index.js`
|
||||
5. `website/static/llms.txt`
|
||||
6. `.github/workflows/deploy-docs.yml`
|
||||
4. `website/src/lib/publishedLanguageScope.js`
|
||||
5. `website/src/pages/index.js`
|
||||
6. `website/src/theme/DocItem/Layout/index.js`
|
||||
7. `website/static/llms.txt`
|
||||
8. `website/scripts/audit-build.cjs`
|
||||
9. `.github/workflows/deploy-docs.yml`
|
||||
|
||||
未来任何会更新 Slidev export 轨道真值的改动,至少都应同步检查:
|
||||
|
||||
|
|
@ -771,12 +779,10 @@ GitHub Pages 这条线当前最大的结构风险不是缺少更多页面,而
|
|||
2. `npm test -- --runInBand`
|
||||
3. `npm run audit:i18n-ui`
|
||||
4. `npm run audit:render-host`
|
||||
5. `cd website && npm run build`
|
||||
6. `test -f website/build/index.html`
|
||||
7. `test -f website/build/zh-CN/index.html`
|
||||
8. `test -f website/build/llms.txt`
|
||||
9. `git diff --check`
|
||||
10. clean 的 `git status --short --branch`
|
||||
5. `npm --prefix website run build`
|
||||
6. `npm --prefix website run audit:build`
|
||||
7. `git diff --check`
|
||||
8. clean 的 `git status --short --branch`
|
||||
|
||||
如果改动触及 Slidev export,还必须额外执行:
|
||||
|
||||
|
|
@ -785,6 +791,11 @@ GitHub Pages 这条线当前最大的结构风险不是缺少更多页面,而
|
|||
|
||||
等渲染后布局 gate 落地后,Slidev export 收尾还必须基于 `docs/architecture.zh-CN.md` 输出非空 layout-audit report,并且 `overflow` 与 `unreadable-scale` 均为零。
|
||||
|
||||
如果改动触及 GitHub Pages / GEO / website language scope,还必须额外执行:
|
||||
|
||||
1. `npm --prefix website run build`
|
||||
2. `npm --prefix website run audit:build`
|
||||
|
||||
## 8. Bottom Line
|
||||
|
||||
当前 `main` 已经不再需要再做一次“provider settings 这条线到底有没有落地”的论证。
|
||||
|
|
@ -795,6 +806,6 @@ GitHub Pages 这条线当前最大的结构风险不是缺少更多页面,而
|
|||
2. 当前 bounded CLI 分层能否继续显式保持,而未来任何 path-based promotion 都坚持 contract-first,而不是 convenience-first;
|
||||
3. Stage-C local-KB / file-selection / chapter-split 工作能否继续补强 mixed-corpus 质量证据,而不是反复重谈“功能是否存在”;
|
||||
4. 当前更宽的 bounded provider discovery surface 能否继续保持 shared-core、lightweight 且边界诚实,并作为维护轨道而不是更大架构声明的借口;
|
||||
5. GitHub Pages / GEO / website language 轨道能否坚持 route-first、truth-first,而不是继续用空 locale 或过时 README 制造弱信号;
|
||||
5. GitHub Pages / GEO / website language 轨道能否坚持 route-first、truth-first,并让 route、sitemap、canonical、`llms.txt` 与可见语言入口通过 build-output gate 保持一致;
|
||||
6. Slidev export 能否继续用真实 UI-equivalent workflow 验证,并补上渲染后布局质量门,而不是退回到“CLI 能 build 就算按钮可用”的弱证明;
|
||||
7. 当前真值文档能否足够快地跟上真实发货分支边界,避免未来会话又退回到 `1.9.0/1.9.1` 时代的旧措辞。
|
||||
|
|
|
|||
100
docs/maintainer/github-pages-language-geo-workflow.md
Normal file
100
docs/maintainer/github-pages-language-geo-workflow.md
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
# GitHub Pages Language And GEO Workflow
|
||||
|
||||
Language: **English** | [简体中文](./github-pages-language-geo-workflow.zh-CN.md)
|
||||
|
||||
This workflow records the current public documentation-site gate for `website/`. It is intentionally separate from runtime plugin i18n and from Slidev export acceptance.
|
||||
|
||||
## Current Contract
|
||||
|
||||
The website publishes one complete language surface and one partial language surface:
|
||||
|
||||
1. English is the canonical complete documentation surface under `https://jacobinwwey.github.io/obsidian-NotEMD/docs/...`.
|
||||
2. Simplified Chinese currently publishes the homepage and FAQ only.
|
||||
3. Untranslated zh-CN fallback docs may still be generated by Docusaurus, but they must be fenced with `noindex,follow` and excluded from the zh-CN sitemap.
|
||||
4. zh-CN homepage entry points for untranslated docs must route to canonical English URLs, not to `/zh-CN/docs/...` fallback pages.
|
||||
5. `llms.txt` must describe the same language boundary so answer engines do not infer full multilingual coverage.
|
||||
|
||||
## Implemented Gate
|
||||
|
||||
The blocking gate is:
|
||||
|
||||
```bash
|
||||
cd website
|
||||
npm run build
|
||||
npm run audit:build
|
||||
```
|
||||
|
||||
`npm run audit:build` executes `website/scripts/audit-build.cjs`. The script checks the built output, not just source files:
|
||||
|
||||
1. root pages exist: `build/index.html` and `build/zh-CN/index.html`;
|
||||
2. root pages have the expected `lang`, canonical URL, and WebPage JSON-LD URL;
|
||||
3. zh-CN homepage links untranslated critical docs to canonical English URLs;
|
||||
4. zh-CN homepage keeps FAQ on `/zh-CN/docs/faq`;
|
||||
5. untranslated zh-CN docs emit `noindex,follow`;
|
||||
6. published zh-CN FAQ does not emit `noindex,follow`;
|
||||
7. sitemap output includes canonical English docs and excludes untranslated zh-CN docs;
|
||||
8. `llms.txt` records the language scope.
|
||||
|
||||
The GitHub Pages workflow now runs this audit before uploading the Pages artifact:
|
||||
|
||||
```text
|
||||
.github/workflows/deploy-docs.yml
|
||||
-> npm run build
|
||||
-> npm run audit:build
|
||||
-> upload-pages-artifact
|
||||
```
|
||||
|
||||
## Source Ownership
|
||||
|
||||
The language scope is shared through:
|
||||
|
||||
```text
|
||||
website/src/lib/publishedLanguageScope.js
|
||||
```
|
||||
|
||||
Current values:
|
||||
|
||||
```text
|
||||
publishedZhCnDocIds = faq
|
||||
publishedZhCnDocPaths = /docs/faq
|
||||
```
|
||||
|
||||
This module is consumed by:
|
||||
|
||||
1. `website/docusaurus.config.js` for sitemap filtering;
|
||||
2. `website/src/theme/DocItem/Layout/index.js` for fallback-doc `noindex,follow`;
|
||||
3. `website/src/pages/index.js` for zh-CN homepage routing.
|
||||
|
||||
This avoids a previous drift pattern where sitemap, noindex, and homepage links each carried a separate copy of the same exception.
|
||||
|
||||
For canonical English links rendered from a zh-CN page, use the canonical origin-relative path with `autoAddBaseUrl: false` and `data-noBrokenLinkCheck`. Docusaurus checks broken links against the current locale route table, so these intentional cross-locale links must not be "fixed" back to localized fallback routes. `npm run audit:build` is the guard that proves the final path is correct.
|
||||
|
||||
## What Was Wrong
|
||||
|
||||
The previous Pages state was better than the old roadmap, but still incomplete:
|
||||
|
||||
1. Docusaurus generated untranslated zh-CN fallback docs.
|
||||
2. Those fallback docs were noindexed and removed from sitemap, which protected crawlers.
|
||||
3. The zh-CN homepage, navbar, and footer still linked users into `/zh-CN/docs/intro` and similar fallback routes.
|
||||
4. The deploy workflow only ran `npm run build`; it did not enforce the language route contract.
|
||||
|
||||
That means the site could pass build while still presenting a weak public language surface. `noindex` is not enough when the UI itself sends readers to the wrong locale route.
|
||||
|
||||
## Better GEO Direction
|
||||
|
||||
The better GEO strategy is still truth-first:
|
||||
|
||||
1. keep English canonical until specific zh-CN pages are fully translated and reviewed;
|
||||
2. only promote a zh-CN doc path by adding it to `publishedLanguageScope.js` in the same batch as the translation;
|
||||
3. require `npm run audit:build` before Pages deploy;
|
||||
4. keep `llms.txt`, sitemap, homepage links, and noindex behavior aligned;
|
||||
5. expand or consolidate thin provider pages before adding more locale surfaces.
|
||||
|
||||
Do not use empty locale folders or fallback English pages as GEO surface area. That produces weak hreflang signals and worse user trust.
|
||||
|
||||
## Next Direction
|
||||
|
||||
1. Translate and review the zh-CN critical path in this order: intro, installation, quick-start, configuration, provider overview, AI knowledge pillar.
|
||||
2. Each translated page promotion must update `publishedLanguageScope.js` and rerun `npm --prefix website run build && npm --prefix website run audit:build`.
|
||||
3. Add a small provider-page quality audit only after the language route gate stays stable; provider thinness is a content quality problem, not a routing problem.
|
||||
4. Keep generated `website/build` output ignored and uncommitted.
|
||||
100
docs/maintainer/github-pages-language-geo-workflow.zh-CN.md
Normal file
100
docs/maintainer/github-pages-language-geo-workflow.zh-CN.md
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
# GitHub Pages 语言与 GEO 工作流
|
||||
|
||||
语言: [English](./github-pages-language-geo-workflow.md) | **简体中文**
|
||||
|
||||
本文记录 `website/` 当前的公开文档站门禁。它与插件 runtime i18n、Slidev export 验收是三条不同轨道。
|
||||
|
||||
## 当前契约
|
||||
|
||||
文档站当前只发布一个完整语言面和一个部分语言面:
|
||||
|
||||
1. 英文是完整 canonical 文档面,路径为 `https://jacobinwwey.github.io/obsidian-NotEMD/docs/...`。
|
||||
2. 简体中文当前只发布首页和 FAQ。
|
||||
3. Docusaurus 仍可能生成未翻译的 zh-CN fallback docs,但这些页面必须标记 `noindex,follow`,并从 zh-CN sitemap 排除。
|
||||
4. zh-CN 首页上指向未翻译文档的入口必须回到 canonical 英文 URL,不能进入 `/zh-CN/docs/...` fallback 页面。
|
||||
5. `llms.txt` 必须声明同一语言边界,避免 answer engine 推断出不存在的完整多语言覆盖。
|
||||
|
||||
## 已落地门禁
|
||||
|
||||
阻断式门禁是:
|
||||
|
||||
```bash
|
||||
cd website
|
||||
npm run build
|
||||
npm run audit:build
|
||||
```
|
||||
|
||||
`npm run audit:build` 执行 `website/scripts/audit-build.cjs`。脚本审计的是 build 产物,而不只是源码:
|
||||
|
||||
1. root 页面存在:`build/index.html` 与 `build/zh-CN/index.html`;
|
||||
2. root 页面具有预期的 `lang`、canonical URL 与 WebPage JSON-LD URL;
|
||||
3. zh-CN 首页把未翻译 critical docs 链到 canonical 英文 URL;
|
||||
4. zh-CN 首页把 FAQ 保持在 `/zh-CN/docs/faq`;
|
||||
5. 未翻译 zh-CN docs 输出 `noindex,follow`;
|
||||
6. 已发布 zh-CN FAQ 不输出 `noindex,follow`;
|
||||
7. sitemap 包含 canonical 英文 docs,并排除未翻译 zh-CN docs;
|
||||
8. `llms.txt` 记录语言范围。
|
||||
|
||||
GitHub Pages workflow 现在会在上传 Pages artifact 前运行这个审计:
|
||||
|
||||
```text
|
||||
.github/workflows/deploy-docs.yml
|
||||
-> npm run build
|
||||
-> npm run audit:build
|
||||
-> upload-pages-artifact
|
||||
```
|
||||
|
||||
## Source Ownership
|
||||
|
||||
语言发布范围由这一处共享:
|
||||
|
||||
```text
|
||||
website/src/lib/publishedLanguageScope.js
|
||||
```
|
||||
|
||||
当前值:
|
||||
|
||||
```text
|
||||
publishedZhCnDocIds = faq
|
||||
publishedZhCnDocPaths = /docs/faq
|
||||
```
|
||||
|
||||
这个模块被三处消费:
|
||||
|
||||
1. `website/docusaurus.config.js` 用于 sitemap 过滤;
|
||||
2. `website/src/theme/DocItem/Layout/index.js` 用于 fallback doc 的 `noindex,follow`;
|
||||
3. `website/src/pages/index.js` 用于 zh-CN 首页路由。
|
||||
|
||||
这样可以避免旧问题:sitemap、noindex 与首页链接各自维护一份 “FAQ 例外”,后续很容易漂移。
|
||||
|
||||
从 zh-CN 页面渲染 canonical 英文链接时,应使用 canonical origin-relative path,并显式设置 `autoAddBaseUrl: false` 与 `data-noBrokenLinkCheck`。Docusaurus 的 broken-link 检查基于当前 locale route table,这类有意的跨 locale 链接不能被“修”回本地化 fallback 路由。最终路径是否正确由 `npm run audit:build` 负责验证。
|
||||
|
||||
## 之前错在哪里
|
||||
|
||||
此前 Pages 状态已经比旧 roadmap 好,但仍未闭环:
|
||||
|
||||
1. Docusaurus 会生成未翻译的 zh-CN fallback docs。
|
||||
2. 这些 fallback docs 已经 noindex 且从 sitemap 排除,这保护了 crawler。
|
||||
3. 但 zh-CN 首页、navbar 和 footer 仍会把用户送进 `/zh-CN/docs/intro` 这类 fallback 路由。
|
||||
4. 部署 workflow 只跑 `npm run build`,没有阻断语言路由契约漂移。
|
||||
|
||||
所以 build 能通过,并不等于公开语言面可靠。`noindex` 不能替代 UI 入口的正确路由。
|
||||
|
||||
## 更好的 GEO 方向
|
||||
|
||||
更好的 GEO 策略仍然是 truth-first:
|
||||
|
||||
1. 在具体 zh-CN 页面翻译并 review 前,英文继续作为 canonical;
|
||||
2. 只有在同批完成翻译时,才把对应 doc path 加入 `publishedLanguageScope.js`;
|
||||
3. Pages deploy 前必须跑 `npm run audit:build`;
|
||||
4. `llms.txt`、sitemap、首页链接与 noindex 行为必须保持一致;
|
||||
5. 增加更多 locale 之前,先扩写或合并 thin provider pages。
|
||||
|
||||
不要把空 locale folder 或 fallback English 页面当成 GEO 面积。这会制造弱 hreflang 信号,也会损害用户信任。
|
||||
|
||||
## 下一步
|
||||
|
||||
1. 按顺序翻译并 review zh-CN critical path:intro、installation、quick-start、configuration、provider overview、AI knowledge pillar。
|
||||
2. 每个页面从 fallback 晋升为已发布页面时,都必须更新 `publishedLanguageScope.js`,并执行 `npm --prefix website run build && npm --prefix website run audit:build`。
|
||||
3. 语言路由门禁稳定后,再添加 provider-page quality audit;provider thinness 是内容质量问题,不是路由问题。
|
||||
4. `website/build` 继续作为生成产物忽略,不提交到 main。
|
||||
|
|
@ -22,6 +22,12 @@ describe('mainline progress audit contract', () => {
|
|||
const matrixDocZh = readDoc('docs/brainstorms/2026-05-20-unified-follow-through-matrix.zh-CN.md');
|
||||
const standaloneAcceptanceDoc = readDoc('docs/maintainer/slidev-standalone-acceptance-2026-06-18.md');
|
||||
const standaloneAcceptanceDocZh = readDoc('docs/maintainer/slidev-standalone-acceptance-2026-06-18.zh-CN.md');
|
||||
const pagesLanguageGeoWorkflowDoc = readDoc('docs/maintainer/github-pages-language-geo-workflow.md');
|
||||
const pagesLanguageGeoWorkflowDocZh = readDoc('docs/maintainer/github-pages-language-geo-workflow.zh-CN.md');
|
||||
const docsReadme = readDoc('docs/README.md');
|
||||
const docsReadmeZh = readDoc('docs/README.zh-CN.md');
|
||||
const websiteReadme = readDoc('website/README.md');
|
||||
const geoRoadmap = readDoc('GEO_ROADMAP.md');
|
||||
|
||||
test('records the current Stage-B2/C/D baseline and fixture anchor in both canonical progress docs', () => {
|
||||
expect(progressDoc).toContain('### 2.7 Current `890b21b` Stage-B2/C/D follow-through baseline');
|
||||
|
|
@ -267,4 +273,35 @@ describe('mainline progress audit contract', () => {
|
|||
expect(standaloneAcceptanceDoc).toContain('Do not commit test/generated output to main');
|
||||
expect(standaloneAcceptanceDocZh).toContain('测试/生成输出不要提交到 main');
|
||||
});
|
||||
|
||||
test('locks the GitHub Pages language and GEO build-output gate', () => {
|
||||
for (const content of [pagesLanguageGeoWorkflowDoc, pagesLanguageGeoWorkflowDocZh]) {
|
||||
expect(content).toContain('website/scripts/audit-build.cjs');
|
||||
expect(content).toContain('website/src/lib/publishedLanguageScope.js');
|
||||
expect(content).toContain('npm run audit:build');
|
||||
expect(content).toContain('noindex,follow');
|
||||
expect(content).toContain('llms.txt');
|
||||
expect(content).toContain('/zh-CN/docs/faq');
|
||||
expect(content).toContain('canonical');
|
||||
expect(content).toContain('autoAddBaseUrl: false');
|
||||
expect(content).toContain('data-noBrokenLinkCheck');
|
||||
}
|
||||
|
||||
expect(docsReadme).toContain('GitHub Pages Language And GEO Workflow');
|
||||
expect(docsReadmeZh).toContain('GitHub Pages 语言与 GEO 工作流');
|
||||
|
||||
for (const content of [progressDoc, progressDocZh, websiteReadme, geoRoadmap]) {
|
||||
expect(content).toContain('website/scripts/audit-build.cjs');
|
||||
expect(content).toContain('website/src/lib/publishedLanguageScope.js');
|
||||
expect(content).toContain('npm run audit:build');
|
||||
expect(content).toContain('noindex,follow');
|
||||
expect(content).toContain('llms.txt');
|
||||
}
|
||||
|
||||
expect(progressDoc).toContain('build-output gate');
|
||||
expect(progressDocZh).toContain('build-output gate');
|
||||
expect(websiteReadme).toContain('zh-CN homepage sends untranslated docs to canonical English URLs');
|
||||
expect(geoRoadmap).toContain('zh-CN homepage does not route users into fallback docs');
|
||||
expect(geoRoadmap).toContain('.github/workflows/deploy-docs.yml');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -23,13 +23,23 @@ This starts a local development server at `http://localhost:3000`.
|
|||
|
||||
```bash
|
||||
npm run build
|
||||
npm run audit:build
|
||||
```
|
||||
|
||||
Generates static content into the `build` directory.
|
||||
Generates static content into the `build` directory, then audits the built Pages output.
|
||||
|
||||
The audit checks the public contract that source review cannot prove by itself:
|
||||
|
||||
- root and zh-CN root pages exist;
|
||||
- canonical and JSON-LD URLs match GitHub Pages routes;
|
||||
- zh-CN homepage sends untranslated docs to canonical English URLs;
|
||||
- zh-CN FAQ remains published under the zh-CN route;
|
||||
- untranslated zh-CN fallback docs are `noindex,follow` and excluded from the zh-CN sitemap;
|
||||
- `llms.txt` still states the real language boundary.
|
||||
|
||||
## Deployment
|
||||
|
||||
The site auto-deploys to GitHub Pages on push to `main` branch (via `.github/workflows/deploy-docs.yml`).
|
||||
The site auto-deploys to GitHub Pages on push to `main` branch (via `.github/workflows/deploy-docs.yml`). The deployment workflow runs both `npm run build` and `npm run audit:build` before uploading the Pages artifact.
|
||||
|
||||
**Live URL**: https://jacobinwwey.github.io/obsidian-NotEMD/
|
||||
|
||||
|
|
@ -59,7 +69,12 @@ The site auto-deploys to GitHub Pages on push to `main` branch (via `.github/wor
|
|||
- Lists canonical docs, provider/runtime topics, and the current language coverage boundary
|
||||
- Keeps GEO strategy focused on verified source pages instead of empty locale expansion
|
||||
|
||||
### 6. FAQ with FAQPage Schema
|
||||
### 6. Build Output Language Gate
|
||||
- `website/scripts/audit-build.cjs`: blocks Pages deployment when build output violates the published language scope
|
||||
- `website/src/lib/publishedLanguageScope.js`: single source for published zh-CN doc ids and paths
|
||||
- Keeps sitemap filtering, fallback `noindex,follow`, and homepage routing aligned
|
||||
|
||||
### 7. FAQ with FAQPage Schema
|
||||
- `docs/faq.mdx`: 12 Q&A pairs
|
||||
- Simplified Chinese translation: `i18n/zh-CN/docusaurus-plugin-content-docs/current/faq.mdx`
|
||||
- 43% of AI citations come from FAQ content (BrightEdge data)
|
||||
|
|
@ -73,6 +88,8 @@ website/
|
|||
├── static/
|
||||
│ ├── llms.txt # AI crawler / answer-engine entry point
|
||||
│ └── img/
|
||||
├── scripts/
|
||||
│ └── audit-build.cjs # Built-output Pages language/GEO gate
|
||||
├── docs/ # English docs
|
||||
│ ├── intro.mdx
|
||||
│ ├── faq.mdx
|
||||
|
|
@ -85,6 +102,8 @@ website/
|
|||
├── src/
|
||||
│ ├── pages/
|
||||
│ │ └── index.js # Locale-aware root homepage
|
||||
│ ├── lib/
|
||||
│ │ └── publishedLanguageScope.js
|
||||
│ ├── components/
|
||||
│ │ └── TLDR/ # TLDR component
|
||||
│ ├── theme/
|
||||
|
|
@ -124,7 +143,14 @@ citations: # Adds Schema.org "citation"
|
|||
|
||||
Do not add a locale to `i18n.locales` just because a translation folder exists. Docusaurus publishes fallback English docs under a locale path when docs are untranslated, which creates weak hreflang signals for search and AI crawlers.
|
||||
|
||||
Current zh-CN policy: the localized homepage and FAQ are published, while untranslated zh-CN doc fallbacks are excluded from sitemap output and marked `noindex,follow` by the swizzled doc layout. A locale should only become fully indexable after the critical path pages are translated and reviewed.
|
||||
Current zh-CN policy: the localized homepage and FAQ are published, while untranslated zh-CN doc fallbacks are excluded from sitemap output and marked `noindex,follow` by the swizzled doc layout. The zh-CN homepage must link untranslated docs to canonical English URLs, not to zh-CN fallback routes. A locale should only become fully indexable after the critical path pages are translated and reviewed.
|
||||
|
||||
When promoting a zh-CN doc from fallback to published content, update `website/src/lib/publishedLanguageScope.js` in the same change as the translation and rerun:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
npm run audit:build
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
|
|
|
|||
|
|
@ -5,20 +5,21 @@
|
|||
// See: https://docusaurus.io/docs/api/docusaurus-config
|
||||
|
||||
import {themes as prismThemes} from 'prism-react-renderer';
|
||||
import {publishedZhCnDocPaths} from './src/lib/publishedLanguageScope.js';
|
||||
|
||||
const siteUrl = 'https://jacobinwwey.github.io';
|
||||
const baseUrl = '/obsidian-NotEMD/';
|
||||
const siteBaseUrl = `${siteUrl}${baseUrl}`;
|
||||
const personId = `${siteBaseUrl}#person-jacobinwwey`;
|
||||
const logoUrl = `${siteBaseUrl}img/logo.svg`;
|
||||
const translatedZhCnDocUrls = new Set([
|
||||
`${siteBaseUrl}zh-CN/docs/faq`,
|
||||
]);
|
||||
const publishedZhCnDocUrls = new Set(
|
||||
Array.from(publishedZhCnDocPaths, (docPath) => `${siteBaseUrl}zh-CN${docPath}`),
|
||||
);
|
||||
|
||||
function sitemapItemMatchesPublishedLanguageScope(item) {
|
||||
const itemUrl = item.url.replace(/\/$/, '');
|
||||
const zhCnDocsPrefix = `${siteBaseUrl}zh-CN/docs/`;
|
||||
return !itemUrl.startsWith(zhCnDocsPrefix) || translatedZhCnDocUrls.has(itemUrl);
|
||||
return !itemUrl.startsWith(zhCnDocsPrefix) || publishedZhCnDocUrls.has(itemUrl);
|
||||
}
|
||||
|
||||
/** @type {import('@docusaurus/types').Config} */
|
||||
|
|
@ -36,6 +37,9 @@ const config = {
|
|||
// GitHub pages deployment config.
|
||||
organizationName: 'Jacobinwwey',
|
||||
projectName: 'obsidian-NotEMD',
|
||||
customFields: {
|
||||
canonicalBasePath: baseUrl,
|
||||
},
|
||||
|
||||
onBrokenLinks: 'warn',
|
||||
// Global JSON-LD Schema for entire site
|
||||
|
|
@ -170,10 +174,11 @@ const config = {
|
|||
},
|
||||
items: [
|
||||
{
|
||||
type: 'docSidebar',
|
||||
sidebarId: 'tutorialSidebar',
|
||||
href: `${baseUrl}docs/intro`,
|
||||
position: 'left',
|
||||
label: 'Docs',
|
||||
autoAddBaseUrl: false,
|
||||
'data-noBrokenLinkCheck': true,
|
||||
},
|
||||
{
|
||||
to: '/docs/faq',
|
||||
|
|
@ -199,7 +204,9 @@ const config = {
|
|||
items: [
|
||||
{
|
||||
label: 'Getting Started',
|
||||
to: '/docs/intro',
|
||||
href: `${baseUrl}docs/intro`,
|
||||
autoAddBaseUrl: false,
|
||||
'data-noBrokenLinkCheck': true,
|
||||
},
|
||||
{
|
||||
label: 'FAQ',
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
"docusaurus": "docusaurus",
|
||||
"start": "docusaurus start",
|
||||
"build": "docusaurus build",
|
||||
"audit:build": "node scripts/audit-build.cjs",
|
||||
"swizzle": "docusaurus swizzle",
|
||||
"deploy": "docusaurus deploy",
|
||||
"clear": "docusaurus clear",
|
||||
|
|
|
|||
157
website/scripts/audit-build.cjs
Normal file
157
website/scripts/audit-build.cjs
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const websiteRoot = path.resolve(__dirname, '..');
|
||||
const buildRoot = path.join(websiteRoot, 'build');
|
||||
const siteRoot = 'https://jacobinwwey.github.io/obsidian-NotEMD/';
|
||||
const basePath = '/obsidian-NotEMD/';
|
||||
const zhRoot = `${siteRoot}zh-CN/`;
|
||||
const zhBasePath = `${basePath}zh-CN/`;
|
||||
const publishedZhCnDocPaths = new Set(['/docs/faq']);
|
||||
const canonicalEnglishDocPaths = [
|
||||
'/docs/intro',
|
||||
'/docs/getting-started/quick-start',
|
||||
'/docs/providers/overview',
|
||||
'/docs/pillar-ai-knowledge',
|
||||
];
|
||||
|
||||
function fail(message) {
|
||||
throw new Error(`[audit-build] ${message}`);
|
||||
}
|
||||
|
||||
function readBuildFile(relativePath) {
|
||||
const filePath = path.join(buildRoot, relativePath);
|
||||
if (!fs.existsSync(filePath)) {
|
||||
fail(`Missing build file: ${relativePath}`);
|
||||
}
|
||||
|
||||
return fs.readFileSync(filePath, 'utf8');
|
||||
}
|
||||
|
||||
function assertContains(content, expected, context) {
|
||||
if (!content.includes(expected)) {
|
||||
fail(`${context} is missing ${expected}`);
|
||||
}
|
||||
}
|
||||
|
||||
function assertNotContains(content, forbidden, context) {
|
||||
if (content.includes(forbidden)) {
|
||||
fail(`${context} contains forbidden ${forbidden}`);
|
||||
}
|
||||
}
|
||||
|
||||
function listHtmlFiles(directory) {
|
||||
const entries = fs.readdirSync(directory, {withFileTypes: true});
|
||||
const files = [];
|
||||
|
||||
for (const entry of entries) {
|
||||
const entryPath = path.join(directory, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
files.push(...listHtmlFiles(entryPath));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry.name === 'index.html') {
|
||||
files.push(entryPath);
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
function routeForZhCnDocIndex(filePath) {
|
||||
const docsRoot = path.join(buildRoot, 'zh-CN', 'docs');
|
||||
const relativePath = path.relative(docsRoot, filePath);
|
||||
const routePath = relativePath.replace(/\\/g, '/').replace(/\/index\.html$/, '');
|
||||
return routePath === 'index.html' ? '/docs' : `/docs/${routePath}`;
|
||||
}
|
||||
|
||||
function auditHomepageRoutes() {
|
||||
const englishHome = readBuildFile('index.html');
|
||||
const zhHome = readBuildFile('zh-CN/index.html');
|
||||
|
||||
assertContains(englishHome, '<html lang="en-US"', 'English homepage');
|
||||
assertContains(englishHome, `rel="canonical" href="${siteRoot}"`, 'English homepage');
|
||||
assertContains(englishHome, `"url":"${siteRoot}"`, 'English homepage JSON-LD');
|
||||
|
||||
assertContains(zhHome, '<html lang="zh-CN"', 'zh-CN homepage');
|
||||
assertContains(zhHome, `rel="canonical" href="${zhRoot}"`, 'zh-CN homepage');
|
||||
assertContains(zhHome, `"url":"${zhRoot}"`, 'zh-CN homepage JSON-LD');
|
||||
assertContains(zhHome, `href="${zhBasePath}docs/faq"`, 'zh-CN homepage');
|
||||
|
||||
for (const docPath of canonicalEnglishDocPaths) {
|
||||
assertContains(zhHome, `href="${basePath}${docPath.slice(1)}"`, 'zh-CN homepage');
|
||||
assertNotContains(zhHome, `href="${basePath}${docPath.slice(1)}" target="_blank"`, 'zh-CN homepage');
|
||||
assertNotContains(zhHome, `href="${siteRoot}${docPath.slice(1)}"`, 'zh-CN homepage');
|
||||
assertNotContains(zhHome, `href="${zhBasePath}${docPath.slice(1)}"`, 'zh-CN homepage');
|
||||
assertNotContains(zhHome, `href="${zhBasePath}${basePath.slice(1)}${docPath.slice(1)}"`, 'zh-CN homepage');
|
||||
}
|
||||
}
|
||||
|
||||
function auditZhCnDocFallbacks() {
|
||||
const zhDocsRoot = path.join(buildRoot, 'zh-CN', 'docs');
|
||||
if (!fs.existsSync(zhDocsRoot)) {
|
||||
fail('Missing zh-CN docs build directory');
|
||||
}
|
||||
|
||||
for (const filePath of listHtmlFiles(zhDocsRoot)) {
|
||||
const routePath = routeForZhCnDocIndex(filePath);
|
||||
const html = fs.readFileSync(filePath, 'utf8');
|
||||
|
||||
if (publishedZhCnDocPaths.has(routePath)) {
|
||||
assertNotContains(html, 'content="noindex,follow"', `published zh-CN doc ${routePath}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
assertContains(html, 'content="noindex,follow"', `untranslated zh-CN fallback ${routePath}`);
|
||||
}
|
||||
}
|
||||
|
||||
function auditSitemaps() {
|
||||
const sitemap = readBuildFile('sitemap.xml');
|
||||
const zhSitemap = readBuildFile('zh-CN/sitemap.xml');
|
||||
|
||||
assertContains(sitemap, siteRoot, 'root sitemap');
|
||||
assertContains(zhSitemap, zhRoot, 'zh-CN sitemap');
|
||||
assertContains(zhSitemap, `${siteRoot}zh-CN/docs/faq`, 'zh-CN sitemap');
|
||||
|
||||
for (const docPath of canonicalEnglishDocPaths) {
|
||||
assertContains(sitemap, `${siteRoot}${docPath.slice(1)}`, 'root sitemap');
|
||||
assertNotContains(zhSitemap, `${siteRoot}zh-CN${docPath}`, 'zh-CN sitemap');
|
||||
}
|
||||
}
|
||||
|
||||
function auditAiRetrievalMap() {
|
||||
const llmsText = readBuildFile('llms.txt');
|
||||
|
||||
assertContains(llmsText, '## Language Scope', 'llms.txt');
|
||||
assertContains(llmsText, 'English is the canonical complete documentation surface.', 'llms.txt');
|
||||
assertContains(llmsText, `${siteRoot}zh-CN/`, 'llms.txt');
|
||||
assertContains(llmsText, `${siteRoot}zh-CN/docs/faq`, 'llms.txt');
|
||||
assertContains(
|
||||
llmsText,
|
||||
'Do not treat fallback English pages under the zh-CN locale path as independently translated Chinese source pages.',
|
||||
'llms.txt',
|
||||
);
|
||||
}
|
||||
|
||||
function auditRequiredFiles() {
|
||||
for (const relativePath of [
|
||||
'index.html',
|
||||
'zh-CN/index.html',
|
||||
'llms.txt',
|
||||
'robots.txt',
|
||||
'sitemap.xml',
|
||||
'zh-CN/sitemap.xml',
|
||||
]) {
|
||||
readBuildFile(relativePath);
|
||||
}
|
||||
}
|
||||
|
||||
auditRequiredFiles();
|
||||
auditHomepageRoutes();
|
||||
auditZhCnDocFallbacks();
|
||||
auditSitemaps();
|
||||
auditAiRetrievalMap();
|
||||
|
||||
console.log('website build audit passed');
|
||||
11
website/src/lib/publishedLanguageScope.js
Normal file
11
website/src/lib/publishedLanguageScope.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
export const publishedZhCnDocIds = new Set(['faq']);
|
||||
export const publishedZhCnDocPaths = new Set(['/docs/faq']);
|
||||
|
||||
export function isPublishedZhCnDocId(docId) {
|
||||
return publishedZhCnDocIds.has(docId);
|
||||
}
|
||||
|
||||
export function isPublishedZhCnDocPath(docPath) {
|
||||
const normalizedPath = docPath.startsWith('/') ? docPath : `/${docPath}`;
|
||||
return publishedZhCnDocPaths.has(normalizedPath.replace(/\/$/, ''));
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import Head from '@docusaurus/Head';
|
|||
import Layout from '@theme/Layout';
|
||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||
import useBaseUrl from '@docusaurus/useBaseUrl';
|
||||
import {isPublishedZhCnDocPath} from '../lib/publishedLanguageScope';
|
||||
import styles from './index.module.css';
|
||||
|
||||
const copyByLocale = {
|
||||
|
|
@ -40,8 +41,8 @@ const copyByLocale = {
|
|||
eyebrow: 'Obsidian 插件文档',
|
||||
heading: 'Notemd',
|
||||
lead: '把笔记处理成可积累的知识资产:wiki 链接、概念笔记、研究总结、翻译、图表,以及可复用的一键工作流。',
|
||||
primary: '阅读文档',
|
||||
secondary: '快速开始',
|
||||
primary: '阅读英文文档',
|
||||
secondary: '英文快速开始',
|
||||
faq: '常见问题',
|
||||
sections: [
|
||||
{
|
||||
|
|
@ -68,6 +69,19 @@ export default function Home() {
|
|||
const logoSrc = useBaseUrl('img/logo.svg');
|
||||
const copy = copyByLocale[i18n.currentLocale] || copyByLocale.en;
|
||||
const pageUrl = new URL(siteConfig.baseUrl, siteConfig.url).toString();
|
||||
const canonicalBasePath = siteConfig.customFields?.canonicalBasePath || siteConfig.baseUrl;
|
||||
const englishDocHref = (docPath) => `${canonicalBasePath}${docPath.replace(/^\//, '')}`;
|
||||
const docLinkProps = (docPath) => {
|
||||
if (i18n.currentLocale === 'zh-CN' && !isPublishedZhCnDocPath(docPath)) {
|
||||
return {
|
||||
href: englishDocHref(docPath),
|
||||
autoAddBaseUrl: false,
|
||||
'data-noBrokenLinkCheck': true,
|
||||
};
|
||||
}
|
||||
|
||||
return {to: docPath};
|
||||
};
|
||||
|
||||
return (
|
||||
<Layout title={copy.title} description={copy.description}>
|
||||
|
|
@ -90,13 +104,16 @@ export default function Home() {
|
|||
<h1>{copy.heading}</h1>
|
||||
<p className={styles.lead}>{copy.lead}</p>
|
||||
<div className={styles.actions}>
|
||||
<Link className="button button--primary button--lg" to="/docs/intro">
|
||||
<Link className="button button--primary button--lg" {...docLinkProps('/docs/intro')}>
|
||||
{copy.primary}
|
||||
</Link>
|
||||
<Link className="button button--secondary button--lg" to="/docs/getting-started/quick-start">
|
||||
<Link
|
||||
className="button button--secondary button--lg"
|
||||
{...docLinkProps('/docs/getting-started/quick-start')}
|
||||
>
|
||||
{copy.secondary}
|
||||
</Link>
|
||||
<Link className="button button--outline button--lg" to="/docs/faq">
|
||||
<Link className="button button--outline button--lg" {...docLinkProps('/docs/faq')}>
|
||||
{copy.faq}
|
||||
</Link>
|
||||
</div>
|
||||
|
|
@ -105,7 +122,7 @@ export default function Home() {
|
|||
</section>
|
||||
<section className={styles.sectionGrid} aria-label={copy.primary}>
|
||||
{copy.sections.map((section) => (
|
||||
<Link className={styles.sectionCard} key={section.href} to={section.href}>
|
||||
<Link className={styles.sectionCard} key={section.href} {...docLinkProps(section.href)}>
|
||||
<h2>{section.title}</h2>
|
||||
<p>{section.body}</p>
|
||||
</Link>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import Layout from '@theme-original/DocItem/Layout';
|
|||
import Head from '@docusaurus/Head';
|
||||
import {useDoc} from '@docusaurus/plugin-content-docs/client';
|
||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||
import {isPublishedZhCnDocId} from '../../../lib/publishedLanguageScope';
|
||||
|
||||
export default function LayoutWrapper(props) {
|
||||
const {frontMatter, metadata} = useDoc();
|
||||
|
|
@ -33,7 +34,7 @@ export default function LayoutWrapper(props) {
|
|||
: undefined;
|
||||
|
||||
const datePublished = frontMatter.datePublished || dateModified;
|
||||
const untranslatedZhCnFallbackDoc = i18n.currentLocale === 'zh-CN' && metadata.id !== 'faq';
|
||||
const untranslatedZhCnFallbackDoc = i18n.currentLocale === 'zh-CN' && !isPublishedZhCnDocId(metadata.id);
|
||||
|
||||
const articleSchema = {
|
||||
'@context': 'https://schema.org',
|
||||
|
|
|
|||
|
|
@ -46,4 +46,4 @@ Latest release entrypoint: https://github.com/Jacobinwwey/obsidian-NotEMD/releas
|
|||
- Preserve the local-vault privacy distinction: Notemd writes results into the user's Markdown vault and can run with local providers such as Ollama.
|
||||
- Do not infer full multilingual documentation coverage from the presence of a locale dropdown.
|
||||
|
||||
Last reviewed: 2026-06-17
|
||||
Last reviewed: 2026-06-19
|
||||
|
|
|
|||
Loading…
Reference in a new issue