国际化实战案例
本教程将带你从零搭建一个完整的中英文双语 VitePress 站点,包含翻译管理、自动翻译工具集成和多语言 SEO 优化。
项目结构
目录结构
docs/
├── .vitepress/
│ ├── config.mts # 主配置(含多语言设置)
│ ├── locales/ # 语言配置
│ │ ├── zh.ts # 中文配置
│ │ └── en.ts # 英文配置
│ └── theme/
│ └── index.ts
├── zh/ # 中文文档
│ ├── index.md # 中文首页
│ ├── guide/
│ │ └── getting-started.md
│ └── about.md
├── en/ # 英文文档
│ ├── index.md # 英文首页
│ ├── guide/
│ │ └── getting-started.md
│ └── about.md
└── public/
└── images/VitePress 国际化机制
VitePress 通过 locales 配置实现多语言:
root- 默认语言(本例为中文)en- 英文语言(路径前缀/en/)
配置文件
主配置
ts
// docs/.vitepress/config.mts
import { defineConfig } from 'vitepress'
import { zh } from './locales/zh'
import { en } from './locales/en'
export default defineConfig({
title: 'My Docs',
// 多语言配置
locales: {
root: {
label: '简体中文',
lang: 'zh-CN',
link: '/',
...zh
},
en: {
label: 'English',
lang: 'en-US',
link: '/en/',
...en
}
},
// 通用配置
lastUpdated: true,
cleanUrls: true,
head: [
['meta', { name: 'theme-color', content: '#6366f1' }]
]
})中文配置
ts
// docs/.vitepress/locales/zh.ts
import type { DefaultTheme } from 'vitepress'
export const zh: DefaultTheme.Config = {
description: '中文文档描述',
themeConfig: {
nav: [
{ text: '首页', link: '/' },
{ text: '指南', link: '/guide/getting-started' },
{ text: '关于', link: '/about' }
],
sidebar: {
'/guide/': [
{
text: '指南',
items: [
{ text: '快速开始', link: '/guide/getting-started' },
{ text: '进阶配置', link: '/guide/advanced' }
]
}
]
},
editLink: {
pattern: 'https://github.com/your-repo/edit/main/docs/:path',
text: '编辑此页'
},
docFooter: {
prev: '上一页',
next: '下一页'
},
outline: {
label: '页面导航'
},
lastUpdated: {
text: '最后更新于',
formatOptions: {
dateStyle: 'short',
timeStyle: 'medium'
}
},
returnToTopLabel: '返回顶部',
sidebarMenuLabel: '菜单',
darkModeSwitchLabel: '主题',
footer: {
message: '基于 MIT 许可发布',
copyright: 'Copyright © 2024-present'
}
}
}英文配置
ts
// docs/.vitepress/locales/en.ts
import type { DefaultTheme } from 'vitepress'
export const en: DefaultTheme.Config = {
description: 'English documentation description',
themeConfig: {
nav: [
{ text: 'Home', link: '/en/' },
{ text: 'Guide', link: '/en/guide/getting-started' },
{ text: 'About', link: '/en/about' }
],
sidebar: {
'/en/guide/': [
{
text: 'Guide',
items: [
{ text: 'Getting Started', link: '/en/guide/getting-started' },
{ text: 'Advanced', link: '/en/guide/advanced' }
]
}
]
},
editLink: {
pattern: 'https://github.com/your-repo/edit/main/docs/:path',
text: 'Edit this page'
},
docFooter: {
prev: 'Previous',
next: 'Next'
},
outline: {
label: 'On this page'
},
lastUpdated: {
text: 'Last updated',
formatOptions: {
dateStyle: 'short',
timeStyle: 'medium'
}
},
returnToTopLabel: 'Return to top',
sidebarMenuLabel: 'Menu',
darkModeSwitchLabel: 'Theme',
footer: {
message: 'Released under the MIT License.',
copyright: 'Copyright © 2024-present'
}
}
}页面内容
中文首页
markdown
---
layout: home
hero:
name: "我的文档"
text: "开发指南"
tagline: 简洁高效的技术文档
actions:
- theme: brand
text: 快速开始
link: /guide/getting-started
- theme: alt
text: 查看 GitHub
link: https://github.com/your-repo
features:
- title: 🚀 快速
details: 基于 Vite 构建,极速的开发体验
- title: 🎨 美观
details: 默认主题优雅美观,支持自定义
- title: 🌐 国际化
details: 内置多语言支持,轻松构建多语言站点
---英文首页
markdown
---
layout: home
hero:
name: "My Docs"
text: "Developer Guide"
tagline: Simple and efficient technical documentation
actions:
- theme: brand
text: Get Started
link: /en/guide/getting-started
- theme: alt
text: View on GitHub
link: https://github.com/your-repo
features:
- title: 🚀 Fast
details: Built on Vite for lightning fast development
- title: 🎨 Beautiful
details: Elegant default theme with customization support
- title: 🌐 i18n
details: Built-in i18n support for multilingual sites
---指南页面(中英对照)
中文版本 docs/zh/guide/getting-started.md:
markdown
---
title: 快速开始
---
# 快速开始
本指南将帮助你快速上手项目开发。
## 环境要求
- Node.js 18+
- npm 9+
## 安装
\`\`\`bash
npm install
\`\`\`
## 启动开发服务器
\`\`\`bash
npm run dev
\`\`\`
访问 `http://localhost:5173` 查看文档。
::: tip 提示
建议使用 VS Code 并安装 Volar 插件以获得最佳开发体验。
:::英文版本 docs/en/guide/getting-started.md:
markdown
---
title: Getting Started
---
# Getting Started
This guide will help you get started with the project development.
## Prerequisites
- Node.js 18+
- npm 9+
## Installation
\`\`\`bash
npm install
\`\`\`
## Start Development Server
\`\`\`bash
npm run dev
\`\`\`
Visit `http://localhost:5173` to view the documentation.
::: tip
We recommend using VS Code with the Volar extension for the best development experience.
:::语言切换组件
添加语言切换下拉菜单
VitePress 默认提供语言切换功能,但可以自定义:
vue
<!-- docs/.vitepress/components/LanguageSwitcher.vue -->
<script setup lang="ts">
import { useData } from 'vitepress'
const { lang, locales } = useData()
const languages = [
{ label: '简体中文', lang: 'zh-CN', link: '/' },
{ label: 'English', lang: 'en-US', link: '/en/' }
]
</script>
<template>
<div class="language-switcher">
<select
:value="lang"
@change="(e) => {
const target = e.target as HTMLSelectElement
const lang = languages.find(l => l.lang === target.value)
if (lang) window.location.href = lang.link
}"
>
<option v-for="lang in languages" :key="lang.lang" :value="lang.lang">
{{ lang.label }}
</option>
</select>
</div>
</template>
<style scoped>
.language-switcher select {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 4px;
padding: 4px 8px;
font-size: 14px;
cursor: pointer;
}
</style>自动翻译工具
方案一:使用 AI 翻译
创建翻译脚本,使用 OpenAI API 进行翻译:
ts
// scripts/translate.ts
import fs from 'fs'
import path from 'path'
import OpenAI from 'openai'
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY
})
async function translate(content: string): Promise<string> {
const response = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [
{
role: 'system',
content: `You are a professional translator. Translate the following Markdown content from Chinese to English.
Keep all Markdown syntax, code blocks, and frontmatter unchanged.
Only translate the text content. Maintain the original formatting.`
},
{ role: 'user', content }
]
})
return response.choices[0].message.content || ''
}
async function translateFile(inputPath: string, outputPath: string) {
const content = fs.readFileSync(inputPath, 'utf-8')
const translated = await translate(content)
fs.mkdirSync(path.dirname(outputPath), { recursive: true })
fs.writeFileSync(outputPath, translated, 'utf-8')
console.log(`✅ Translated: ${inputPath} -> ${outputPath}`)
}
// 批量翻译
async function translateAll() {
const zhDir = 'docs/zh'
const enDir = 'docs/en'
const files = getAllMarkdownFiles(zhDir)
for (const file of files) {
const relativePath = path.relative(zhDir, file)
const outputPath = path.join(enDir, relativePath)
await translateFile(file, outputPath)
}
}
function getAllMarkdownFiles(dir: string): string[] {
const files: string[] = []
const entries = fs.readdirSync(dir, { withFileTypes: true })
for (const entry of entries) {
const fullPath = path.join(dir, entry.name)
if (entry.isDirectory()) {
files.push(...getAllMarkdownFiles(fullPath))
} else if (entry.name.endsWith('.md')) {
files.push(fullPath)
}
}
return files
}
translateAll()方案二:使用翻译平台 API
DeepL 集成:
ts
// scripts/deepl-translate.ts
import fs from 'fs'
import axios from 'axios'
const DEEPL_API_KEY = process.env.DEEPL_API_KEY
async function translateWithDeepL(text: string): Promise<string> {
const response = await axios.post(
'https://api-free.deepl.com/v2/translate',
new URLSearchParams({
auth_key: DEEPL_API_KEY!,
text,
source_lang: 'ZH',
target_lang: 'EN'
})
)
return response.data.translations[0].text
}package.json 脚本
json
{
"scripts": {
"i18n:translate": "tsx scripts/translate.ts",
"i18n:sync": "tsx scripts/sync-translations.ts",
"i18n:check": "tsx scripts/check-translations.ts"
}
}翻译一致性检查
检查脚本
ts
// scripts/check-translations.ts
import fs from 'fs'
import path from 'path'
function checkTranslationStatus() {
const zhDir = 'docs/zh'
const enDir = 'docs/en'
const zhFiles = getAllMarkdownFiles(zhDir)
const enFiles = getAllMarkdownFiles(enDir)
const zhRelative = zhFiles.map(f => path.relative(zhDir, f))
const enRelative = enFiles.map(f => path.relative(enDir, f))
const missing = zhRelative.filter(f => !enRelative.includes(f))
const extra = enRelative.filter(f => !zhRelative.includes(f))
console.log('📊 Translation Status Report\n')
console.log(`Chinese files: ${zhFiles.length}`)
console.log(`English files: ${enFiles.length}`)
if (missing.length) {
console.log('\n❌ Missing English translations:')
missing.forEach(f => console.log(` - ${f}`))
}
if (extra.length) {
console.log('\n⚠️ Extra English files (no Chinese source):')
extra.forEach(f => console.log(` - ${f}`))
}
if (!missing.length && !extra.length) {
console.log('\n✅ All translations are in sync!')
}
}
function getAllMarkdownFiles(dir: string): string[] {
if (!fs.existsSync(dir)) return []
const files: string[] = []
const entries = fs.readdirSync(dir, { withFileTypes: true })
for (const entry of entries) {
const fullPath = path.join(dir, entry.name)
if (entry.isDirectory()) {
files.push(...getAllMarkdownFiles(fullPath))
} else if (entry.name.endsWith('.md')) {
files.push(fullPath)
}
}
return files
}
checkTranslationStatus()多语言 SEO
hreflang 标签
在 config.mts 中添加 transformHead 钩子:
ts
// docs/.vitepress/config.mts
export default defineConfig({
transformHead({ pageData }) {
const url = 'https://your-domain.com'
const path = pageData.relativePath.replace(/\.md$/, '')
// 判断当前页面语言
const isEnglish = path.startsWith('en/')
const zhPath = isEnglish ? path.replace('en/', '') : path
const enPath = isEnglish ? path : `en/${path}`
return [
// 中文版本的 hreflang
['link', { rel: 'alternate', hreflang: 'zh-CN', href: `${url}/${zhPath}` }],
// 英文版本的 hreflang
['link', { rel: 'alternate', hreflang: 'en', href: `${url}/${enPath}` }],
// 默认语言
['link', { rel: 'alternate', hreflang: 'x-default', href: `${url}/${zhPath}` }]
]
}
})sitemap 多语言
VitePress 自动生成的 sitemap 包含多语言页面。如需自定义:
ts
// docs/.vitepress/config.mts
export default defineConfig({
sitemap: {
hostname: 'https://your-domain.com',
transformItems(items) {
return items.map(item => {
// 添加多语言链接
if (item.url.startsWith('/en/')) {
item.links = [
{ lang: 'zh-CN', url: item.url.replace('/en/', '/') }
]
} else {
item.links = [
{ lang: 'en', url: `/en${item.url}` }
]
}
return item
})
}
}
})进阶技巧
条件渲染多语言内容
vue
<!-- docs/.vitepress/components/LocalizedContent.vue -->
<script setup lang="ts">
import { useData } from 'vitepress'
const { lang } = useData()
</script>
<template>
<div v-if="lang === 'zh-CN'">
这是中文内容
</div>
<div v-else>
This is English content
</div>
</template>共享组件翻译
ts
// docs/.vitepress/locales/messages.ts
export const messages = {
'zh-CN': {
readMore: '阅读更多',
lastUpdated: '最后更新',
contributors: '贡献者'
},
'en-US': {
readMore: 'Read More',
lastUpdated: 'Last Updated',
contributors: 'Contributors'
}
}vue
<!-- 使用翻译 -->
<script setup lang="ts">
import { useData } from 'vitepress'
import { messages } from '../locales/messages'
const { lang } = useData()
const t = (key: string) => messages[lang as keyof typeof messages]?.[key as keyof typeof messages['zh-CN']] || key
</script>
<template>
<span>{{ t('readMore') }}</span>
</template>进阶挑战
| 挑战 | 难度 | 说明 |
|---|---|---|
| 自动翻译 CI 集成 | ⭐⭐⭐ | PR 更新中文时自动翻译并提交英文版本 |
| 翻译记忆系统 | ⭐⭐⭐⭐ | 使用 PO 文件或 JSON 管理翻译,避免重复翻译 |
| 实时预览翻译 | ⭐⭐⭐⭐⭐ | 开发时实时调用翻译 API 预览效果 |