Umi-OCR 多语言体系实战:从 .ts 翻译工程到 .qm 语言包的完整本地化流水线
Umi-OCR 是一款离线 OCR 软件,其界面基于 Qt/QML 构建,并内置多国语言库。本文基于仓库中 本地化工具目录 及其配套脚本,系统讲解 Umi-OCR 的两套翻译机制:主程序基于 Qt Linguist 的 .ts → .qm 编译流水线,以及插件侧轻量的 i18n.csv 翻译方案。读完本文,你可以独立完成翻译文件刷新、语言包编译、放入软件目录,并理解语言加载在源码层面的实现细节,从而为 Umi-OCR 贡献或维护一种语言。
一、两套本地化机制总览
Umi-OCR 的本地化分为两个层次,这也是 dev-tools/i18n/README.md 的核心脉络:
- 主程序(QML/Py)翻译:遵循 Qt 标准国际化流程——用
lupdate从 QML 和 Python 源码中提取待翻译字符串,生成.ts工程文件;译者完成翻译后,用lrelease编译为二进制.qm语言包,软件启动时由QTranslator加载。 - 插件翻译:插件(如 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。阅读源码可以看到它分三步工作:
-
扫描 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.py 中DefaultLang = "zh_CN"也印证了这一点。当前脚本中激活的语言列表
LangList(见 L9-L25)包括:en_US(英语)、zh_TW(繁体中文)、ja_JP(日语)、fr_FR(法语)、pt(葡萄牙语)、ru_RU(俄语)、uz(乌兹别克语)、vi(越南语)、ta(泰米尔语)。被注释掉的nb_NO、it_IT、es_ES、de_DE、ko_KR、pt_BR表示这些语言暂未启用。 -
扫描 Python 目录生成
_2.ts。脚本递归收集 py_src 下的所有.py文件,写入一个临时temp.pro工程文件(含CODECFORTR = UTF-8等指令),再调用pyside2-lupdate.exe生成各语言的xxx_2.ts(见 L58-L74)。注意源码中sys.exit()的位置表明 Py 部分目前仍标记为“待定”,实际生效的主要是 QML 部分的提取。 -
合并两个工程文件。脚本用
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_HK、en_GB、en_CA、es_MX、fr_CA、de_AT、de_CH、pt_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中点击菜单File→Release,在当前目录生成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 必须写为直接换行。 源码中的 \n 在 linguist 中会被转换为直接换行(结尾符号类似音符),译文中也应直接回车,而不是输入字面 \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 tools → languageScale (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 给出的操作流程是:
- 打开插件仓库,找到对应插件的目录;
- 目录中通常有
i18n.csv,用 Excel 或 WPS 打开(乱码时可用网上常见方法处理,例如以 UTF-8 导入 CSV); - 在表格中编辑译文;
- 保存回 csv,务必确保文件存储为 UTF-8 编码(可用 VSCode 打开并转换编码);
- 向插件仓库提交 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("...") 调用,生成带 key 与 en_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.py、lrelease_all.py)实现批量自动化;插件则以 i18n.csv + 极简 Translator 降低参与门槛。运行时 i18n_configs.py 负责语言包发现、地区代码映射与加载回退,plugin_i18n.py 负责插件翻译列选择与降级策略。掌握 .ts 刷新、翻译规范(换行/占位符/Markdown/字体缩放)与 .qm 编译放置这三个环节,就能完整地参与或维护 Umi-OCR 的多语言支持。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
