Pulled ALL skills from 15 source repositories: - anthropics/skills: 16 (docs, design, MCP, testing) - obra/superpowers: 14 (TDD, debugging, agents, planning) - coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth) - better-auth/skills: 5 (auth patterns) - vercel-labs/agent-skills: 5 (React, design, Vercel) - antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo) - Plus 13 individual skills from various repos Mosaic Stack is not limited to coding — the Orchestrator and subagents serve coding, business, design, marketing, writing, logistics, analysis, and more. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
300 lines
5.2 KiB
Markdown
300 lines
5.2 KiB
Markdown
---
|
|
name: vitepress-internationalization
|
|
description: Setting up multi-language sites with locale configuration and RTL support
|
|
---
|
|
|
|
# Internationalization
|
|
|
|
VitePress supports multi-language sites through locale configuration.
|
|
|
|
## Directory Structure
|
|
|
|
Organize content by locale:
|
|
|
|
```
|
|
docs/
|
|
├─ en/
|
|
│ ├─ guide.md
|
|
│ └─ index.md
|
|
├─ zh/
|
|
│ ├─ guide.md
|
|
│ └─ index.md
|
|
└─ fr/
|
|
├─ guide.md
|
|
└─ index.md
|
|
```
|
|
|
|
Or with root as default language:
|
|
|
|
```
|
|
docs/
|
|
├─ guide.md # English (root)
|
|
├─ index.md
|
|
├─ zh/
|
|
│ ├─ guide.md
|
|
│ └─ index.md
|
|
└─ fr/
|
|
├─ guide.md
|
|
└─ index.md
|
|
```
|
|
|
|
## Configuration
|
|
|
|
```ts
|
|
// .vitepress/config.ts
|
|
import { defineConfig } from 'vitepress'
|
|
|
|
export default defineConfig({
|
|
locales: {
|
|
root: {
|
|
label: 'English',
|
|
lang: 'en'
|
|
},
|
|
zh: {
|
|
label: '简体中文',
|
|
lang: 'zh-CN',
|
|
link: '/zh/'
|
|
},
|
|
fr: {
|
|
label: 'Français',
|
|
lang: 'fr',
|
|
link: '/fr/'
|
|
}
|
|
}
|
|
})
|
|
```
|
|
|
|
## Locale-Specific Config
|
|
|
|
Override site config per locale:
|
|
|
|
```ts
|
|
locales: {
|
|
root: {
|
|
label: 'English',
|
|
lang: 'en',
|
|
title: 'My Docs',
|
|
description: 'Documentation site',
|
|
themeConfig: {
|
|
nav: [
|
|
{ text: 'Guide', link: '/guide/' }
|
|
],
|
|
sidebar: {
|
|
'/guide/': [
|
|
{ text: 'Introduction', link: '/guide/' }
|
|
]
|
|
}
|
|
}
|
|
},
|
|
zh: {
|
|
label: '简体中文',
|
|
lang: 'zh-CN',
|
|
link: '/zh/',
|
|
title: '我的文档',
|
|
description: '文档站点',
|
|
themeConfig: {
|
|
nav: [
|
|
{ text: '指南', link: '/zh/guide/' }
|
|
],
|
|
sidebar: {
|
|
'/zh/guide/': [
|
|
{ text: '介绍', link: '/zh/guide/' }
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Locale-Specific Properties
|
|
|
|
Each locale can override:
|
|
|
|
```ts
|
|
interface LocaleSpecificConfig {
|
|
lang?: string
|
|
dir?: string // 'ltr' or 'rtl'
|
|
title?: string
|
|
titleTemplate?: string | boolean
|
|
description?: string
|
|
head?: HeadConfig[] // Merged with existing
|
|
themeConfig?: ThemeConfig // Shallow merged
|
|
}
|
|
```
|
|
|
|
## Search i18n
|
|
|
|
### Local Search
|
|
|
|
```ts
|
|
themeConfig: {
|
|
search: {
|
|
provider: 'local',
|
|
options: {
|
|
locales: {
|
|
zh: {
|
|
translations: {
|
|
button: {
|
|
buttonText: '搜索',
|
|
buttonAriaLabel: '搜索'
|
|
},
|
|
modal: {
|
|
noResultsText: '没有结果',
|
|
resetButtonTitle: '重置搜索',
|
|
footer: {
|
|
selectText: '选择',
|
|
navigateText: '导航',
|
|
closeText: '关闭'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Algolia Search
|
|
|
|
```ts
|
|
themeConfig: {
|
|
search: {
|
|
provider: 'algolia',
|
|
options: {
|
|
appId: '...',
|
|
apiKey: '...',
|
|
indexName: '...',
|
|
locales: {
|
|
zh: {
|
|
placeholder: '搜索文档',
|
|
translations: {
|
|
button: { buttonText: '搜索文档' }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Separate Locale Directories
|
|
|
|
For fully separated locales without root fallback:
|
|
|
|
```
|
|
docs/
|
|
├─ en/
|
|
│ └─ index.md
|
|
├─ zh/
|
|
│ └─ index.md
|
|
└─ fr/
|
|
└─ index.md
|
|
```
|
|
|
|
Requires server redirect for `/` → `/en/`. Netlify example:
|
|
|
|
```
|
|
/* /en/:splat 302 Language=en
|
|
/* /zh/:splat 302 Language=zh
|
|
/* /en/:splat 302
|
|
```
|
|
|
|
## Persisting Language Choice
|
|
|
|
Set cookie on language change:
|
|
|
|
```vue
|
|
<!-- .vitepress/theme/Layout.vue -->
|
|
<script setup>
|
|
import DefaultTheme from 'vitepress/theme'
|
|
import { useData, inBrowser } from 'vitepress'
|
|
import { watchEffect } from 'vue'
|
|
|
|
const { lang } = useData()
|
|
|
|
watchEffect(() => {
|
|
if (inBrowser) {
|
|
document.cookie = `nf_lang=${lang.value}; expires=Mon, 1 Jan 2030 00:00:00 UTC; path=/`
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<DefaultTheme.Layout />
|
|
</template>
|
|
```
|
|
|
|
## RTL Support (Experimental)
|
|
|
|
For right-to-left languages:
|
|
|
|
```ts
|
|
locales: {
|
|
ar: {
|
|
label: 'العربية',
|
|
lang: 'ar',
|
|
dir: 'rtl'
|
|
}
|
|
}
|
|
```
|
|
|
|
Requires PostCSS plugin like `postcss-rtlcss`:
|
|
|
|
```ts
|
|
// postcss.config.js
|
|
import rtlcss from 'postcss-rtlcss'
|
|
|
|
export default {
|
|
plugins: [
|
|
rtlcss({
|
|
ltrPrefix: ':where([dir="ltr"])',
|
|
rtlPrefix: ':where([dir="rtl"])'
|
|
})
|
|
]
|
|
}
|
|
```
|
|
|
|
## Organizing Config
|
|
|
|
Split config into separate files:
|
|
|
|
```
|
|
.vitepress/
|
|
├─ config/
|
|
│ ├─ index.ts # Main config, merges locales
|
|
│ ├─ en.ts # English config
|
|
│ ├─ zh.ts # Chinese config
|
|
│ └─ shared.ts # Shared config
|
|
```
|
|
|
|
```ts
|
|
// .vitepress/config/index.ts
|
|
import { defineConfig } from 'vitepress'
|
|
import { shared } from './shared'
|
|
import { en } from './en'
|
|
import { zh } from './zh'
|
|
|
|
export default defineConfig({
|
|
...shared,
|
|
locales: {
|
|
root: { label: 'English', ...en },
|
|
zh: { label: '简体中文', ...zh }
|
|
}
|
|
})
|
|
```
|
|
|
|
## Key Points
|
|
|
|
- Use `locales` object in config with `root` for default language
|
|
- Each locale can override title, description, and themeConfig
|
|
- `themeConfig` is shallow merged (define complete nav/sidebar per locale)
|
|
- Don't override `themeConfig.algolia` at locale level
|
|
- `dir: 'rtl'` enables RTL with PostCSS plugin
|
|
- Language switcher appears automatically in nav
|
|
|
|
<!--
|
|
Source references:
|
|
- https://vitepress.dev/guide/i18n
|
|
-->
|