Pagefind Multilingual Search in Astro: Keep Language Results Separate
A single search box can quietly break the experience of a multilingual blog. An English query may return Traditional Chinese articles, category counts may imply content that is not visible, and a translated placeholder can hide the fact that the underlying index is still mixed.
Pagefind has a simpler boundary available: the lang attribute on each generated HTML document. In an Astro static build, that lets one Pagefind build produce language-specific indexes without running a second search service.
How Pagefind chooses a language index
Pagefind’s multilingual documentation describes two matching steps:
- During indexing, Pagefind reads the
<html lang>value and indexes each detected language independently. - In the browser, Pagefind reads the current page’s language and loads the matching index.
That means these pages belong to different search indexes:
<!-- Existing Traditional Chinese page --><html lang="zh-TW">
<!-- English section --><html lang="en">You do not need to model language as a custom Pagefind filter for this behavior. You also should not pass --force-language, because that option deliberately opts out of multilingual indexing and creates one language index for the build.
Make Astro render the canonical locale
The language boundary should come from the route’s content query, not from browser detection. Define the supported values once:
export const SUPPORTED_LOCALES = ["zh-TW", "en"] as const;export type Locale = (typeof SUPPORTED_LOCALES)[number];Then pass the route locale into the root layout:
---interface Props { lang?: "zh-TW" | "en";}
const { lang = "zh-TW" } = Astro.props;---
<html lang={lang}> <slot /></html>The default-language route can keep its existing URL and default prop. The /en/ list and /en/posts/.../ routes must pass lang="en" explicitly.
This approach matters for static generation. There is no browser request language when Astro writes files into dist/, so client-side locale detection cannot determine which Pagefind index a document should enter.
Keep content discovery separated too
Pagefind only controls search. It does not prevent a mixed homepage or cross-language related posts. Before rendering either locale, filter the content collection by the same canonical locale used by the layout.
const englishPosts = allPosts.filter( (post) => normalizeLocale(post.data.lang) === "en",);Apply that filter before sorting, pagination, previous/next assignment, tag aggregation, and related-post scoring. This gives the search index and visible navigation the same language boundary.
For a complete route and content mapping, see Build an Astro Multilingual Blog Without Migrating Existing URLs.
Localize the search interface with a prop
Pagefind can select an index from the document language, but your own Svelte or Astro search controls still need localized labels. Pass the locale into the search island instead of deriving it from the URL:
<script lang="ts"> let { lang = "zh-TW" }: { lang?: "zh-TW" | "en" } = $props();
const copy = lang === "en" ? { placeholder: "Search articles", empty: "No matching articles", } : { placeholder: translatedSearchPlaceholder, empty: translatedEmptyState, };</script>
<input aria-label={copy.placeholder} placeholder={copy.placeholder} />When the island initializes Pagefind on an English document, the client library sees lang="en" on the root element. If a single-page navigation system replaces the document content, verify that it also updates the root language or reinitializes Pagefind when the language changes. Pagefind’s API documentation notes that dynamically changing page language requires reinitialization.
Validate the production indexes
Development-mode mock search results do not prove that the production index is separated. Run the full build, then inspect Pagefind’s output.
pnpm build
find dist/pagefind -maxdepth 2 -type f | sortrg 'lang="en"' dist/en/index.htmlrg 'lang="zh-TW"' dist/index.htmlPagefind’s build log should report both detected languages. The generated index filenames include language-specific metadata; exact hashed names can change between builds, so assertions should inspect content or use Pagefind’s API rather than hard-code a hash.
For a browser-level check, open /en/, search for a distinctive English phrase from an English article, and confirm that the returned URLs begin with /en/posts/. Then open / and search for a distinctive Traditional Chinese phrase. The result sets should remain in their own locale.
You can also use the Pagefind JavaScript API from a small verification script or browser console:
const search = await pagefind.search("multilingual index");const results = await Promise.all(search.results.map((item) => item.data()));
console.table(results.map((item) => ({ url: item.url, title: item.meta.title,})));Run that code from the locale whose index you want to test. Pagefind initializes against the current document language.
Diagnose a mixed-language result
If an English page returns Traditional Chinese results, check these in order:
- Inspect the generated English HTML, not the Astro source, and confirm
<html lang="en">. - Confirm the English article itself has
lang: enand was generated below/en/posts/. - Check that Pagefind was not built with
--force-languageorPAGEFIND_FORCE_LANGUAGE. - Clear stale static assets and browser caches after rebuilding.
- If navigation changes languages without a full page load, reinitialize Pagefind after the root
langvalue changes.
The important distinction is that localized UI and localized search are separate concerns. Translate the controls with locale props, but let a correct <html lang> value define Pagefind’s index boundary.
FAQ
Q: Does Pagefind automatically create separate language indexes?
A: Pagefind separates detected languages during one indexing run when the generated HTML documents have accurate lang attributes. In the browser, it uses the current document language to select the matching index, so the production HTML is the boundary to verify.
Q: Should I use --force-language for a multilingual Pagefind site?
A: No. --force-language deliberately forces the build into one language index, which bypasses Pagefind’s multilingual separation. Let each generated page declare its actual language instead.
Q: Why does an English Pagefind search return pages in another language?
A: Start by inspecting the generated English page for <html lang="en">. Then check for --force-language, stale Pagefind assets, and client-side navigation that changes page content without updating the root language or reinitializing Pagefind.
References:
Report a typo or broken link, or suggest a related topic.