---
title: "Internationalization"
description: "Organize translated content, routes, interface messages, search filters, and locale-aware SEO."
---

> Documentation Index
> Fetch the complete documentation index at: https://svedocs.pwp.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Internationalization

A multilingual site needs more than translated interface labels. In svedocs, every page belongs to a locale, so its route, navigation entry, search results, Ask AI citations, HTML language attributes, and SEO alternates stay in sync.

## Configure locales

```ts title="svedocs.config.ts"
import { defineConfig } from 'svedocs/config';

export default defineConfig({
  site: {
    name: 'Acme Docs',
    url: 'https://docs.acme.com'
  },
  i18n: {
    defaultLocale: 'en-US',
    prefixDefaultLocale: false,
    locales: [
      {
        code: 'en-US',
        label: 'English',
        path: 'en',
        hreflang: 'en-US',
        dir: 'ltr'
      },
      {
        code: 'zh-Hans',
        label: '中文',
        path: 'zh',
        hreflang: 'zh-CN',
        dir: 'ltr'
      },
      {
        code: 'ar',
        label: 'العربية',
        path: 'ar',
        hreflang: 'ar',
        dir: 'rtl'
      }
    ]
  }
});
```

The fields separate the locale's internal identity from its public URL and language tag:

| Field | Purpose |
| --- | --- |
| `code` | Stable internal identifier stored on pages, search records, and AI filters. |
| `label` | Human-readable name shown by the locale switcher. Use the language's own name. |
| `path` | One directory and URL segment used to discover and route localized content. Defaults to `code`. |
| `hreflang` | BCP 47 language tag used by SEO metadata and the document `lang` attribute. Defaults to `code`. |
| `dir` | Document direction, `ltr` or `rtl`. Defaults to `ltr`. |

Keeping `code` and `path` separate lets a stable identifier such as `zh-Hans` use the shorter `/zh` route. Search filters and stored data can then remain unchanged if the public URL changes later.

svedocs validates locale settings before discovering content. It rejects duplicate codes, paths, or language tags; an unknown `defaultLocale`; invalid or multi-segment paths; paths that conflict with `/docs`; and message catalogs for unconfigured locales. Duplicate checks are case-insensitive so the result does not depend on the host file system.

## Organize content

Use the locale `path` as the first directory under both content roots:

```txt
content/
├── docs/
│   ├── en/
│   │   ├── index.md
│   │   └── guides/deploy.md
│   ├── zh/
│   │   ├── index.md
│   │   └── guides/deploy.md
│   └── ar/
│       ├── index.md
│       └── guides/deploy.md
└── pages/
    ├── en/index.md
    ├── zh/index.md
    └── ar/index.md
```

With `defaultLocale: 'en-US'` and `prefixDefaultLocale: false`, the routes are:

| Source | Route | Locale code | Translation group |
| --- | --- | --- | --- |
| `content/docs/en/index.md` | `/docs` | `en-US` | `/docs` |
| `content/docs/zh/index.md` | `/docs/zh` | `zh-Hans` | `/docs` |
| `content/docs/ar/guides/deploy.md` | `/docs/ar/guides/deploy` | `ar` | `/docs/guides/deploy` |
| `content/pages/en/index.md` | `/` | `en-US` | `/` |
| `content/pages/zh/index.md` | `/zh` | `zh-Hans` | `/` |

Set `prefixDefaultLocale: true` when every public URL must include a locale. The English routes above then become `/docs/en` and `/en`.

Default-locale files may also live directly under `content/docs` and `content/pages`. Use either root files or a default-locale directory, but not both, because matching files would produce duplicate routes. Mirrored locale directories are usually easier to compare and make it simpler to change the URL-prefix strategy later.

Translate frontmatter along with the body. Titles, descriptions, `navTitle`, keywords, and meaningful image alt text appear in navigation, search, and page metadata. Keep matching files at the same relative path in each locale directory so svedocs can recognize them as translations of one another.

## Localize the shell

`i18n.messages` overrides built-in theme strings per locale. Missing values fall back to the English base catalog.

```ts title="svedocs.config.ts"
export default defineConfig({
  theme: {
    nav: [
      { label: 'Docs', labelKey: 'nav.docs', href: '/docs' },
      { label: 'Guides', labelKey: 'nav.guides', href: '/docs/guides' }
    ],
    home: {
      primaryAction: {
        label: 'Read the guides',
        labelKey: 'home.readGuides',
        href: '/docs/guides'
      }
    }
  },
  i18n: {
    defaultLocale: 'en',
    locales: ['en', 'zh'],
    messages: {
      en: {
        'nav.guides': 'Guides',
        'home.readGuides': 'Read the guides'
      },
      zh: {
        'nav.guides': '指南',
        'home.readGuides': '阅读指南',
        'search.placeholder': '搜索文档',
        'ask.label': '问 AI'
      }
    }
  }
});
```

Built-in message keys cover navigation, search, Ask AI, the table of contents, article controls, code tools, landing-page cards, errors, and rendering errors. You can add your own keys as well. Navigation, social, footer, and home-action items accept `labelKey`; `brand.labelKey`, `home.kickerKey`, and `home.visual.altKey` work the same way. Keep the plain text value as the default shown when a translation is unavailable.

Custom components can read both the internal locale code and the HTML language tag from `SvedocsThemeContext`:

```svelte
<script lang="ts">
  import type { SvedocsThemeContext } from 'svedocs/theme/types';

  export let context: SvedocsThemeContext;
</script>

<p lang={context.languageTag} dir={context.locale?.dir ?? 'ltr'}>
  {context.t('home.readGuides')}
</p>
```

Use `context.localeCode` for filters and stored state. Use `context.languageTag` for HTML and locale-sensitive formatting.

## Locale-aware links

For internal links in the brand, top navigation, social links, footer, home actions, and Markdown content, svedocs looks for the matching page in the active locale. A configured `/docs/guides` link therefore becomes `/docs/zh/guides` on a Chinese page when that translation exists.

The locale switcher uses the same translation mapping. If the current page is unavailable in a language, that option is disabled instead of claiming the translation exists. Other internal links fall back to the default-locale page when it exists. Direct requests for a missing localized route receive a temporary HTTP 307 redirect on edge builds. Static and SPA builds prerender an equivalent redirect document for every finite fallback route, so static hosts can send the browser to the canonical default-locale page even though they do not return the 307 status themselves. If neither page exists, the request remains a 404.

Relative Markdown links between mirrored files are resolved to generated routes, including links that retain a `.md`, `.mdx`, or `.svx` extension:

```md
Read the [deployment guide](../guides/deploy.md).
```

A locale-neutral absolute link such as `/docs/guides/deploy` is localized in the same way. An explicit locale route such as `/docs/zh/guides/deploy` keeps the language selected by the author. Query strings and heading fragments are preserved.

Custom Svelte and theme components can use `LocalizedLink` to apply the same behavior in the browser without duplicating route rules:

```svelte
<script lang="ts">
  import { LocalizedLink } from 'svedocs/theme';
  import type { SvedocsThemeContext } from 'svedocs/theme/types';

  export let context: SvedocsThemeContext;
</script>

<LocalizedLink {context} href="/docs/guides/deploy">Deploy</LocalizedLink>
```

## Search and Ask AI

Pages and section records carry `metadata.locale = code`. With the default `scope: 'current'`, search and Ask AI requests include the active locale code, so results do not mix languages. Set a provider's scope to `all` only when you actually want cross-language results.

Hosted indexes must preserve `locale` as a filterable field. The built-in Algolia, Typesense, Cloudflare AI Search, and local adapters already pass this scope through.

## SEO and document language

Set `site.url` to generate absolute canonical and alternate URLs. For every group of translations, svedocs emits:

- A canonical URL for the current route.
- Reciprocal `hreflang` links for translations that actually exist.
- `x-default` pointing to the default-locale page when it exists.
- Sitemap alternate links with the same mapping.
- Open Graph locale and available alternate locales.
- JSON-LD `inLanguage`.
- `<html lang>` from `languageTag` and `<html dir>` from `dir`.

Missing translations are not advertised as Open Graph or `hreflang` alternates. Custom root layouts should call `createPageMetadata(config, page, pages)` so Open Graph alternates can be derived from the complete page set.

## Check translation coverage

Enable the project check or request it from the CLI:

```ts title="svedocs.config.ts"
export default defineConfig({
  checks: {
    translations: true
  }
});
```

```sh
svedocs check --translations
svedocs check --translations --strict
```

The check matches public docs and standalone pages by relative path, then reports any configured locale that is missing a translation. Hidden pages are excluded. With `--strict`, warnings cause the command to fail.

Run the check after adding, renaming, or removing localized files. Also test at least one route per locale, one missing translation in the switcher, and one localized 404 route before release.

Source: https://svedocs.pwp.sh/docs/configuration/i18n
