Skip to content

Give each language edition its own search index - #30

Merged
mairas merged 1 commit into
mainfrom
feat/per-language-search
Aug 27, 2026
Merged

Give each language edition its own search index#30
mairas merged 1 commit into
mainfrom
feat/per-language-search

Conversation

@mairas

@mairas mairas commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Searching from a translated page returns hits in the other nine languages. mkdocs-static-i18n merges every edition into one search/search_index.json by design, and Material resolves that file as new URL("search/search_index.json", __config.base) where base is the site root on every page, translated or not. Its reconfigure_search option does not separate them — it only adds each locale to lunr's lang list and drops duplicates.

hooks/i18n_search.py splits the merged index by locale after the build, strips the locale prefix from each entry's location, and repoints __config.base on that edition's pages at the edition root. base appears three times in the whole Material bundle: twice for the search index URL, once for a sitemap stream only subscribed under navigation.instant, which this site does not enable — so the repoint changes nothing but search.

Measured here after the change:

before after
indexes 1 (771 docs) 10 (76–78 per edition, 771 total)
each edition's lunr lang all ten at once its own (nb falls back to en, which has no lunr stemmer)

mkdocs build --strict clean, check_anchors.py site resolving 2600 links across 142 pages, and every locale page carries the edition-root base — checked programmatically against the expected depth rather than by eye.

The hook fails the build when an edition ends up with no entries, when the merged index is missing, or when a page carries no __config script. Those are the three ways it could silently revert to site-wide search while the build stayed green; each guard was exercised on halpi2 rather than assumed.

Worth noting for this repository specifically: the counts differ per edition because tutorials/openplotter-server/index.md is missing in all nine locales. That is the same gap the translation gate reports, and this change neither fixes nor worsens it — a locale short a page simply gets a smaller index.

Ported unchanged from hatlabs/halpi2, where it has been in production since this morning.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Improved multilingual documentation search by generating separate search indexes for each language.
    • Preserved default-language content in the main search index.
    • Updated translated pages so navigation and links correctly use their localized base paths.
  • Bug Fixes
    • Added validation and clear build errors when search data or translated page configuration is incomplete.

`mkdocs-static-i18n` merges every edition into one
`search/search_index.json`, and Material resolves that file against
`__config.base`, which is the site root on every page. Searching from a
translated page therefore returns hits in the other languages, and the
top results are often not in the language being read.

Split the merged index per locale after the build and repoint
`__config.base` on each edition's pages at the edition root — the only
value Material derives the index URL from. The hook aborts the build
when an edition ends up with no entries, when the merged index is
absent, or when a page carries no `__config` script, so a change in any
of those mechanisms fails loudly instead of silently restoring
site-wide search.

Ported from hatlabs/halpi2, where it is in production.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 27, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 440b4946-d04d-46f9-9b0c-59a870779422

📥 Commits

Reviewing files that changed from the base of the PR and between 72e54bc and 461117c.

📒 Files selected for processing (2)
  • hooks/i18n_search.py
  • mkdocs.yml

Included review availability: Your plan provides up to 10 included reviews per hour; 6 remain after this review.


📝 Walkthrough

Walkthrough

The PR adds an MkDocs post-build hook that validates the merged search index, creates locale-specific indexes, retains default-language documents in the root index, and rewrites translated page base paths.

Changes

Internationalized search build

Layer / File(s) Summary
Hook wiring and search configuration
hooks/i18n_search.py, mkdocs.yml
The new hook defines search-index processing helpers and is registered in the MkDocs configuration.
Locale index processing
hooks/i18n_search.py
The post-build hook skips nested builds, validates locale data, writes locale indexes with matching language settings, updates the root index, and rewrites translated page __config.base values.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 46111

The change gives each language edition its own search index, preventing cross-language search results. The guarded build behavior and validation support merge readiness, with no actionable merge-blocking risk remaining beyond normal checks.

Sequence Diagram(s)

sequenceDiagram
  participant MkDocs
  participant i18n_search
  participant SearchIndex
  participant LocaleIndexes
  participant TranslatedPages
  MkDocs->>i18n_search: Run post-build hook
  i18n_search->>SearchIndex: Validate and load merged index
  i18n_search->>LocaleIndexes: Write locale-specific indexes
  i18n_search->>TranslatedPages: Rewrite __config.base
  i18n_search->>SearchIndex: Update root index with default-language documents
Loading

Suggested reviewers: helmihyo

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 1 files. (1 skipped: 1… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: each language edition receives its own search index.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 1 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/per-language-search

Comment @coderabbitai help to get the list of available commands.

@mairas
mairas merged commit b9e3a48 into main Aug 27, 2026
2 checks passed
@mairas
mairas deleted the feat/per-language-search branch August 27, 2026 12:12
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.

1 participant