{"id":11337,"date":"2024-10-01T10:43:01","date_gmt":"2024-10-01T08:43:01","guid":{"rendered":"https:\/\/www.basyskom.de\/?p=11337"},"modified":"2024-10-01T10:47:34","modified_gmt":"2024-10-01T08:47:34","slug":"translating-qt-applications","status":"publish","type":"post","link":"https:\/\/www.basyskom.de\/en\/translating-qt-applications\/","title":{"rendered":"Translating Qt Applications"},"content":{"rendered":"<div data-elementor-type=\"wp-post\" data-elementor-id=\"11337\" class=\"elementor elementor-11337\" data-elementor-post-type=\"post\">\n\t\t\t\t<div class=\"elementor-element elementor-element-0454de1 e-flex e-con-boxed wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-column-slider-no wpr-equal-height-no e-con e-parent\" data-id=\"0454de1\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-06ea5cb elementor-widget elementor-widget-heading\" data-id=\"06ea5cb\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Translating Qt Applications<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-ffaf1d1 elementor-widget elementor-widget-text-editor\" data-id=\"ffaf1d1\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Translating a Qt application, executing the right tool at the right time, can be a daunting task. This blog post gives an overview of the differences between what it was like during the Qt 5 times, how Qt 6 improved upon it and what new functionality Qt 6.7 brings.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-d10e79f elementor-widget elementor-widget-text-editor\" data-id=\"d10e79f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Generally, there\u2019s two tools involved: <strong>lupdate<\/strong> and <strong>lrelease<\/strong>.<\/p><ul><li><strong>lupdate<\/strong>, extracts translatable strings from your source code by scanning it for the relevant keywords (<strong>qsTr, tr, QT_TR_NOOP<\/strong> and others) and places them in a `.ts` file, either creating a new one or updating an existing one. Those `.ts` files are XML files that can be edited in Qt Linguist or sent to a translation service company.<\/li><li><strong>lrelease<\/strong> converts them into binary `.qm` files that get bundled with and loaded by the application.<\/li><\/ul>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-6f988df elementor-widget elementor-widget-heading\" data-id=\"6f988df\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">The Qt 5.15 way <\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-5874bdf elementor-widget elementor-widget-text-editor\" data-id=\"5874bdf\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Qt has always provided CMake API for dealing with `<strong>lupdate<\/strong>` and `<strong>lrelease<\/strong>` in the `<strong>LinguistTools<\/strong>` package. In Qt 5 days you typically found yourself using:<\/p><ul><li>`<strong>qt_create_translation<\/strong>` runs `lupdate` and creates or updates the `.ts` files<\/li><li>`<strong>qt_add_translation<\/strong>` runs `lrelease` and converts the `.ts` files to `.qm` files for consumption by the application<\/li><\/ul>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-fc72d21 elementor-widget elementor-widget-text-editor\" data-id=\"fc72d21\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>However, it was your responsibility to run the right commands at the proper time. You probably don\u2019t want to update translations during every build to avoid cluttering the translation files with temporary changes. Therefore, you either had to add a custom build target or add a CMake option and build with something custom like `<strong>-UPDATE_TS_FILES=ON<\/strong>`. As was custom at the time, both CMake commands return the list of `.qm` files in a variable that you then have to add to your project sources and\/or application resources. Additionally, you had to manually take care of the list of languages you wanted to support.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-58e0cd1 elementor-widget elementor-widget-heading\" data-id=\"58e0cd1\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">The Qt 6 way <\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-d1b50ea elementor-widget elementor-widget-text-editor\" data-id=\"d1b50ea\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Enter Qt 6: <em>CMake has become the default<\/em> and recommended build system generator for Qt projects and building Qt itself. Therefore, Qt has a vested interest in providing good CMake API for all its features and `<strong>LinguistTools<\/strong>` is no exception.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-c455c41 elementor-widget elementor-widget-text-editor\" data-id=\"c455c41\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p><strong>Note<\/strong>: There are changes on how translations are added to cmake within the Qt6 Series. We will discuss those changes down below. For a start we will describe on how it is done using <strong>Qt6.7+<\/strong>.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-ecbd18f elementor-widget elementor-widget-text-editor\" data-id=\"ecbd18f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p><span style=\"text-decoration: underline;\"><strong>Qt 6.2<\/strong><\/span> added a new `<strong>qt_add_translation<em><span style=\"text-decoration: underline;\">s<\/span><\/em><\/strong>` command. Note the plural _s_. Keep this in mind, since the old Qt5 command is still available. <br \/>The new <strong>qt_add_translations<\/strong> combines the previous `create` and `add` commands and adds build targets for both `lupdate` and `lrelease`.<\/p><p>A `&lt;target&gt;_lupdate` naturally runs `lupdate` while `&lt;target&gt;_lrelease` runs `release`.<\/p><p>The latter is actually built by default with your project. Additionally, a global `update_translations` and `release_translations` target is created for all translations within the project. Their names can be adjusted using the `<strong>QT_GLOBAL_LUPDATE_TARGET<\/strong>` and `<strong>QT_GLOBAL_LRELEASE_TARGET<\/strong>` variables.<\/p><p>In order to specify which languages your application supports, either set the `<strong>QT_I18N_TRANSLATED_LANGUAGES<\/strong>` variable, or use `<strong>I18N_TRANSLATED_LANGUAGES<\/strong>` argument of `<strong>qt_standard_project_setup<\/strong>`. The source language defaults to \u201cen\u201d but can be configured using `(QT_)I18N_SOURCE_LANGUAGE`.<\/p><p>Even if you merely migrate an application to the new way and have existing `.ts` files already, you want to set this variable since some platforms and distribution methods require the application to report its supported language in its metadata.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-4bf6af6 elementor-widget elementor-widget-elementor-syntax-highlighter\" data-id=\"4bf6af6\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"elementor-syntax-highlighter.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<pre data-no-translation=\"\"><code class='language-clike' data-no-translation=\"\">\nset(QT_I18N_TRANSLATED_LANGUAGES de fr)\nadd_executable(qstrtest main.cpp)\nqt_add_translation(qstrtest) <\/code><\/pre><script>\nif (!document.getElementById('syntaxed-prism')) {\n\tvar my_awesome_script = document.createElement('script');\n\tmy_awesome_script.setAttribute('src','https:\/\/www.basyskom.de\/wp-content\/plugins\/syntax-highlighter-for-elementor\/assets\/prism2.js');\n\tmy_awesome_script.setAttribute('id','syntaxed-prism');\n\tdocument.body.appendChild(my_awesome_script);\n} else {\n\twindow.Prism && Prism.highlightAll();\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-1ea1e3b elementor-widget elementor-widget-text-editor\" data-id=\"1ea1e3b\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>The translations are by default added to the Qt resource system in the `\/i18n` prefix with the files named after your target. Therefore, from your `main()` load the translations like so:<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-0f36425 elementor-widget elementor-widget-elementor-syntax-highlighter\" data-id=\"0f36425\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"elementor-syntax-highlighter.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<pre data-no-translation=\"\"><code class='language-clike' data-no-translation=\"\">QTranslator translator;\nif (translator.load(QLocale(), &quot;qstrtest&quot;_L1, &quot;_&quot;_L1, &quot;:\/i18n&quot;_L1)) {\n    QCoreApplication::installTranslator(&amp;translator);\n} <\/code><\/pre><script>\nif (!document.getElementById('syntaxed-prism')) {\n\tvar my_awesome_script = document.createElement('script');\n\tmy_awesome_script.setAttribute('src','https:\/\/www.basyskom.de\/wp-content\/plugins\/syntax-highlighter-for-elementor\/assets\/prism2.js');\n\tmy_awesome_script.setAttribute('id','syntaxed-prism');\n\tdocument.body.appendChild(my_awesome_script);\n} else {\n\twindow.Prism && Prism.highlightAll();\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-ff92bcd elementor-widget elementor-widget-text-editor\" data-id=\"ff92bcd\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>By the way, please use the `<strong>QTranslator::load<\/strong>` overload that takes a proper `<strong>QLocale<\/strong>`, don\u2019t just hardcode a look-up based on the locale\u2019s name. A user might want to use their home locale (24 hour clock, currency, and all) but with an English user interface.<\/p><p>And that\u2019s it.<\/p><p>Build the `<strong>update_translations<\/strong>` target:<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-177aa76 elementor-widget elementor-widget-elementor-syntax-highlighter\" data-id=\"177aa76\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"elementor-syntax-highlighter.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<pre data-no-translation=\"\"><code class='language-bash' data-no-translation=\"\">cmake --build . --target update_translations <\/code><\/pre><script>\nif (!document.getElementById('syntaxed-prism')) {\n\tvar my_awesome_script = document.createElement('script');\n\tmy_awesome_script.setAttribute('src','https:\/\/www.basyskom.de\/wp-content\/plugins\/syntax-highlighter-for-elementor\/assets\/prism2.js');\n\tmy_awesome_script.setAttribute('id','syntaxed-prism');\n\tdocument.body.appendChild(my_awesome_script);\n} else {\n\twindow.Prism && Prism.highlightAll();\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-9e0aeab elementor-widget elementor-widget-text-editor\" data-id=\"9e0aeab\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Of course this sounds too good to be true, right?<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-eb3f81e elementor-widget elementor-widget-heading\" data-id=\"eb3f81e\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Customizing translations<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-a008acc elementor-widget elementor-widget-text-editor\" data-id=\"a008acc\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>For starters, you\u2019ll find that `<strong>lupdate<\/strong>` blatantly placed the ts files, i.e. `<strong>qstrtest_de.ts<\/strong>` and `<strong>qstrtest_fr.ts<\/strong>` files alongside your `<strong>main.cpp<\/strong>`. If you want them in a dedicated folder instead, set the `<strong>TS_FILES_DIR<\/strong>` parameter of `<strong>qt_add_translations<\/strong>`. The default resource prefix can be changed using `<strong>RESOURCE_PREFIX<\/strong>`.<\/p><p>In case you don\u2019t want to automatically add the `.qm` files to the application resources or you need more control on how they\u2019re added, set `<strong>QM_FILES_OUTPUT_VARIABLE<\/strong>`. This disables automatic resource handling and instead writes the list of generated `.qm` files to that variable. From there you can postprocess the files any way you like, e.g. install them into a specific directory or add them to your existing resource file.<\/p><p>If you also need to adjust where the `.qm` files are created, set the `<strong>OUTPUT_LOCATION<\/strong>` file property using `<strong>set_source_files_properties<\/strong>`. Sadly, this needs to be done on each individual `.ts` file, defying the automatic determination of `.ts` file names based on the supported languages. On top of that, the property has to be set <strong><em><span style=\"text-decoration: underline;\">_before_<\/span><\/em><\/strong> calling `<strong>qt_add_translations<\/strong>`!<\/p><p>Additional command-line arguments for `lupdate` and `lrelease` can be specified using the `<strong>LUPDATE_OPTIONS<\/strong>` and `<strong>LRELEASE_OPTIONS<\/strong>` arguments, respectively. For example, set `<strong>LRELEASE_OPTIONS<\/strong>\u00a0 <strong>-idbased<\/strong>` if your application uses ID-based translations.<\/p><p>\u00a0<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-9575b99 elementor-widget elementor-widget-heading\" data-id=\"9575b99\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Important differences in Qt lower than version Qt6.7<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-4b4b15b elementor-widget elementor-widget-text-editor\" data-id=\"4b4b15b\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>The biggest showstopper of the snippet above, however, is that it only works like this from <strong>Qt 6.7<\/strong> onwards. <strong>When you are targeting Qt 6.5 LTS<\/strong> you need to do things a litte bit differently.<\/p><p><strong>In Qt 6.5 LTS you have to: <\/strong><\/p><ul><li>specify the `<strong>.ts<\/strong>` files manually using the `<strong>TS_FILES<\/strong>` option<\/li><li>in case the target you need to attach your qm files at is not your build target, you have to first create a custom target, add your translations against it and configure dependencies. From Qt6.7 onward you can set the output target to be not your build target.<\/li><\/ul><p>Luckily, like with most recent improvements in Qt, be it in QML or in our case CMake, the new features are opt-in and a Qt6.5 LTS setup will continue to work as you upgrade to the upcoming Qt 6.8 LTS.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-da23773 elementor-widget elementor-widget-heading\" data-id=\"da23773\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Comparison Qt5.15, Qt6.5, Qt6.7<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-3015ec7 elementor-widget elementor-widget-text-editor\" data-id=\"3015ec7\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Let\u2019s compare the syntax you would use for a project built with Qt 5.15, 6.5, and 6.7, respectively. Assume the project was originally written against Qt 5.15 and should be ported to modern Qt CMake infrastructure. This should ideally be done without having to shuffle translation files around and without touching the C++ code that loads them.<\/p><p>Therefore, the location and name of the `<strong>.ts<\/strong>` files in the source tree must remain the same as well as the location within the application\u2019s resources.<\/p><p>In our example, the `<strong>.ts<\/strong>` files will be<\/p><ul><li>`<strong>translations\/app_de.ts<\/strong>` and<\/li><li>`<strong>translations\/app_fr.ts<\/strong>`,<\/li><\/ul><p>loaded using: `<strong><code data-no-translation=\"\">translator.load(QLocale(), QStringLiteral(\"app\"), QStringLiteral(\"_\"), QStringLiteral(\":\/translations\"));<\/code><\/strong>`.<\/p><p>\u00a0<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-2906133 elementor-widget elementor-widget-heading\" data-id=\"2906133\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Qt5.15<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-c8902f8 elementor-widget elementor-widget-elementor-syntax-highlighter\" data-id=\"c8902f8\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"elementor-syntax-highlighter.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<pre data-no-translation=\"\"><code class='language-clike' data-no-translation=\"\">find_package(Qt5 REQUIRED COMPONENTS Core LinguistTools)\n\noption(UPDATE_TS_FILES &quot;Update .ts files&quot; OFF)\nset(LANGUAGES de fr)\n\nadd_executable(qstrtest main.cpp)\n\nforeach(LANGUAGE ${LANGUAGES})\n    list(APPEND TS_FILES translations\/app_${LANGUAGE}.ts)\nendforeach()\n\nif (UPDATE_TS_FILES)\n    qt_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES} OPTIONS -locations none)\nelse()\n    qt_add_translation(QM_FILES ${TS_FILES})\nendif()\n\nset(TRANSLATIONS_QRC_FILE ${CMAKE_CURRENT_BINARY_DIR}\/translations.qrc)\n\nfile(WRITE ${TRANSLATIONS_QRC_FILE} &quot;&lt;!DOCTYPE RCC&gt;\\n&lt;RCC version=\\&quot;1.0\\&quot;&gt;\\n\\t&lt;qresource prefix=\\&quot;\/translations\\&quot;&gt;\\n&quot;)\nforeach(QM_FILE ${QM_FILES})\n    get_filename_component(QM_FILE_NAME ${QM_FILE} NAME)\n    file(APPEND ${TRANSLATIONS_QRC_FILE} &quot;\\t\\t&lt;file alias=\\&quot;${QM_FILE_NAME}\\&quot;&gt;${QM_FILE}&lt;\/file&gt;\\n&quot;)\nendforeach()\nfile(APPEND ${TRANSLATIONS_QRC_FILE} &quot;\\t&lt;\/qresource&gt;\\n&lt;\/RCC&gt;&quot;)\n\ntarget_sources(qstrtest PRIVATE ${TRANSLATIONS_QRC_FILE} ${QM_FILES}) <\/code><\/pre><script>\nif (!document.getElementById('syntaxed-prism')) {\n\tvar my_awesome_script = document.createElement('script');\n\tmy_awesome_script.setAttribute('src','https:\/\/www.basyskom.de\/wp-content\/plugins\/syntax-highlighter-for-elementor\/assets\/prism2.js');\n\tmy_awesome_script.setAttribute('id','syntaxed-prism');\n\tdocument.body.appendChild(my_awesome_script);\n} else {\n\twindow.Prism && Prism.highlightAll();\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-d670151 elementor-widget elementor-widget-text-editor\" data-id=\"d670151\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Awful, isn\u2019t it? Sure, we could hardcode the `<strong>.qrc<\/strong>` file or use `<strong>configure_file<\/strong>` for it but if we want the flexibility of easily adding new languages, this is what we ought to do.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-4808d08 elementor-widget elementor-widget-heading\" data-id=\"4808d08\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Qt6.5<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-b49fc25 elementor-widget elementor-widget-elementor-syntax-highlighter\" data-id=\"b49fc25\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"elementor-syntax-highlighter.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<pre data-no-translation=\"\"><code class='language-clike' data-no-translation=\"\">find_package(Qt6 REQUIRED COMPONENTS Core LinguistTools)\n\nqt_standard_project_setup(REQUIRES 6.5)\n\nqt_add_executable(qstrtest main.cpp)\n\nset(LANGUAGES de fr)\nforeach(LANGUAGE ${LANGUAGES})\n    list(APPEND TS_FILES translations\/app_${LANGUAGE}.ts)\nendforeach()\n\nqt_add_translations(qstrtest\n    TS_FILES ${TS_FILES}\n    RESOURCE_PREFIX translations\n    LUPDATE_OPTIONS -locations none\n) <\/code><\/pre><script>\nif (!document.getElementById('syntaxed-prism')) {\n\tvar my_awesome_script = document.createElement('script');\n\tmy_awesome_script.setAttribute('src','https:\/\/www.basyskom.de\/wp-content\/plugins\/syntax-highlighter-for-elementor\/assets\/prism2.js');\n\tmy_awesome_script.setAttribute('id','syntaxed-prism');\n\tdocument.body.appendChild(my_awesome_script);\n} else {\n\twindow.Prism && Prism.highlightAll();\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-0a9d5a5 elementor-widget elementor-widget-text-editor\" data-id=\"0a9d5a5\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Qt 6.5, as you can clearly tell, is a lot nicer. You still need to collect the list of `<strong>.ts<\/strong>` files yourself but other than that it\u2019s pretty much automagic. The only key difference is that instead of building the project with `<strong><code data-no-translation=\"\">-DUPDATE_TS_FILES=ON<\/code><\/strong>` you just build the `update_translations` target as needed<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-598b79e elementor-widget elementor-widget-heading\" data-id=\"598b79e\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Qt6.7<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-88bccca elementor-widget elementor-widget-elementor-syntax-highlighter\" data-id=\"88bccca\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"elementor-syntax-highlighter.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<pre data-no-translation=\"\"><code class='language-clike' data-no-translation=\"\">ind_package(Qt6 REQUIRED COMPONENTS Core LinguistTools)\n\nqt_standard_project_setup(\n    REQUIRES 6.7\n    I18N_TRANSLATED_LANGUAGES de fr\n)\n\nqt_add_executable(qstrtest main.cpp)\n\nqt_add_translations(qstrtest\n    TS_FILE_BASE app\n    TS_FILE_DIR translations\n    RESOURCE_PREFIX translations\n    LUPDATE_OPTIONS -locations none\n) <\/code><\/pre><script>\nif (!document.getElementById('syntaxed-prism')) {\n\tvar my_awesome_script = document.createElement('script');\n\tmy_awesome_script.setAttribute('src','https:\/\/www.basyskom.de\/wp-content\/plugins\/syntax-highlighter-for-elementor\/assets\/prism2.js');\n\tmy_awesome_script.setAttribute('id','syntaxed-prism');\n\tdocument.body.appendChild(my_awesome_script);\n} else {\n\twindow.Prism && Prism.highlightAll();\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-47bd295 elementor-widget elementor-widget-text-editor\" data-id=\"47bd295\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Qt 6.7 takes it one step further: you specify the list of supported languages and what file name format the resulting files should have. You can continue to use `<strong>TS_FILES<\/strong>` and provide an explicit list if you prefer.<\/p><p>There\u2019s a lot more you can control with `<strong>qt_add_translations<\/strong>`, such as excluding certain files and folders. If it\u2019s too much magic for your taste, there\u2019s also more aptly named individual\u00a0<code data-no-translation=\"\">qt_add_lupdate<\/code>\u00a0 and <code data-no-translation=\"\">qt_add_lrelease<\/code> commands.<\/p><p>Nevertheless, this should give you a good idea of what it takes to port your Qt 5 translation setup to modern Qt CMake infrastructure.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>","protected":false},"excerpt":{"rendered":"<p>Translating a Qt application, can be a daunting task. This is an overview from Qt 5 to Qt 6 and what new functionality Qt 6.7 brings.<\/p>","protected":false},"author":23,"featured_media":4011,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[1,2,8],"tags":[15],"class_list":["post-11337","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-allgemein","category-blog","category-qt","tag-qt"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/posts\/11337","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/users\/23"}],"replies":[{"embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/comments?post=11337"}],"version-history":[{"count":11,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/posts\/11337\/revisions"}],"predecessor-version":[{"id":11348,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/posts\/11337\/revisions\/11348"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/media\/4011"}],"wp:attachment":[{"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/media?parent=11337"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/categories?post=11337"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/tags?post=11337"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}