文件结构
显示 supported language codes
显示 supported language codes
ar- 阿拉伯语ca- 加泰罗尼亚语cs- 捷克语zhorzh-Hans- 中文(简体)zh-Hant- 中文(繁体)de- 德语en- 英语es- 西班牙语fr- 法语he- 希伯来语hi- 印地语id- 印度尼西亚语it- 意大利语jp- 日语ko- 韩语lv- 拉脱维亚语nl- 荷兰语no- 挪威语pl- 波兰语ptorpt-BR- 葡萄牙语ro- 罗马尼亚语ru- 俄语sv- 瑞典语tr- 土耳其语ua- 乌克兰语uz- 乌兹别克语vi- 越南语
Example file structure
报告错误代码
复制
询问AI
docs/
├── index.mdx # English (default)
├── quickstart.mdx
├── fr/
│ ├── index.mdx # French
│ ├── quickstart.mdx
├── es/
│ ├── index.mdx # Spanish
│ ├── quickstart.mdx
└── zh/
├── index.mdx # Chinese
└── quickstart.mdx
在所有语言中保持相同的文件名和目录结构,这样更便于维护翻译并识别缺失的内容。
配置语言切换器
docs.json 的 navigation 配置中设置 languages 数组。
docs.json
报告错误代码
复制
询问AI
{
"navigation": {
"languages": [
{
"language": "en",
"groups": [
{
"group": "快速入门",
"pages": ["index", "quickstart"]
}
]
},
{
"language": "es",
"groups": [
{
"group": "Comenzando",
"pages": ["es/index", "es/quickstart"]
}
]
}
]
}
}
languages 数组中的每个语言条目都需要:
language:ISO 639-1 语言代码- 完整的导航结构
- 指向已翻译文件的路径
设置默认语言
languages 数组中的第一个语言会自动作为默认语言。若要使用其他语言作为默认语言,可以重新调整数组顺序,或添加 default 属性:
docs.json
报告错误代码
复制
询问AI
{
"navigation": {
"languages": [
{
"language": "es",
"groups": [...]
},
{
"language": "en",
"groups": [...]
}
]
}
}
default 属性来指定顺序:
docs.json
报告错误代码
复制
询问AI
{
"navigation": {
"languages": [
{
"language": "en",
"groups": [...]
},
{
"language": "es",
"default": true,
"groups": [...]
}
]
}
}
单语言文档
languages 字段,而是直接定义导航结构:
docs.json
报告错误代码
复制
询问AI
{
"navigation": {
"tabs": [
{
"tab": "Documentation",
"groups": [
{
"group": "Getting started",
"pages": ["index", "quickstart"]
}
]
}
]
}
}
将导航标签(例如分组或标签页名称)翻译为与内容语言一致的文本,可以为用户提供完全本地化的体验。
全局导航
docs.json 的 navigation 配置中设置 global 对象。
docs.json
报告错误代码
复制
询问AI
{
"navigation": {
"global": {
"anchors": [
{
"anchor": "Documentation",
"href": "https://example.com/docs"
},
{
"anchor": "Blog",
"href": "https://example.com/blog"
}
]
},
"languages": [
// 特定语言导航
]
}
}
维护翻译
翻译工作流程
- 在主语言中更新源内容。
- 确定已更改的内容。
- 翻译已更改的内容。
- 审核译文的准确性。
- 更新翻译文件。
- 验证导航和链接是否正常工作。
自动化翻译
外部翻译服务商
- 导出源内容:提取需要翻译的 MDX 文件。
- 发送给译员:将文件提供给翻译服务商。
- 接收译文:取回翻译后的 MDX 文件。
- 导入并部署:将翻译文件添加到语言目录中,并更新导航。
.github/workflows/export-for-translation.yml
报告错误代码
复制
询问AI
name: Export content for translation
on:
push:
branches: [main]
paths:
- '*.mdx'
- '!es/**'
- '!fr/**'
- '!zh/**'
# Prevent concurrent workflow runs to avoid race conditions
concurrency:
group: translation-export-${{ github.ref }}
cancel-in-progress: false
jobs:
export:
runs-on: ubuntu-latest
# Early exit if no changes detected (optional - acts as additional safety)
outputs:
files-changed: ${{ steps.changed.outputs.has-files }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 2
- name: Get changed MDX files
id: changed
run: |
# Check if parent commit exists (handles initial push)
if ! git rev-parse HEAD~1 >/dev/null 2>&1; then
echo "has-files=false" >> $GITHUB_OUTPUT
echo "files=" >> $GITHUB_OUTPUT
echo "No parent commit found - skipping export"
exit 0
fi
# Get list of changed MDX files (excluding translation dirs)
files=$(git diff --name-only HEAD~1 HEAD -- '*.mdx' ':!es/' ':!fr/' ':!zh/' | tr '\n' ' ')
if [ -z "$files" ]; then
echo "has-files=false" >> $GITHUB_OUTPUT
echo "files=" >> $GITHUB_OUTPUT
echo "No MDX files changed - skipping export"
else
echo "has-files=true" >> $GITHUB_OUTPUT
echo "files=$files" >> $GITHUB_OUTPUT
echo "Found changed files: $files"
fi
shell: bash
- name: Create translation package directory
if: steps.changed.outputs.has-files == 'true'
run: |
mkdir -p translation-export
echo "Created translation-export directory"
- name: Copy changed files to export directory
if: steps.changed.outputs.has-files == 'true'
run: |
failed_count=0
for file in ${{ steps.changed.outputs.files }}; do
if [ -f "$file" ]; then
target_dir="translation-export/$(dirname "$file")"
mkdir -p "$target_dir"
cp "$file" "$target_dir/"
echo "✓ Copied: $file"
else
echo "✗ File not found: $file"
((failed_count++))
fi
done
if [ $failed_count -gt 0 ]; then
echo "Warning: $failed_count file(s) could not be copied"
fi
shell: bash
- name: Validate translation package
if: steps.changed.outputs.has-files == 'true'
run: |
echo "Translation package contents:"
find translation-export -type f -name "*.mdx" | sort
echo ""
file_count=$(find translation-export -type f -name "*.mdx" | wc -l)
echo "Total MDX files: $file_count"
- name: Upload translation package
if: steps.changed.outputs.has-files == 'true'
uses: actions/upload-artifact@v4
with:
name: translation-export-${{ github.sha }}
path: translation-export/
retention-days: 30
if-no-files-found: error
compression-level: 9
- name: Print job summary
if: steps.changed.outputs.has-files == 'true'
run: |
echo "## Translation Export Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Artifact:** \`translation-export-${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Changed Files:**" >> $GITHUB_STEP_SUMMARY
echo "${{ steps.changed.outputs.files }}" | tr ' ' '\n' | sed 's/^/- /' >> $GITHUB_STEP_SUMMARY
.github/workflows/import-translations.yml
报告错误代码
复制
询问AI
name: Import translations
on:
pull_request:
paths:
- 'es/**'
- 'fr/**'
- 'zh/**'
# Define explicit permissions
permissions:
contents: read
pull-requests: write
jobs:
validate:
runs-on: ubuntu-latest
outputs:
validation-status: ${{ steps.final-check.outputs.status }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history to ensure origin/main is available
- name: Fetch origin/main reference
run: |
git fetch origin main:origin/main 2>/dev/null || echo "origin/main not available, using latest"
continue-on-error: true
- name: Get changed translation files
id: changed-files
run: |
# Get all changed MDX files in translation directories
files=$(git diff --name-only origin/main..HEAD -- 'es/**/*.mdx' 'fr/**/*.mdx' 'zh/**/*.mdx' | sort)
if [ -z "$files" ]; then
echo "No translation MDX files detected in this PR"
echo "files=" >> $GITHUB_OUTPUT
echo "count=0" >> $GITHUB_OUTPUT
else
echo "Found $(echo "$files" | wc -l) translation files"
echo "$files"
echo "files=$files" >> $GITHUB_OUTPUT
echo "count=$(echo "$files" | wc -l)" >> $GITHUB_OUTPUT
fi
shell: bash
- name: Validate frontmatter
id: frontmatter
if: steps.changed-files.outputs.count > 0
run: |
failed_files=()
success_count=0
total=${{ steps.changed-files.outputs.count }}
while IFS= read -r file; do
if [ ! -f "$file" ]; then
echo "✗ File not found: $file"
failed_files+=("$file")
continue
fi
# Check for valid frontmatter (lines 1-2 must be ---)
first_line=$(sed -n '1p' "$file")
second_line=$(sed -n '2p' "$file")
last_line=$(awk 'NF' "$file" | tail -1)
if [ "$first_line" = "---" ] && grep -q "^---$" "$file"; then
echo "✓ Valid frontmatter: $file"
((success_count++))
else
echo "✗ Invalid frontmatter in $file"
echo " Line 1: '$first_line'"
failed_files+=("$file")
fi
done <<< "${{ steps.changed-files.outputs.files }}"
echo ""
echo "Frontmatter check: $success_count/$total passed"
if [ ${#failed_files[@]} -gt 0 ]; then
echo "frontmatter_valid=false" >> $GITHUB_OUTPUT
printf 'failed_files=%s\n' "${failed_files[@]}" >> $GITHUB_OUTPUT
else
echo "frontmatter_valid=true" >> $GITHUB_OUTPUT
fi
shell: bash
- name: Check file structure
id: structure
if: steps.changed-files.outputs.count > 0
run: |
missing_sources=()
orphaned_count=0
while IFS= read -r translated_file; do
# Extract language and relative path
# e.g., "es/docs/guide.mdx" -> lang="es", relative_path="docs/guide.mdx"
lang=$(echo "$translated_file" | cut -d'/' -f1)
relative_path=$(echo "$translated_file" | cut -d'/' -f2-)
source_file="$relative_path"
if [ ! -f "$source_file" ]; then
echo "Missing source: $translated_file -> $source_file"
missing_sources+=("$translated_file")
((orphaned_count++))
else
echo "✓ Found source: $translated_file -> $source_file"
fi
done <<< "${{ steps.changed-files.outputs.files }}"
echo ""
echo "Structure check: $orphaned_count orphaned file(s)"
if [ $orphaned_count -gt 0 ]; then
echo "structure_valid=false" >> $GITHUB_OUTPUT
printf 'missing_sources=%s\n' "${missing_sources[@]}" >> $GITHUB_OUTPUT
else
echo "structure_valid=true" >> $GITHUB_OUTPUT
fi
shell: bash
- name: Validate file integrity
id: integrity
if: steps.changed-files.outputs.count > 0
run: |
integrity_passed=true
while IFS= read -r file; do
# Check file is readable and not empty
if [ ! -r "$file" ] || [ ! -s "$file" ]; then
echo "✗ File integrity issue: $file (not readable or empty)"
integrity_passed=false
fi
# Basic check: file should have content after frontmatter
line_count=$(wc -l < "$file")
if [ "$line_count" -lt 5 ]; then
echo "File is suspiciously short: $file ($line_count lines)"
fi
done <<< "${{ steps.changed-files.outputs.files }}"
if [ "$integrity_passed" = true ]; then
echo "integrity_valid=true" >> $GITHUB_OUTPUT
else
echo "integrity_valid=false" >> $GITHUB_OUTPUT
fi
shell: bash
- name: Generate validation report
if: always()
run: |
echo "## Translation Validation Report" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Files Changed:** ${{ steps.changed-files.outputs.count }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ steps.changed-files.outputs.count }}" = "0" ]; then
echo "No translation MDX files found in this PR" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "> This could mean:" >> $GITHUB_STEP_SUMMARY
echo "- Only non-MDX files in es/, fr/, or zh/ directories were changed" >> $GITHUB_STEP_SUMMARY
echo "- Workflow was triggered but no translation content to validate" >> $GITHUB_STEP_SUMMARY
else
echo "### Validation Results" >> $GITHUB_STEP_SUMMARY
echo "- Frontmatter: ${{ steps.frontmatter.outputs.frontmatter_valid }}" >> $GITHUB_STEP_SUMMARY
echo "- File Structure: ${{ steps.structure.outputs.structure_valid }}" >> $GITHUB_STEP_SUMMARY
echo "- File Integrity: ${{ steps.integrity.outputs.integrity_valid }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
fi
shell: bash
- name: Final validation check
id: final-check
# Only run this check if we actually had MDX files to validate
if: steps.changed-files.outputs.count > 0
run: |
validation_failed=false
if [ "${{ steps.frontmatter.outputs.frontmatter_valid }}" != "true" ]; then
echo "Frontmatter validation failed"
validation_failed=true
fi
if [ "${{ steps.structure.outputs.structure_valid }}" != "true" ]; then
echo "File structure validation failed"
validation_failed=true
fi
if [ "${{ steps.integrity.outputs.integrity_valid }}" != "true" ]; then
echo "File integrity validation failed"
validation_failed=true
fi
if [ "$validation_failed" = true ]; then
echo "status=failed" >> $GITHUB_OUTPUT
exit 1
else
echo "status=passed" >> $GITHUB_OUTPUT
echo "All validations passed"
fi
shell: bash
- name: Handle no-files-to-validate case
# Run only when there are no MDX files to validate
if: steps.changed-files.outputs.count == 0
run: |
echo "No translation MDX files to validate - PR is valid"
echo "status=no-changes" >> ${{ steps.final-check.outputs }}
shell: bash
- 保留 frontmatter:确保译员保持 YAML frontmatter 完整,仅翻译
title和description的值。 - 保护代码块:将代码块标记为“请勿翻译”,并告知翻译供应商。
- 使用翻译记忆库:提供术语表,列出应保留英文或需采用特定译法的技术术语。
- 自动化验证:在合并翻译内容前,使用 CI 检查验证 MDX 语法和 frontmatter。
- 版本控制:跟踪每份译文对应的源版本,以识别过时内容。
图片与媒体
报告错误代码
复制
询问AI
images/
├── dashboard.png # 英文版
├── fr/
│ └── dashboard.png # 法文版
└── es/
└── dashboard.png # 西班牙文版
es/index.mdx
报告错误代码
复制
询问AI

多语言网站的 SEO(搜索引擎优化)
页面元数据
fr/index.mdx
报告错误代码
复制
询问AI
---
title: "开始使用"
description: "了解如何开始使用我们的产品。"
keywords: ["入门", "教程", "指南"]
---
最佳实践
日期和数字格式
- 日期格式:MM/DD/YYYY vs DD/MM/YYYY
- 数字格式:1,000.00 vs 1.000,00
- 货币符号:$100.00 vs 100,00€
保持一致性
- 在所有语言中保持内容对齐,确保每位用户获取同等质量的信息。
- 为技术术语创建翻译术语表。
- 在不同语言中保持相同的内容结构。
- 匹配源内容的语气和风格。
- 使用 Git branch 将翻译工作与主内容更新分开管理。
布局差异
- 导航显示正常且不会被截断。
- 代码块不会溢出。
- 表格和其他格式化文本保持良好的可读性。
- 图片能够适当缩放。