Warning, /frameworks/extra-cmake-modules/modules/ECMPoQmTools.cmake is written in an unsupported language. File is not indexed.

0001 # SPDX-FileCopyrightText: 2007-2009 Kitware, Inc.
0002 # SPDX-FileCopyrightText: 2007 Alexander Neundorf <neundorf@kde.org>
0003 # SPDX-FileCopyrightText: 2014 Aurélien Gâteau <agateau@kde.org>
0004 #
0005 # SPDX-License-Identifier: BSD-3-Clause
0006 
0007 #[=======================================================================[.rst:
0008 ECMPoQmTools
0009 ------------
0010 
0011 This module provides the ``ecm_process_po_files_as_qm`` and
0012 ``ecm_install_po_files_as_qm`` functions for generating QTranslator (.qm)
0013 catalogs from Gettext (.po) catalogs, and the ``ecm_create_qm_loader``
0014 function for generating the necessary code to load them in a Qt application
0015 or library.
0016 
0017 ::
0018 
0019   ecm_process_po_files_as_qm(<lang> [ALL]
0020                              [INSTALL_DESTINATION <install_destination>]
0021                              PO_FILES <pofile> [<pofile> [...]])
0022 
0023 Compile .po files into .qm files for the given language.
0024 
0025 If ``INSTALL_DESTINATION`` is given, the .qm files are installed in
0026 ``<install_destination>/<lang>/LC_MESSAGES``. Typically,
0027 ``<install_destination>`` is set to ``share/locale``.
0028 
0029 ``ecm_process_po_files_as_qm`` creates a "translations" target. This target
0030 builds all .po files into .qm files.  If ``ALL`` is specified, these rules are
0031 added to the "all" target (and so the .qm files will be built by default).
0032 
0033 ::
0034 
0035   ecm_create_qm_loader(<sources_var_name(|target (since 5.83))> <catalog_name>)
0036 
0037 Generates C++ code which ensures translations are automatically loaded at
0038 startup. The generated files are appended to the variable named
0039 ``<sources_var_name>`` or, if the first argument is a target (since 5.83), to
0040 the ``SOURCES`` property of ``<target>``. Any target must be created with
0041 ``add_executable()`` or ``add_library()`` and not be an alias.
0042 
0043 It assumes that the .qm file for the language code ``<lang>`` is installed as
0044 ``<sharedir>/locale/<lang>/LC_MESSAGES/<catalog_name>.qm``, where
0045 ``<sharedir>`` is one of the directories given by the ``GenericDataLocation``
0046 of ``QStandardPaths``.
0047 
0048 Typical usage is like:
0049 
0050 .. code-block:: cmake
0051 
0052   set(mylib_SRCS foo.cpp bar.cpp)
0053   ecm_create_qm_loader(mylib_SRCS mycatalog)
0054   add_library(mylib ${mylib_SRCS})
0055 
0056   # Or, since 5.83:
0057   add_library(mylib foo.cpp bar.cpp)
0058   ecm_create_qm_loader(mylib mycatalog)
0059 
0060 ::
0061 
0062   ecm_install_po_files_as_qm(<podir>)
0063 
0064 Searches for .po files and installs them to the standard location.
0065 
0066 This is a convenience function which relies on all .po files being kept in
0067 ``<podir>/<lang>/``, where ``<lang>`` is the language the .po files are
0068 written in.
0069 
0070 For example, given the following directory structure::
0071 
0072  po/
0073    fr/
0074      mylib.po
0075 
0076 ``ecm_install_po_files_as_qm(po)`` compiles ``mylib.po`` into ``mylib.qm`` and
0077 installs it in ``<install_destination>/fr/LC_MESSAGES``.
0078 ``<install_destination>`` defaults to ``${LOCALE_INSTALL_DIR}`` if defined,
0079 otherwise it uses ``${CMAKE_INSTALL_LOCALEDIR}`` if that is defined, otherwise
0080 it uses ``share/locale``.
0081 
0082 Since pre-1.0.0.
0083 #]=======================================================================]
0084 
0085 include(${CMAKE_CURRENT_LIST_DIR}/QtVersionOption.cmake)
0086 
0087 
0088 # Copied from FindGettext.cmake
0089 function(_ecm_qm_get_unique_target_name _name _unique_name)
0090    set(propertyName "_ECM_QM_UNIQUE_COUNTER_${_name}")
0091    get_property(currentCounter GLOBAL PROPERTY "${propertyName}")
0092    if(NOT currentCounter)
0093       set(currentCounter 1)
0094    endif()
0095    set(${_unique_name} "${_name}_${currentCounter}" PARENT_SCOPE)
0096    math(EXPR currentCounter "${currentCounter} + 1")
0097    set_property(GLOBAL PROPERTY ${propertyName} ${currentCounter} )
0098 endfunction()
0099 
0100 
0101 function(ecm_create_qm_loader sourcesvar_or_target catalog_name)
0102     if (TARGET ${sourcesvar_or_target})
0103         get_target_property(target_type ${sourcesvar_or_target} TYPE)
0104         set(allowed_types "EXECUTABLE" "STATIC_LIBRARY" "MODULE_LIBRARY" "SHARED_LIBRARY" "OBJECT_LIBRARY" "INTERFACE_LIBRARY")
0105         if (NOT target_type IN_LIST allowed_types)
0106             message(FATAL_ERROR "Target argument passed to ecm_create_qm_loader is not an executable or a library: ${appsources_or_target}")
0107         endif()
0108         get_target_property(aliased_target ${sourcesvar_or_target} ALIASED_TARGET)
0109         if(aliased_target)
0110             message(FATAL_ERROR "Target argument passed to ecm_create_qm_loader must not be an alias: ${sourcesvar_or_target}")
0111         endif()
0112     endif()
0113     set(loader_base ${CMAKE_CURRENT_BINARY_DIR}/ECMQmLoader-${catalog_name})
0114 
0115     set(QM_LOADER_CATALOG_NAME "${catalog_name}")
0116 
0117     set(QM_LOADER_CPP_FILE "${loader_base}.cpp")
0118     configure_file(
0119         ${ECM_MODULE_DIR}/ECMQmLoader.cpp.in
0120         ${QM_LOADER_CPP_FILE}
0121         @ONLY
0122     )
0123     if (TARGET ${sourcesvar_or_target})
0124         target_sources(${sourcesvar_or_target} PRIVATE ${QM_LOADER_CPP_FILE})
0125     else()
0126         set(${sourcesvar_or_target} "${${sourcesvar_or_target}}" ${QM_LOADER_CPP_FILE} PARENT_SCOPE)
0127     endif()
0128 endfunction()
0129 
0130 
0131 function(ecm_process_po_files_as_qm lang)
0132     # Parse arguments
0133     set(options ALL)
0134     set(oneValueArgs INSTALL_DESTINATION)
0135     set(multiValueArgs PO_FILES)
0136     cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
0137 
0138     if(ARGS_UNPARSED_ARGUMENTS)
0139         message(FATAL_ERROR "Unknown keywords given to ecm_process_po_files_as_qm(): \"${ARGS_UNPARSED_ARGUMENTS}\"")
0140     endif()
0141 
0142     if(NOT ARGS_PO_FILES)
0143         message(FATAL_ERROR "ecm_process_po_files_as_qm() must be called with PO_FILES argument")
0144     endif()
0145 
0146     # Find lrelease and lconvert
0147     if (QT_MAJOR_VERSION EQUAL "5")
0148         find_package(Qt5LinguistTools CONFIG REQUIRED)
0149     else()
0150         find_package(Qt6 COMPONENTS LinguistTools CONFIG REQUIRED)
0151     endif()
0152 
0153     if(TARGET Qt${QT_MAJOR_VERSION}::lconvert)
0154         set(lconvert_executable Qt${QT_MAJOR_VERSION}::lconvert)
0155     else()
0156         # Qt < 5.3.1 does not define Qt5::lconvert
0157         get_target_property(lrelease_location Qt5::lrelease LOCATION)
0158         get_filename_component(lrelease_path ${lrelease_location} PATH)
0159         find_program(lconvert_executable
0160             NAMES lconvert-qt5 lconvert
0161             PATHS ${lrelease_path}
0162             NO_DEFAULT_PATH
0163             )
0164     endif()
0165 
0166     # Create commands to turn po files into qm files
0167     set(qm_files)
0168     foreach (po_file ${ARGS_PO_FILES})
0169         get_filename_component(po_file ${po_file} ABSOLUTE)
0170         get_filename_component(filename_base ${po_file} NAME_WE)
0171 
0172         # Use own ECMPoQm/ subfolder for processing the files, to avoid cluttering
0173         # the default build dir as well as potential file/dir name clashes from
0174         # other build artifacts.
0175         # Include ${lang} in build dir because we might be called multiple times
0176         # with the same ${filename_base}
0177         set(build_dir ${CMAKE_CURRENT_BINARY_DIR}/ECMPoQm/${lang})
0178         set(ts_file ${build_dir}/${filename_base}.ts)
0179         set(qm_file ${build_dir}/${filename_base}.qm)
0180 
0181         file(MAKE_DIRECTORY ${build_dir})
0182 
0183         # lconvert from .po to .ts, then lrelease from .ts to .qm.
0184         add_custom_command(OUTPUT ${qm_file}
0185             COMMAND ${lconvert_executable}
0186                 ARGS -i ${po_file} -o ${ts_file} -target-language ${lang}
0187             COMMAND Qt${QT_MAJOR_VERSION}::lrelease
0188                 ARGS -removeidentical -nounfinished -silent ${ts_file} -qm ${qm_file}
0189             DEPENDS ${po_file}
0190             )
0191         if (ARGS_INSTALL_DESTINATION)
0192             install(
0193                 FILES ${qm_file}
0194                 DESTINATION ${ARGS_INSTALL_DESTINATION}/${lang}/LC_MESSAGES
0195             )
0196         endif()
0197         list(APPEND qm_files ${qm_file})
0198     endforeach()
0199 
0200     # Hook qm files to targets
0201     if(NOT TARGET translations)
0202         add_custom_target(translations)
0203     endif()
0204 
0205     _ecm_qm_get_unique_target_name(translations target_name)
0206 
0207     if (ARGS_ALL)
0208         add_custom_target(${target_name} ALL DEPENDS ${qm_files})
0209     else()
0210         add_custom_target(${target_name} DEPENDS ${qm_files})
0211     endif()
0212 
0213     add_dependencies(translations ${target_name})
0214 endfunction()
0215 
0216 
0217 function(ecm_install_po_files_as_qm podir)
0218     if (LOCALE_INSTALL_DIR)
0219         set(install_destination "${LOCALE_INSTALL_DIR}")
0220     elseif (CMAKE_INSTALL_LOCALEDIR)
0221         set(install_destination "${CMAKE_INSTALL_LOCALEDIR}")
0222     else()
0223         set(install_destination share/locale)
0224     endif()
0225 
0226     get_filename_component(absolute_podir ${podir} ABSOLUTE)
0227 
0228     # we try to find the po directory in the binary directory, in case it was downloaded
0229     # using ECM
0230     if (NOT (EXISTS "${absolute_podir}" AND IS_DIRECTORY "${absolute_podir}"))
0231         get_filename_component(absolute_podir ${CMAKE_BINARY_DIR}/${podir} ABSOLUTE)
0232     endif()
0233 
0234     if (NOT (EXISTS "${absolute_podir}" AND IS_DIRECTORY "${absolute_podir}"))
0235         # Nothing to do if there's no podir and it would create an empty
0236         # LOCALE_INSTALL_DIR in that case.
0237         return()
0238     endif()
0239 
0240     file(GLOB po_files "${absolute_podir}/*/*.po")
0241     foreach(po_file ${po_files})
0242         get_filename_component(po_dir ${po_file} DIRECTORY)
0243         get_filename_component(lang ${po_dir} NAME)
0244         ecm_process_po_files_as_qm(
0245             ${lang} ALL
0246             PO_FILES ${po_file}
0247             INSTALL_DESTINATION ${install_destination}
0248         )
0249     endforeach()
0250 endfunction()