Internationalization (i18n)
Learn how to create multilingual documentation sites with VitePress.
Basic Setup
Directory Structure
Organize your content by language:
docs/
├── en/
│ ├── index.md
│ ├── guide/
│ │ └── getting-started.md
│ └── api/
│ └── reference.md
├── zh/
│ ├── index.md
│ ├── guide/
│ │ └── getting-started.md
│ └── api/
│ └── reference.md
└── .vitepress/
└── config.js
Configuration
Configure multiple locales in your config:
javascript
// .vitepress/config.js
export default {
locales: {
root: {
label: 'English',
lang: 'en',
title: 'VitePress',
description: 'Vite & Vue powered static site generator'
},
zh: {
label: '简体中文',
lang: 'zh-CN',
title: 'VitePress',
description: 'Vite & Vue 驱动的静态网站生成器',
themeConfig: {
nav: [
{ text: '指南', link: '/zh/guide/' },
{ text: 'API', link: '/zh/api/' }
],
sidebar: {
'/zh/guide/': [
{
text: '指南',
items: [
{ text: '快速开始', link: '/zh/guide/getting-started' }
]
}
]
}
}
}
},
themeConfig: {
// Default locale navigation
nav: [
{ text: 'Guide', link: '/guide/' },
{ text: 'API', link: '/api/' }
],
sidebar: {
'/guide/': [
{
text: 'Guide',
items: [
{ text: 'Getting Started', link: '/guide/getting-started' }
]
}
]
}
}
}
Language Switching
Automatic Language Switcher
VitePress automatically adds a language switcher when multiple locales are configured:
javascript
// The language switcher appears in the navigation bar
// Users can switch between configured languages
Custom Language Switcher
Create a custom language switcher component:
vue
<!-- .vitepress/theme/components/LanguageSwitcher.vue -->
<template>
<div class="language-switcher">
<select v-model="currentLocale" @change="switchLanguage">
<option
v-for="locale in locales"
:key="locale.code"
:value="locale.code"
>
{{ locale.label }}
</option>
</select>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import { useData, useRouter } from 'vitepress'
const { site, page } = useData()
const router = useRouter()
const locales = computed(() => {
return Object.entries(site.value.locales || {}).map(([code, config]) => ({
code: code === 'root' ? '' : code,
label: config.label,
lang: config.lang
}))
})
const currentLocale = ref(getCurrentLocale())
function getCurrentLocale() {
const path = page.value.relativePath
for (const locale of locales.value) {
if (locale.code && path.startsWith(locale.code + '/')) {
return locale.code
}
}
return ''
}
function switchLanguage() {
const newLocale = currentLocale.value
const currentPath = page.value.relativePath
// Remove current locale prefix
let newPath = currentPath
for (const locale of locales.value) {
if (locale.code && currentPath.startsWith(locale.code + '/')) {
newPath = currentPath.slice(locale.code.length + 1)
break
}
}
// Add new locale prefix
const finalPath = newLocale ? `/${newLocale}/${newPath}` : `/${newPath}`
router.go(finalPath)
}
</script>
Content Translation
Page-by-Page Translation
Translate each page individually:
markdown
<!-- docs/en/guide/getting-started.md -->
---
title: Getting Started
description: Learn how to get started with VitePress
---
# Getting Started
Welcome to VitePress! This guide will help you get started.
## Installation
```bash
npm create vitepress@latest
title: 快速开始 description: 学习如何开始使用 VitePress
快速开始
欢迎使用 VitePress!本指南将帮助您快速开始。
安装
bash
npm create vitepress@latest
Shared Content
For content that doesn't need translation, use shared directories:
docs/
├── .vitepress/
├── public/ # Shared assets
│ ├── images/
│ └── downloads/
├── en/
│ └── guide/
└── zh/
└── guide/
Navigation Translation
Locale-Specific Navigation
Configure navigation for each locale:
javascript
// .vitepress/config.js
export default {
locales: {
root: {
themeConfig: {
nav: [
{ text: 'Home', link: '/' },
{ text: 'Guide', link: '/guide/' },
{ text: 'API', link: '/api/' },
{
text: 'Resources',
items: [
{ text: 'Examples', link: '/examples/' },
{ text: 'FAQ', link: '/faq/' }
]
}
],
sidebar: {
'/guide/': [
{
text: 'Introduction',
items: [
{ text: 'What is VitePress?', link: '/guide/what-is-vitepress' },
{ text: 'Getting Started', link: '/guide/getting-started' }
]
}
]
}
}
},
zh: {
themeConfig: {
nav: [
{ text: '首页', link: '/zh/' },
{ text: '指南', link: '/zh/guide/' },
{ text: 'API', link: '/zh/api/' },
{
text: '资源',
items: [
{ text: '示例', link: '/zh/examples/' },
{ text: '常见问题', link: '/zh/faq/' }
]
}
],
sidebar: {
'/zh/guide/': [
{
text: '介绍',
items: [
{ text: '什么是 VitePress?', link: '/zh/guide/what-is-vitepress' },
{ text: '快速开始', link: '/zh/guide/getting-started' }
]
}
]
}
}
}
}
}
Dynamic Navigation
Generate navigation dynamically:
javascript
// .vitepress/config.js
function getNavigation(locale) {
const nav = {
en: [
{ text: 'Guide', link: '/guide/' },
{ text: 'API', link: '/api/' }
],
zh: [
{ text: '指南', link: '/zh/guide/' },
{ text: 'API', link: '/zh/api/' }
]
}
return nav[locale] || nav.en
}
export default {
locales: {
root: {
themeConfig: {
nav: getNavigation('en')
}
},
zh: {
themeConfig: {
nav: getNavigation('zh')
}
}
}
}
SEO for Multilingual Sites
Language Meta Tags
Add proper language meta tags:
markdown
---
title: Getting Started
description: Learn how to get started
head:
- - meta
- property: og:locale
content: en_US
- - link
- rel: alternate
hreflang: en
href: https://example.com/guide/getting-started
- - link
- rel: alternate
hreflang: zh-CN
href: https://example.com/zh/guide/getting-started
- - link
- rel: alternate
hreflang: x-default
href: https://example.com/guide/getting-started
---
Sitemap Generation
Generate locale-specific sitemaps:
javascript
// .vitepress/config.js
export default {
sitemap: {
hostname: 'https://example.com',
transformItems: (items) => {
// Add locale-specific URLs
return items.map(item => ({
...item,
links: [
{ lang: 'en', url: item.url },
{ lang: 'zh-CN', url: item.url.replace(/^\//, '/zh/') }
]
}))
}
}
}
Translation Workflow
Content Management
Organize translation workflow:
docs/
├── locales/
│ ├── en.json # English strings
│ ├── zh.json # Chinese strings
│ └── index.js # Locale loader
├── en/
├── zh/
└── .vitepress/
└── config.js
Translation Strings
Manage translatable strings:
json
// locales/en.json
{
"nav": {
"home": "Home",
"guide": "Guide",
"api": "API Reference"
},
"sidebar": {
"introduction": "Introduction",
"getting-started": "Getting Started"
},
"footer": {
"copyright": "Copyright © 2024"
}
}
json
// locales/zh.json
{
"nav": {
"home": "首页",
"guide": "指南",
"api": "API 参考"
},
"sidebar": {
"introduction": "介绍",
"getting-started": "快速开始"
},
"footer": {
"copyright": "版权所有 © 2024"
}
}
Using Translation Strings
javascript
// locales/index.js
import en from './en.json'
import zh from './zh.json'
export const messages = { en, zh }
export function t(key, locale = 'en') {
const keys = key.split('.')
let value = messages[locale]
for (const k of keys) {
value = value?.[k]
}
return value || key
}
Advanced Features
RTL Support
Support right-to-left languages:
css
/* .vitepress/theme/custom.css */
[dir="rtl"] .VPNavBar {
direction: rtl;
}
[dir="rtl"] .VPSidebar {
right: 0;
left: auto;
}
[dir="rtl"] .VPContent {
margin-right: var(--vp-sidebar-width);
margin-left: 0;
}
Date and Number Formatting
Format dates and numbers per locale:
vue
<script setup>
import { useData } from 'vitepress'
const { site } = useData()
function formatDate(date, locale) {
return new Intl.DateTimeFormat(locale).format(new Date(date))
}
function formatNumber(number, locale) {
return new Intl.NumberFormat(locale).format(number)
}
</script>
<template>
<div>
<p>{{ formatDate('2024-01-15', site.lang) }}</p>
<p>{{ formatNumber(1234567, site.lang) }}</p>
</div>
</template>
Best Practices
Content Strategy
- Plan your content structure before translation
- Use consistent terminology across languages
- Consider cultural differences in content
- Maintain parallel content structure
Technical Implementation
- Use proper HTML lang attributes
- Implement hreflang tags for SEO
- Test navigation in all languages
- Optimize for search engines in each locale
Maintenance
- Keep translations synchronized
- Use translation management tools
- Implement content review processes
- Monitor analytics per locale
Performance
- Consider lazy loading for non-active locales
- Optimize bundle sizes per language
- Use CDN for global content delivery
- Implement proper caching strategies