在Qt6发布后,官方明确将CMake作为首选的构建系统,这对习惯了QMake的开发者来说是个不小的挑战。我最近在将一个大型Qt项目从QMake迁移到CMake时,最头疼的就是国际化工作流的重构。QMake对翻译文件的管理简直太友好了,只需要在.pro文件中添加TRANSLATIONS配置,Qt Creator就能自动生成对应的菜单项。但换成CMake后,事情就没那么简单了。
Qt Linguist工具链包含两个核心组件:lupdate用于扫描源代码生成.ts翻译文件,lrelease则将翻译完成的.ts文件编译为.qm二进制格式。在QMake体系中,这两个工具与IDE深度集成,开发者几乎感受不到它们的存在。但在CMake项目中,我们需要自己搭建这套流程。
这里有个常见的误区:很多开发者以为CMakeLists.txt可以直接替代.pro文件的功能。实际上,lupdate工具无法直接解析CMake脚本,这也是为什么直接运行lupdate CMakeLists.txt会失败的原因。我们需要明确告诉lupdate哪些源文件包含可翻译字符串。
让我们从一个基础CMake项目开始。假设项目结构如下:
code复制project/
├── CMakeLists.txt
├── main.cpp
├── MainWindow.cpp
├── MainWindow.h
└── res.qrc
首先确保CMake能正确找到Qt Linguist工具:
cmake复制find_package(QT NAMES Qt6 Qt5 COMPONENTS LinguistTools REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS LinguistTools REQUIRED)
接下来定义翻译文件列表。我建议将翻译文件放在项目根目录下,方便翻译人员访问:
cmake复制set(TS_FILES ${CMAKE_CURRENT_SOURCE_DIR}/translations/app_zh_CN.ts)
set(QM_FILES)
关键的一步是配置源代码扫描。Qt5和Qt6提供了不同的宏:
cmake复制if(QT_VERSION_MAJOR EQUAL 5)
qt5_create_translation(QM_FILES ${TS_FILES} ${PROJECT_SOURCES})
else()
qt_create_translation(QM_FILES ${PROJECT_SOURCES} ${TS_FILES})
endif()
这个宏背后做了三件事:
我遇到过的一个坑是:当项目包含子目录时,需要确保CMAKE_INCLUDE_CURRENT_DIR设置为ON,否则lupdate会找不到头文件中的翻译字符串。
为了让翻译流程更符合开发习惯,我们可以创建两个自定义目标:
cmake复制add_custom_target(update_translations
DEPENDS ${TS_FILES}
COMMENT "Updating translation files"
)
add_custom_target(release_translations
DEPENDS ${QM_FILES}
COMMENT "Compiling translations to QM format"
)
这里有个实用技巧:如果你希望翻译文件能自动更新,可以添加文件依赖:
cmake复制set_source_files_properties(${TS_FILES}
PROPERTIES
MAIN_DEPENDENCY "${PROJECT_SOURCES}"
)
对于需要支持多语言的项目,可以这样管理:
cmake复制set(SUPPORTED_LANGUAGES zh_CN fr_DE ja_JP)
foreach(LANG ${SUPPORTED_LANGUAGES})
list(APPEND TS_FILES
${CMAKE_CURRENT_SOURCE_DIR}/translations/app_${LANG}.ts)
endforeach()
我曾在一个项目中需要处理20多种语言,这种自动化方式节省了大量手动配置时间。
虽然CMake支持不如QMake完善,但我们可以手动配置Qt Creator:
翻译文件最终需要打包到应用中。在qrc文件中添加:
xml复制<qresource prefix="/translations">
<file>translations/app_zh_CN.qm</file>
</qresource>
然后在代码中加载翻译:
cpp复制QTranslator translator;
if(translator.load(":/translations/app_zh_CN.qm")) {
QCoreApplication::installTranslator(&translator);
}
翻译不生效:检查.qm文件是否被正确打包到资源中,以及加载路径是否正确。我遇到过因为路径大小写问题导致加载失败的情况。
字符串未提取:确保所有待翻译字符串都使用了tr()或QT_TR_NOOP()宏。曾经有个项目因为使用了自定义的翻译宏导致lupdate无法识别。
增量更新问题:lupdate默认会保留已翻译内容。如果需要完全重新扫描,可以使用-no-obsolete参数。
对于大型项目,lupdate扫描可能很耗时。可以通过以下方式优化:
-locations none参数减少.ts文件体积我在一个包含2000+源文件的项目中,通过模块化翻译管理将lupdate时间从3分钟缩短到30秒。
在团队开发环境中,可以考虑将翻译流程集成到CI系统中。以下是一个GitLab CI的示例配置:
yaml复制stages:
- translations
update_translations:
stage: translations
script:
- cmake --build . --target update_translations
artifacts:
paths:
- translations/*.ts
这样每次代码更新都会自动生成最新的翻译文件,翻译人员只需要下载最新的.ts文件即可。
不同平台下的换行符处理可能会影响.ts文件的版本控制。建议在.gitattributes中添加:
code复制*.ts text eol=lf
另外,Windows下路径长度限制可能导致问题。我曾经遇到因为构建路径过长导致lupdate失败的情况,解决方案是使用较短的构建路径或subst命令创建虚拟驱动器。