Files
mintlify__docs/zh/guides/internationalization.mdx
mintlify[bot] 6a95003775 Add warning about duplicating paths across locales (#5487)
* Add warning about duplicating paths across locales

Generated-By: mintlify-agent

* Apply suggestion from @ethanpalm

---------

Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
Co-authored-by: Ethan Palm <56270045+ethanpalm@users.noreply.github.com>
2026-04-22 11:01:09 -07:00

793 lines
25 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: "如何设置多语言文档"
sidebarTitle: "国际化"
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` - 西班牙语
* `fi` - 芬兰语
* `fr` - 法语
* `fr-CA` - 法语 (加拿大)
* `he` - 希伯来语
* `hi` - 印地语
* `hu` - 匈牙利语
* `id` - 印度尼西亚语
* `it` - 意大利语
* `ja` - 日语
* `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 语言代码
* 完整的导航结构
* 指向已翻译文件的路径
不同语言的导航结构可以有所不同,以满足各语言特定的内容需求。
<Warning>
请勿在多个语言环境中使用相同的页面路径。跨语言环境复制路径会导致未定义的行为。每个页面路径应仅出现在一种语言的导航中。
</Warning>
<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 编码,以便正确显示使用不同字母表并包含特殊字符的各种语言中的所有字符。
<div id="frequently-asked-questions">
## 常见问题
</div>
<AccordionGroup>
<Accordion title="在上线新语言之前,是否需要翻译每一个页面?">
不需要。你可以在部分翻译的情况下上线某种语言,并随着时间逐步扩展。常见的做法是先翻译访问量最高的页面——通常是入门内容、认证相关内容和热门操作指南——将访问量较低的参考内容保留为默认语言,直到翻译完成。用户通常更喜欢有一些翻译内容,而不是完全没有。
</Accordion>
<Accordion title="当翻译页面缺失时会发生什么?">
如果用户访问一个不存在的翻译 URL,他们会看到 404 页面。为避免这种情况,你可以只在对应语言的导航中包含已翻译的页面,或者保持默认语言和翻译内容之间的一致性。在各语言之间使用相同的文件结构可以方便地识别哪些翻译缺失。
</Accordion>
<Accordion title="导航标签是否需要翻译?">
是的。导航标签——分组名称、标签页标题、锚点文本——应当与内容语言一致。在西班牙语文档中出现英文的"Getting started"标签会造成不协调的体验。Mintlify 支持特定语言的导航结构,因此每种语言都可以拥有完全翻译的标签。
</Accordion>
<Accordion title="如何处理翻译内容中的代码示例?">
代码本身不应翻译——变量名、函数调用和语法是语言无关的。代码块中的注释如果用于解释用户需要理解的概念,可以进行翻译。代码块周围的说明文字应完全翻译。
</Accordion>
<Accordion title="Mintlify 是否支持从右到左书写的语言,如阿拉伯语或希伯来语?">
是的。阿拉伯语(`ar`)和希伯来语(`he`)都在支持的语言代码列表中。当配置了这些语言代码时,Mintlify 会自动处理 RTL 布局。请测试你的文档在 RTL 模式下的显示效果,以验证导航、表格和代码块是否正确显示。
</Accordion>
</AccordionGroup>