################################################# # # (C) 2018 Slávek Banko # slavek (DOT) banko (AT) axis.cz # # Improvements and feedback are welcome # # This file is released under GPL >= 2 # ################################################# ##### include essential TDE macros ############## set( MASTER_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" ) include( TDEMacros ) ##### verify required programs ################## if( NOT DEFINED TDE_PREFIX AND IS_DIRECTORY /opt/trinity ) set( TDE_PREFIX "/opt/trinity" ) else( ) set( TDE_PREFIX "/usr" ) endif( ) if( NOT DEFINED KDE_XGETTEXT_EXECUTABLE ) find_program( KDE_XGETTEXT_EXECUTABLE NAMES kde-xgettext HINTS "${TDE_PREFIX}/bin" ) if( NOT KDE_XGETTEXT_EXECUTABLE ) tde_message_fatal( "kde-xgettext is required but not found" ) endif( ) endif( ) if( NOT DEFINED EXTRACTRC_EXECUTABLE ) find_program( EXTRACTRC_EXECUTABLE NAMES extractrc HINTS "${TDE_PREFIX}/bin" ) if( NOT EXTRACTRC_EXECUTABLE ) tde_message_fatal( "extractrc is required but not found" ) endif( ) endif( ) if( NOT DEFINED EXTRACTATTR_EXECUTABLE ) find_program( EXTRACTATTR_EXECUTABLE NAMES extractattr HINTS "${TDE_PREFIX}/bin" ) if( NOT EXTRACTATTR_EXECUTABLE ) tde_message_fatal( "extractattr is required but not found" ) endif( ) endif( ) if( NOT DEFINED XGETTEXT_EXECUTABLE ) find_program( XGETTEXT_EXECUTABLE NAMES xgettext HINTS "${TDE_PREFIX}/bin" ) if( NOT XGETTEXT_EXECUTABLE ) tde_message_fatal( "xgettext is required but not found" ) endif( ) execute_process( COMMAND ${XGETTEXT_EXECUTABLE} --version OUTPUT_VARIABLE _xgettext_version ERROR_VARIABLE _xgettext_version ) string( REGEX REPLACE "^xgettext[^\n]* ([^ ]*)\n.*" "\\1" _xgettext_version ${_xgettext_version} ) if( "${_xgettext_version}" VERSION_LESS "0.19" ) tde_message_fatal( "xgettext version >= 0.19 is required but found only ${_xgettext_version}" ) endif( ) endif( ) if( NOT DEFINED MSGUNIQ_EXECUTABLE ) find_program( MSGUNIQ_EXECUTABLE NAMES msguniq HINTS "${TDE_PREFIX}/bin" ) if( NOT MSGUNIQ_EXECUTABLE ) tde_message_fatal( "msguniq is required but not found" ) endif( ) endif( ) if( NOT DEFINED MSGCAT_EXECUTABLE ) find_program( MSGCAT_EXECUTABLE NAMES msgcat HINTS "${TDE_PREFIX}/bin" ) if( NOT MSGCAT_EXECUTABLE ) tde_message_fatal( "msgcat is required but not found" ) endif( ) endif( ) ################################################# ##### ##### tde_l10n_add_subdirectory ##### ##### The function simulates the add_subdirectory() behavior, but ##### the CMakeL10n.txt file is used instead of CMakeLists.txt. ##### function( tde_l10n_add_subdirectory _dir ) set( CMAKE_CURRENT_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${_dir}" ) set( CMAKE_CURRENT_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/${_dir}" ) include( ${CMAKE_CURRENT_SOURCE_DIR}/CMakeL10n.txt ) endfunction( ) ################################################# ##### ##### tde_l10n_auto_add_subdirectories ##### ##### The function is equivalent to tde_auto_add_subdirectories, but ##### the CMakeL10n.txt file is used instead of CMakeLists.txt. ##### function( tde_l10n_auto_add_subdirectories ) file( GLOB _dirs RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/*" ) foreach( _dir ${_dirs} ) if( IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${_dir} AND NOT ${_dir} STREQUAL ".svn" AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${_dir}/CMakeL10n.txt ) tde_l10n_add_subdirectory( ${_dir} ) endif( ) endforeach( ) endfunction( ) ################################################# ##### ##### tde_l10n_create_template ##### ##### Macro is used to generate a translation template - POT file. ##### ##### Syntax: ##### tde_l10n_create_template( ##### [CATALOG] file_name ##### [SOURCES source_spec [source_spec]] ##### [EXCLUDES regex [regex]] ##### [KEYWORDS keyword [keyword]] ##### [ATTRIBUTES attrib_spec [attrib_spec]] ##### [DESTINATION directory] ##### ) ##### ##### Where: ##### CATALOG determines the target file name (without pot suffix). ##### SOURCES can be specified by several options: ##### a) Do not specify anything ##### - all usual source files will be automatically searched. ##### b) Enter the directory name - for example, '.' or 'src' ##### - all the usual source files in the specified directory ##### and subdirectories will be searched. ##### c) Enter the mask - for example '*.cpp' ##### - all files with the specified mask will be searched. ##### d) Specify the name of the individual file. ##### The methods from b) to d) can be combined. ##### EXCLUDES determines which files are to be excluded from processing ##### KEYWORDS determines additional keywords for xgettext. ##### ATTRIBUTES determines files and specification for extractattr: ##### source_spec:element,attribute[,context] ##### DESTINATION determines directory to save translation template. ##### The destination directory is determined as follows: ##### a) Directory is specified as an argument. ##### b) The variable POT_SOURCE_DIR is set. ##### c) There is a 'translations' directory. ##### d) There is a 'po' directory. ##### ##### Note: ##### Editing the _files list inside foreach( ${_files} ) below in the ##### code is safe, because in CMake foreach parameters are evaluated ##### before the loop starts. Therefore, the changes in the list inside ##### the loop do not have an unwanted impact on the loop processing. ##### macro( tde_l10n_create_template ) unset( _catalog ) unset( _sources ) unset( _excludes ) unset( _files ) unset( _rcs ) unset( _desktops ) unset( _pots ) unset( _dest ) unset( _keywords ) unset( _attributes ) unset( _pot ) unset( _directive ) set( _var _catalog ) foreach( _arg ${ARGN} ) # found directive "CATALOG" if( "+${_arg}" STREQUAL "+CATALOG" ) unset( _catalog ) set( _var _catalog ) set( _directive 1 ) endif( ) # found directive "SOURCES" if( "+${_arg}" STREQUAL "+SOURCES" ) unset( _sources ) set( _var _sources ) set( _directive 1 ) endif( ) # found directive "EXCLUDES" if( "+${_arg}" STREQUAL "+EXCLUDES" ) unset( _excludes ) set( _var _excludes ) set( _directive 1 ) endif( ) # found directive "DESTINATION" if( "+${_arg}" STREQUAL "+DESTINATION" ) unset( _dest ) set( _var _dest ) set( _directive 1 ) endif( ) # found directive "KEYWORDS" if( "+${_arg}" STREQUAL "+KEYWORDS" ) unset( _keywords ) set( _var _keywords ) set( _directive 1 ) endif( ) # found directive "ATTRIBUTES" if( "+${_arg}" STREQUAL "+ATTRIBUTES" ) unset( _attributes ) set( _var _attributes ) set( _directive 1 ) endif( ) # collect data if( _directive ) unset( _directive ) elseif( _var ) list( APPEND ${_var} ${_arg} ) endif( ) endforeach( ) # verify catalog if( NOT _catalog ) tde_message_fatal( "the name of the translation catalog is not defined" ) endif( ) # determine the destination directory if( NOT _dest ) if( POT_SOURCE_DIR ) set( _dest ${POT_SOURCE_DIR} ) elseif( EXISTS "${MASTER_SOURCE_DIR}/translations" ) set( _dest "${MASTER_SOURCE_DIR}/translations/" ) elseif( EXISTS "${MASTER_SOURCE_DIR}/po" ) set( _dest "${MASTER_SOURCE_DIR}/po/" ) else( ) tde_message_fatal( "cannot determine destination directory" ) endif( ) endif( ) if( ${_dest} MATCHES "[^/]$" ) set( _dest "${_dest}/" ) endif( ) if( ${_dest} MATCHES "^[^/]" ) set( _dest "${CMAKE_CURRENT_SOURCE_DIR}/${_dest}" ) endif( ) get_filename_component( _potFilename "${_dest}${_catalog}.pot" ABSOLUTE ) file( RELATIVE_PATH _potFilename ${CMAKE_SOURCE_DIR} ${_potFilename} ) message( STATUS "Create translation template ${_potFilename}" ) # verify sources if( NOT _sources AND NOT _attributes ) # add current directory list( APPEND _sources "." ) endif( ) foreach( _src ${_sources} ) # add all source files from directory if( IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${_src} ) if( ${_src} STREQUAL "." ) set( _dir "${CMAKE_CURRENT_SOURCE_DIR}" ) else( ) set( _dir "${CMAKE_CURRENT_SOURCE_DIR}/${_src}" ) endif( ) file( GLOB_RECURSE _add_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${_dir}/*.c ${_dir}/*.cc ${_dir}/*.cpp ${_dir}/*.h ${_dir}/*.kcfg ${_dir}/*.rc ${_dir}/*.ui ) list( SORT _add_files ) list( APPEND _files ${_add_files} ) # add files by the specified mask elseif( ${_src} MATCHES "(\\*|\\?)" ) file( GLOB_RECURSE _add_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/${_src} ) list( SORT _add_files ) list( APPEND _files ${_add_files} ) # add a individual file elseif( EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${_src} ) list( APPEND _files ${_src} ) endif( ) endforeach( ) # add common translator info if( _files ) set( _translatorinfo "i18n(\"NAME OF TRANSLATORS\", \"Your names\")\n" "i18n(\"EMAIL OF TRANSLATORS\", \"Your emails\")\n" ) file( WRITE ${CMAKE_CURRENT_SOURCE_DIR}/_translatorinfo.tde_l10n ${_translatorinfo} ) list( INSERT _files 0 "_translatorinfo.tde_l10n" ) endif( ) # filter files by excludes if( _excludes ) foreach( _src ${_files} ) foreach( _exclude ${_excludes} ) if( ${_src} MATCHES ${_exclude} ) list( REMOVE_ITEM _files ${_src} ) endif( ) endforeach( ) endforeach( ) endif( ) if( NOT _files AND NOT _attributes ) tde_message_fatal( "no source files found" ) endif( ) # prepare additional keywords unset( _add_keywords ) foreach( _keyword ${_keywords} ) list( APPEND _add_keywords "-k${_keyword}" ) endforeach( ) # pick resource files *.kcfg, *.rc and *.ui foreach( _src ${_files} ) if( ${_src} MATCHES "\\.(kcfg|rc|ui)(\\.cmake)?(\\.tde_l10n)?$" ) list( APPEND _rcs ${_src} ) list( REMOVE_ITEM _files ${_src} ) endif( ) endforeach( ) # prepare extracted-rc.tde_l10n if( _rcs OR _attributes ) file( WRITE ${CMAKE_CURRENT_SOURCE_DIR}/extracted-rc.tde_l10n "" ) list( APPEND _files extracted-rc.tde_l10n ) endif( ) # process resource files if( _rcs ) execute_process( COMMAND ${EXTRACTRC_EXECUTABLE} ${_rcs} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE _sources_rc ) file( APPEND ${CMAKE_CURRENT_SOURCE_DIR}/extracted-rc.tde_l10n ${_sources_rc} ) endif( ) # extract attributes if( _attributes ) foreach( _attrib ${_attributes} ) if( ${_attrib} MATCHES "^([^:]+):(.+)$" ) string( REGEX REPLACE "^([^:]+):(.+)$" "\\1" _attrib_glob ${_attrib} ) string( REGEX REPLACE "^([^:]+):(.+)$" "\\2" _attrib_spec ${_attrib} ) file( GLOB_RECURSE _attrib_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/${_attrib_glob} ) if( _attrib_files ) list( SORT _attrib_files ) execute_process( COMMAND ${EXTRACTATTR_EXECUTABLE} --attr=${_attrib_spec} ${_attrib_files} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE _attrib_rc ) file( APPEND ${CMAKE_CURRENT_SOURCE_DIR}/extracted-rc.tde_l10n ${_attrib_rc} ) endif( ) endif( ) endforeach( ) endif( ) # prepare tips foreach( _src ${_files} ) if( ${_src} MATCHES "(^|/)tips$" ) tde_l10n_preparetips( ${_src} ) list( REMOVE_ITEM _files ${_src} ) list( APPEND _files "${_src}.tde_l10n" ) endif( ) endforeach( ) # pick desktop files *.desktop and *.protocol foreach( _src ${_files} ) if( ${_src} MATCHES "\\.(desktop|protocol)(\\.cmake)?(\\.tde_l10n)?$" ) list( APPEND _desktops ${_src} ) list( REMOVE_ITEM _files ${_src} ) endif( ) endforeach( ) # pick pot files *.pot foreach( _src ${_files} ) if( ${_src} MATCHES "\\.pot(\\.cmake)?(\\.tde_l10n)?$" ) list( APPEND _pots ${_src} ) list( REMOVE_ITEM _files ${_src} ) endif( ) endforeach( ) # create translation template if( _files ) execute_process( COMMAND ${KDE_XGETTEXT_EXECUTABLE} --foreign-user -C -ki18n -ki18n:1,2 -ktr2i18n -ktr2i18n:1,2 -kI18N_NOOP -kI18N_NOOP2 ${_add_keywords} -o - ${_files} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE _pot ) # set charset and encoding headers if( _pot ) string( REPLACE "Content-Type: text/plain charset=CHARSET" "Content-Type: text/plain charset=UTF-8" _pot ${_pot} ) string( REPLACE "Content-Transfer-Encoding: ENCODING" "Content-Transfer-Encoding: 8bit" _pot ${_pot} ) endif( ) endif( ) # process desktop files if( _desktops ) # create translation template for desktop files if( _pot ) set( _withPotHeader "--omit-header" ) else( ) set( _withPotHeader "--foreign-user" ) endif( ) execute_process( COMMAND ${XGETTEXT_EXECUTABLE} ${_withPotHeader} -L Desktop -kDescription -kExtraNames -kX-TDE-Submenu ${_add_keywords} -o - ${_desktops} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE _potDesktop ) # merge translation templates if( _potDesktop ) if( _pot ) file( WRITE ${CMAKE_CURRENT_SOURCE_DIR}/extracted-pot.tmp ${_pot} ) file( APPEND ${CMAKE_CURRENT_SOURCE_DIR}/extracted-pot.tmp ${_potDesktop} ) execute_process( COMMAND ${MSGUNIQ_EXECUTABLE} --use-first INPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/extracted-pot.tmp WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE _pot ) file( REMOVE ${CMAKE_CURRENT_SOURCE_DIR}/extracted-pot.tmp ) else( ) set( _pot ${_potDesktop} ) # set charset and encoding headers string( REPLACE "Content-Type: text/plain charset=CHARSET" "Content-Type: text/plain charset=UTF-8" _pot ${_pot} ) string( REPLACE "Content-Transfer-Encoding: ENCODING" "Content-Transfer-Encoding: 8bit" _pot ${_pot} ) endif( ) endif( ) endif( ) # merge additional pot files if( _pots ) file( WRITE ${CMAKE_CURRENT_SOURCE_DIR}/extracted-pot.tmp ${_pot} ) execute_process( COMMAND ${MSGCAT_EXECUTABLE} --use-first extracted-pot.tmp ${_pots} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE _pot ) file( REMOVE ${CMAKE_CURRENT_SOURCE_DIR}/extracted-pot.tmp ) # set charset and encoding headers string( REPLACE "Content-Type: text/plain charset=CHARSET" "Content-Type: text/plain charset=UTF-8" _pot ${_pot} ) string( REPLACE "Content-Transfer-Encoding: ENCODING" "Content-Transfer-Encoding: 8bit" _pot ${_pot} ) endif( ) # finalize translation template if( _pot ) # update references for resources to original files and line numbers if( _rcs OR _attributes ) file( READ "${CMAKE_CURRENT_SOURCE_DIR}/extracted-rc.tde_l10n" _extractedRC ) string( REGEX REPLACE "[^\n]" "" _extractedRC_len "${_extractedRC}" ) string( LENGTH "+${_extractedRC_len}" _extractedRC_len ) set( _rcPos 0 ) while( _rcPos LESS ${_extractedRC_len} ) string( REGEX REPLACE "^([^\n]*)\n(.*)" "\\1" _rcLine "${_extractedRC}" ) string( REGEX REPLACE "^([^\n]*)\n(.*)" "\\2" _extractedRC "${_extractedRC}" ) math( EXPR _rcPos "${_rcPos}+1" ) if( "${_rcLine}" MATCHES "^//i18n: file .* line [0-9]*$" ) string( REGEX REPLACE "^//i18n: file (.*) line ([0-9]*)$" "\\1:\\2" _rcOrig ${_rcLine} ) endif( ) if( "${_rcLine}" MATCHES "^i18n\\(" AND _rcOrig ) string( REGEX REPLACE "(^|\n)(#:.*) extracted-rc.tde_l10n:${_rcPos}( |\n)" "\\1\\2 ${_rcOrig}\\3" _pot ${_pot} ) unset( _rcOrig ) endif( ) endwhile( ) endif( ) # update references for modified source files (".tde_l10n" extension) string( REGEX REPLACE "\\.tde_l10n(:[0-9]+)" "\\1" _pot ${_pot} ) # save translation template if( EXISTS "${_dest}${_catalog}.pot" ) file( READ "${_dest}${_catalog}.pot" _potOrig ) else( ) unset( _potOrig ) endif( ) if( _potOrig ) string( REGEX REPLACE "\n\"POT-Creation-Date: [^\"]*\"\n" "" _potOrig ${_potOrig} ) string( REGEX REPLACE "\n\"POT-Creation-Date: [^\"]*\"\n" "" _potNew ${_pot} ) endif( ) if( NOT _potOrig OR NOT "${_potNew}" STREQUAL "${_potOrig}" ) file( WRITE "${_dest}${_catalog}.pot" ${_pot} ) endif( ) endif( _pot ) # cleanup foreach( _file ${_files} ${_rsc} ${_desktops} ) if( "${_file}" MATCHES "\\.tde_l10n$" ) file( REMOVE ${CMAKE_CURRENT_SOURCE_DIR}/${_file} ) endif( ) endforeach( ) endmacro( ) ################################################# ##### ##### tde_l10n_preparetips ##### ##### Macro is used to prepare tips file for xgettext ##### macro( tde_l10n_preparetips _tips ) if( EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${_tips} ) file( READ ${CMAKE_CURRENT_SOURCE_DIR}/${_tips} _tips_html ) string( REGEX REPLACE "[^\n]" "" _tips_len ${_tips_html} ) string( LENGTH "+${_tips_len}" _tips_len ) unset( _tips_inside ) unset( _tips_l10n ) set( _tips_pos 0 ) while( _tips_pos LESS ${_tips_len} ) string( REGEX REPLACE "^([^\n]*)\n(.*)" "\\1" _tips_line "${_tips_html}" ) string( REGEX REPLACE "^([^\n]*)\n(.*)" "\\2" _tips_html "${_tips_html}" ) math( EXPR _tips_pos "${_tips_pos}+1" ) string( REGEX REPLACE "\\\\" "\\\\\\\\" _tips_line "${_tips_line}" ) string( REGEX REPLACE "\\\"" "\\\\\"" _tips_line "${_tips_line}" ) if( NOT _tips_inside ) if( "${_tips_line}" MATCHES "" ) set( _tips_inside 1 ) string( REGEX REPLACE ".*" "" _tips_line "${_tips_line}" ) string( REGEX REPLACE "(.+)" "\"\\1\\\\n\"" _tips_line "${_tips_line}" ) set( _tips_line "i18n(${_tips_line}" ) else( ) set( _tips_line "" ) endif( ) else( ) if( "${_tips_line}" MATCHES "" ) unset( _tips_inside ) string( REGEX REPLACE ".*" "" _tips_line "${_tips_line}" ) string( REGEX REPLACE "(.+)" "\"\\1\\\\n\"" _tips_line "${_tips_line}" ) set( _tips_line "${_tips_line});" ) else( ) set( _tips_line "\"${_tips_line}\\n\"" ) endif( ) endif( ) set( _tips_l10n "${_tips_l10n}${_tips_line}\n" ) endwhile( ) file( WRITE ${CMAKE_CURRENT_SOURCE_DIR}/${_tips}.tde_l10n "${_tips_l10n}" ) endif( ) endmacro( )