Skip to content

Give each language edition its own search index - #25

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

Give each language edition its own search index#25
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 (420 docs) 10 (41–43 per edition, 420 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 1220 links across 82 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.

Ported unchanged from hatlabs/halpi2, where it has been in production since this morning — docs.hatlabs.fi/halpi2 serves per-edition indexes now.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Improved multilingual documentation search by providing separate search indexes for each locale.
    • Updated translated pages so search results link to the correct documentation edition.
    • Preserved the default-language search index for the primary documentation site.
  • Bug Fixes

    • Added validation and clear build errors when required search or page configuration data is missing.

`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) <[email protected]>
@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: 4ff0b1e9-790d-4edf-a7f1-8af53fa6d033

📥 Commits

Reviewing files that changed from the base of the PR and between e9bb8b8 and 0af1396.

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

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


📝 Walkthrough

Walkthrough

The PR adds an MkDocs post-build hook that splits the merged i18n search index into locale-specific indexes, updates locale search settings, rewrites translated page base paths, and preserves the default-language index.

Changes

Internationalized search processing

Layer / File(s) Summary
Hook registration and build selection
hooks/i18n_search.py, mkdocs.yml
MkDocs registers the post-build hook. The hook skips nested i18n builds, locates the merged search index, validates required artifacts, and identifies buildable locales.
Locale index generation and base rewriting
hooks/i18n_search.py
The hook partitions documents by locale, writes locale-specific indexes with locale stemmer settings, rewrites translated page __config.base values, and restores the default-language index.

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

Merge Risk: ⚪ Minimal · up to 0af13

The change gives each language edition its own search index and preserves the documented site behavior; the supplied validation is clean, so no actionable merge-blocking risk remains beyond normal checks and review.

Suggested reviewers: helmihyo

Sequence Diagram(s)

sequenceDiagram
  participant MkDocs
  participant on_post_build
  participant MergedSearchIndex
  participant LocaleSearchIndex
  participant GeneratedPages

  MkDocs->>on_post_build: Run post-build hook
  on_post_build->>MergedSearchIndex: Read merged index
  MergedSearchIndex-->>on_post_build: Return locale documents
  on_post_build->>LocaleSearchIndex: Write per-locale index and stemmer settings
  on_post_build->>GeneratedPages: Rewrite __config.base
  on_post_build->>MergedSearchIndex: Retain default-language documents
Loading
🚥 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 summarizes the primary change: separate search indexes for each language edition.
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 e03fa8f 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