一份 PDF 报告翻译完成,正文顺畅,表格也还在原位。原文里的 1,250.50 变成 1.250,50,到底是翻译错了,还是目标文档采用了另一套数字写法?
直接比字符串会误报;把逗号和句点全部删掉,则可能漏掉真正的小数点错误。比较之前,应该先确认数字采用什么约定、对应哪个字段、带什么单位。
本文做一个范围明确的小工具:检查已经配对好的数字字符串。它不调用翻译服务,不自动识别整份 PDF 的语言,也不把数字相同等同于翻译正确。
1. "1.250"不是一个脱离上下文的答案
考虑下面三组输入:
| 原文与译文 | 已确认的数字约定 | 应如何处理 |
|---|---|---|
| 1,250.50 → 1.250,50 | 左侧小数点为点,右侧小数点为逗号 | 两边都表示 1250.50 |
| 1,250 → 1.250 | 左侧逗号分组,右侧点分组 | 两边都表示 1250 |
| 1,250 → 1.250 | 两边都用点作为小数分隔符 | 左侧 1250,右侧 1.250,数值不同 |
同一串字符,在不同约定下可能表示不同数值。 源语言和目标语言只能提供线索:文档可能沿用原始表格格式,也可能混用不同地区的数字习惯。不要仅凭"翻成德语了"就全局替换分隔符。

图中是人工构造的格式例子,不是某个产品的错误截图。实际处理时,将确认过的约定写进检查任务;未知时进入人工复核。
2. 用 Decimal 比较数值,先拒绝不支持的格式
下面的代码只支持两种约定:dot 表示点作小数分隔符、逗号作三位分组符;comma 表示逗号作小数分隔符、点作三位分组符。分组是可选的,但出现时必须是每组三位。
它故意不支持百分号、货币符号、科学计数法、括号负数、空格分组和其他数字字符。碰到这些情况返回 REVIEW,避免"清洗得越多,猜得越多"。
Python 标准库即可运行。Decimal 从规范化后的字符串构造,避免先经过二进制浮点数再转换;但它本身不会识别地区格式,分隔符处理仍由显式规则完成。
python
import re
from decimal import Decimal
# Only these two explicitly configured notation conventions are accepted.
PATTERNS = {
'dot': r'[+-]?(?:[0-9]+|[0-9]{1,3}(?:,[0-9]{3})+)(?:\.[0-9]+)?',
'comma': r'[+-]?(?:[0-9]+|[0-9]{1,3}(?:\.[0-9]{3})+)(?:,[0-9]+)?',
}
def parse_number(raw, convention):
if convention not in PATTERNS:
raise ValueError('unknown convention')
token = raw.strip()
if not re.fullmatch(PATTERNS[convention], token):
raise ValueError(f'unsupported token: {raw!r}')
if convention == 'dot':
token = token.replace(',', '')
else:
token = token.replace('.', '').replace(',', '.')
return Decimal(token)
def check_pair(source, target, source_style, target_style):
try:
left = parse_number(source, source_style)
right = parse_number(target, target_style)
except ValueError:
return 'REVIEW'
return 'SAME_VALUE' if left == right else 'DIFFERENT_VALUE'
cases = [
('1,250.50', '1.250,50', 'dot', 'comma'),
('1,250', '1.250', 'dot', 'comma'),
('1,250', '1.250', 'dot', 'dot'),
('-12.5', '12,5', 'dot', 'comma'),
('1,25', '1.25', 'dot', 'dot'),
]
for row in cases:
print(row, check_pair(*row))
本地运行结果如下。这五组数据由代码手工构造,用来验证规则分支,并非翻译质量评测。
| 检查项 | 输出 |
|---|---|
| 1,250.50 / 1.250,50,dot → comma | SAME_VALUE |
| 1,250 / 1.250,dot → comma | SAME_VALUE |
| 1,250 / 1.250,dot → dot | DIFFERENT_VALUE |
| -12.5 / 12,5,dot → comma | DIFFERENT_VALUE |
| 1,25 / 1.25,dot → dot | REVIEW |
最后一组没有被"修复"为 125。因为 1,25 不符合这里规定的三位分组格式,程序直接拒绝解释。它在别的约定下可能合法,这正是需要保留原字符串的原因。
3. 接到 PDF 翻译流程时,最难的是正确配对
不要把整份 PDF 中所有数字提取出来,排序后比较。即使两个文件的数字集合完全相同,数字也可能被放到了错误的行或列。
例如,原表写"型号 A:12;型号 B:24",译文写成"型号 A:24;型号 B:12"。全局数字计数完全一致,两个字段却都错了。检查单位应该从"整份文件"收窄到"已对齐的字段"。
建议保留这些信息:
json
{
"field_id": "table_2.model_a.capacity",
"source_page": 4,
"target_page": 4,
"source_raw": "1,250.50",
"target_raw": "1.250,50",
"source_convention": "dot",
"target_convention": "comma",
"unit": "mL"
}
这里的页码和字段是结构示例,不是实际提取结果。真实文件中还可以记录区域坐标、表头、行标签和原图裁片;翻译可能改变分页,所以不能仅靠"页码相同、出现顺序相同"自动配对。

接入时分三层处理:
- 提取层: 原生文字 PDF 读取现有文本;扫描件先 OCR。无法提取或识别置信不足时记录失败,不返回空集合当作检查通过。
- 对齐层: 根据表头、型号、编号及上下文定位同一字段。无法可靠配对时标为待复核,不强行把第 N 个数字配给第 N 个数字。
- 数值层: 约定已确认、字段已配对之后,才运行本文的小函数。输出"相同数值""不同数值"或"格式待复核"。
PDF 文本提取可能受到阅读顺序、字体编码、表格布局影响;pypdf 也不是 OCR 工具。这里没有给出"全自动找出所有数字"的正则表达式,因为提取与对齐失败无法靠一个数字正则解决。
4. 数值一致之后,还要检查单位和限定条件
SAME_VALUE 只说明两个已解析的数相等。它没有回答下面这些问题:
5 mg是否变成了5 g?数字没变,量级已经不同。5%是否被当成5?百分比和普通数值不能直接混比。1.20与1.2是否允许视为同一表达?数值相等,但有效位数或展示精度可能有要求。- "不超过 12"是否变成"超过 12"?数字和单位都对,条件仍可能反了。
- 型号
0012是否变成12?标识符应该按业务规则比较字符串,不应交给数值解析器。

单位换算也不要偷偷混进字符串清洗。例如 1000 mL 和 1 L 可能等价,但需要先确认单位体系和字段含义,再进行显式换算。本文函数没有实现这些语义判断,不能用它签发"整份译文通过"的结论。
5. 把翻译结果当作核对的起点
如果先用现成工具生成整篇译文,比如 pdf translator org,其当前公开流程支持上传 PDF、选择语言与翻译风格,完成后预览或下载,并提供扫描件 OCR 翻译能力。拿到结果后,可以对关键表格和参数按上述方法检查。
上面的 Python 代码是独立的数字检查例子,不代表该工具内置了同样的检查功能,也不是对其翻译结果的实测。
对于报告与技术手册,先把"数值、单位、字段归属、限定词"四件事记录清楚,再决定哪些能自动比较、哪些必须人工看原文。这样一份差异清单,比一个看似漂亮的"数字一致率"更容易定位问题。