跳转至

为 MkDocs 添加多语言翻译功能

💾 AI智能摘要 (GLM-4)

本文介绍了为 MkDocs 网站添加基于 JavaScript 的客户端翻译功能,支持多种语言的实时切换,无需刷新页面,优化了移动端和桌面端显示。

📖 阅读信息

阅读时间:3 分钟 | 中文字符:1148 | 有效代码行数:328

在全球化的今天,为网站添加多语言支持已成为提升用户体验的重要手段。本文将详细介绍如何为 MkDocs 网站添加基于 JavaScript 的客户端翻译功能,支持多种语言的实时切换。

快速查看效果


特别感谢Aaron对翻译方案的提议与实践👋

🌍 方案概述

我们采用的翻译方案具有以下特点:

  • 🚀 客户端翻译:使用 translate.js 库实现前端翻译
  • 🌐 多语言支持:支持中文、英文、日文、韩文、阿拉伯文等 9 种语言
  • ⚡ 实时切换:无需刷新页面即可切换语言
  • 🎨 样式优化:针对不同语言进行样式适配
  • 📱 响应式支持:移动端和桌面端都有优化

📋 实现步骤

1. 引入翻译库

首先在页脚或头部模板(例如:docs/overrides/partials/footer.html)中引入 translate.js 库:

<!-- 引入翻译库 -->
<script src="https://cdn.staticfile.net/translate.js/3.12.0/translate.js"></script>

2. 配置翻译参数

创建翻译配置脚本,设置基本参数:

<script>
(function() {
  // 设置不翻译的元素类名
  translate.ignore.class.push(
    'md-select',           // Material 选择框
    'footer-highlight',    // 页脚高亮文本
    'md-footer-copyright', // 版权信息
    'no-translate'         // 通用不翻译类
  );

  // 设置本地语种(默认中文简体)
  translate.language.setLocal('chinese_simplified');

  // 自动识别用户首选语言
  translate.setAutoDiscriminateLocalLanguage();

  // 隐藏默认语言选择框
  translate.selectLanguageTag.show = false;

  // 设置翻译服务通道
  translate.service.use('client.edge');

  // 执行翻译初始化
  translate.execute();

  console.log('翻译功能已初始化');
})();
</script>

目录结构如下:

$ tree -a
.
├── .github
│   ├── .DS_Store
│   └── workflows
│       └── ci.yml
├── docs
│   └── index.md
│   └──overrides
│       └──assets
│       └──partials
│          └──footer.html
└── mkdocs.yml

重点提示

请参考下方的footer.html示例代码

页脚教程: Mkdocs页脚设计

footer.html示例代码(无需修改,直接复制粘贴即可使用)
{#-
  This file was automatically generated - do not edit
-#}
<footer class="md-footer">
  {% if "navigation.footer" in features %}
    {% if page.previous_page or page.next_page %}
      {% if page.meta and page.meta.hide %}
        {% set hidden = "hidden" if "footer" in page.meta.hide %}
      {% endif %}
      <nav class="md-footer__inner md-grid" aria-label="{{ lang.t('footer') }}" {{ hidden }}>
        {% if page.previous_page %}
          {% set direction = lang.t("footer.previous") %}
          <a href="{{ page.previous_page.url | url }}" class="md-footer__link md-footer__link--prev" aria-label="{{ direction }}: {{ page.previous_page.title | e }}">
            <div class="md-footer__button md-icon">
              {% set icon = config.theme.icon.previous or "material/arrow-left" %}
              {% include ".icons/" ~ icon ~ ".svg" %}
            </div>
            <div class="md-footer__title">
              <span class="md-footer__direction">
                {{ direction }}
              </span>
              <div class="md-ellipsis">
                {{ page.previous_page.title }}
              </div>
            </div>
          </a>
        {% endif %}
        {% if page.next_page %}
          {% set direction = lang.t("footer.next") %}
          <a href="{{ page.next_page.url | url }}" class="md-footer__link md-footer__link--next" aria-label="{{ direction }}: {{ page.next_page.title | e }}">
            <div class="md-footer__title">
              <span class="md-footer__direction">
                {{ direction }}
              </span>
              <div class="md-ellipsis">
                {{ page.next_page.title }}
              </div>
            </div>
            <div class="md-footer__button md-icon">
              {% set icon = config.theme.icon.next or "material/arrow-right" %}
              {% include ".icons/" ~ icon ~ ".svg" %}
            </div>
          </a>
        {% endif %}
      </nav>
    {% endif %}
  {% endif %}
  <div class="md-footer-meta md-typeset">
    <div class="md-footer-meta__inner md-grid">
      {% include "partials/copyright.html" %}
            <script src="https://cdn.staticfile.net/translate.js/3.12.0/translate.js"></script>
      <script>
        // 翻译配置 - 包装在立即执行函数中避免全局污染
        (function() {
          // 通过 class 设置不翻译的元素
          translate.ignore.class.push('md-select', 'footer-highlight', 'md-footer-copyright');
          // 自定义术语库,纠正翻译结果
          translate.nomenclature.append('chinese_simplified','english',`
                快讯=Newsflash
                访问量=Page Views
                本站已经运行=Site has been running
                天=days
                时=hours
                分=minutes
                秒=seconds
                萌ICP备=Moe ICP
                版权所有=Copyright
                制作工具=Made with
        `);
          // 设置本地语种
          translate.language.setLocal('chinese_simplified');     
          // 设置首次使用时自动识别语种
          translate.setAutoDiscriminateLocalLanguage();
          // 不显示 select 语言选择框
          translate.selectLanguageTag.show = false;
          // 设置机器翻译服务通道
          translate.service.use('client.edge');
          // 执行翻译
          translate.execute();
          // 监听语言切换事件,确保动态内容也能被翻译
          window.addEventListener('translate.languagechange', function() {
            // 延迟重新翻译动态更新的内容
            setTimeout(() => {
              translate.execute();
            }, 500);
          });
          console.log('翻译功能已初始化');
        })();
      </script>
      {% if config.extra.social %}
        {% include "partials/social.html" %}
      {% endif %}
    </div>
  </div>
</footer>

3. 在 mkdocs.yml 中配置语言切换

首先在mkdocs.yml文件中添加custom_dir:

1
2
3
theme:
  name: material
  custom_dir: docs/overrides #覆写路径

然后在配置文件中添加多语言切换选项:

theme:
  name: material
  language: zh
  features:
    - navigation.tabs
    - navigation.sections

extra:
  alternate:
    - name: 中文
      link: "javascript:translate.changeLanguage('chinese_simplified');"
      lang: zh
    - name: English
      link: "javascript:translate.changeLanguage('english');"
      lang: en
    - name: 한국어
      link: "javascript:translate.changeLanguage('korean');"
      lang: ko
    - name: 日本語
      link: "javascript:translate.changeLanguage('japanese');"
      lang: ja
    - name: بالعربية
      link: "javascript:translate.changeLanguage('arabic');"
      lang: ar
    - name: Deutsch
      link: "javascript:translate.changeLanguage('german');"
      lang: de
    - name: Français
      link: "javascript:translate.changeLanguage('french');"
      lang: fr
    - name: Español
      link: "javascript:translate.changeLanguage('spanish');"
      lang: es
    - name: português
      link: "javascript:translate.changeLanguage('portuguese');"
      lang: pt

至此已经配置完成。简单!快速!



后续步骤皆为可选,根据需求进行配置。可以不看。

4. 添加自定义术语库

为了提高翻译准确性,添加专业术语映射:

// 自定义术语库配置
const nomenclatures = {
  english: `
访问量=Page Views
本站已经运行=Site has been running for
天=days
时=hours
分=minutes
秒=seconds
版权所有=Copyright
制作工具=Made with
统计中=Loading
`,
  japanese: `
访问量=アクセス数
本站已经运行=サイト運営期間
天=日
时=時間
分=分
秒=秒
版权所有=著作権
制作工具=制作ツール
`,
  korean: `
访问量=방문수
本站已经运行=사이트 운영 기간
天=일
时=시간
分=분
秒=초
版权所有=저작권
制作工具=제작 도구
`
  // ... 其他语言配置
};

// 批量添加术语库
Object.entries(nomenclatures).forEach(([lang, terms]) => {
  translate.nomenclature.append('chinese_simplified', lang, terms);
});

5. 处理动态内容翻译

对于实时更新的内容(如计时器、访问量等),需要特殊处理:

// 监听语言切换事件
window.addEventListener('translate.languagechange', function() {
  // 延迟重新翻译动态内容
  setTimeout(() => {
    translate.execute();
  }, 500);
});

// 在动态内容更新时触发翻译
function updateDynamicContent() {
  // 更新内容...

  // 如果当前不是中文,重新执行翻译
  if (window.translate && translate.currentLanguage !== 'chinese_simplified') {
    setTimeout(() => translate.execute(), 100);
  }
}

🎨 多语言样式优化

1. 基础响应式样式

为不同语言文本长度进行适配:

/* 为翻译后的元素添加基础样式 */
[data-translate-lang]:not([data-translate-lang="chinese_simplified"]) .footer-item {
  text-align: center;
  justify-content: center;
  min-height: 2.5rem;
  padding: 0.5rem 1rem;
  line-height: 1.4;
}

/* 移动端长文本优化 */
@media (max-width: 768px) {
  [data-translate-lang]:not([data-translate-lang="chinese_simplified"]) .footer-visit-count-mobile {
    flex-direction: column;
    gap: 0.4em;
    line-height: 1.6;
  }
}

2. 特定语言样式优化

针对不同语言的特殊需求:

/* 英文样式优化 */
[data-translate-lang="english"] .footer-item {
  font-size: 0.75rem;
  word-spacing: 0.15em;
}

/* 日文样式优化 */
[data-translate-lang="japanese"] .footer-item {
  font-size: 0.8rem;
  letter-spacing: 0.05em;
  line-height: 1.5;
}

/* 阿拉伯文 RTL 支持 */
[data-translate-lang="arabic"] .footer-wrapper {
  direction: rtl;
  text-align: center;
}

[data-translate-lang="arabic"] .footer-item {
  direction: rtl;
  text-align: center;
  word-spacing: 0.2em;
}

3. 深色模式兼容

确保所有语言在深色模式下的显示效果:

/* 深色模式下的翻译优化 */
[data-md-color-scheme="slate"] .footer-counter-value,
[data-md-color-scheme="slate"] .no-translate {
  color: #e2e8f0 !important;
}

/* 高对比度模式支持 */
@media (prefers-contrast: high) {
  [data-translate-lang]:not([data-translate-lang="chinese_simplified"]) .footer-item {
    border-width: 2px;
    font-weight: 600;
  }
}

🔧 高级功能

1. 语言检测与样式应用

function applyLanguageStyles(lang) {
  // 设置语言属性
  document.documentElement.setAttribute('data-translate-lang', lang || 'chinese_simplified');

  // RTL 语言处理
  if (lang === 'arabic') {
    document.documentElement.setAttribute('dir', 'rtl');
    document.body.classList.add('rtl-language');
  } else {
    document.documentElement.setAttribute('dir', 'ltr');
    document.body.classList.remove('rtl-language');
  }
}

2. 数字和特殊内容保护

确保数字、链接等不被误翻译:

function protectSpecialContent() {
  // 保护数字不被翻译
  const highlightElements = document.querySelectorAll('.footer-highlight');
  highlightElements.forEach(el => {
    if (!el.classList.contains('no-translate')) {
      el.classList.add('no-translate');
    }
  });

  // 保护 ICP 号码
  const icpLinks = document.querySelectorAll('.icp-link');
  icpLinks.forEach(el => {
    const icpNumber = el.textContent.match(/\d+/);
    if (icpNumber) {
      el.innerHTML = el.innerHTML.replace(
        /(\d+)/g, 
        '<span class="no-translate">$1</span>'
      );
    }
  });
}

🚀 最佳实践

1. 性能优化

  • 延迟执行:翻译操作延迟执行,避免阻塞页面渲染
  • 避免重复翻译:检查是否已翻译过相同内容
  • 清理资源:页面切换时正确清理翻译相关资源

2. 用户体验优化

  • 记住用户选择:使用 localStorage 保存用户的语言偏好
  • 平滑过渡:添加过渡动画使语言切换更自然
  • 加载状态:在翻译过程中显示加载提示

3. 可访问性考虑

  • 屏幕阅读器支持:正确设置 lang 属性
  • 键盘导航:确保语言切换按钮可通过键盘访问
  • 对比度:保证翻译后文本的可读性

📱 移动端适配

1. 响应式布局

@media (max-width: 768px) {
  /* 长语言文本换行优化 */
  [data-translate-lang="german"] .footer-visit-count-mobile,
  [data-translate-lang="french"] .footer-visit-count-mobile {
    gap: 0.6em;
    line-height: 1.7;
  }

  /* 移动端语言切换优化 */
  .md-header__option {
    min-width: auto;
  }
}

2. 触摸友好设计

确保语言切换按钮在移动设备上易于点击:

1
2
3
4
5
6
7
/* 移动端按钮优化 */
@media (max-width: 768px) {
  .md-header__option {
    padding: 0.5rem;
    min-height: 44px; /* iOS 推荐的最小触摸目标 */
  }
}

🔍 故障排除

常见问题及解决方案

  1. 翻译不生效
  2. 检查 translate.js 是否正确加载
  3. 确认没有 JavaScript 错误阻止执行

  4. 动态内容不翻译

  5. 在内容更新后调用 translate.execute()
  6. 检查动态元素是否被忽略类名覆盖

  7. 样式错乱

  8. 为不同语言添加对应的 CSS 规则
  9. 检查文本长度差异导致的布局问题

  10. RTL 语言显示异常

  11. 正确设置 dir="rtl" 属性
  12. 为 RTL 语言添加特殊样式

📊 效果展示

实现后的效果:

2.gif

  • 多语言切换:支持 9 种语言实时切换
  • 样式适配:每种语言都有优化的显示效果
  • 响应式设计:桌面端和移动端都完美适配
  • 性能优良:客户端翻译,服务器无额外负担
  • 用户友好:无需刷新页面,切换流畅

🎯 总结

通过以上步骤,我们成功为 MkDocs 网站添加了完整的多语言翻译功能。这个方案具有以下优势:

  1. 实现简单:仅需几个文件修改即可完成
  2. 功能完整:支持多种语言和样式适配
  3. 性能优秀:客户端翻译,不增加服务器负担
  4. 用户体验佳:切换流畅,样式美观

虽然客户端翻译在 SEO 方面不如服务端多语言方案,但对于技术文档、个人博客等场景,这种方案提供了极佳的性价比和用户体验。


🔗 相关资源

希望这个教程能帮助你为自己的 MkDocs 网站添加多语言支持!如果遇到问题,欢迎在评论区讨论。

💬 评论

评论系统加载中...