Internationalization (i18n)
Internationalization makes your documentation accessible to a global audience. Rspress provides comprehensive i18n support, making it easy to create multilingual documentation sites.
Understanding i18n
What is i18n?
Internationalization (i18n) is the process of designing software to support multiple languages and regions without requiring engineering changes.
Key Concepts:
- Locales: Language and region combinations (en-US, zh-CN, ja-JP)
- Translation: Converting content from one language to another
- Localization (l10n): Adapting content for specific regions
- Default Locale: The fallback language for your site
Why Internationalize?
- Broader Audience: Reach non-English speaking users
- Better User Experience: Users prefer content in their language
- Improved Engagement: Higher engagement with localized content
- SEO Benefits: Better search rankings in different regions
Basic i18n Setup
Configuration
Configure locales in rspress.config.ts:
// rspress.config.ts
import { defineConfig } from 'rspress/config';
export default defineConfig({
locales: [
{
lang: 'en',
label: 'English',
title: 'My Documentation',
description: 'Documentation site',
},
{
lang: 'zh',
label: '简体中文',
title: '我的文档',
description: '文档网站',
},
{
lang: 'ja',
label: '日本語',
title: '私のドキュメント',
description: 'ドキュメントサイト',
},
],
});Directory Structure
Organize content by locale:
docs/
├── en/ # English content
│ ├── index.md
│ ├── guide/
│ │ ├── getting-started.md
│ │ └── configuration.md
│ └── api/
│ └── reference.md
├── zh/ # Chinese content
│ ├── index.md
│ ├── guide/
│ │ ├── getting-started.md
│ │ └── configuration.md
│ └── api/
│ └── reference.md
└── ja/ # Japanese content
├── index.md
└── guide/
└── getting-started.mdDefault Locale
Set the default locale:
export default defineConfig({
defaultLocale: 'en', // Default language
locales: [
{
lang: 'en',
label: 'English',
},
{
lang: 'zh',
label: '简体中文',
},
],
});Language Switching
Automatic Language Selector
Rspress automatically adds a language selector to the navbar:
// No additional configuration needed!
// Language selector appears automatically when multiple locales are configuredCustom Language Selector Position
Control where the language selector appears:
export default defineConfig({
themeConfig: {
// Language selector appears in top-right by default
// Can be customized via theme
},
});Language Detection
Rspress can detect user's preferred language:
export default defineConfig({
locales: [...],
// Automatically redirect based on browser language
// User can still manually switch languages
});Translating Content
Page Translation
Create corresponding files for each language:
<!-- docs/en/guide/getting-started.md -->
---
title: Getting Started
description: Learn how to get started
---
# Getting Started
Welcome to our documentation!
## Installation
Run the following command:<!-- docs/zh/guide/getting-started.md -->
---
title: 快速开始
description: 学习如何开始使用
---
# 快速开始
欢迎使用我们的文档!
## 安装
运行以下命令:Maintaining URL Structure
Keep URLs consistent across languages:
English: /en/guide/getting-started
Chinese: /zh/guide/getting-started
Japanese: /ja/guide/getting-startedFrontmatter Translation
Translate frontmatter metadata:
<!-- English -->
---
title: Advanced Configuration
description: Learn advanced configuration options
keywords: [config, advanced, setup]
---
<!-- Chinese -->
---
title: 高级配置
description: 学习高级配置选项
keywords: [配置, 高级, 设置]
---Navigation Translation
Per-locale Navigation
Configure different navigation for each language:
export default defineConfig({
locales: [
{
lang: 'en',
label: 'English',
themeConfig: {
nav: [
{ text: 'Home', link: '/en/' },
{ text: 'Guide', link: '/en/guide/' },
{ text: 'API', link: '/en/api/' },
],
sidebar: {
'/en/guide/': [
{ text: 'Getting Started', link: '/en/guide/getting-started' },
{ text: 'Configuration', link: '/en/guide/configuration' },
],
},
},
},
{
lang: 'zh',
label: '简体中文',
themeConfig: {
nav: [
{ text: '首页', link: '/zh/' },
{ text: '指南', link: '/zh/guide/' },
{ text: 'API', link: '/zh/api/' },
],
sidebar: {
'/zh/guide/': [
{ text: '快速开始', link: '/zh/guide/getting-started' },
{ text: '配置', link: '/zh/guide/configuration' },
],
},
},
},
],
});Shared Navigation Structure
Use the same structure with different labels:
const createNav = (t: Record<string, string>) => [
{ text: t.home, link: '/' },
{ text: t.guide, link: '/guide/' },
{ text: t.api, link: '/api/' },
];
export default defineConfig({
locales: [
{
lang: 'en',
themeConfig: {
nav: createNav({
home: 'Home',
guide: 'Guide',
api: 'API',
}),
},
},
{
lang: 'zh',
themeConfig: {
nav: createNav({
home: '首页',
guide: '指南',
api: 'API',
}),
},
},
],
});UI Text Translation
Theme Text
Translate built-in UI text:
export default defineConfig({
locales: [
{
lang: 'en',
themeConfig: {
// Customize UI text
searchPlaceholder: 'Search documentation',
lastUpdated: 'Last Updated',
editLink: 'Edit this page',
},
},
{
lang: 'zh',
themeConfig: {
searchPlaceholder: '搜索文档',
lastUpdated: '最后更新',
editLink: '编辑本页',
},
},
],
});Custom UI Text
Define custom translations:
// locales/en.ts
export const en = {
common: {
readMore: 'Read More',
download: 'Download',
share: 'Share',
},
guide: {
next: 'Next Step',
previous: 'Previous',
},
};
// locales/zh.ts
export const zh = {
common: {
readMore: '阅读更多',
download: '下载',
share: '分享',
},
guide: {
next: '下一步',
previous: '上一步',
},
};Use in components:
import { useI18n } from 'rspress/runtime';
import { en } from './locales/en';
import { zh } from './locales/zh';
const translations = { en, zh };
export function CustomComponent() {
const { lang } = useI18n();
const t = translations[lang] || translations.en;
return (
<button>{t.common.readMore}</button>
);
}Language-specific Features
Date Formatting
Format dates per locale:
import { useI18n } from 'rspress/runtime';
export function DateDisplay({ date }: { date: Date }) {
const { lang } = useI18n();
const formatted = new Intl.DateTimeFormat(lang, {
year: 'numeric',
month: 'long',
day: 'numeric',
}).format(date);
return <time>{formatted}</time>;
}Number Formatting
export function NumberDisplay({ value }: { value: number }) {
const { lang } = useI18n();
const formatted = new Intl.NumberFormat(lang).format(value);
return <span>{formatted}</span>;
}Currency Formatting
export function PriceDisplay({ amount }: { amount: number }) {
const { lang } = useI18n();
const currencyMap = {
en: 'USD',
zh: 'CNY',
ja: 'JPY',
};
const formatted = new Intl.NumberFormat(lang, {
style: 'currency',
currency: currencyMap[lang] || 'USD',
}).format(amount);
return <span>{formatted}</span>;
}RTL Support
Right-to-Left Languages
Support RTL languages (Arabic, Hebrew):
export default defineConfig({
locales: [
{
lang: 'en',
label: 'English',
},
{
lang: 'ar',
label: 'العربية',
dir: 'rtl', // Enable RTL
},
],
});RTL CSS
Handle RTL styling:
/* Automatically flipped in RTL mode */
.element {
margin-left: 20px; /* Becomes margin-right in RTL */
text-align: left; /* Becomes text-align: right in RTL */
}
/* Explicit RTL handling */
[dir='rtl'] .element {
/* RTL-specific styles */
}
[dir='ltr'] .element {
/* LTR-specific styles */
}SEO for Multilingual Sites
HTML Lang Attribute
Rspress automatically sets the lang attribute:
<!-- English page -->
<html lang="en">
<!-- Chinese page -->
<html lang="zh">hreflang Tags
Add alternate language links:
export default defineConfig({
head: [
['link', { rel: 'alternate', hreflang: 'en', href: 'https://example.com/en/' }],
['link', { rel: 'alternate', hreflang: 'zh', href: 'https://example.com/zh/' }],
['link', { rel: 'alternate', hreflang: 'x-default', href: 'https://example.com/en/' }],
],
});Sitemap for Multiple Languages
Generate multilingual sitemap:
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
<url>
<loc>https://example.com/en/guide</loc>
<xhtml:link rel="alternate" hreflang="zh" href="https://example.com/zh/guide"/>
<xhtml:link rel="alternate" hreflang="en" href="https://example.com/en/guide"/>
</url>
<url>
<loc>https://example.com/zh/guide</loc>
<xhtml:link rel="alternate" hreflang="en" href="https://example.com/en/guide"/>
<xhtml:link rel="alternate" hreflang="zh" href="https://example.com/zh/guide"/>
</url>
</urlset>Translation Workflow
Translation Process
- Write Original Content (usually English)
- Prepare for Translation
- Extract translatable text
- Note context for translators
- Translate Content
- Professional translators
- Community contributions
- Machine translation + review
- Review & QA
- Native speakers review
- Check formatting
- Verify links
- Deploy
Translation Tools
Use tools to manage translations:
# Extract translatable strings
npm run extract-i18n
# Generate translation templates
npm run generate-translations
# Validate translations
npm run validate-i18nVersion Control
Track translations separately:
.
├── docs/
│ ├── en/ # English (source)
│ └── zh/ # Chinese (translations)
└── translations/
├── status.json # Translation status
├── glossary.md # Translation glossary
└── guide.md # Translation guidePartial Translation
Fallback to Default
Show default locale when translation is missing:
export default defineConfig({
locales: [
{
lang: 'en',
label: 'English',
},
{
lang: 'zh',
label: '简体中文',
fallback: 'en', // Use English if Chinese not available
},
],
});Translation Status
Show translation status:
// components/TranslationStatus.tsx
export function TranslationStatus() {
const { lang } = useI18n();
if (lang !== 'en') {
return (
<div className="translation-notice">
🌐 This page is available in English. Help us translate!
</div>
);
}
return null;
}Best Practices
Content Organization
- Keep Structure Identical
✅ Good:
docs/en/guide/getting-started.md
docs/zh/guide/getting-started.md
❌ Bad:
docs/en/guide/start.md
docs/zh/guide/getting-started.md- Use Consistent URLs
✅ Good: /en/guide/api vs /zh/guide/api
❌ Bad: /en/guide/api vs /zh/zhinan/apiTranslation Quality
- Professional Translation: Use professional translators when possible
- Native Review: Have native speakers review translations
- Context: Provide context for translators
- Consistency: Use glossaries for consistent terminology
Maintenance
- Keep Translations in Sync
# Script to find outdated translations
npm run check-translations
# Output: zh/guide/config.md is outdated- Version Translations
<!-- Add translation date -->
---
title: Getting Started
translationDate: 2024-01-15
sourceHash: abc123
---Testing
Test all languages:
# Test each locale
npm run dev -- --locale en
npm run dev -- --locale zh
# Build all locales
npm run build
# Test language switching
npm run previewTroubleshooting
Missing Translations
- Check file path: Ensure file exists in locale directory
- Verify config: Check locale configuration
- Check fallback: Ensure fallback is configured
Incorrect Language Display
- Check HTML lang: Verify lang attribute in HTML
- Check URL: Ensure URL includes locale prefix
- Clear cache: Browser or build cache issues
Encoding Issues
// Ensure UTF-8 encoding
export default defineConfig({
head: [
['meta', { charset: 'utf-8' }],
],
});Next Steps
- Learn about Deployment with multiple languages
- Explore Static Assets localization
- Read about SSG/SSR for i18n sites
Translation Tips
- Start with high-traffic pages
- Use professional translators for critical content
- Maintain a glossary for consistency
- Test with native speakers
- Keep translations synchronized
Common Mistakes
- Don't use machine translation without review
- Don't forget to translate navigation
- Don't hardcode text in components
- Don't mix languages in URLs