尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
当 mkdocs-static-i18n 遇上 Read the Docs 多版本:踩坑与解法
背景使用 MkDocs Material 主题 mkdocs-static-i18n 构建多语言文档站点时如果同时部署到 Read the Docs 并启用多版本支持会遇到语言切换链接路径错误的问题。本文记录了这一问题的排查过程和解决方案。技术栈mkdocs1.6.0 mkdocs-material9.5.24 mkdocs-static-i18n1.2.3问题描述现象在 Read the Docs 多版本模式下右上角的语言切换按钮和 README 中的中英文切换链接点击后返回 404。根因MkDocs 构建后的语言切换链接默认为绝对路径ahref/hreflangenEnglish/aahref/zh/hreflangzh中文/a在 Read the Docs 多版本模式Multiple versions without translations下站点部署在/version/子路径下如/latest/、/v2.0/。点击/zh/会跳转到site.readthedocs.io/zh/而正确的目标应该是site.readthedocs.io/latest/zh/。同样的问题也出现在 README 中的手动语言切换链接和自动语言跳转脚本中。解决方案1.site_url: 配置site_url:MkDocs 默认会根据site_url生成页面中的部分绝对路径如 sitemap、canonical link。如果不设置site_url或留空字符串MkDocs 在构建时使用相对路径不会硬编码域名或路径前缀。这对于 Read the Docs 多版本部署很重要site_url设置效果(空字符串)所有链接使用相对路径适配任意部署路径https://site.io/链接硬编码为根路径多版本下可能指向错误位置https://site.io/latest/只在 latest 版本正确其他版本如 v2.0路径错误设为空字符串后语言切换器、导航链接、CSS/JS 资源路径全部使用相对路径不受部署路径影响。2. 语言切换器使用相对路径在mkdocs.yml中通过extra.alternate配置语言切换按钮使用相对路径代替绝对路径extra:alternate:-name:Englishlink:.lang:en-name:中文link:zh/lang:zhmkdocs-static-i18n插件会根据当前页面的语言自动调整extra.alternate中的相对路径当前页面位置English 链接中文链接说明/latest/.→/latest/zh/→/latest/zh/英文页 → 中文进入子目录/latest/zh/..→/latest/./→/latest/zh/中文页 → 英文返回上级/v1.0/.→/v1.0/zh/→/v1.0/zh/不同版本自动适配相对路径不包含版本号或语言前缀因此在任意版本下都能正确解析。3. Hook 重写 README 中的语言链接项目根目录的 README 通过软链接docs/README.md → ../README.md作为文档首页。README 中包含手动的语言切换链接[中文文档](README.zh.md) | **English**MkDocs 构建后README.zh.md不再是有效的文件路径i18n 插件将其合并到/zh/路径下需要通过on_page_markdownhook 重写。docs/hooks/rewrite_paths.py完整代码 修复 README 中的相对路径使其在 mkdocs 中正常工作。 README.md 位于项目根目录使用 ./docs/imgs/... 等路径引用资源。 当通过软链接 docs/README.md → ../README.md 在 docs_dir: docs/ 下构建时 ./docs/ 前缀会导致路径解析到不存在的 docs/docs/ 目录需要去除该前缀。 同时将中英文切换链接README.zh.md / README.md重写为 mkdocs-static-i18n 插件生成的语言 URL确保链接在构建后的站点中正常跳转。 from__future__importannotationsimportrefromtypingimportAnydefon_page_markdown(markdown:str,page:Any,config:Any,files:Any)-str:# 仅对根目录的 README / index 页面生效ifpage.file.namenotin(README,index):returnmarkdown# 去除多余的 ./docs/ 前缀# 示例: ](./docs/imgs/foo.png) → ](imgs/foo.png)markdownre.sub(r\]\(\./docs/,](,markdown)# 将语言切换链接重写为相对路径兼容 Read the Docs 多版本 URL# 中文页 (/zh/): [English](README.md) → [English](../) 从 /zh/ 向上回到根# 英文页 (/): [中文文档](README.zh.md) → [中文文档](zh/) 从根进入 /zh/localegetattr(page.file,locale,None)iflocalezh:markdownre.sub(r\]\(README\.md\),](../),markdown)else:markdownre.sub(r\]\(README\.zh\.md\),](zh/),markdown)returnmarkdown关键: 英文页到中文的链接使用zh/不是../zh/。原因假设英文页 URL 为 /latest/ ../zh/ → 先到 /再到 /zh/ — ❌ 脱离了版本路径 zh/ → 直接到 /latest/zh/ — ✅ 正确4. 浏览器语言自动跳转脚本docs/js/lang-redirect.js实现了首次访问时根据浏览器语言自动跳转到中文版本的功能。docs/js/lang-redirect.js完整代码/** * 浏览器语言自动跳转脚本。 * * 首次访问时根据浏览器语言设置自动跳转到对应的语言版本。 * 跳转 URL 从 Material 主题的语言切换器获取使用相对路径兼容 Read the Docs 多版本。 * * 使用 sessionStorage 记录用户主动切换语言的行为防止自动跳转覆盖用户选择。 * 标签页关闭后记录清除下次访问重新按浏览器语言跳转。 */document.addEventListener(DOMContentLoaded,function(){varSTORAGE_KEYpyimgproc_lang_override;// 监听 Material 主题语言切换器的点击记录用户主动选择varswitcherdocument.querySelectorAll(.md-select__link[hreflang]);switcher.forEach(function(link){link.addEventListener(click,function(){sessionStorage.setItem(STORAGE_KEY,link.getAttribute(hreflang));});});// 已有用户主动选择的记录跳过自动跳转varoverridesessionStorage.getItem(STORAGE_KEY);if(override)return;// 检测当前页面是否在中文版本路径下匹配任意位置的 /zh/兼容多版本 URLvarpathwindow.location.pathname;varisZh/\/zh\//.test(path)||/\/zh$/.test(path);// 通过 referrer 检测用户从站内跨语言导航如从 /zh/ 点击到 /en/// 视为主动切换设置覆盖标记并跳过自动跳转varrefdocument.referrer;if(ref){try{varrefUrlnewURL(ref);if(refUrl.hostwindow.location.host){varrefIsZh/\/zh\//.test(refUrl.pathname)||/\/zh$/.test(refUrl.pathname);if(refIsZh!isZh){sessionStorage.setItem(STORAGE_KEY,isZh?zh:en);return;}}}catch(e){}}// 浏览器语言为中文但当前不在中文版本自动跳转到中文varbrowserLang(navigator.language||navigator.userLanguage||).toLowerCase();varwantZhbrowserLang.indexOf(zh)0;if(wantZh!isZh){varzhLinkdocument.querySelector(.md-select__link[hreflangzh]);if(zhLinkzhLink.href){window.location.replace(zhLink.href);}}});多版本兼容的关键设计路径检测不限位置:/\/zh\//.test(path)匹配/latest/zh/FAQ/等任意位置的/zh/跳转 URL 从 DOM 获取: 读取 Material 主题语言切换器的href由extra.alternate的相对路径在浏览器中解析为完整 URL自动包含版本前缀sessionStorage 防冲突: 用户点击语言切换器或从站内跨语言导航时记录覆盖标记阻止自动跳转选择sessionStorage而非localStorage标签页关闭后自动清除Read the Docs 网站配置以下操作均在 Read the Docs 后台完成地址为https://readthedocs.org/dashboard/project/。4.1 URL 前缀方案选择路径: Settings → General SettingsRead the Docs 的 URL 结构由两个维度决定语言Language和版本Versioning。选择Multiple versions without translations/version/filenameURL 只包含版本前缀不包含语言前缀site.readthedocs.io/latest/ → 英文首页 site.readthedocs.io/latest/zh/ → 中文首页 site.readthedocs.io/v2.0/ → 英文首页 (v2.0) site.readthedocs.io/v2.0/zh/ → 中文首页 (v2.0)如果选择了包含 translations 的方案如/language/version/filenameURL 会出现/en/latest/或/zh-cn/latest/前缀与 mkdocs-static-i18n 生成的/zh/子目录叠加产生混乱。4.2 项目语言设置路径: Settings → General Settings → Language将Language设为English。该设置决定项目的主语言影响 Read the Docs 搜索索引和界面文字。即使文档有中文版本主语言也应设为 English——中文版本由 mkdocs-static-i18n 在/zh/子目录下管理不需要 Read the Docs 层面感知。4.3 版本管理路径: VersionsRead the Docs 自动检测 Git 仓库中的分支和 tag创建对应的文档版本。操作说明激活版本勾选要构建的分支/tag默认只构建latest对应master/main分支默认版本Settings → Default version通常设为latest或stable隐藏旧版本取消勾选不需要的旧 tag减少构建资源占用4.4 不创建翻译项目路径: Settings → Translations不要创建 Translation 子项目。Read the Docs 的翻译机制是为每种语言创建独立的项目和仓库与mkdocs-static-i18n的单仓库、后缀命名模式冲突。如果已创建需要在此页面解除关联。4.5 构建配置确保仓库中存在.readthedocs.yamlversion:2build:os:ubuntu-22.04tools:python:3.9mkdocs:configuration:mkdocs.ymlpython:install:-requirements:requirements_docs.txtrequirements_docs.txt需包含mkdocs、mkdocs-material、mkdocs-static-i18n及其依赖。推荐使用pip-compile从.in文件生成锁定版本的.txt文件。4.6 Webhook 触发构建路径: Settings → Integrations默认情况下Read the Docs 会在仓库中添加 GitHub Webhookpush 到master/main后自动触发latest版本重建。创建新 tag 后如果该 tag 对应的版本已激活也会自动构建。如果 Webhook 未生效检查:GitHub 仓库 Settings → Webhooks 中是否有 readthedocs 的 hookRead the Docs 项目 Integrations 中是否显示 GitHub incoming webhookURL 结构对比Multiple versions without translations(/version/filename):页面URL英文首页 (latest)site.readthedocs.io/latest/中文首页 (latest)site.readthedocs.io/latest/zh/英文首页 (v2.0)site.readthedocs.io/v2.0/中文 Reference (v2.0)site.readthedocs.io/v2.0/zh/Reference/中文 FAQ (latest)site.readthedocs.io/latest/zh/FAQ/旧版本 404 问题在 i18n 配置之前发布的旧 tag如 v1.3.6其构建产物中不包含/zh/路径。访问site.readthedocs.io/v1.3.6/zh/会返回 404。Read the Docs 不会用新代码重建旧 tag——每个 tag 永远使用其对应的代码快照。处理方式:在 RTD Versions 页面取消激活不需要的旧版本后续发布的新 tag 自动包含 i18n 配置不需要额外操作完整配置参考mkdocs.ymlsite_name:Image Processing Librarysite_url:repo_url:https://github.com/skylerhu/py-img-processordocs_dir:docshooks:-docs/hooks/rewrite_paths.pytheme:name:materiallogo:imgs/logo.svgfavicon:imgs/favicon.icopalette:scheme:defaultfeatures:-navigation.tabs-toc.integrateextra:alternate:-name:Englishlink:.lang:en-name:中文link:zh/lang:zhnav:-Home:README.md-Reference:Reference.md-FAQ:FAQ.md-Changelog:CHANGELOG-1.x.md-Contributing:CONTRIBUTING.mdextra_css:-css/custom.cssextra_javascript:-js/lang-redirect.jsplugins:-search-i18n:docs_structure:suffixlanguages:-locale:endefault:truename:Englishbuild:true-locale:zhname:中文build:truesite_name:参数化图像处理库nav_translations:Home:首页Reference:图像处理参数FAQ:常见问题Changelog:更新日志Contributing:贡献者指南.readthedocs.yamlversion:2build:os:ubuntu-22.04tools:python:3.9mkdocs:configuration:mkdocs.ymlpython:install:-requirements:requirements_docs.txt总结mkdocs-static-i18n和 Read the Docs 各自管理语言和版本两者叠加时的核心矛盾是路径前缀冲突——插件生成的绝对路径不包含 Read the Docs 注入的版本前缀。解决思路只有一个全部使用相对路径。site_url: 让 MkDocs 不硬编码域名和路径extra.alternate用.和zh/替代/和/zh/Hook 中英文页用zh/不是../zh/中文页用../JS 跳转 URL 从 DOM 读取而非拼接配合 Read the Docs 后台选择 Multiple versions without translations、不创建翻译项目即可在任意版本/latest/、/v2.0/下正常使用多语言切换。
RELATED

相关推荐

出院后制氧机租赁,需要注意哪些问题?

出院后制氧机租赁,需要注意哪些问题?

出院不是照护的结束,尤其当患者回家后仍需继续使用制氧设备时,如何顺利从医院过渡到家庭照护,就成了很多家庭最先要面对的问题。直接买一台制氧机动辄数千甚至上万元,不少家庭因此选择制氧机租赁,用多少租多久&#xf…

📅 2026/11/11 10:51:32
论文AIGC率居高不下?2026年4个指令+3个实测技巧,从50%降至10%稳了!

论文AIGC率居高不下?2026年4个指令+3个实测技巧,从50%降至10%稳了!

写论文最让人崩溃的瞬间是什么?不是熬夜肝格式改到眼瞎,也不是导师追着要修改意见,而是AIGC检测率死活降不下来!明明是自己熬了好几个通宵抠出来的内容,系统却一口判定“AI痕迹过重”,轻则返工重写&#xf…

📅 2026/11/11 10:51:32
走栈道-破障

走栈道-破障

昨日团队团建,到石崮寨游玩,走栈道的事情值得一记。栈道建于山顶峭壁,探出崖壁大约1米的样子,1米多高的护栏,从栈道向外看一览无遗,我没敢向下看。初踏栈道,没有感觉到什么,向前看是…

📅 2026/11/6 8:15:34
MORE NEWS

更多资讯

📰

利川市电工证办理值不值得考?外省证怎么转过来

利川市电工证办理值不值得考?外省证怎么转过来 之前考的证在外省能不能转过来?这是很多人在考虑要不要考电工证时最关心的问题。其实,答案是肯定的,只要符合条件,外省电工证是可以转到利川市使用的。但关键问题来了—— 值不值得考…

📰

电工证挂牌规定在哪考?别怕考不过白交培训费

电工证挂牌规定在哪考?别怕考不过白交培训费 怕考不过白交培训费?电工证挂牌规定在哪考,这个问题很多想考证的小伙伴都问过。别急,下面我就把整个流程、材料、注意事项讲清楚,让你少走弯路,一次性通过。 电工证挂牌规定是啥?…

📰

特种作业证指挥考试题网上查询之前考的证在外省能不能转过来

特种作业证指挥考试题网上查询之前考的证在外省能不能转过来 之前考的证在外省能不能转过来?这是很多特种作业人员在换城市、换岗位,甚至换行业时最头疼的问题。特别是像我们这些在 鹤壁水利工程…

📰

铁厂煤气证作业考试值不值得考?别再被中介坑了,全流程干货来了

铁厂煤气证作业考试值不值得考?别再被中介坑了,全流程干货来了 不知道去哪报名怕被中介坑?铁厂煤气证作业考试值不值得考?这些问题可能是你正在经历的。作为开封一家中小施工企业的负责人,我深知在办理这类证书时,信息不对称和操作流程不透明带来的困扰…

📰

低压电工证考试接触器在哪考?怕考不过白交培训费的看过来

低压电工证考试接触器在哪考?怕考不过白交培训费的看过来 你是不是也遇到过这种情况:花了大把钱报了低压电工证考试接触器培训,结果一上考场就懵,最后钱花了、证没拿到,还得重头再来?别急,今天咱们就聊聊怎么避开这些坑,顺便告诉你…

📰

南京怎么报考电工证 高学历不是门槛 含金量才是关键

南京怎么报考电工证 高学历不是门槛 含金量才是关键 学历不高怕报不上名?这事儿我懂,我认识的不少施工队老大哥,高中毕业就出来打工,干了十几年电工,现在想考证,一听说要学历门槛,立马打退堂鼓。但你知道吗? 南京怎么报考电工证…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬