On Tue, Mar 18, 2014, at 9:49, Aurélien Gâteau wrote:

On Tue, Mar 18, 2014, at 9:07, Aleix Pol wrote:

On Tue, Mar 18, 2014 at 4:12 PM, Aurélien Gâteau <[1]agat...@kde.org>
wrote:

On Tue, Mar 18, 2014, at 6:20, Aleix Pol wrote:

On Tue, Mar 18, 2014 at 2:05 PM, Aurélien Gâteau <[2]agat...@kde.org>
wrote:

Hi,


I started working on how to handle Qt based translations, and make it
as

simple as possible to work with for framework maintainers as well as

framework users.


I picked KBookmarks as my guinea pig and got to the point where

kbookmarkdialogtest shows a translated dialog.


Here is how it currently works. All of this is liberally inspired from

the way Trojita works:


# String extraction


I created a src/Messages.sh which contains the following:


lupdate -silent -recursive . -ts $podir/tmp.ts

lconvert $podir/tmp.ts --sort-contexts --output-format pot -o

$podir/kbookmarks5.pot

rm $podir/tmp.ts


# String compilation


I modified the toplevel CMakeLists.txt, adding these lines:


if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/po)

    include(${CMAKE_CURRENT_SOURCE_DIR}/QmSupport.cmake)

    qm_setup(kbookmarks5 po)

endif()


I created a QmSupport.cmake file, which exposes a qm_setup() function.

This function does three things:

1. Create a "qm" target which turns all .po into .qm files.


2. Call install(FILES...) on the generated qm files, installing them in

share/${name}/locale, where ${name} is the firt argument of qm_setup().


3. Generate a "${name}_translation.h" which contain two inline
functions

to make it easy to load the translations.

Using the translation is then just a matter of including

${name}_translation.h and calling ${name}_installTranslator(). If more

control is needed, ${name}_installTranslator() also accepts an optional

argument: the language. For even finer control, the .h also contains a

${name}_createTranslator() function, which returns a QTranslator loaded

with strings for the right language.


# Questions


Does this approach sounds sane to you?


I think QmSupport.cmake should go to extra-cmake-modules. Any

objections?


Right now qm_setup() is very inflexible: it installs files and creates

the _translation.h file based on the name argument, meaning in my

example it creates share/kbookmarks5/locale/kbookmarks5_*.qm and

include/kbookmarks5_translation.h, which contains the functions

kbookmarks5_createTranslator and kbookmarks5_installTranslator.


I think we want to be able to customize the install dir of the

_translation.h file because some frameworks install header files in a

subdir, others do not.


Should we also be able to customize the install data dir for qm files,

as well as the prefix of the function names from _translation.h? I am

tempted to default to ${PROJECT_NAME} for the prefix of the function

names, its lowercase version for the install data dir and add an

optional PROJECT_NAME argument to qm_setup(). Opinions?


I am attaching the diff of the current state. I do not intend to commit

it as is since po files are not supposed to be in the framework

repository, but it should make it easy for you to try it if you are

interested.


Aurélien


_______________________________________________

Kde-frameworks-devel mailing list

[3]Kde-frameworks-devel@kde.org

[4]https://mail.kde.org/mailman/listinfo/kde-frameworks-devel



Hi Aurélien,
Wouldn't it make sense that the library called the createTranslation
itself, instead of expecting the application to call it? I can easily
see applications forgetting it.
Maybe using Q_COREAPP_STARTUP_FUNCTION?


That could work, but would remove the ability to change the language
later (Some Qt apps like to let the user use a different language than
the system one for some reason). Not sure we care about this. It
certainly sounds more foolproof. On the other hand, you already *must*
load translations yourself if you are using any Qt standard dialog,
otherwise it won't be translated either.

Aurélien


_______________________________________________

Kde-frameworks-devel mailing list

[5]Kde-frameworks-devel@kde.org

[6]https://mail.kde.org/mailman/listinfo/kde-frameworks-devel


Well, for KI18n changing the language at run-time is not possible.

Maybe we can set it up magically for general use and still install the
createTranslation thing in case the application likes to do it
specifically?


That is right. Let's keep it simple then and not provide a feature
which is not supported by KI18n-powered frameworks.

This means we need to add a generated .cpp file in the framework
target(s). Going to look into it.


Some feedback on this: using Q_COREAPP_STARTUP_FUNCTION works as
expected. I think I have something usable now. Adapting a Qt-translated
framework requires the following changes (using kbookmarks as example
again):

1. Create src/Messages.sh with the following content:

    rm -f $podir/tmp.ts

    lupdate -silent -recursive . -ts $podir/tmp.ts

    lconvert $podir/tmp.ts --sort-contexts --output-format pot -o
$podir/kbookmarks5.pot

    rm $podir/tmp.ts


Note: As much as possible of these lines should move to the l10n
scripts to reduce duplication.

2. Edit the top-level CMakeLists.txt, add the following *before*
add_subdirectory(src):

    include(ECMSetupQtTranslations)

    ecm_setup_qt_translations(

        PO_DIR po

        POT_NAME kbookmarks5.pot

        INSTALL_SUB_DIR kbookmarks

        CREATE_LOADER)



3. Edit src/CMakelists.txt, add ${ECM_QT_TRANSLATION_LOADER} to
kbookmark_SRCS:



    set(kbookmarks_SRCS

      kbookmark.cpp
      #...

      ${ECM_QT_TRANSLATION_LOADER}

    )



This works with the attached ECMSetupQtTranslations.cmake file.



When those changes are done, one gets translations installed and
loaded, as long as the source used to build contains a properly
populated po/ dir. This should be taken care of by the release scripts.



If this makes sense to you, I am going to propose
ECMSetupQtTranslations.cmake for inclusion in extra-cmake-modules.



Aurélien

References

1. mailto:agat...@kde.org
2. mailto:agat...@kde.org
3. mailto:Kde-frameworks-devel@kde.org
4. https://mail.kde.org/mailman/listinfo/kde-frameworks-devel
5. mailto:Kde-frameworks-devel@kde.org
6. https://mail.kde.org/mailman/listinfo/kde-frameworks-devel
# This file provides the macro ecm_setup_qt_translations().
# ECM_SETUP_QT_TRANSLATIONS() setup the necessary rules to compile .po files
# into .qm files, usable by QTranslator. It can also generates a trloader.cpp
# file which takes care of automatically loading those translations.
#
#  ecm_setup_qt_translations(PO_DIR <po_dir>
#                            POT_NAME <pot_name>
#                            [INSTALL_SUB_DIR <install_sub_dir>]
#                            [CREATE_LOADER])
#
# - <po_dir> must points to a dir containing .po files.
# - <pot_name> must be the name of the .pot file for the project. This file must
#   be in <po_dir>.
# - If set <install_sub_dir> defines where the .qm files will be installed.
#   <install_sub_dir> defaults to <pot_name> without the .pot extension.
#   The final install dir is share/<install_sub_dir>
#
# ecm_setup_qt_translations creates a "qm" target. This target builds all .po
# files into ".qm" files.
#
# If ecm_setup_qt_translations is called with the CREATE_LOADER option, it
# generates a trloader.cpp file which ensures translations are automatically
# loaded at startup.
# The path to trloader.cpp is set in the ECM_QT_TRANSLATION_LOADER variable,
# which must be added to the list of sources to build, like this:
#
#   ecm_setup_qt_translations(PO_DIR po POT_NAME mylib CREATE_LOADER)
#   set(mylib_SRC foo.cpp bar.cpp ${ECM_QT_TRANSLATION_LOADER})
#   add_library(mylib ${mylib_SRC})

# This gives us Qt5::lrelease and Qt5::lupdate but unfortunately no Qt5::lconvert
find_package(Qt5LinguistTools CONFIG REQUIRED)

# Find lconvert
get_target_property(lrelease_location Qt5::lrelease LOCATION)
get_filename_component(lrelease_path ${lrelease_location} PATH)
find_program(lconvert_executable
    NAMES lconvert-qt5 lconvert
    PATHS ${lrelease_path}
    )

function(_qm_create_target po_dir install_sub_dir)
    file(GLOB po_files "${po_dir}/*.po")
    foreach (it ${po_files})
        get_filename_component(it ${it} ABSOLUTE)
        # PO files are foo-en_GB.po not foo_en_GB.po like Qt expects
        get_filename_component(fileWithDash ${it} NAME_WE)
        string(REPLACE "-" "_" filenameBase "${fileWithDash}")
        file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
        set(tsfile ${CMAKE_CURRENT_BINARY_DIR}/${filenameBase}.ts)
        set(qmfile ${CMAKE_CURRENT_BINARY_DIR}/${filenameBase}.qm)

        # lconvert from PO to TS and then run lupdate to generate the correct strings
        # finally run lrelease as used above
        add_custom_command(OUTPUT ${qmfile}
            COMMAND ${lconvert_executable}
                ARGS -i ${it} -o ${tsfile}
            COMMAND Qt5::lupdate
                ARGS ${CMAKE_SOURCE_DIR}/src -silent -noobsolete -ts ${tsfile}
            COMMAND Qt5::lrelease
                ARGS -compress -removeidentical -silent ${tsfile} -qm ${qmfile}
            DEPENDS ${it}
            )
        set(qmfiles ${qmfiles} ${qmfile})
    endforeach(it)

    add_custom_target(qm ALL DEPENDS ${qmfiles})

    install(FILES ${qmfiles} DESTINATION share/${install_sub_dir})
endfunction()

function(_qm_create_trloader pot_name install_sub_dir)
    get_filename_component(qm_name ${pot_name} NAME_WE)
    configure_file(trloader.cpp.in trloader.cpp @ONLY)
endfunction()

macro(ECM_SETUP_QT_TRANSLATIONS)
    cmake_parse_arguments(ESQT "CREATE_LOADER" "PO_DIR;POT_NAME;INSTALL_SUB_DIR" "" ${ARGN})

    if(ESQT_UNPARSED_ARGUMENTS)
        message(FATAL_ERROR "Unknown keywords given to ECM_SETUP_QT_TRANSLATIONS(): \"${ESQT_UNPARSED_ARGUMENTS}\"")
    endif()

    if(NOT ESQT_PO_DIR)
        message(FATAL_ERROR "Required argument PO_DIR missing in ECM_SETUP_QT_TRANSLATIONS() call")
    endif()

    if(NOT ESQT_POT_NAME)
        message(FATAL_ERROR "Required argument POT_NAME missing in ECM_SETUP_QT_TRANSLATIONS() call")
    endif()

    if(NOT ESQT_INSTALL_SUB_DIR)
        get_filename_component(ESQT_INSTALL_SUB_DIR "${ESQT_POT_NAME}" NAME_WE)
    endif()

    _qm_create_target(${ESQT_PO_DIR} ${ESQT_INSTALL_SUB_DIR})
    if (ESQT_CREATE_LOADER)
        _qm_create_trloader(${ESQT_POT_NAME} ${ESQT_INSTALL_SUB_DIR})
        set(ECM_QT_TRANSLATION_LOADER ${CMAKE_CURRENT_BINARY_DIR}/trloader.cpp)
    endif()
endmacro()
_______________________________________________
Kde-frameworks-devel mailing list
Kde-frameworks-devel@kde.org
https://mail.kde.org/mailman/listinfo/kde-frameworks-devel

Reply via email to