翻译验收

翻译文案里的 {name} 丢了,怎样在上线前拦住

本文详解如何利用Python标准库string.Formatter.parse方法对翻译文案中的占位符进行结构化校验。文章从正则表达式的局限性切入,通过合成示例演示正确解析与典型边界故障,提供可直接运行的校验脚本,并给出自动化与人工审查的决策边界及OmniDoc工作流衔接建议。内容严格基于PEP 3101语法规范,未虚构任何工具能力。

阅读文本 / Reading text
本文使用合成样本演示处理方法。代码已通过隔离环境中的示例检查;这不代表对所有真实文件或产品效果的保证。处理重要文件前,请保留原件,并按文中的边界与验收项复核。 “欢迎,{name}” 翻译成另一种语言以后,把变量名一起翻了,页面通常会在真正填值时才报错。更早的检查点是翻译交付时:把格式字符串中的字段取出来,核对字段身份、出现次数和格式规格,而不只是数花括号。 先选定格式语法 下面只处理 Python str.format 风格,不覆盖 JavaScript 模板、ICU MessageFormat 或其他占位符系统。不同系统的嵌套、转义和复数规则不同,不能用一个正则声称全部支持。示例明确要求命名字段,拒绝自动编号字段。 转义的花括号不是变量 {{name}} 表示显示文字 {name} ,不应算成字段。格式规格里也可能嵌套字段,例如 {value:{width}} 。下面递归读取规格,并对同一字段的重复出现保留计数。 from string import Formatter from collections import Counter def contract(text, depth=0): if depth > 5: raise ValueError("nested format budget") result = Counter() for literal, field, spec, conversion in Formatter().parse(text): if field is None: continue if not field or field != field.strip(): raise ValueError("named field without surrounding whitespace required") result[(field, conversion, spec)] += 1 if spec: result.update(contract(spec, depth + 1)) return result assert contract("Hi {name}, total {amount:.2f}") == contract("{name}:金额 {amount:.2f}") assert contract("{{name}}") == Counter() assert contract("{name} {name}") != contract("{name}") assert contract("{value:{width}}")[("width", None, "")] == 1 assert contract("{amount:.2f}") != contract("{amount:.0f}") for bad in ["Hi {name", "{ name}", "{}"]: try: contract(bad) except ValueError: pass else: raise AssertionError("invalid template accepted") 这个契约刻意偏严格 这里把格式规格也当作受保护内容,因此改变小数位会被标记。某些本地化任务确实需要改变数字呈现,应为它建立独立规则,而不是取消所有占位符检查。嵌套深度限制是示例预算,不是语言规范要求。 变化 本例结果 审校动作 字段换位置 接受 阅读语序是否自然 同字段少一次 拒绝契约 检查是否漏译 变量名被翻译 拒绝契约 恢复接口字段名 小数规格变化 标记不一致 按本地化协议确认 不执行不受信任模板 这段代码只解析,不实际调用 format 填入对象。它也不是模板安全沙箱。后续渲染还需限制可用字段、对象访问与输出上下文,特别是 HTML 页面中的转义规则。 翻译与上线之间留一道检查 使用 OmniDoc(产品入口) 处理含模板内容的材料时,先列出哪些字段必须保持不变。译后把结构检查与语言审校分开做:一句话读起来顺,并不能证明变量契约仍然有效。 参考: Python string 与 Formatter 。 - 1 -
更新于 2026-10-08