首页
/ Umi-OCR 多语言体系实战:从 .ts 翻译工程到 .qm 语言包的完整本地化流水线

Umi-OCR 多语言体系实战:从 .ts 翻译工程到 .qm 语言包的完整本地化流水线

2026-09-05 21:58:56作者:郁楠烈Hubert

Umi-OCR 是一款离线 OCR 软件,其界面基于 Qt/QML 构建,并内置多国语言库。本文基于仓库中 本地化工具目录 及其配套脚本,系统讲解 Umi-OCR 的两套翻译机制:主程序基于 Qt Linguist 的 .ts.qm 编译流水线,以及插件侧轻量的 i18n.csv 翻译方案。读完本文,你可以独立完成翻译文件刷新、语言包编译、放入软件目录,并理解语言加载在源码层面的实现细节,从而为 Umi-OCR 贡献或维护一种语言。

Umi-OCR 在多种语言下的界面截图

一、两套本地化机制总览

Umi-OCR 的本地化分为两个层次,这也是 dev-tools/i18n/README.md 的核心脉络:

  1. 主程序(QML/Py)翻译:遵循 Qt 标准国际化流程——用 lupdate 从 QML 和 Python 源码中提取待翻译字符串,生成 .ts 工程文件;译者完成翻译后,用 lrelease 编译为二进制 .qm 语言包,软件启动时由 QTranslator 加载。
  2. 插件翻译:插件(如 OCR 引擎组件)不依赖 Qt 工具链,而是使用同目录下的 i18n.csv 文件承载译文,运行时由主程序提供的极简 Translator 类读取。

两类机制的分工在源码中清晰可辨:主程序语言加载逻辑位于 i18n_configs.py,插件翻译 API 位于 plugin_i18n.py

二、译者入口:Weblate 在线协作

本地化 README 指出,非开发者译者无需搭建本地环境,可直接前往 Weblate 在线翻译平台(使用 GitHub 账户登录)参与工作:

  • 补充、订正现有语言的翻译;
  • 创建新的语言。

这种方式与 Qt 工具链是互补的:.ts 文件由维护者从源码刷新后推送到平台,译者在线编辑,最终仍会回流到仓库中编译。对只想贡献译文的译者来说,这是成本最低的参与路径。

若希望本地完整走一遍流程,翻译步骤(简易).md 提供了面向非程序员译者的简化指引:下载仓库代码包(强烈建议只取主分支,因为部分分支含有体积很大的二进制库),用 linguist 打开对应语言的 .ts 文件翻译,完成后编译并放置语言包。

三、从源码生成 .ts 翻译工程文件

3.1 单语言刷新:lupdate 命令

翻译步骤(完整).md 给出了刷新单个语言工程文件的标准命令(工作目录为 dev-tools/i18n,以 en_US 为例):

lupdate.exe "../../UmiOCR-data/qt_res/qml" -recursive -ts "en_US.ts"

若系统提示无法识别 lupdate.exe,在指令前加 ./ 即可。执行成功时的典型输出形如:

Scanning directory '../../UmiOCR-data/qt_res/qml'...
Updating 'en_US.ts'...
    Found 307 source text(s) (140 new and 167 already existing)
    Kept 42 obsolete entries
    Same-text heuristic provided 21 translation(s)

由于 lupdate 会保留已有译文并合并相同文本的启发式翻译,已经开始编辑 .ts 文件后重新执行该命令是安全的,不会丢失已完成的翻译进度(当然仍建议备份)。

3.2 全语言刷新:lupdate_all.py

当源码中新增了文本、需要刷新全部语言时,运行 lupdate_all.py。阅读源码可以看到它分三步工作:

  1. 扫描 QML 目录。对 LangList 中的每种语言,调用(见 L30-L39):

    lupdate.exe \
    "../../UmiOCR-data/qt_res/qml" \
    -recursive \
    -no-obsolete \
    -source-language "zh_CN" \
    -target-language "{l}" \
    -ts "release/{l}.ts"
    

    几个关键参数值得注意:-recursive 递归扫描 qt_res/qml 下全部 QML 文件;-no-obsolete 丢弃源码中已不存在的过期条目,保持工程文件精简;-source-language "zh_CN" 声明简体中文为源语言——这与软件中 qsTr()/tr() 标记的原生语言一致,i18n_configs.pyDefaultLang = "zh_CN" 也印证了这一点。

    当前脚本中激活的语言列表 LangList(见 L9-L25)包括:en_US(英语)、zh_TW(繁体中文)、ja_JP(日语)、fr_FR(法语)、pt(葡萄牙语)、ru_RU(俄语)、uz(乌兹别克语)、vi(越南语)、ta(泰米尔语)。被注释掉的 nb_NOit_ITes_ESde_DEko_KRpt_BR 表示这些语言暂未启用。

  2. 扫描 Python 目录生成 _2.ts。脚本递归收集 py_src 下的所有 .py 文件,写入一个临时 temp.pro 工程文件(含 CODECFORTR = UTF-8 等指令),再调用 pyside2-lupdate.exe 生成各语言的 xxx_2.ts(见 L58-L74)。注意源码中 sys.exit() 的位置表明 Py 部分目前仍标记为“待定”,实际生效的主要是 QML 部分的提取。

  3. 合并两个工程文件。脚本用 xml.etree.ElementTree 解析 {l}.ts{l}_2.ts,以 context 名称为键建字典,将 Python 部分的 message 逐条追加到 QML 部分的同名 context 中;若 Python 部分不足 10 行则视为无需翻译,跳过合并(见 L76-L107)。合并结果写回 release/{l}.ts,并删除临时文件。

刷新完成后的协作流程即 README 中的第二步:提交更改,等待 Weblate 平台同步更新,译者可在线继续翻译。

3.3 语言标识符与地区映射

各语言对应的工程文件命名与语言标识符一一对应。文档中列出的完整标识符表如下:

标识符 语言 标识符 语言
zh_CN 简体中文 ja_JP 日本語
zh_TW 繁體中文 ru_RU Русский
en_US English pt_BR Português
es_ES Español it_IT Italiano
fr_FR Français de_DE Deutsch
ko_KR 한국어

与主语种相同但地区不同的代码(如 zh_HKen_GBen_CAes_MXfr_CAde_ATde_CHpt_PT)会映射到最接近的翻译文件。这一映射在运行时同样有明确实现:i18n_configs.py 中的 LanguageCodes 字典将 zh_HK/zh_MO 指向“繁體中文”、en_GB/en_AU/en_CA 指向 "English"、pt_PT 指向 "Português" 等,注释中明确说明“每个语种只有第一个语言代码是有效的(对应到翻译文件 .qm),其余的语言代码会映射到第一个”。若要新增一个不属于现有语种的语言,需要在该文件(文档路径写作 UmiOCR-data\py_src\utils\i18n_configs.py)中补充对应的标识符映射。

四、从 .ts 编译二进制 .qm 语言包

4.1 单文件编译

翻译完成后有两种编译方式:

  • linguist 中点击菜单 FileRelease,在当前目录生成 en_US.qm

  • 命令行调用:

    lrelease.exe "en_US.ts"
    

4.2 批量编译:lrelease_all.py

批量场景下运行 lrelease_all.py。源码实现非常直接:遍历 release 目录,收集所有 .ts 文件,对每个文件执行 lrelease.exe "release/{t}"(见 L5-L16),在同目录生成同名 .qm

4.3 语言包放置与验证

编译后的 .qm 文件需要剪贴到软件数据目录。README 中写明的目标是 Umi-OCR/UmiOCR-data/i18n;在当前仓库结构中对应目录为 UmiOCR-data/i18n。启动 Umi-OCR 后,在全局设置(界面和外观语言 / Language)中即可切换新语言。切换时应重点检查译文是否正确显示、控件尺寸是否合适——长文本语言(如德语、俄语)通常需要借助下一节介绍的字体缩放系数适配。

运行时软件如何发现语言包,可以对照源码确认:_getLangPath 方法遍历 i18n 目录下所有 .qm 文件,以文件名去掉扩展名作为语言代码建立 langDict,并保证默认语言 zh_CN 始终存在(见 i18n_configs.py#L107-L143)。若预配置中无有效语言,软件会读取系统 locale 尝试匹配 LanguageCodes,匹配失败则回退到 zh_CN 并记录警告日志。加载阶段由 init 方法完成:创建 QTranslator、调用 translator.load(path)qtApp.installTranslator(translator),任一环节失败都会弹出中英文错误提示(见 i18n_configs.py#L68-L92)。

五、翻译实践要点:换行、占位符、Markdown 与字体缩放

翻译注意事项.md 提出了四条译者必须遵守的规则,每条都对应 QML 界面的具体渲染机制:

1. 换行符 \n 必须写为直接换行。 源码中的 \nlinguist 中会被转换为直接换行(结尾符号类似音符),译文中也应直接回车,而不是输入字面 \n。例如源文本 这是第一行\n这是第二行,正确译文是两行独立文本,而非 This is the first line\nThis is the second line

2. 占位符 %数字 必须原样保留。 形如 原端口号%1被占用,\n切换为新端口号%2。 的文本,译文需保留 %1%2,Qt 运行时才会将实际参数填充进译文。

3. Markdown 格式长字符串必须保持原格式。 部分源文件(如 PagesManager)包含 Markdown 长文本,译文要保留 # 标题符、以及“1 个全角空格 + 2 个半角空格”这样的特殊空白序列——该序列用于让 QML 的 md 解析器空出一行,直接复制原文中这段空白即可。

4. 字体缩放系数(languageScale)。 Umi-OCR 的 UI 组件尺寸与行高绑定(例如按钮宽度为 5 倍行高、高度为 1.5 倍行高),且这些系数以中文为基准设定。英文等更长占地的语言需要缩小字号来适配。在 Size_ context 中有一条待翻译文本 1.0,对应源码:

property string languageScale: qsTr("1.0")

将其翻译为 0.92 即调整了该语言的字体缩放系数。为方便测试,可先在 Umi-OCR 全局设置 标签页勾选 高级,滚动到底部 developer toolslanguageScale (textScale),实时预览缩放效果后再把数值写回翻译文件。

六、纯文本转换与机器翻译辅助

为提高批量翻译效率,仓库提供了两个转换脚本,与 linguist 工作流配合使用:

  • ts → txt:将 en_US.ts 拖动到 convert_ts_txt.py,或命令行执行 convert_ts_txt.py en_US.ts,得到逐行对应原文的 en_US.txt。脚本遍历 .ts 的 XML 结构,只提取每个 message 的 source 节点,换行暂时以字面 \n 表示(见 convert_ts_txt.py#L27-L32)。
  • 机器翻译:对 en_US.txt 使用 LLM 等自动化手段逐行翻译时,需提示其保留换行、% 占位符和 Markdown 格式;译文覆盖写回 txt 后,用带行号的编辑器逐行核对与原文的对齐关系。
  • txt → ts:将写好的 en_US.txt 拖入 convert_txt_ts.py,脚本以原 .ts 为模板,按行顺序把译文填入每个 message 的 translation 节点,并把字面 \n 还原为真实换行(见 convert_txt_ts.py#L15-L27),输出 en_US.txt.ts。最后用 linguist 打开校对、编译为 en_US.txt.qm,重命名为 en_US.qm 即可导入测试。

七、插件翻译:i18n.csv 轻量机制

Umi-OCR 中的插件(如引擎组件)不使用 Qt 工具链,而是走另一套轻量翻译机制。README 给出的操作流程是:

  1. 打开插件仓库,找到对应插件的目录;
  2. 目录中通常有 i18n.csv,用 Excel 或 WPS 打开(乱码时可用网上常见方法处理,例如以 UTF-8 导入 CSV);
  3. 在表格中编辑译文;
  4. 保存回 csv,务必确保文件存储为 UTF-8 编码(可用 VSCode 打开并转换编码);
  5. 向插件仓库提交 PR。

该机制的运行时实现就在主程序中:plugin_i18n.py 提供 Translator 类与全局 setLangCode。主程序初始化语言时会调用 setLangCode(self.langCode) 同步当前语言(见 i18n_configs.py#L78)。插件侧 Translator.__init__ 的读取逻辑(见 plugin_i18n.py#L19-L43)包含两个值得注意的降级规则:

  • 表头中找不到当前语言列时:若语言代码以 zh_ 开头则取第 0 列(即中文原文),否则取第 1 列(约定为英文列);
  • 查询时原文 key 在字典中不存在,则直接返回原文。

这与 CSV 的 key,en_US 双列约定相吻合。对于需要为插件新建/刷新翻译底稿的场景,仓库还提供了 plugins_tr.py:用正则 tr\("\'["\']\) 从插件源码文件中提取所有 tr("...") 调用,生成带 keyen_US 两列的 待翻译.csv(见 plugins_tr.py#L9-L31),译者只需补齐英文列后再扩展其他语言。

八、提交贡献与文件布局约定

两种角色的产出物位置约定如下:

产出物 位置 说明
.ts 工程文件 dev-tools/i18n 目录 翻译工程源,随代码一起管理、供 Weblate 同步
.qm 二进制语言包 UmiOCR-data/i18n 运行时被软件扫描加载
插件 i18n.csv 插件仓库对应插件目录 UTF-8 编码,走插件仓库 PR 流程

提交 PR 时只包含上述相关改动,不要夹带无关文件。对开发者而言,完整的本地化维护循环即:修改源码文本 → 运行 lupdate_all.py 刷新 .ts → 提交后等待 Weblate 同步 → 译者在线/离线翻译 → 运行 lrelease_all.py 编译 .qm → 放入 i18n 目录验证 → 随版本发布。

小结

Umi-OCR 的本地化体系在工程上有明确的分层:主程序依托 Qt 标准工具链(lupdate/linguist/lrelease),以 dev-tools/i18n 下的脚本(lupdate_all.pylrelease_all.py)实现批量自动化;插件则以 i18n.csv + 极简 Translator 降低参与门槛。运行时 i18n_configs.py 负责语言包发现、地区代码映射与加载回退,plugin_i18n.py 负责插件翻译列选择与降级策略。掌握 .ts 刷新、翻译规范(换行/占位符/Markdown/字体缩放)与 .qm 编译放置这三个环节,就能完整地参与或维护 Umi-OCR 的多语言支持。

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