SveltePress can serve multiple locales from one site. Internationalization is opt-in: omit locales and the site stays single-locale with unchanged behavior.

The Playground Internationalization Entry boots a three-locale Default Theme starter as authored at config/locales.ts. Chinese and Bengali Playground URLs keep that same starter and start the preview on /zh/ or /bn/. This is the exception: other Playground Entries are single-locale trees written in the page language at the default paths.

Opt-in locales

Enable locales

Pass a locales map to sveltepress(). Keys are URL prefixes ('/' for the default locale, '/zh/', '/bn/', …). Each entry needs a BCP 47 lang, a user-facing label for the language switcher, and that locale's full theme options:

vite.config.ts
import { const defaultTheme: ThemeDefaultdefaultTheme } from '@sveltepress/theme-default'
import { const sveltepress: (options?: SveltepressVitePluginOptions) => PluginOptionsveltepress } from '@sveltepress/vite'
import { function defineConfig(config: UserConfig): UserConfig (+5 overloads)

Type helper to make it easier to use vite.config.ts accepts a direct

UserConfig

object, or a function that returns it. The function receives a

ConfigEnv

object.

defineConfig
} from 'vite'
import { const locales: LocalesConfig

The merged documentation site's locales. Each locale carries its full theme options; the theme resolves them per route via virtual:sveltepress/locale.

locales
} from './config/locales'
export default function defineConfig(config: UserConfig): UserConfig (+5 overloads)

Type helper to make it easier to use vite.config.ts accepts a direct

UserConfig

object, or a function that returns it. The function receives a

ConfigEnv

object.

defineConfig
({
UserConfig.plugins?: PluginOption[] | undefined

Array of vite plugins to use.

plugins
: [
function sveltepress(options?: SveltepressVitePluginOptions): PluginOptionsveltepress({ SveltepressVitePluginOptions.theme?: ResolvedTheme | undefinedtheme: function defaultTheme(themeOptions?: DefaultThemeOptions | undefined): ResolvedThemedefaultTheme({ // Site-wide options shared by every locale (logo, github, pwa, ...) }), SveltepressVitePluginOptions.locales?: LocalesConfig<any> | undefined

Multi-locale site configuration keyed by URL prefix ('/' for the default locale, '/zh/', '/bn/', ...). Each entry carries that locale's lang, a user-facing label for the switcher, and its full theme options.

When omitted the site stays single-locale and behavior is unchanged.

locales
,
}), ], })
ts
config/locales.ts
import type { 
type LocalesConfig<ThemeOptions = any> = {
    [x: string]: LocaleConfig<ThemeOptions>;
}

Multi-locale site configuration keyed by URL prefix.

LocalesConfig
} from '@sveltepress/vite'
import import navbarnavbar from './navbar' import import sidebarsidebar from './sidebar' import import zhI18nzhI18n from './zh/i18n' import import zhNavbarzhNavbar from './zh/navbar' import import zhSidebarzhSidebar from './zh/sidebar' export const const locales: LocalesConfiglocales:
type LocalesConfig<ThemeOptions = any> = {
    [x: string]: LocaleConfig<ThemeOptions>;
}

Multi-locale site configuration keyed by URL prefix.

LocalesConfig
= {
'/': { LocaleConfig<any>.lang: string

BCP 47 language tag, e.g. 'en', 'zh-CN', 'bn'.

lang
: 'en',
LocaleConfig<any>.label: string

User-facing label rendered in the language switcher.

label
: 'English',
LocaleConfig<any>.theme: any

The locale's full theme options.

theme
: { navbar: anynavbar, sidebar: anysidebar },
}, '/zh/': { LocaleConfig<any>.lang: string

BCP 47 language tag, e.g. 'en', 'zh-CN', 'bn'.

lang
: 'zh',
LocaleConfig<any>.label: string

User-facing label rendered in the language switcher.

label
: '中文',
LocaleConfig<any>.theme: any

The locale's full theme options.

theme
: {
navbar: anynavbar: import zhNavbarzhNavbar, sidebar: anysidebar: import zhSidebarzhSidebar, i18n: anyi18n: import zhI18nzhI18n, }, }, }
ts

Without locales, SveltePress does not rewrite paths, emit a language switcher, or scan locale route trees.

Locale routes and prefixes

Routes and prefixes

Place translated pages beside the default locale under matching route IDs:

src/routes/
├─ guide/introduction/+page.md          # English → /guide/introduction/
├─ zh/guide/introduction/+page.md       # Chinese → /zh/guide/introduction/
└─ bn/guide/introduction/+page.md       # Bengali → /bn/guide/introduction/
txt

The default locale stays unprefixed at /. Other locales use their configured prefixes (/zh/, /bn/, …). Logical paths are the same across locales; only the prefix changes. Navbar, sidebar, home action, and feature-card links should use those logical paths (for example /guide/introduction/); the Default Theme prefixes them with the active locale.

Language switcher and fallbacks

Language switcher and fallbacks

When locales is configured, the Default Theme renders a language switcher in the navbar. Switching locales keeps the same documentation version, the same logical page, and the in-view heading (#hash). On a historical version page (/v/<id>/… or /zh/v/<id>/…), the switcher prefers the same frozen version in the target locale. Only if that frozen page is missing does it fall back to the current version of the same logical page, then to that locale's home with a short notice (svp-locale-fallback=1).

Customize switcher and notice copy with theme i18n.localeSwitcher and i18n.localePageUnavailable.

virtual/locale

virtual:sveltepress/locale

Themes and custom layouts can import locale helpers from the virtual module:

import {
  const locales: LocalesConfig | nulllocales,
  const resolveLocale: (pathname: string, base?: string) => ResolvedLocale | nullresolveLocale,
  const resolveLocaleSwitch: (pathname: string, targetPrefix: string, base?: string) => LocaleSwitchTarget | nullresolveLocaleSwitch,
  const resolveLocalizedPath: (to: string, locale: ResolvedLocale | null, base?: string) => stringresolveLocalizedPath,
} from 'virtual:sveltepress/locale'

const const active: ResolvedLocale<any> | nullactive = function resolveLocale(pathname: string, base?: string): ResolvedLocale | nullresolveLocale('/zh/guide/introduction/')
const const href: stringhref = function resolveLocalizedPath(to: string, locale: ResolvedLocale | null, base?: string): stringresolveLocalizedPath('/guide/quick-start/', const active: ResolvedLocale<any> | nullactive)
const const target: LocaleSwitchTarget | nulltarget = function resolveLocaleSwitch(pathname: string, targetPrefix: string, base?: string): LocaleSwitchTarget | nullresolveLocaleSwitch('/zh/guide/introduction/', '/')
ts
  • locales — the configured map, or null when i18n is off
  • resolveLocale(pathname) — active locale (lang, label, prefix, theme, …)
  • resolveLocalizedPath(to, locale) — rewrite an internal link into the active locale
  • resolveLocaleSwitch(pathname, targetPrefix){ href, fallback } for the switcher
createLocaleHandle

SSR <html lang>

Use createLocaleHandle from @sveltepress/vite/hooks so the initial HTML carries the correct language for crawlers and assistive technology. The Default Theme keeps document.documentElement.lang in sync after client-side navigation.

src/hooks.server.js
// @noErrors
import { createLocaleHandle } from '@sveltepress/vite/hooks'
import { locales } from '../config/locales'

export const handle = createLocaleHandle(locales)
js
Per-locale llms and hreflang sitemap

Per-locale llms and sitemap

With llms enabled, production builds write locale-scoped llms.txt / llms-full.txt (for example /llms.txt and /zh/llms.txt), listing only that locale's pages with prefixed URLs. Historical indexes land under each locale's version base (/v/<id>/, /zh/v/<id>/, …).

sitemap.xml lists every locale's current pages with hreflang alternates for locales that share the logical route, plus eligible historical version URLs from each locale manifest. EOL history is excluded unless a version opts out with noIndex: false.

Locale-aware versioning

Locale-aware versioning

Adding a locale does not require changes to SveltePress packages. Configure the prefix in locales, put pages under src/routes/<slug>/, and if you use document versions run sveltepress versions init --locale <slug>. Language switching, sidebar localization, and historical URLs follow that prefix automatically.

Each locale can keep its own document-version manifest:

Locale prefixManifestVersion base
/ (default)sveltepress.versions.json/v
/<slug>/ (for example /zh/, /bn/, /ja/)sveltepress.versions.<slug>.json/<slug>/v

A default sveltepress versions build drafts every configured locale and composes each locale's /v/ tree into one output. Pass --locale to init, create, validate, and single-locale draft jobs:

pnpm exec sveltepress versions build
pnpm exec sveltepress versions create 8.2 --label "8.2" --locale zh
pnpm exec sveltepress versions validate --locale zh
sh

versions init --locale zh creates sveltepress.versions.zh.json with base path /zh/v by default. Incremental deltas live in version-deltas-zh/ (or the manifest's artifacts.sources). See Document version management for the full release workflow.

Search per locale

Local Search indexes respect <html lang="…">. DocSearch and custom search should use per-locale theme options (separate indexes). Details and version-aware behavior: Search.