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.
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:
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: LocalesConfigThe 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[] | undefinedArray 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> | undefinedMulti-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,
}),
],
}) 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: stringBCP 47 language tag, e.g. 'en', 'zh-CN', 'bn'.
lang: 'en',
LocaleConfig<any>.label: stringUser-facing label rendered in the language switcher.
label: 'English',
LocaleConfig<any>.theme: anyThe locale's full theme options.
theme: { navbar: anynavbar, sidebar: anysidebar },
},
'/zh/': {
LocaleConfig<any>.lang: stringBCP 47 language tag, e.g. 'en', 'zh-CN', 'bn'.
lang: 'zh',
LocaleConfig<any>.label: stringUser-facing label rendered in the language switcher.
label: '中文',
LocaleConfig<any>.theme: anyThe locale's full theme options.
theme: {
navbar: anynavbar: import zhNavbarzhNavbar,
sidebar: anysidebar: import zhSidebarzhSidebar,
i18n: anyi18n: import zhI18nzhI18n,
},
},
} Without locales, SveltePress does not rewrite paths, emit a language switcher, or scan locale route trees.
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/ 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
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: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/', '/') locales— the configured map, ornullwhen i18n is offresolveLocale(pathname)— active locale (lang,label,prefix,theme, …)resolveLocalizedPath(to, locale)— rewrite an internal link into the active localeresolveLocaleSwitch(pathname, targetPrefix)—{ href, fallback }for the switcher
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.
// @noErrors
import { createLocaleHandle } from '@sveltepress/vite/hooks'
import { locales } from '../config/locales'
export const handle = createLocaleHandle(locales) 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
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 prefix | Manifest | Version 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 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.