跳转到主要内容
国际化(i18n)是将软件或内容设计为适配不同语言和地区设置的过程。本指南将说明如何规划文件结构、配置导航,以及高效维护翻译内容,从而帮助用户以其首选语言访问你的文档,并提升全球触达能力。

文件结构

将翻译内容组织在对应的语言目录中,以保持文档的可维护性,并按语言构建导航结构。 使用 ISO 639-1 语言代码 为每种语言分别创建一个单独的目录。将已翻译文件放入这些目录中,并保持与默认语言相同的结构。
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
在所有语言中保持相同的文件名和目录结构,这样更便于维护翻译并识别缺失的内容。

配置语言切换器

要在文档中添加语言切换器,请在 docs.jsonnavigation 配置中设置 languages 数组。
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 语言代码
  • 完整的导航结构
  • 指向已翻译文件的路径
不同语言的导航结构可以有所不同,以满足各语言特定的内容需求。

设置默认语言

languages 数组中的第一个语言会自动作为默认语言。若要使用其他语言作为默认语言,可以重新调整数组顺序,或添加 default 属性:
docs.json
{
  "navigation": {
    "languages": [
      {
        "language": "es",
        "groups": [...]
      },
      {
        "language": "en",
        "groups": [...]
      }
    ]
  }
}
或者使用 default 属性来指定顺序:
docs.json
{
  "navigation": {
    "languages": [
      {
        "language": "en",
        "groups": [...]
      },
      {
        "language": "es",
        "default": true,
        "groups": [...]
      }
    ]
  }
}

单语言文档

如果你只想提供一种语言并且不需要语言切换器,请从 navigation 配置中移除 languages 字段,而是直接定义导航结构:
docs.json
{
  "navigation": {
    "tabs": [
      {
        "tab": "Documentation",
        "groups": [
          {
            "group": "Getting started",
            "pages": ["index", "quickstart"]
          }
        ]
      }
    ]
  }
}
这会以单一语言显示你的文档,并且不提供语言切换器界面。
将导航标签(例如分组或标签页名称)翻译为与内容语言一致的文本,可以为用户提供完全本地化的体验。
要添加在所有语言版本中都显示的全局导航元素,请在 docs.jsonnavigation 配置中设置 global 对象。
docs.json
{
  "navigation": {
    "global": {
      "anchors": [
        {
          "anchor": "Documentation",
          "href": "https://example.com/docs"
        },
        {
          "anchor": "Blog",
          "href": "https://example.com/blog"
        }
      ]
    },
    "languages": [
      // 特定语言导航
    ]
  }
}

维护翻译

确保译文准确无误,并与源内容保持同步。

翻译工作流程

  1. 在主语言中更新源内容。
  2. 确定已更改的内容。
  3. 翻译已更改的内容。
  4. 审核译文的准确性。
  5. 更新翻译文件。
  6. 验证导航和链接是否正常工作。

自动化翻译

如需自动化翻译解决方案,请联系 Mintlify 销售团队

外部翻译服务商

如果你与自己的翻译服务商或区域译员合作,可以使用 GitHub Actions 或类似的 CI/CD 工具,将他们的工作流程集成到 Mintlify 文档中。
  1. 导出源内容:提取需要翻译的 MDX 文件。
  2. 发送给译员:将文件提供给翻译服务商。
  3. 接收译文:取回翻译后的 MDX 文件。
  4. 导入并部署:将翻译文件添加到语言目录中,并更新导航。
当 PR 合并到 main 时,此 GitHub Actions 工作流程会自动导出已更改的英文内容以供翻译。
.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 添加翻译内容时进行验证并导入。
.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 完整,仅翻译 titledescription 的值。
  • 保护代码块:将代码块标记为“请勿翻译”,并告知翻译供应商。
  • 使用翻译记忆库:提供术语表,列出应保留英文或需采用特定译法的技术术语。
  • 自动化验证:在合并翻译内容前,使用 CI 检查验证 MDX 语法和 frontmatter。
  • 版本控制:跟踪每份译文对应的源版本,以识别过时内容。

图片与媒体

将各语言版本的图片存放在对应的语言目录中。
images/
├── dashboard.png          # 英文版
├── fr/
│   └── dashboard.png     # 法文版
└── es/
    └── dashboard.png     # 西班牙文版
在译文中使用相对路径引用图像。
es/index.mdx
![控制台截图](/images/es/dashboard.png)

多语言网站的 SEO(搜索引擎优化)

针对每种语言版本进行搜索引擎优化。

页面元数据

在每个文件的 frontmatter 中包含已翻译的页面元数据:
fr/index.mdx
---
title: "开始使用"
description: "了解如何开始使用我们的产品。"
keywords: ["入门", "教程", "指南"]
---

最佳实践

日期和数字格式

注意针对不同地区使用合适的日期和数字格式。
  • 日期格式:MM/DD/YYYY vs DD/MM/YYYY
  • 数字格式:1,000.00 vs 1.000,00
  • 货币符号:$100.00 vs 100,00€
为每种语言使用相应格式的示例,或采用通用且易于理解的格式。

保持一致性

  • 在所有语言中保持内容对齐,确保每位用户获取同等质量的信息。
  • 为技术术语创建翻译术语表。
  • 在不同语言中保持相同的内容结构。
  • 匹配源内容的语气和风格。
  • 使用 Git branch 将翻译工作与主内容更新分开管理。

布局差异

有些语言相比英语需要更多或更少的空间。请在不同屏幕尺寸上测试你的翻译内容,以确保:
  • 导航显示正常且不会被截断。
  • 代码块不会溢出。
  • 表格和其他格式化文本保持良好的可读性。
  • 图片能够适当缩放。

字符编码

请确保你的开发环境和部署流水线支持 UTF-8 编码,以便正确显示使用不同字母表并包含特殊字符的各种语言中的所有字符。