---
title: "I18n Routing"
description: "I18n Routing provides the infrastructure for multi-language support across the documentation system. It handles the critical tasks of detecting user locale, managing URL prefixes, and ensuring that..."
last_updated: "2026-07-02T09:46:38.959543+00:00"
canonical_url: "https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/technical/content-engine/i18n-routing"
---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [packages/core/src/i18n/middleware.ts](https://github.com/blade47/fumadocs/blob/main/packages/core/src/i18n/middleware.ts)
- [packages/core/src/source/page-tree/builder.ts](https://github.com/blade47/fumadocs/blob/main/packages/core/src/source/page-tree/builder.ts)
- [packages/core/src/source/loader.ts](https://github.com/blade47/fumadocs/blob/main/packages/core/src/source/loader.ts)
- [packages/language/src/zh-tw.ts](https://github.com/blade47/fumadocs/blob/main/packages/language/src/zh-tw.ts)
- [packages/language/src/zh-cn.ts](https://github.com/blade47/fumadocs/blob/main/packages/language/src/zh-cn.ts)
- [packages/radix-ui/src/contexts/i18n.tsx](https://github.com/blade47/fumadocs/blob/main/packages/radix-ui/src/contexts/i18n.tsx)
- [packages/base-ui/src/contexts/i18n.tsx](https://github.com/blade47/fumadocs/blob/main/packages/base-ui/src/contexts/i18n.tsx)
- [packages/preview/src/pages/[...slugs].tsx](https://github.com/blade47/fumadocs/blob/main/packages/preview/src/pages/%5B...slugs%5D.tsx)
- [packages/core/src/source/dynamic.ts](https://github.com/blade47/fumadocs/blob/main/packages/core/src/source/dynamic.ts)
- [packages/core/src/search/orama/create-server.ts](https://github.com/blade47/fumadocs/blob/main/packages/core/src/search/orama/create-server.ts)
- [packages/core/src/source/llms.ts](https://github.com/blade47/fumadocs/blob/main/packages/core/src/source/llms.ts)
- [packages/core/src/i18n/index.ts](https://github.com/blade47/fumadocs/blob/main/packages/core/src/i18n/index.ts)
- [packages/base-ui/src/layouts/shared/slots/language-select.tsx](https://github.com/blade47/fumadocs/blob/main/packages/base-ui/src/layouts/shared/slots/language-select.tsx)
- [packages/core/src/source/storage/content.ts](https://github.com/blade47/fumadocs/blob/main/packages/core/src/source/storage/content.ts)
- [packages/radix-ui/src/layouts/shared/slots/language-select.tsx](https://github.com/blade47/fumadocs/blob/main/packages/radix-ui/src/layouts/shared/slots/language-select.tsx)
- [packages/base-ui/src/i18n.tsx](https://github.com/blade47/fumadocs/blob/main/packages/base-ui/src/i18n.tsx)
- [packages/radix-ui/src/i18n.tsx](https://github.com/blade47/fumadocs/blob/main/packages/radix-ui/src/i18n.tsx)
- [packages/core/src/search/flexsearch.ts](https://github.com/blade47/fumadocs/blob/main/packages/core/src/search/flexsearch.ts)
- [packages/core/src/negotiation/index.ts](https://github.com/blade47/fumadocs/blob/main/packages/core/src/negotiation/index.ts)
- [packages/openapi/src/i18n.ts](https://github.com/blade47/fumadocs/blob/main/packages/openapi/src/i18n.ts)
- [packages/core/src/framework/index.tsx](https://github.com/blade47/fumadocs/blob/main/packages/core/src/framework/index.tsx)
- [packages/preview/src/waku.server.tsx](https://github.com/blade47/fumadocs/blob/main/packages/preview/src/waku.server.tsx)
- [packages/core/src/framework/react-router.tsx](https://github.com/blade47/fumadocs/blob/main/packages/core/src/framework/react-router.tsx)
- [packages/core/package.json](https://github.com/blade47/fumadocs/blob/main/packages/core/package.json)
- [packages/base-ui/src/provider/base.tsx](https://github.com/blade47/fumadocs/blob/main/packages/base-ui/src/provider/base.tsx)
- [packages/api-docs/src/i18n.ts](https://github.com/blade47/fumadocs/blob/main/packages/api-docs/src/i18n.ts)
- [packages/core/src/search/orama-cloud-legacy.ts](https://github.com/blade47/fumadocs/blob/main/packages/core/src/search/orama-cloud-legacy.ts)
- [packages/asyncapi/src/i18n.ts](https://github.com/blade47/fumadocs/blob/main/packages/asyncapi/src/i18n.ts)
- [packages/story/src/i18n.ts](https://github.com/blade47/fumadocs/blob/main/packages/story/src/i18n.ts)
- [packages/radix-ui/src/provider/base.tsx](https://github.com/blade47/fumadocs/blob/main/packages/radix-ui/src/provider/base.tsx)
</details>

I18n Routing provides the infrastructure for multi-language support across the documentation system. It handles the critical tasks of detecting user locale, managing URL prefixes, and ensuring that page trees and content are correctly scoped to the active language. By integrating with Next.js middleware and specialized loaders, it ensures that users are consistently directed to the correct content translation.

The architecture centers on the `I18nConfig` definition and a dedicated middleware. The system uses a negotiator to determine the user's preferred language, which is then reconciled against the configured `languages` array. This process determines whether a URL rewrite (to keep the locale hidden) or a redirect (to establish the explicit locale in the URL) is necessary, effectively abstracting the complexity of localized pathing away from the UI components.

Beyond routing, the system bridges the gap between language configuration and UI components via context providers. These providers (e.g., `I18nProvider`) manage state such as the current locale and available translations, enabling language switchers and localized text rendering. The system is designed to scale across different packages, allowing UI layers to register translations that are then merged and injected into the React tree.

## Middleware Mechanism
The `createI18nMiddleware` function is the gatekeeper for localized requests. It processes incoming URLs using a `URLFormatter` to identify potential locale codes in the pathname. If no locale is detected or the detected locale is invalid, it negotiates the preferred language based on `request.headers` using `@formatjs/intl-localematcher`.

The middleware implements three `hideLocale` strategies:
- `'never'`: The locale is always visible in the URL path.
- `'default-locale'`: The locale prefix is stripped for the default language but visible for others.
- `'always'`: The locale prefix is hidden entirely; the locale is tracked via a cookie (default: `FD_LOCALE`).

Sources: [packages/core/src/i18n/middleware.ts:56-110](https://github.com/blade47/fumadocs/blob/main/packages/core/src/i18n/middleware.ts#L56-L110)

> [!NOTE]
> When `hideLocale` is set to `always`, the middleware performs a `NextResponse.rewrite` to the locale-prefixed URL if the user doesn't have a locale preference, otherwise it uses the locale found in the cookie.

Sources: [packages/core/src/i18n/middleware.ts:89-93](https://github.com/blade47/fumadocs/blob/main/packages/core/src/i18n/middleware.ts#L89-L93)

## Storage and Locale Parsing
The `createContentStorageBuilder` utility organizes source content by locale. It utilizes a configurable parser to map file system paths to internal virtual paths.

| Parser | Strategy | Path Example |
| :--- | :--- | :--- |
| `dir` | Prefixes the path with a locale directory | `en/docs/page.mdx` → `docs/page.mdx` |
| `dot` | Uses dot notation in the filename | `page.en.mdx` → `page.mdx` |

Sources: [packages/core/src/source/storage/content.ts:54-87](https://github.com/blade47/fumadocs/blob/main/packages/core/src/source/storage/content.ts#L54-L87)

The builder scan process separates files into locale-specific maps. If a `fallbackLanguage` is defined, the storage builder performs a chained lookup, inheriting files from the fallback storage when a missing translation is requested.

Sources: [packages/core/src/source/storage/content.ts:153-167](https://github.com/blade47/fumadocs/blob/main/packages/core/src/source/storage/content.ts#L153-L167)

## Page Tree Building
The `PageTreeBuilder` creates the hierarchical structure used by sidebars. When initialized with locale support (as an array of `[locale, storages]`), it creates a `PageTreeBuilderContext` specific to that locale. This ensures that the tree built for `/zh-TW` correctly references only the content files assigned to that locale.

```mermaid
flowchart TD
    A["createPageTreeBuilder"] --> B["Build Context (Storage + Locale)"]
    B --> C["Scan Storage Files"]
    C --> D["buildFolder"]
    D --> E["Apply Transformers (e.g. Fallback)"]
    E --> F["Return PageTree.Root"]
```
Sources: [packages/core/src/source/page-tree/builder.ts:89-136](https://github.com/blade47/fumadocs/blob/main/packages/core/src/source/page-tree/builder.ts#L89-L136)

> [!TIP]
> The builder relies on the `own()` function to prevent duplicate node registration when multiple folders reference the same file content; it uses a `priority` check to decide which folder "claims" the node.

Sources: [packages/core/src/source/page-tree/builder.ts:158-184](https://github.com/blade47/fumadocs/blob/main/packages/core/src/source/page-tree/builder.ts#L158-L184)

## Translation Management
Translations are defined using `defineI18n` and can be extended with specific keys via `TranslationExtension`. The API supports multiple language presets.

```typescript
import { defineI18n } from 'fumadocs-core/i18n';
import { zhTW } from 'packages/language/src/zh-tw';

const i18n = defineI18n({
  languages: ['en', 'zh-TW'],
  defaultLanguage: 'en',
});

const t = i18n.translations().preset('zh-TW', zhTW());
```
Sources: [packages/core/src/i18n/index.ts:120-165](https://github.com/blade47/fumadocs/blob/main/packages/core/src/i18n/index.ts#L120-L165)

## UI Context Provider
The `I18nProvider` in both `base-ui` and `radix-ui` shares a common mechanism. It consumes the current locale and a change handler, providing these via React context. The `onChange` implementation computes the new path by replacing the existing locale segment in the URL or prepending a new one if necessary, ensuring the user stays on the same page while switching languages.

Sources: [packages/base-ui/src/contexts/i18n.tsx:42-55](https://github.com/blade47/fumadocs/blob/main/packages/base-ui/src/contexts/i18n.tsx#L42-L55)

## Search Localization
The search subsystem supports locale-specific indexing. `createI18nSearchAPI` initializes separate Orama search servers for each locale, allowing queries to be routed to the correct language index.

```mermaid
sequenceDiagram
    participant C as Client
    participant API as SearchAPI
    participant S as Server Map
    C->>API: search(query, {locale: 'zh-TW'})
    API->>S: get('zh-TW')
    S-->>API: returns handler
    API->>API: execute localized search
```
Sources: [packages/core/src/search/orama/create-server.ts:190-237](https://github.com/blade47/fumadocs/blob/main/packages/core/src/search/orama/create-server.ts#L190-L237)

## Related

- [Layout System](https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/technical/rendering-ui/layout-system)


## Sitemap

See the full [sitemap](https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/llms.txt) for all pages in this wiki.
