diff --git a/src/corelib/Qt6CoreDeploySupport.cmake b/src/corelib/Qt6CoreDeploySupport.cmake index 7c307d18e0..c2ff58a669 100644 --- a/src/corelib/Qt6CoreDeploySupport.cmake +++ b/src/corelib/Qt6CoreDeploySupport.cmake @@ -394,3 +394,147 @@ function(_qt_internal_show_skip_runtime_deploy_message qt_build_type_string) "this target platform (${__QT_DEPLOY_SYSTEM_NAME}, ${qt_build_type_string})." ) endfunction() + +# This function is currently in Technical Preview. +# Its signature and behavior might change. +function(qt6_deploy_translations) + set(no_value_options VERBOSE) + set(single_value_options + LCONVERT + ) + set(multi_value_options + CATALOGS + LOCALES + ) + cmake_parse_arguments(PARSE_ARGV 0 arg + "${no_value_options}" "${single_value_options}" "${multi_value_options}" + ) + + set(verbose OFF) + if(arg_VERBOSE OR __QT_DEPLOY_VERBOSE) + set(verbose ON) + endif() + + if(arg_CATALOGS) + set(catalogs ${arg_CATALOGS}) + else() + set(catalogs qt qtbase) + + # Find the translations that belong to the Qt modules that are used by the project. + # "Used by the project" means just all modules that are pulled in via find_package for now. + set(modules ${__QT_DEPLOY_ALL_MODULES_FOUND_VIA_FIND_PACKAGE}) + + set(module_catalog_mapping + "Bluetooth|Nfc" qtconnectivity + "Help" qt_help + "Multimedia(Widgets|QuickPrivate)?" qtmultimedia + "Qml|Quick" qtdeclarative + "SerialPort" qtserialport + "WebEngine" qtwebengine + "WebSockets" qtwebsockets + ) + list(LENGTH module_catalog_mapping max_i) + math(EXPR max_i "${max_i} - 1") + foreach(module IN LISTS modules) + foreach(i RANGE 0 ${max_i} 2) + list(GET module_catalog_mapping ${i} module_rex) + if(NOT module MATCHES "^${module_rex}") + continue() + endif() + math(EXPR k "${i} + 1") + list(GET module_catalog_mapping ${k} catalog) + list(APPEND catalogs ${catalog}) + endforeach() + endforeach() + endif() + + get_filename_component(qt_translations_dir "${__QT_DEPLOY_QT_INSTALL_TRANSLATIONS}" ABSOLUTE + BASE_DIR "${__QT_DEPLOY_QT_INSTALL_PREFIX}" + ) + set(locales ${arg_LOCALES}) + if(NOT locales) + file(GLOB locales RELATIVE "${qt_translations_dir}" "${qt_translations_dir}/*.qm") + list(TRANSFORM locales REPLACE "\\.qm$" "") + list(TRANSFORM locales REPLACE "^qt_help" "qt-help") + list(TRANSFORM locales REPLACE "^[^_]+" "") + list(TRANSFORM locales REPLACE "^_" "") + list(REMOVE_DUPLICATES locales) + endif() + + # Ensure existence of the output directory. + set(output_dir "${QT_DEPLOY_PREFIX}/${QT_DEPLOY_TRANSLATIONS_DIR}") + if(NOT EXISTS "${output_dir}") + file(MAKE_DIRECTORY "${output_dir}") + endif() + + # Locate lconvert. + if(arg_LCONVERT) + set(lconvert "${arg_LCONVERT}") + else() + set(lconvert "${__QT_DEPLOY_QT_INSTALL_PREFIX}/${__QT_DEPLOY_QT_INSTALL_BINS}/lconvert") + if(CMAKE_HOST_WIN32) + string(APPEND lconvert ".exe") + endif() + if(NOT EXISTS ${lconvert}) + message(STATUS "lconvert was not found. Skipping deployment of translations.") + return() + endif() + endif() + + # Find the .qm files for the selected locales + if(verbose) + message(STATUS "Looking for translations in ${qt_translations_dir}") + endif() + foreach(locale IN LISTS locales) + set(qm_files "") + foreach(catalog IN LISTS catalogs) + set(qm_file "${catalog}_${locale}.qm") + if(EXISTS "${qt_translations_dir}/${qm_file}") + list(APPEND qm_files ${qm_file}) + endif() + endforeach() + + if(NOT qm_files) + message(WARNING "No translations found for requested locale '${locale}'.") + continue() + endif() + + if(verbose) + foreach(qm_file IN LISTS qm_files) + message(STATUS "found translation file: ${qm_file}") + endforeach() + endif() + + # Merge the .qm files into one qt_${locale}.qm file. + set(output_file_name "qt_${locale}.qm") + set(output_file_path "${output_dir}/${output_file_name}") + message(STATUS "Creating: ${output_file_path}") + set(extra_options) + if(verbose) + list(APPEND extra_options COMMAND_ECHO STDOUT) + endif() + execute_process( + COMMAND ${lconvert} -if qm -o ${output_file_path} ${qm_files} + WORKING_DIRECTORY ${qt_translations_dir} + RESULT_VARIABLE process_result + ${extra_options} + ) + if(NOT process_result EQUAL "0") + if(process_result MATCHES "^[0-9]+$") + message(WARNING "lconvert failed with exit code ${process_result}.") + else() + message(WARNING "lconvert failed: ${process_result}.") + endif() + endif() + endforeach() +endfunction() + +if(NOT __QT_NO_CREATE_VERSIONLESS_FUNCTIONS) + function(qt_deploy_translations) + if(__QT_DEFAULT_MAJOR_VERSION EQUAL 6) + qt6_deploy_translations(${ARGV}) + else() + message(FATAL_ERROR "qt_deploy_translations() is only available in Qt 6.") + endif() + endfunction() +endif() diff --git a/src/corelib/Qt6CoreMacros.cmake b/src/corelib/Qt6CoreMacros.cmake index 3bea0f135b..c6d0d765e8 100644 --- a/src/corelib/Qt6CoreMacros.cmake +++ b/src/corelib/Qt6CoreMacros.cmake @@ -2464,6 +2464,9 @@ endif() if(NOT QT_DEPLOY_QML_DIR) set(QT_DEPLOY_QML_DIR \"qml\") endif() +if(NOT QT_DEPLOY_TRANSLATIONS_DIR) + set(QT_DEPLOY_TRANSLATIONS_DIR \"translations\") +endif() if(NOT QT_DEPLOY_PREFIX) set(QT_DEPLOY_PREFIX \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}\") endif() @@ -2484,7 +2487,9 @@ set(__QT_NO_CREATE_VERSIONLESS_FUNCTIONS \"${QT_NO_CREATE_VERSIONLESS_FUNCTIONS} set(__QT_DEFAULT_MAJOR_VERSION \"${QT_DEFAULT_MAJOR_VERSION}\") set(__QT_DEPLOY_QT_ADDITIONAL_PACKAGES_PREFIX_PATH \"${QT_ADDITIONAL_PACKAGES_PREFIX_PATH}\") set(__QT_DEPLOY_QT_INSTALL_PREFIX \"${QT6_INSTALL_PREFIX}\") +set(__QT_DEPLOY_QT_INSTALL_BINS \"${QT6_INSTALL_BINS}\") set(__QT_DEPLOY_QT_INSTALL_PLUGINS \"${QT6_INSTALL_PLUGINS}\") +set(__QT_DEPLOY_QT_INSTALL_TRANSLATIONS \"${QT6_INSTALL_TRANSLATIONS}\") set(__QT_DEPLOY_PLUGINS \"\") # Define the CMake commands to be made available during deployment. @@ -2640,6 +2645,7 @@ function(qt6_generate_deploy_script) set(boiler_plate "include(${QT_DEPLOY_SUPPORT}) include(\"\${CMAKE_CURRENT_LIST_DIR}/${arg_TARGET}-plugins${config_infix}.cmake\" OPTIONAL) +set(__QT_DEPLOY_ALL_MODULES_FOUND_VIA_FIND_PACKAGE \"${QT_ALL_MODULES_FOUND_VIA_FIND_PACKAGE}\") ") list(TRANSFORM arg_CONTENT REPLACE "\\$" "\$") file(GENERATE OUTPUT ${file_name} CONTENT "${boiler_plate}${arg_CONTENT}") diff --git a/src/corelib/doc/src/cmake/cmake-deploy-variables.qdoc b/src/corelib/doc/src/cmake/cmake-deploy-variables.qdoc index b10de9dc3b..8182dd74e4 100644 --- a/src/corelib/doc/src/cmake/cmake-deploy-variables.qdoc +++ b/src/corelib/doc/src/cmake/cmake-deploy-variables.qdoc @@ -49,7 +49,7 @@ install location and just use the prefix-relative \c{QT_DEPLOY_..._DIR} variables. \sa QT_DEPLOY_SUPPORT, QT_DEPLOY_BIN_DIR, QT_DEPLOY_LIB_DIR, - QT_DEPLOY_PLUGINS_DIR, QT_DEPLOY_QML_DIR + QT_DEPLOY_PLUGINS_DIR, QT_DEPLOY_QML_DIR, QT_DEPLOY_TRANSLATIONS_DIR */ /*! @@ -90,7 +90,7 @@ should not be used for that scenario. \include cmake-deploy-runtime-dependencies.qdocinc \sa QT_DEPLOY_SUPPORT, QT_DEPLOY_PREFIX, QT_DEPLOY_LIB_DIR, - QT_DEPLOY_PLUGINS_DIR, QT_DEPLOY_QML_DIR + QT_DEPLOY_PLUGINS_DIR, QT_DEPLOY_QML_DIR, QT_DEPLOY_TRANSLATIONS_DIR */ /*! @@ -132,7 +132,7 @@ should not be used for that scenario. \include cmake-deploy-modified-variable-values.qdocinc \sa QT_DEPLOY_SUPPORT, QT_DEPLOY_PREFIX, QT_DEPLOY_BIN_DIR, - QT_DEPLOY_PLUGINS_DIR, QT_DEPLOY_QML_DIR + QT_DEPLOY_PLUGINS_DIR, QT_DEPLOY_QML_DIR, QT_DEPLOY_TRANSLATIONS_DIR */ /*! @@ -168,7 +168,7 @@ bundle contents. \include cmake-deploy-modified-variable-values.qdocinc \sa QT_DEPLOY_SUPPORT, QT_DEPLOY_PREFIX, QT_DEPLOY_BIN_DIR, QT_DEPLOY_LIB_DIR, - QT_DEPLOY_QML_DIR + QT_DEPLOY_QML_DIR, QT_DEPLOY_TRANSLATIONS_DIR */ /*! @@ -206,5 +206,38 @@ to be deployed to different locations within the app bundle. \include cmake-deploy-modified-variable-values.qdocinc \sa QT_DEPLOY_SUPPORT, QT_DEPLOY_PREFIX, QT_DEPLOY_BIN_DIR, QT_DEPLOY_LIB_DIR, - QT_DEPLOY_PLUGINS_DIR + QT_DEPLOY_PLUGINS_DIR, QT_DEPLOY_TRANSLATIONS_DIR +*/ + +/*! +\page cmake-variable-QT_DEPLOY_TRANSLATIONS_DIR.html +\ingroup cmake-variables-qtcore + +\title QT_DEPLOY_TRANSLATIONS_DIR +\target cmake-variable-QT_DEPLOY_TRANSLATIONS_DIR + +\summary {Prefix-relative subdirectory for deploying Qt translations on some target platforms.} + +\include cmake-deploy-var-usage.qdocinc + +\cmakevariablesince 6.5 +\preliminarycmakevariable + +Projects should use \c QT_DEPLOY_TRANSLATIONS_DIR in their deploy scripts to +avoid hard-coding a particular directory under which to deploy translations. + +\c QT_DEPLOY_TRANSLATIONS_DIR defaults to the value \c{translations}. To change +the value of \c QT_DEPLOY_TRANSLATIONS_DIR, set it in the project deployment +script before \c QT_DEPLOY_SUPPORT is included. + +The \c QT_DEPLOY_TRANSLATIONS_DIR path is relative to \l{QT_DEPLOY_PREFIX}. + +This variable is not meaningful when deploying on macOS or Windows. + +\section1 Example + +\include cmake-deploy-modified-variable-values.qdocinc + +\sa QT_DEPLOY_SUPPORT, QT_DEPLOY_PREFIX, QT_DEPLOY_BIN_DIR, QT_DEPLOY_LIB_DIR, + QT_DEPLOY_PLUGINS_DIR, QT_DEPLOY_QML_DIR */ diff --git a/src/corelib/doc/src/cmake/qt_deploy_translations.qdoc b/src/corelib/doc/src/cmake/qt_deploy_translations.qdoc new file mode 100644 index 0000000000..87ef7cd2fc --- /dev/null +++ b/src/corelib/doc/src/cmake/qt_deploy_translations.qdoc @@ -0,0 +1,76 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! +\page qt_deploy_translations.html +\ingroup cmake-commands-qtcore + +\title qt_deploy_translations +\target qt_deploy_translations + +\summary {Deploy Qt translations needed by an executable.} + +\include cmake-find-package-core.qdocinc + +Unlike most other CMake commands provided by Qt, \c{qt_deploy_translations()} +can only be called from a deployment script. It cannot be called directly by the +project during the configure stage. + +\cmakecommandsince 6.5 +\preliminarycmakecommand +\note This command does not usually need to be called directly. It is used + internally by other higher level commands, but projects wishing to + implement more customized deployment logic may find it useful. + +\section1 Synopsis + +\badcode +qt_deploy_translations( + [CATALOGS catalogs] + [LOCALES locales] + [LCONVERT lconvert_executable] + [VERBOSE] +) +\endcode + +\section1 Description + +When installing an application, it may be desirable to also install the +translations that belong to the used Qt modules. The \c qt_deploy_translations +command collects the necessary \c{.qm} file from the Qt installation and +compiles them into one \c{qt_${language}.qm} file per language. The \c{.qm} +files are installed into \c{QT_DEPLOY_TRANSLATIONS_DIR}. + +\section1 Arguments + +The \c LOCALES argument specifies for which locales translations should be +deployed. This is a list of language/region combinations as described in +\l{Changing the Target Locale}{Qt Linguist's manual for translators}. Examples +for valid locales are: \c{de}, \c{pl}, or \c{pt_BR}. + +If \c LOCALES is omitted, then all available locales are deployed. + +The \c CATALOGS argument specifies a list of \l{Available +Catalogs}{translation catalogs} to be deployed. If this argument is +omitted, then all catalogs are deployed that belong to any Qt module +that is used in the project via \c{find_package}. + +The \c LCONVERT argument specifies the \c lconvert executable that is used to +combine the catalogs. By default, the Qt installation's \c lconvert is used. + +For debugging purposed, the \c VERBOSE argument can be set to turn on diagnostic +messages. + +\sa QT_DEPLOY_TRANSLATIONS_DIR + +\section1 Example + +The following example deploys Danish and German translations of the Qt +libraries. + +\badcode +qt_deploy_translations( + LOCALES da de +) +\endcode +*/