Skip to content

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/

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

VitePress Development Guide