首页
/ Umi-OCR 多语言翻译实战:换行、占位符、Markdown 格式与 languageScale 字体缩放系数详解

Umi-OCR 多语言翻译实战:换行、占位符、Markdown 格式与 languageScale 字体缩放系数详解

2026-09-05 13:31:33作者:齐添朝

本文为 Umi-OCR 开源 OCR 软件的本地化(i18n)译者指南。它完整覆盖了官方《翻译注意事项》中的全部核心规范——\n 换行规则、%1 占位符保留规则、Markdown 长字符串格式规则、languageScale 字体缩放系数的设定方法,以及“ts 转 txt → 机器翻译 → txt 转回 ts”的自动化翻译工作流;同时结合仓库中 Size_.qml 的源码实现与 convert_ts_txt.pyconvert_txt_ts.py 两个转换脚本的实现细节,说明每条规则背后的技术原因,帮助你在完成一套 UI 翻译并导入 Umi-OCR 测试时做到一次通过。

1. Umi-OCR 的翻译体系:ts 工程文件与 qm 二进制包

Umi-OCR 的界面基于 Qt-Qml 框架,其翻译流程分为三步(假设目标语言为英语 en_US):

  1. 从源代码中提取所有待翻译字符串,生成 en_US.ts 工程文件;
  2. 编辑 en_US.ts,将其中文本翻译为目标语言;
  3. 将翻译完成的 en_US.ts 编译为 en_US.qm 二进制文件,放入 UmiOCR-data/i18n/ 目录后即可被软件加载。

仓库中的对应产物可以佐证这一链路:

下面 4 条注意事项,就是翻译 en_US.ts 这类文件时必须遵守的内容规范。

2. 关于换行符 \n:在 linguist 中必须用真实回车

源代码中以 \n 表示的换行符,在翻译工具 linguist 中会被转换为一行直接换行(编辑器中看起来像结尾带音符的断行)。因此翻译时也应使用回车键输入真实换行,而不是把字符串 \n 写进译文。

举例说明。源代码文本为:

这是第一行\n这是第二行

错误的翻译(把 \n 当纯文本写进去):

This is the first line\nThis is the second line

正确的翻译格式(用真实换行分隔两行):

This is the first line
This is the second line

这条规则的底层原因可以从 QML 字符串机制理解:qsTr() 捕获的原文本中包含的是转义序列 \n,Qt 在运行时将其解释为换行;而 linguist 的编辑器面向的是解析后的文本,其换行即真实换行符。若译者写入字面 \n,运行时界面就会显示出一个原样的 \n 字符,造成显示错误。

3. 关于占位符 %1:必须原样保留

待翻译文本中可能含有 %数字 格式的占位符(Qt 的 QString::arg() 风格参数),翻译时必须保留所有占位符,且编号不能改动,否则运行时参数替换会失败或缺失。例:

源代码文本:

原端口号%1被占用,\n切换为新端口号%2。

正确的翻译格式:

The original port number %1 is occupied.
Switching to the new port number %2.

注意此例同时体现了第 2 节的换行规则:原文的 \n 在译文中也应处理为真实换行。

4. 关于 Markdown 格式 #:保留特殊字符与全角空格

有一些源文件(如 PagesManager)中包含 Markdown 格式的长字符串,这些文本会被渲染进界面。翻译时务必遵照原文本格式,保留如 #  这样的特殊字符。

特别地,  (1 个全角空格 + 2 个半角空格)是用于让 QML 中 Markdown 解析器空出一行的技巧,直接复制这一串字符即可,不要自行删改。例:

源代码:

# 截图OCR



屏幕截图,快捷转文字。也支持粘贴图片。

正确的翻译格式(标题层级、空行数量、全角空格串均原样保留):

# Screenshot OCR



Screenshot, quick text conversion. Also supports pasting images.

5. 关于字体缩放系数 languageScale

5.1 为什么需要语言级缩放

在 Umi-OCR 的界面设计中,很多 UI 组件的尺寸与行高绑定,例如按钮宽度定义为 5 倍行高、高度为 1.5 倍行高,这些宽高系数以中文为基准设定。相同含义的文字,不同语言的占地并不相同——截屏Screenshot 相比,英文明显更长。因此当 UI 语言为英文等长文本语言时,应适当缩小文字尺寸来适应 UI:行高不变,只调整文字大小

5.2 源码实现:Size_.qml

这一机制的实现位于 UmiOCR-data/qt_res/qml/MainWindow/Size_.qml。该文件是全局尺寸组件,核心逻辑为(Size_.qml#L13-L30):

// 行高
property int line: 16 * scale        // 主要文字
property int smallLine: 13 * scale   // 较小的文字
property int largeLine: 20 * scale  // 较大的文字

// 文字缩放值
property real textScale: 1 // 由下列 languageScale 控制
property int text: line * textScale       // 行高 × 缩放 = 实际字号
property int smallText: smallLine * textScale
property int largeText: largeLine * textScale

而缩放系数的入口正是待翻译文本 1.0Size_.qml#L47-L60):

// 语言缩放系数
// 在相同的行高内,有些语言经过缩放可以表现更好。如英文可以比汉字的字号更小。
// 通过在翻译文件中定义 languageScale 可以单独修改这种语言的缩放。
property string languageScale: qsTr("1.0")

Component.onCompleted: {
    const s = parseFloat(languageScale)
    if(!isNaN(s)) {
        textScale = s
    }
    else {
        console.warn("语言缩放系数无法应用:", languageScale)
    }
}

从源码结构看:languageScale 本身也是一个 qsTr() 字符串,因此它会出现在翻译文件中;组件加载完成后 parseFloat 解析该译文——若译文是 0.92,则 textScale = 0.92,所有文字尺寸按 0.92 倍收缩,而 line/smallLine/largeLine 等行高保持不变;若译文不是合法数字,则打印警告并保持 textScale = 1。这就是“在翻译文件里改一个数字即可调整该语言字体缩放”的原理。

5.3 实操:先实时预览,再回填翻译文件

为方便测试,可以按以下步骤操作(引自 翻译注意事项):

  1. 打开 Umi-OCR 的 全局设置 标签页,勾选 高级
  2. 拉到最底下,找到 developer toolslanguageScale (textScale)
  3. 调整该数字,实时预览字体缩放效果;
  4. 确定数值后,将同样的数字填入翻译文件(.ts)中对应 1.0 的译文位置。

该开发者选项对应 GlobalConfigs.qmldeveloper tools 分组下的 "languageScale (textScale)" 配置项(GlobalConfigs.qml#L286),它直接修改全局 Size_ 的缩放值,与翻译文件中写入的 languageScale 译文作用于同一参数,因此预览所见即最终所得。

6. 机器翻译工作流:ts → txt → 自动翻译 → ts → qm

当本地已安装 Python 时,可以借助 dev-tools/i18n 目录下的两个转换脚本,把 .ts 文件变成纯文本供机器翻译处理,再写回 .ts

6.1 第一步:ts 转 txt

en_US.ts 直接拖动到 convert_ts_txt.py 图标上,或命令行调用:

convert_ts_txt.py en_US.ts

结果:en_US.tsen_US.txt,txt 中每行对应一条原文本。此步骤中所有换行暂时以 \n 字符串表示,后续将 txt 转回 ts 时脚本会自动处理还原(与第 2 节的规则互为配合:linguist 里用真实换行,txt 里用 \n 占位)。

从源码看,convert_ts_txt.pyxml.etree.ElementTree 解析 ts 的 XML 结构,遍历每个 <message><source> 节点,输出时执行 source.replace("\n", r"\n"),把真实换行统一转成字面 \n,再逐条追加换行写入:

for item in message:
    if item.tag == "source":
        source = item.text
        data[-1].append({"source": source})
        ...
s += d["source"].replace("\n", r"\n") + "\n"  # 换行转\n

6.2 第二步:机器翻译 txt

得到 en_US.txt 后,可以用任意自动化手段批量翻译。例如将提示词与文本一起交给大模型:

请你充当翻译器,协助完成一个软件的UI翻译工作。
下面将输入多行文本。每行是一个原文。请你将其翻译为译文。
请保持原文的特殊格式,原文中的换行、%开头的标识符、mardown等格式予以保留。
首先,将简体中文翻译为英语。

【放文本】

提示词中“保留换行、% 开头标识符、Markdown 格式”的要求,正对应本文第 2、3、4 节的三条规范——这也是机器翻译结果需要人工校对的焦点所在。

6.3 第三步:txt 写回并转回 ts

  1. 将翻译好的文本逐行覆盖写回 en_US.txt(写之前先备份 txt)。务必使用带行号的文本编辑器,逐行核对每条译文与原文一一对应,没有错位、缺行;
  2. 将写好的 en_US.txt 拖入 convert_txt_ts.py,脚本会覆盖写入 en_US.txt.ts(先备份现有 ts);
  3. linguist 打开 en_US.txt.ts,检查、校对翻译,编译为 en_US.txt.qm
  4. 重命名为 en_US.qm,放入 UmiOCR-data/i18n/ 目录,即可导入 Umi-OCR 测试。

convert_txt_ts.py 的实现与上一步严格对称:它先解析原 ts 文件得到 XML 骨架,再按行读取 txt,对每个 <translation> 节点执行 txt[tranIndex].replace(r"\n", "\n") 把字面 \n 还原为真实换行后写回,最后 tree.write() 输出新 ts。也就是说,txt 的行序必须与 ts 中消息条目的顺序严格一致——这正是第 6.3 步要求“逐行核对”的原因。

7. 交付与检查清单

翻译完成、en_US.qm 导入后,建议在 Umi-OCR 中做一轮完整走查,对照本文各节确认:

检查项 对应规范
界面中是否出现原样的 \n 字符 第 2 节:linguist 内使用真实换行
带数字参数的句子(如端口提示)中参数是否正常替换 第 3 节:%1/%2 占位符原样保留
帮助类长文本的 Markdown 标题、空行渲染是否正常 第 4 节:保留 # 与全角空格串  
长单词语言(如英文)是否撑破按钮/面板 第 5 节:通过 languageScale 微调字号,行高不变

.ts 文件提交至 dev-tools/i18n/release/.qm 文件提交至 UmiOCR-data/i18n/,完整的提交与 PR 流程参见 翻译步骤(完整).md。掌握本文的格式规范、languageScale 缩放原理与 txt 转换工作流后,即可独立完成一套高质量的语言翻译并闭环验证。

登录后查看全文
热门项目推荐
相关项目推荐