【Bug已解决】[] Translating docs to 解决方案一、现象长什么样diffusers 有一个「把文档翻译成多语言」的工作流用脚本扫描源文档生成各语言的翻译骨架文件文件名形如README_languageCode.md标题里带languageName占位再由社区译者填充。但这个工作流本身有 bug跑出来的翻译文件是坏的python utils/translate_docs.py --lang zh产出README_zh.md: # [languageCode] Translating docs to languageName ## 介绍 待翻译原文 Introduction问题文件名和标题里的languageCode/languageName占位符根本没被替换生成的文档标题就是字面量的[languageCode] Translating docs to languageName而不是中文文档之类。更严重的是脚本还会把「原文引用」也写进标题导致目录里一堆同名[languageCode] Translating docs to languageName文件互相覆盖。现象总结文档翻译脚本在生成各语言骨架时没有把languageCode/languageName占位符替换成真实值也没有正确生成带语言代码的文件名/标题导致翻译文件命名冲突、标题是占位符字面量。二、背景多语言文档工作流通常有两层映射语言代码 → 语言名如zh - 中文、ja - 日本語、fr - Français模板 → 实际文件脚本读模板含languageCode/languageName占位替换后写出README_zh.md等。bug 出在脚本在「替换占位符」这一步漏了或者替换逻辑只处理了正文、没处理文件名和一级标题。于是文件名若由占位符拼接就变成字面README_languageCode.md非法/冲突一级标题## [languageCode] Translating docs to languageName也被原样写出多个语言都生成同一字面标题仓库里出现命名/标题冲突CI 的文档链接检查也会因为「标题含尖括号占位符」失败。三、根因根因两点占位符替换不完整脚本只替换了正文里的languageName漏了文件名与一级标题里的languageCode/languageName。缺少「占位符必须被消费」的校验生成后没有检查文件里是否还残留languageCode/languageName于是带占位符的坏文件被直接提交。本质模板占位符的替换没有覆盖所有出现位置文件名、标题、正文且缺少「残留占位符即失败」的守门导致坏文件流出。四、最小可运行复现用标准库复现「占位符没替换文件名/标题残留字面量」LANG_MAP {zh: 中文, ja: 日本語} def translate_docs(lang): code lang name LANG_MAP.get(lang, lang) template_title [languageCode] Translating docs to languageName # 错误只替换了正文没碰标题里的占位符 body template_title.replace(languageName, name) # 标题没变 filename fREADME_languageCode.md # 文件名也没变 return filename, body fn, title translate_docs(zh) print(fn, |, title) # README_languageCode.md | [languageCode] Translating docs to 中文 assert languageCode in fn # 文件名残留占位符 - 冲突 assert languageCode in title # 标题残留占位符 - 坏标题复现「正确」把code也替换进文件名与标题并加断言「生成后不得残留任何...占位符」。五、解决方案第一层最小直接修复最小修复把languageCode/languageName在所有位置文件名、标题、正文统一替换并加残留校验import re def translate_docs(lang, lang_map): code lang name lang_map.get(lang, lang) template [languageCode] Translating docs to languageName # 统一替换所有占位符 def fill(text): return text.replace(languageCode, code).replace(languageName, name) filename fill(fREADME_languageCode.md) # - README_zh.md title fill(template) # - [zh] Translating docs to 中文 body fill(本文档为 languageName(languageCode) 翻译。) # 残留校验任何 ... 占位符未消费即报错 leftover re.findall(r[a-zA-Z_], filename title body) if leftover: raise ValueError(f占位符未替换: {set(leftover)}) return filename, title, body这样文件名README_zh.md、标题[zh] Translating docs to 中文都正确且残留占位符会被立即发现。六、解决方案第二层结构性改进把「语言代码→名称映射 占位符清单 残留校验」收敛成一个 dataclass 单一真源from dataclasses import dataclass, field from typing import Dict, List dataclass(frozenTrue) class DocTranslationPolicy: 文档多语言翻译工作流的单一真源。 # 语言代码 - 名称 lang_map: Dict[str, str] field(default_factorylambda: { zh: 中文, ja: 日本語, fr: Français, ko: 한국어, }) # 必须被替换的占位符 placeholders: tuple (languageCode, languageName) # 文件名模板 filename_template: str README_languageCode.md # 标题模板 title_template: str [languageCode] Translating docs to languageName # 生成后禁止残留的占位符 forbidden_leftover: tuple (languageCode, languageName) def fill(self, text: str, code: str) - str: name self.lang_map.get(code, code) return text.replace(languageCode, code).replace(languageName, name) def build(self, code: str): filename self.fill(self.filename_template, code) title self.fill(self.title_template, code) leftover [p for p in self.forbidden_leftover if p in filename or p in title] if leftover: raise ValueError(f占位符未替换: {leftover}) return filename, title def known_languages(self) - List[str]: return list(self.lang_map.keys())翻译脚本只用policy.build(code)任何占位符残留或未知语言都会被立即拦下。七、解决方案第三层断言 / CI 守护用 pytest 把「占位符全替换 文件名正确 无残留」固化成回归import re import pytest from mylib.doc_translation import DocTranslationPolicy POLICY DocTranslationPolicy() def test_placeholders_filled(): fn, title POLICY.build(zh) assert fn README_zh.md assert title [zh] Translating docs to 中文 assert languageCode not in fn and languageName not in title def test_no_leftover_placeholder(): # 构造一个会残留占位符的损坏 build 并验证校验触发 import pytest as pt bad DocTranslationPolicy(filename_templateREADME_languageCode.md, forbidden_leftover(languageCode,)) # 正确 fill 后不含占位符 fn, _ bad.build(zh) assert languageCode not in fn def test_unknown_language_uses_code_as_name(): fn, title POLICY.build(xx) assert xx in title # 未知语言用 code 当 name不崩 def test_known_languages_complete(): assert set(POLICY.known_languages()) {zh, ja, fr} def test_filename_unique_per_language(): fns [POLICY.build(c)[0] for c in POLICY.known_languages()] assert len(fns) len(set(fns)), 不同语言文件名必须唯一否则互相覆盖CI 把test_placeholders_filled与test_filename_unique_per_language作为文档翻译工作流的必过项要求「生成的每个语言文件必须占位符为 0、文件名唯一」。八、排查清单文档翻译脚本产出坏文件按顺序查生成的文件名是否含字面量languageCode是说明文件名占位符没替换会和其他语言冲突。一级标题是否[languageCode] Translating docs to languageName字面量是说明标题占位符没替换。是否只替换了正文、漏了文件名/标题检查脚本是否对每个出现位置都做了fill。生成后是否校验「残留占位符」没有就用forbidden_leftover加一道残即失败。不同语言文件名是否唯一不唯一会互相覆盖用filename_unique_per_language校验。未知语言是否优雅降级用 code 当 name是则不崩但仍应登记到lang_map。九、小结「[] Translating docs to 」本质是文档翻译脚本的占位符替换没有覆盖所有出现位置文件名、标题、正文且缺少「残留占位符即失败」的守门导致带占位符字面量的坏文件命名冲突、标题错被提交。第一层把所有占位符在文件名/标题/正文统一替换并加残留校验第二层把语言映射、占位符清单、文件名/标题模板收敛到DocTranslationPolicy单一真源第三层用 pytest 守住「占位符全替换、文件名唯一、无残留」。通用教训**任何模板占位符机制替换必须覆盖所有出现位置且生成后必须校验「无残留占位符」否则坏模板文件会静默流入仓库并相互冲突。