PDF 翻译后 1,250 变成 1.250,是错了吗?用 Python 核对数字格式

一份 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"
}

这里的页码和字段是结构示例,不是实际提取结果。真实文件中还可以记录区域坐标、表头、行标签和原图裁片;翻译可能改变分页,所以不能仅靠"页码相同、出现顺序相同"自动配对。

接入时分三层处理:

  1. 提取层: 原生文字 PDF 读取现有文本;扫描件先 OCR。无法提取或识别置信不足时记录失败,不返回空集合当作检查通过。
  2. 对齐层: 根据表头、型号、编号及上下文定位同一字段。无法可靠配对时标为待复核,不强行把第 N 个数字配给第 N 个数字。
  3. 数值层: 约定已确认、字段已配对之后,才运行本文的小函数。输出"相同数值""不同数值"或"格式待复核"。

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 代码是独立的数字检查例子,不代表该工具内置了同样的检查功能,也不是对其翻译结果的实测。

对于报告与技术手册,先把"数值、单位、字段归属、限定词"四件事记录清楚,再决定哪些能自动比较、哪些必须人工看原文。这样一份差异清单,比一个看似漂亮的"数字一致率"更容易定位问题。

参考资料

相关推荐
Yolanda_20221 小时前
30.利用GPU训练(一)
python
迅猛龙办公室1 小时前
Python实现PM空气质量提醒
服务器·前端·python
小马不起床1 小时前
Django 后端实战指南_9:前后端完整联调和问题修复
python·django·状态模式
pride.li2 小时前
第12章 Python 教程-流程控制:循环结构
linux·服务器·python·算法
鬼手点金2 小时前
模型即 Agent:当模型本身成为产品
开发语言·前端·人工智能·python·机器学习
田里的水稻2 小时前
PyTorch-Dataset\DataLoader数据加载
人工智能·pytorch·python
卷无止境2 小时前
Python 3.14 自由线程:拆掉 GIL 之后,我们到底得到了什么
后端·python
染指11102 小时前
147.Agent-LangGraph归并函数
开发语言·人工智能·python·langchain·agents
小白学大数据3 小时前
逆向分析短视频平台 a_bogus 参数:从 JavaScript 混淆到 Python 复现
javascript·python·音视频