--- description: Set up internationalization (i18n) in VitePress to support multiple languages for your site. --- # Internationalization To use the built-in i18n features, one needs to create a directory structure as follows: ``` docs/ ├─ es/ │ ├─ foo.md ├─ fr/ │ ├─ foo.md ├─ foo.md ``` Then in `docs/.vitepress/config.ts`: ```ts [docs/.vitepress/config.ts] import { defineConfig } from 'vitepress' export default defineConfig({ // shared properties and other top-level stuff... locales: { root: { label: 'English', lang: 'en' }, fr: { label: 'French', lang: 'fr', // optional, will be added as `lang` attribute on `html` tag link: '/fr/guide' // default /fr/ -- shows on navbar translations menu, can be external // other locale specific properties... } } }) ``` The following properties can be overridden for each locale (including root): ```ts interface LocaleSpecificConfig { lang?: string dir?: string title?: string titleTemplate?: string | boolean description?: string head?: HeadConfig[] // will be merged with existing head entries, duplicate meta tags are automatically removed themeConfig?: ThemeConfig // will be shallow merged, common stuff can be put in top-level themeConfig entry } ``` Refer [`DefaultTheme.Config`](https://github.com/vuejs/vitepress/blob/main/types/default-theme.d.ts) interface for details on customizing the placeholder texts of the default theme. Don't override `themeConfig.algolia` or `themeConfig.carbonAds` at locale-level. Refer [Algolia docs](../reference/default-theme-search#i18n) for using multilingual search. **Pro tip:** Config file can be stored at `docs/.vitepress/config/index.ts` too. It might help you organize stuff by creating a configuration file per locale and then merge and export them from `index.ts`. ## Per-locale Markdown Strings Strings baked into pages by the markdown renderer - the default titles of [custom containers](./markdown#custom-containers) and [GitHub-flavored alerts](./markdown#github-flavored-alerts), and the strings of the code copy button - can be overridden per locale with the `markdown` key of a locale entry: ```ts [docs/.vitepress/config.ts] import { defineConfig } from 'vitepress' export default defineConfig({ locales: { root: { label: 'English', lang: 'en' }, zh: { label: '简体中文', lang: 'zh-Hans', markdown: { container: { tipLabel: '提示', warningLabel: '警告' // ...the other labels, and titles of `customContainers` }, codeCopyButton: { tooltipText: '复制代码', copiedText: '已复制' } } } } }) ``` Values fall back to the root-level `markdown` options when a locale leaves them unset. Locale entries can only override the titles of containers registered at the root level - registering new containers per locale is not supported. Also note that since the markdown renderer is created once for the whole site, these can only be declared in the main config file, not in additional configs. ## Separate directory for each locale The following is a perfectly fine structure: ``` docs/ ├─ en/ │ ├─ foo.md ├─ es/ │ ├─ foo.md ├─ fr/ ├─ foo.md ``` However, VitePress won't redirect `/` to `/en/` by default. You'll need to configure your server for that. For example, on Netlify, you can add a `docs/public/_redirects` file like this: ``` /* /es/:splat 302 Language=es /* /fr/:splat 302 Language=fr /* /en/:splat 302 ``` **Pro tip:** If using the above approach, you can use `nf_lang` cookie to persist user's language choice: ```ts [docs/.vitepress/theme/index.ts] import DefaultTheme from 'vitepress/theme' import Layout from './Layout.vue' export default { extends: DefaultTheme, Layout } ``` ```vue [docs/.vitepress/theme/Layout.vue] ``` ## RTL Support (Experimental) For RTL support, specify `dir: 'rtl'` in config and use some RTLCSS PostCSS plugin like , or . You'll need to configure your PostCSS plugin to use `:where([dir="ltr"])` and `:where([dir="rtl"])` as prefixes to prevent CSS specificity issues.