
在 Linux 上用 Qt6 写界面写了小半年一直以为 Linguist语言家是 Qt 安装包自带的标配工具。直到有一天构建服务器上跑翻译批量导出脚本日志里甩出一行lrelease: command not found我才意识到问题没这么简单。本地开发机的菜单里明明能打开 Linguist怎么换台机器就不认识了后来仔细查了才发现Qt6 编译安装后Linguist、lupdate、lrelease 这套翻译工具链和 Qt 库本体是分开走的。更麻烦的是像我们这种离线内网环境既不能用包管理器在线装也没法跑官方在线安装器最后只剩一条路自己从源码把 qttools 模块编译出来。这条路走通之后我把完整过程整理成这篇实操记录给同样被 Linguist 安装问题卡住的人一个可以直接抄的作业。1. 为什么要单独折腾 linguistQt6 工具链的拆分现状很多从 Qt5 时代过来的开发者会有个惯性认知装了 Qt 就等于装了全套开发工具。这个认知在 Qt6 里已经不完全成立了。1.1 从 Qt5 到 Qt6翻译工具经历了什么变化Qt5 时期Linguist、lupdate、lrelease 等工具被集成在 qt5tools 或者随 Qt 库一起发布装完 Qt 基础库之后这些二进制通常就在可执行路径上。很多发行版也会直接把翻译工具作为 qt5-tools 这类包名打进去一条apt install就能解决。到了 Qt6翻译工具链被收到了 qttools 模块里而这个模块和 qtbase 是分开发布、分开编译的。你用包管理器装qt6-base系统只会给你 Qt Core、Qt GUI、Qt Widgets 这些运行库和对应的 CMake 配置不会默认带上 Linguist 图形界面工具也不一定带 lrelease、lupdate、lconvert 这三个命令行工具。更隐蔽的是Qt 官方在线安装器虽然有个 Qt Tools 组件但里面放的是 Qt Creator 相关的附属工具语言家很多时候并不在那个组件里。哪怕你勾选了一整套Linguist 也可能只有一个孤立可执行文件lrelease 之类的命令行工具在里面时有时无。所以我那次在服务器上遇到lrelease: command not found其实不是服务器环境坏了而是从设计层面上Qt6 就不再把翻译工具当默认依赖看待了。理解这一点后面所有操作就都说得通了。1.2 什么样的情况需要源码编译这个模块从我实际接触到的场景看需要手工编译 qttools 的人通常属于以下三类之一第一类是离线内网环境。开发机和目标服务器物理隔离无法访问发行版软件源也访问不了 Qt 官方在线仓库只能拿离线安装包或者源码包进去编译。我就是这种情况。第二类是发行版软件源里没有对应版本的包。比如你用的是滚动更新发行版系统源里是 Qt 6.7 的包但你的工程是基于 6.5 LTS 编译的直接装源里的 linguist 可能连 lrelease 出来的 .qm 文件格式都跟主版本有兼容性差别。这种版本错位在多人协作、统一构建环境时特别烦。第三类是嵌入式或交叉编译环境。Qt 本体是交叉编译出来的工具链跑在 x86 主机上目标板是 ARM。这时候主机上必须有一套能在 x86 上运行的 lupdate/lrelease 用来生成翻译文件而这套工具必须跟交叉编译用的 Qt 版本严格对应用发行版自带的版本很可能因为 Qt 版本不一致而出现字符串提取规则差异。如果你只是个人开发、本地环境能上网那源码编译不是唯一选择后面我会把几种路线对比一下。但如果你是上面三类情况中的任何一种源码编译基本是绕不开的。2. 三条安装路线怎么选以及我为什么走了源码编译动手之前先把可选项盘一遍。我自己把安装 Linguist 的路线归纳为三种发行版软件源安装、aqtinstall 脚本化安装、源码编译。三条路各有适用边界选错容易浪费时间。2.1 发行版软件源先试着一条命令解决如果你的系统能联网先别急着编译试试包管理器能不能一条命令搞定。Debian/Ubuntu 系Qt6 翻译工具链在qttools6-dev-tools这个包里部分版本叫qt6-linguist安装命令是sudo apt install qttools6-dev-tools装完之后lrelease、lupdate、lconvert会进/usr/lib/qt6/bin/目录。注意这个目录不一定在 PATH 里所以装完先which lrelease看一下找不到就手动加环境变量。Fedora/RHEL 系对应的包名是qt6-linguist和qt6-qttoolssudo dnf install qt6-linguist这条路线最大的优点是省事但它有两个天然局限一是软件源里的 Qt6 版本跟你的工程目标版本不一定匹配二是很多定制化系统或离线系统根本没有这些包。所以我建议把软件源作为第一尝试但心里要有备案。2.2 aqtinstall跨平台脚本化安装aqtinstall 是一个用 Python 写的 Qt 官方安装器替代工具适合在 CI 或脚本环境里统一装 Qt。它也能单独拉 qttools 模块pip install aqtinstall aqt install-qt linux desktop 6.5.3 linux_gcc_64 -m qttools-m qttools表示额外安装 qttools 模块装完之后 Linguist 相关工具会出现在 Qt 安装目录下的libexec/里。这条路线对 CI 场景非常友好版本可控也不需要图形界面。但 aqtinstall 本质上还是从 Qt 官方 CDN 下载二进制离线内网环境同样走不通。另外它拉下来的 qttools 在少数发行版上会有 glibc 版本要求的限制太老的系统跑不起来。2.3 源码编译什么时候非它不可源码编译适合以下情况离线环境、需要精确匹配 Qt 版本、需要给自己的 Qt2 定制补丁、或者目标平台不在官方预编译列表里。我当时的环境是内网构建服务器操作系统是比较老的 CentOS 7 兼容环境开发机上用的 Qt 版本是 6.5.3服务器上需要一套完全一致的工具链做自动化翻译提取。软件源里没有 Qt6aqtinstall 拉不了外网只能让运维帮我拷进去一份 qttools 源码包然后从编译开始一步步来。源码编译的好处是自由度最大坏处是依赖检查比较繁琐一个依赖不满足可能编译到一半才报错。我把我走通的完整过程放在下一节每一步都带上参数解释和排查思路方便你照做。关于三条路线的取舍这里做一个直观的对比对比项软件源安装aqtinstall源码编译安装方式apt/dnf 一条命令pip 后拉取模块CMake 完整构建版本控制跟随发行版仓库可指定 Qt 小版本完全自主可控离线支持一般不支持支持目标平台覆盖取决于系统源官方预编译平台任意平台额外依赖少少qtbase 等较多适合场景快速开发环境CI 自动化离线/跨平台/定制3. 源码编译全流程qttools 模块的 configure 与构建如果你已经确认要源码编译接下来的内容就是核心了。我以 Qt 6.5.3 在 Linux x86_64 环境下的完整编译过程为例版本不同但思路一致。3.1 前置依赖的检查清单编译 qttools 之前系统里必须先有一份可用的 Qt6 库。qttools 本身不是一个独立的全家桶源码它相当于 Qt 官方仓库里的一个子模块编译时依赖 qtbase 提供的头文件、CMake 配置文件和运行库部分功能还依赖 qtdeclarative。在开始之前用下面几条命令检查依赖是否齐全# 检查 qtbase 的 CMake 配置是否存在 ls /opt/Qt/6.5.3/gcc_64/lib/cmake/Qt6/Qt6Config.cmake # 检查 qmake 或 cmake 是否可用 /opt/Qt/6.5.3/gcc_64/bin/qmake --version # 检查编译器和构建工具 gcc --version cmake --version make --version如果你的 Qt6 是系统包管理器装的路径通常在/usr/lib/x86_64-linux-gnu/cmake/Qt6/如果是自己编译的或官方二进制包路径看你安装到哪里。这里的关键是找到 Qt6Config.cmake 的位置CMake 就是靠它来感知你已经装好的 Qt 库。除了 Qt 库本身qttools 的 lupdate 工具若要支持解析 C 代码里的tr()字符串需要系统里有 Clang 的库文件。这个依赖很容易忽略因为 configure 阶段不一定报错可能只是某个特性被静默关闭了后面我专门用一节来展开。确认这些之后建议把系统里的 LLVM/Clang 开发包也装好Debian/Ubuntu 下是libclang-devFedora 下是llvm-devel和clang-devel。如果你的内网环境没有这些包后面 CMake 配置时就需要显式关掉 clang 相关的 feature代价是 lupdate 对 C 工程的字符串提取能力会大打折扣。3.2 获取匹配版本的 qttools 源码源码版本必须和你已安装的 Qt 库版本一致这是我在这个项目里确认过的最重要的一条。如果你用 Qt 6.5.3 的库却拉了 6.6 分支的 qttools 源码编译时大概率会遇到 CMake 报错因为 Qt6 模块的版本兼容检查会在 configure 阶段直接拒绝。获取源码有两个途径。一是用 git 拉取对应分支git clone --branch v6.5.3 --depth 1 https://github.com/qt/qttools.git二是从 Qt 官方提供的源码压缩包中解压出 qttools 目录。离线环境下通常拿到的就是源码压缩包解压后目录名类似qttools-everywhere-src-6.5.3。无论哪种方式进入源码根目录后先确认目录下的.cmake.conf或CMakeLists.txt头部的版本号与你的 Qt 环境一致。这一步花不了几秒钟但能省下编译到一半才发现版本不对的返工时间。3.3 CMake 配置阶段的关键参数qttools 的构建系统是 CMake和 qtbase 早期那种 qmake 的情况不同。进入源码目录建议建一个独立的构建目录不要把编译产物混在源码里。这是我在内网环境中实际跑通的配置命令cmake -S qttools -B build-qttools \ -DCMAKE_PREFIX_PATH/opt/Qt/6.5.3/gcc_64 \ -DCMAKE_BUILD_TYPERelease \ -DCMAKE_INSTALL_PREFIX/opt/Qt/6.5.3/gcc_64 \ -DBUILD_WITH_PCHON逐个解释一下这些参数CMAKE_PREFIX_PATH告诉 CMake 去哪里找已安装的 Qt6 组件。这个路径要指到 Qt 库实际安装位置也就是包含lib/cmake/Qt6的那个目录而不是 Qt 安装目录的根。如果你装到了/usr这个参数可以用/usr。CMAKE_BUILD_TYPE建议设成 Release因为 Linguist 和 lrelease 这类工具是给开发者用的没有调试需求Release 编译更快、运行更稳定。CMAKE_INSTALL_PREFIX是安装路径。我这边直接装回到 Qt 自己的安装目录这样后面工程通过find_package(Qt6)时能顺理成章地在同一个前缀下找到 Linguist 工具。如果你希望工具链放系统目录可以改成/usr/local但要注意别和系统已有的 Qt 版本冲突。BUILD_WITH_PCH是预编译头文件选项开起来能明显缩短编译时间。如果你发现编译过程中预编译头相关报错可以试着关掉它这个选项的收益主要在速度上不影响最终功能。如果内网环境确实没有 Clang 开发库需要在配置命令里追加-DFEATURE_clangcppOFF注意clangcpp是 qttools 中 lupdate 用来解析 C 代码的功能特性。关闭后lupdate 对纯 C 的.cpp文件里的tr()提取会走旧式正则匹配对复杂模板代码的识别率会明显下降。我的建议是能用 Clang 就尽量用实在没有库文件再关。3.4 编译与安装配置成功之后CMake 会打印出将要构建的组件列表。这一步值得仔细看一眼确认列表中包含linguist、lupdate、lrelease、lconvert。有些版本的 qttools 会把 QML 相关的工具也列出来那些是额外组件不影响核心功能。然后开始编译。第一次编译建议不要急着并行开满先确认流程没问题cmake --build build-qttools -j$(nproc)$(nproc)会取 CPU 核心数。如果内存不太充裕比如 8G 以下建议手动限制并行数比如-j4避免编译过程中 OOM 导致进程被杀。我踩过一次服务器内存满了整个构建目录处于半写入状态最后只能删掉build-qttools重新来。编译成功之后安装cmake --install build-qttools安装完成后检查一下几个关键文件是否就位ls -l /opt/Qt/6.5.3/gcc_64/bin/linguist ls -l /opt/Qt/6.5.3/gcc_64/bin/lrelease ls -l /opt/Qt/6.5.3/gcc_64/bin/lupdate ls -l /opt/Qt/6.5.3/gcc_64/bin/lconvert看到这四个文件都在说明核心工具已经安装完成。如果不想把工具装进 Qt 库目录只想装到/usr/local把前面的CMAKE_INSTALL_PREFIX改掉重跑 configure 和 install 就行不需要重新编译。4. 我踩过的四个坑按排查链路排列这部分单独拿出来写是因为整个过程里最花时间的不是编译本身而是各种环境和版本问题带来的隐性报错。我把踩过的坑按排查链路整理出来希望对你有实际参考价值。4.1 坑一qtbase 版本与 qttools 分支对不上第一次编译时我图省事直接拉了 qttools 的 dev 分支结果 CMake configure 阶段报了一堆版本不匹配的错误类似Found Qt6: 6.6.0 but at least 6.5.3 is required。当时我还以为是有多个 Qt 版本在环境变量里串了查了半天最终确认就是分支版本不匹配。后来我统一改用 tag 拉取开发机 Qt 是什么版本qttools 就拉什么 tag。这个教训在 Qt 生态里特别通用Qt6 的模块仓库版本匹配非常严格小版本不一致虽然偶尔能编译过但运行期可能出现 lupdate 解析行为不一致这种隐性问题比编译失败更麻烦。排查建议是用qmake --version和grep -r MODULE_VERSION qttools/.cmake.conf对比两边版本号确认完全一致后再开始 configure。4.2 坑二缺少 clang 导致 lupdate 的 C 解析功能失效这个坑最隐蔽。最初我在内网编译时没有装 Clang 开发包CMake configure 阶段根本没有报错整个编译过程也非常顺利Linguist 和 lrelease 都正常出来了。当时我以为万事大吉结果拿一个真实工程跑lupdate .时发现.cpp文件里的tr(你好)一个都提取不出来只有.qml文件里的字符串能正常提取。排查链路是这样的先跑了lupdate -version确认工具本身没问题然后lupdate -help也没有异常。最后翻了编译时的 CMake 缓存日志才发现FEATURE_clangcpp的值是 OFF。因为系统里没有clang头文件和库CMake 自动关闭了这个特性lupdate 对 C 代码的解析直接退化。解决方法是补装libclang-dev然后删掉构建目录重新 configure、重新编译。装完 Clang 开发包后再跑cmake -S时你会看到输出里多了一行关于 clangcpp 的 Feature 开启信息这时再编译出来的 lupdate 才能完整提取 C 和 QML 两种工程里的字符串。这里也引申出一个建议编译大型 Qt 工具链时不要只盯着“能不能编译过”还要注意 CMake 输出里的 Feature 开关状态宁愿多花几分钟仔细看一遍 configure 输出也不要装完发现某个核心功能被静默关闭。4.3 坑三编译到一半报 Qt6 包找不到第二次在新服务器上编译时我用了旧的构建目录结果编译到一半冒出来类似Could not find a package configuration file provided by Qt6Qml的错误。问题出在不完整的自动清理上我改了CMAKE_PREFIX_PATH但没有删除之前生成的 CMakeCache.txt导致 CMake 拿着旧的缓存路径去找 Qt6 包。解决办法是把整个build-qttools目录删掉重新执行 configure。这个坑的根源在于 Qt6 的 CMake 包路径是写进缓存的手动改环境变量或命令行参数不会触发缓存刷新CMake 会一直沿用旧路径。之后我给自己定了个规矩每次切换 Qt 版本或调整前缀路径必须删除构建目录重新配置绝不在旧构建目录上做增量修改。4.4 坑四安装完 lrelease 依然无法运行这个坑发生在装完工具之后。安装路径是/opt/Qt/6.5.3/gcc_64/bin但这个路径不在服务器的 PATH 环境变量里直接敲lrelease依然提示命令找不到和最初的问题一样。我当时的第一反应是安装出了问题重新跑了一遍 cmake --install文件都在但还是提示找不到命令。后来才意识到是 PATH 的问题。Qt 工具安装目录往往不在系统默认 PATH 里需要手动添加。解决方案是把 Qt 工具目录加到 PATH 里并写进 shell 配置文件echo export PATH/opt/Qt/6.5.3/gcc_64/bin:$PATH ~/.bashrc source ~/.bashrc做完之后lrelease -version就能正常输出了。这个坑很小但对自动化脚本来说很致命因为 CI 环境通常不会自动加载某个用户目录下的.bashrc需要在构建脚本里显式设置 PATH。5. 安装成功后的验证与 Linguist 日常使用建议工具装上之后还有几个验证方法和使用细节需要说一下否则只是“装好了”而不知道“好不好用”。5.1 三条命令确认安装结果我习惯用三条命令确认整套工具链可用lrelease -version lupdate -version linguist -version三条都正常输出版本信息说明安装成功。如果linguist -version报缺少图形库相关的错误检查一下系统里有没有 X11 或 Wayland 的运行库Linguist 是 GUI 程序纯命令行服务器上跑不了它但 lrelease 和 lupdate 没问题。再进一步可以拿一个小测试工程验证 lupdate 的提取功能。新建一个简单的main.cpp里面写个QObject::tr(hello world)然后跑lupdate . -ts test_zh.ts打开test_zh.ts能看到源字符串hello world被提取出来说明 lupdate 工作正常。5.2 与 CMake 工程的配合如果你是用 CMake 组织的 Qt6 工程编译完 Linguist 工具链后还可以考虑在 CMakeLists.txt 里加上自动翻译更新规则方便团队协作时一键同步翻译文件。Qt6 的 CMake 模块里提供了qt_add_translations或qt6_add_translations等函数只要 CMake 能找到 Qt6LinguistTools就会自动处理 ts 文件的更新和发布find_package(Qt6 REQUIRED COMPONENTS Core LinguistTools) qt_add_translations(myapp TS_FILES translations/myapp_zh_CN.ts )注意LinguistTools是组件名在 qttools 安装之后才可用。CMake configure 时能正常找到这个组件说明 qttools 的安装目录已经被正确识别。5.3 让翻译文件不出现乱码的两个小设置使用 Linguist 时最容易遇到的问题是中文乱码。这里有两个我实测有效的设置。第一个是在.ts文件里保证声明了正确的字符编码。Qt6 的 ts 文件默认 UTF-8用 lupdate 提取后自动生成一般不会有乱码。但如果你手动编辑过 ts 文件或从旧项目迁移过来打开 ts 文件检查开头声明是否为?xml version1.0 encodingutf-8?不是 UTF-8 的先用编辑器另存为 UTF-8。第二个是使用 Linguist 工具时的界面编码偏好。在菜单里可以找到“编辑”-“首选项”把默认编码设置为 UTF-8防止在个别老版本系统上出现界面输入中文乱码的情况。这个设置不常用但做中文项目的团队最好是提前预设好否则等翻译人员反馈“输入框里显示乱码”再排查沟通成本高得多。根据我这段折腾经历如果你只是临时需要一个 Linguist 工具来处理翻译优先尝试apt install qttools6-dev-tools或 aqtinstall这是性价比最高的路径但如果遇到离线环境、版本不匹配、交叉编译这类场景直接把本文的编译步骤套上去配置阶段把clangcpp这个 feature 关注好整个编译过程基本不会有大问题。最后再提醒一句务必确保 qttools 源码版本和你的 Qt 库版本严格一致这是我在多次实践中确认的最重要因素没有之一。