Files
mintlify__docs/zh/guides/internationalization.mdx
2026-03-16 09:26:03 -07:00

761 lines
22 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: "国际化"
description: "设置多语言文档以触达全球用户,包括语言配置、翻译和导航结构。"
keywords: ["国际化", "i18n", "多语言", "翻译", "本地化", "语言切换器"]
---
国际化(i18n)是将软件或内容设计为适配不同语言和地区设置的过程。本指南将说明如何规划文件结构、配置导航,以及高效维护翻译内容,从而帮助用户以其首选语言访问你的文档,并提升全球触达能力。
<div id="file-structure">
## 文件结构
</div>
将翻译内容组织在对应的语言目录中,以保持文档的可维护性,并按语言构建导航结构。
使用 [ISO 639-1 语言代码](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) 为每种语言分别创建一个单独的目录。将已翻译文件放入这些目录中,并保持与默认语言相同的结构。
<Expandable title="supported language codes">
* `ar` - 阿拉伯语
* `ca` - 加泰罗尼亚语
* `cs` - 捷克语
* `zh` or `zh-Hans` - 中文 (简体)
* `zh-Hant` - 中文 (繁体)
* `de` - 德语
* `en` - 英语
* `es` - 西班牙语
* `fr` - 法语
* `he` - 希伯来语
* `hi` - 印地语
* `hu` - 匈牙利语
* `id` - 印度尼西亚语
* `it` - 意大利语
* `jp` - 日语
* `ko` - 韩语
* `lv` - 拉脱维亚语
* `nl` - 荷兰语
* `no` - 挪威语
* `pl` - 波兰语
* `pt` or `pt-BR` - 葡萄牙语
* `ro` - 罗马尼亚语
* `ru` - 俄语
* `sv` - 瑞典语
* `tr` - 土耳其语
* `uk` - 乌克兰语
* `uz` - 乌兹别克语
* `vi` - 越南语
</Expandable>
```text Example file structure
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
```
<Tip>
在所有语言中保持相同的文件名和目录结构,这样更便于维护翻译并识别缺失的内容。
</Tip>
<div id="configure-the-language-switcher">
## 配置语言切换器
</div>
要在文档中添加语言切换器,请在 `docs.json` 的 `navigation` 配置中设置 `languages` 数组。
```json docs.json
{
"navigation": {
"languages": [
{
"language": "en",
"groups": [
{
"group": "快速入门",
"pages": ["index", "quickstart"]
}
]
},
{
"language": "es",
"groups": [
{
"group": "Comenzando",
"pages": ["es/index", "es/quickstart"]
}
]
}
]
}
}
```
`languages` 数组中的每个语言条目都需要:
* `language`:ISO 639-1 语言代码
* 完整的导航结构
* 指向已翻译文件的路径
不同语言的导航结构可以有所不同,以满足各语言特定的内容需求。
<div id="set-default-language">
### 设置默认语言
</div>
`languages` 数组中的第一个语言会自动作为默认语言。若要使用其他语言作为默认语言,可以重新调整数组顺序,或添加 `default` 属性:
```json docs.json
{
"navigation": {
"languages": [
{
"language": "es",
"groups": [...]
},
{
"language": "en",
"groups": [...]
}
]
}
}
```
或者使用 `default` 属性来指定顺序:
```json docs.json
{
"navigation": {
"languages": [
{
"language": "en",
"groups": [...]
},
{
"language": "es",
"default": true,
"groups": [...]
}
]
}
}
```
<div id="single-language-documentation">
### 单语言文档
</div>
如果你只想提供一种语言并且不需要语言切换器,请从 navigation 配置中移除 `languages` 字段,而是直接定义导航结构:
```json docs.json
{
"navigation": {
"tabs": [
{
"tab": "Documentation",
"groups": [
{
"group": "Getting started",
"pages": ["index", "quickstart"]
}
]
}
]
}
}
```
这会以单一语言显示你的文档,并且不提供语言切换器界面。
<Tip>
将导航标签(例如分组或标签页名称)翻译为与内容语言一致的文本,可以为用户提供完全本地化的体验。
</Tip>
<div id="global-navigation">
### 全局导航
</div>
要添加在所有语言版本中都显示的全局导航元素,请在 `docs.json` 的 `navigation` 配置中设置 `global` 对象。
```json docs.json
{
"navigation": {
"global": {
"anchors": [
{
"anchor": "Documentation",
"href": "https://example.com/docs"
},
{
"anchor": "Blog",
"href": "https://example.com/blog"
}
]
},
"languages": [
// 特定语言导航
]
}
}
```
<div id="localized-footer-and-navbar">
### 本地化页脚和导航栏
</div>
为每种语言自定义页脚和导航栏,以显示翻译后的内容和特定区域的链接。
将 `footer` 和 `navbar` 属性添加到每种语言的配置中:
```json docs.json
{
"navigation": {
"languages": [
{
"language": "en",
"footer": {
"socials": {
"x": "https://x.com/mintlify"
},
"links": [
{
"header": "Resources",
"items": [
{ "label": "Documentation", "href": "/en/docs" },
{ "label": "Blog", "href": "https://mintlify.com/blog" }
]
}
]
},
"navbar": {
"links": [
{ "label": "Docs", "href": "/en/docs" }
],
"primary": {
"type": "button",
"label": "Get Started",
"href": "/en/quickstart"
}
},
"groups": [
{
"group": "Getting started",
"pages": ["en/quickstart", "en/index"]
}
]
},
{
"language": "es",
"footer": {
"socials": {
"x": "https://x.com/mintlify"
},
"links": [
{
"header": "Recursos",
"items": [
{ "label": "Documentación", "href": "/es/docs" },
{ "label": "Blog", "href": "https://mintlify.com/blog" }
]
}
]
},
"navbar": {
"links": [
{ "label": "Documentación", "href": "/es/docs" }
],
"primary": {
"type": "button",
"label": "Comenzar",
"href": "/es/quickstart"
}
},
"groups": [
{
"group": "Comenzando",
"pages": ["es/quickstart", "es/index"]
}
]
}
]
}
}
```
语言专属的 `footer` 和 `navbar` 会覆盖该语言的全局设置。如果某种语言未定义这些属性,则会继承全局配置。
你也可以按相同方式配置语言专属的 `banner`。
<div id="maintain-translations">
## 维护翻译
</div>
确保译文准确无误,并与源内容保持同步。
<div id="translation-workflow">
### 翻译工作流程
</div>
1. 在主语言中更新源内容。
2. 确定已更改的内容。
3. 翻译已更改的内容。
4. 审核译文的准确性。
5. 更新翻译文件。
6. 验证导航和链接是否正常工作。
<div id="automated-translations">
### 自动化翻译
</div>
如需自动化翻译解决方案,请[联系 Mintlify 销售团队](mailto:gtm@mintlify.com)。
<div id="external-translation-providers">
### 外部翻译服务商
</div>
如果你与自己的翻译服务商或区域译员合作,可以使用 GitHub Actions 或类似的 CI/CD 工具,将他们的工作流程集成到 Mintlify 文档中。
1. **导出源内容**:提取需要翻译的 MDX 文件。
2. **发送给译员**:将文件提供给翻译服务商。
3. **接收译文**:取回翻译后的 MDX 文件。
4. **导入并部署**:将翻译文件添加到语言目录中,并更新导航。
当 PR 合并到 main 时,此 GitHub Actions 工作流程会自动导出已更改的英文内容以供翻译。
```yaml .github/workflows/export-for-translation.yml
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 Actions 工作流程会在通过 PR 添加翻译内容时进行验证并导入。
```yaml .github/workflows/import-translations.yml
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。
- **版本控制**:跟踪每份译文对应的源版本,以识别过时内容。
<div id="images-and-media">
### 图片与媒体
</div>
将各语言版本的图片存放在对应的语言目录中。
```
images/
├── dashboard.png # 英文版
├── fr/
│ └── dashboard.png # 法文版
└── es/
└── dashboard.png # 西班牙文版
```
在译文中使用相对路径引用图像。
```mdx es/index.mdx
![控制台截图](/images/es/dashboard.png)
```
<div id="seo-for-multi-language-sites">
## 多语言网站的 SEO(搜索引擎优化)
</div>
针对每种语言版本进行搜索引擎优化。
<div id="page-metadata">
### 页面元数据
</div>
在每个文件的 frontmatter 中包含已翻译的页面元数据:
```mdx fr/index.mdx
---
title: "开始使用"
description: "了解如何开始使用我们的产品。"
keywords: ["入门", "教程", "指南"]
---
```
<div id="best-practices">
## 最佳实践
</div>
<div id="date-and-number-formats">
### 日期和数字格式
</div>
注意针对不同地区使用合适的日期和数字格式。
- 日期格式:MM/DD/YYYY vs DD/MM/YYYY
- 数字格式:1,000.00 vs 1.000,00
- 货币符号:$100.00 vs 100,00€
为每种语言使用相应格式的示例,或采用通用且易于理解的格式。
<div id="maintain-consistency">
### 保持一致性
</div>
- 在所有语言中保持内容对齐,确保每位用户获取同等质量的信息。
- 为技术术语创建翻译术语表。
- 在不同语言中保持相同的内容结构。
- 匹配源内容的语气和风格。
- 使用 Git branch 将翻译工作与主内容更新分开管理。
<div id="layout-differences">
### 布局差异
</div>
有些语言相比英语需要更多或更少的空间。请在不同屏幕尺寸上测试你的翻译内容,以确保:
- 导航显示正常且不会被截断。
- 代码块不会溢出。
- 表格和其他格式化文本保持良好的可读性。
- 图片能够适当缩放。
<div id="character-encoding">
### 字符编码
</div>
请确保你的开发环境和部署流水线支持 UTF-8 编码,以便正确显示使用不同字母表并包含特殊字符的各种语言中的所有字符。