Advance Wayland and KDE package bring-up

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-04-14 10:51:06 +01:00
parent 51f3c21121
commit cf12defd28
15214 changed files with 20594243 additions and 269 deletions
@@ -0,0 +1,175 @@
cmake_minimum_required(VERSION 3.5)
project(ECMPoQmToolsTest)
set(ECM_MODULE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../modules")
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../modules)
# make sure the test install dir is clean
file(REMOVE_RECURSE "${CMAKE_INSTALL_PREFIX}")
include(ECMPoQmTools)
include(../test_helpers.cmake)
##############################
# #
# ecm_process_po_files_as_qm #
# #
##############################
# Should create a process-and-install.qm file and install it
ecm_process_po_files_as_qm(fr ALL
INSTALL_DESTINATION share/locale
PO_FILES process-and-install.po
)
# Should create a only-process.qm file, without installing it
ecm_process_po_files_as_qm(fr ALL
PO_FILES only-process.po
)
##############################
# #
# ecm_install_po_files_as_qm #
# #
##############################
# Should create a bunch of .qm files and install them in share/locale.
# Should ignore files directly under po/ as well as directories under po/ which
# do not contain any .po files.
ecm_install_po_files_as_qm(po)
# Should create a bunch of .qm files and install them in
# ${CMAKE_INSTALL_LOCALEDIR}
set(CMAKE_INSTALL_LOCALEDIR custom-dir1)
ecm_install_po_files_as_qm(po-custom-dir1)
# Should create a bunch of .qm files and install them in
# ${LOCALE_INSTALL_DIR}
set(LOCALE_INSTALL_DIR custom-dir2)
ecm_install_po_files_as_qm(po-custom-dir2)
unset(CMAKE_INSTALL_LOCALEDIR)
unset(LOCALE_INSTALL_DIR)
########################
# #
# ecm_create_qm_loader #
# #
########################
find_package(Qt${QT_MAJOR_VERSION}Core CONFIG REQUIRED)
ecm_install_po_files_as_qm(tr_test-po)
#
# single-threaded test, sources var arg
#
set(tr_test_SRCS
tr_test.cpp
)
ecm_create_qm_loader(tr_test_SRCS catalog)
add_executable(tr_test ${tr_test_SRCS})
target_link_libraries(tr_test PRIVATE Qt${QT_MAJOR_VERSION}::Core)
#
# single-threaded test, target arg
#
add_executable(tr_test_target tr_test.cpp)
ecm_create_qm_loader(tr_test_target catalog)
target_link_libraries(tr_test_target PRIVATE Qt${QT_MAJOR_VERSION}::Core)
#
# single-threaded test (different catalog name, automoc)
#
# This is to check we don't overwrite previously-generated files.
set(tr_test_2_SRCS
tr_test.cpp
)
ecm_create_qm_loader(tr_test_2_SRCS catalog2)
add_executable(tr_test_2 ${tr_test_2_SRCS})
set_target_properties(tr_test_2 PROPERTIES AUTOMOC ON)
target_include_directories(tr_test_2 PRIVATE "${CMAKE_CURRENT_BINARY_DIR}")
target_link_libraries(tr_test_2 PRIVATE Qt${QT_MAJOR_VERSION}::Core)
#
# module for tr_thread_test
#
add_library(tr_thread_module MODULE tr_thread_test_module.cpp ${QMLOADER_FILES})
target_link_libraries(tr_thread_module PRIVATE Qt${QT_MAJOR_VERSION}::Core)
#
# loading a module on a thread other than the main thread
# (automoc)
#
set(tr_thread_test_SRCS
tr_thread_test.cpp
)
ecm_create_qm_loader(tr_thread_test_SRCS catalog)
add_executable(tr_thread_test ${tr_thread_test_SRCS})
set_target_properties(tr_thread_test PROPERTIES AUTOMOC ON)
target_include_directories(tr_thread_test PRIVATE "${CMAKE_CURRENT_BINARY_DIR}")
target_compile_definitions(tr_thread_test PRIVATE "MODULE_PATH=\"$<TARGET_FILE:tr_thread_module>\"")
target_link_libraries(tr_thread_test PRIVATE Qt${QT_MAJOR_VERSION}::Core)
#
# loading a module on a thread other than the main thread
# (different catalog, no AUTOMOC)
#
# make sure the moc file is only visible to this test/target
set(MOC_DIR "${CMAKE_CURRENT_BINARY_DIR}/tr_thread_test_2_moc")
qt_generate_moc(tr_thread_test.cpp "${MOC_DIR}/tr_thread_test.moc")
# Unset SKIP_AUTOMOC again, to not interfer with AUTOMOC as set for tr_thread_test
set_source_files_properties(tr_thread_test PROPERTIES SKIP_AUTOMOC OFF)
set(tr_thread_test_2_SRCS
tr_thread_test.cpp
"${MOC_DIR}/tr_thread_test.moc"
)
ecm_create_qm_loader(tr_thread_test_2_SRCS catalog2)
add_executable(tr_thread_test_2 ${tr_thread_test_2_SRCS})
set_target_properties(tr_thread_test_2 PROPERTIES AUTOMOC OFF)
target_include_directories(tr_thread_test_2 PRIVATE "${CMAKE_CURRENT_BINARY_DIR}" "${MOC_DIR}")
target_compile_definitions(tr_thread_test_2 PRIVATE "MODULE_PATH=\"$<TARGET_FILE:tr_thread_module>\"")
target_link_libraries(tr_thread_test_2 PRIVATE Qt${QT_MAJOR_VERSION}::Core)
#
# call to ecm_create_qm_loader is in a different CMakeLists.txt to where
# the target it is added to is defined
#
# This is not something we want people to do, but it's unfortunately something
# projects have done and we need to keep them building.
unset(QMLOADER_FILES)
ecm_create_qm_loader(QMLOADER_FILES catalog)
assert_var_defined(QMLOADER_FILES)
add_subdirectory(subdir)
# when building against a static Qt the tests doing dynamic loading of a module using Qt wont work
if (NOT DEFINED QT6_IS_SHARED_LIBS_BUILD OR QT6_IS_SHARED_LIBS_BUILD)
file(GENERATE
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/check_conf.cmake"
INPUT "${CMAKE_CURRENT_SOURCE_DIR}/check_conf.cmake.in"
)
else()
file(GENERATE
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/check_conf.cmake"
INPUT "${CMAKE_CURRENT_SOURCE_DIR}/check_conf_static.cmake.in"
)
endif()
configure_file(check.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/check.cmake" @ONLY)
@@ -0,0 +1,124 @@
set(BINARY_DIR "@CMAKE_CURRENT_BINARY_DIR@")
set(ACTUAL_TREE "@CMAKE_INSTALL_PREFIX@")
include("${BINARY_DIR}/check_conf.cmake")
set(fail OFF)
macro(mark_failed msg)
message(WARNING "FAIL: ${msg}")
set(fail ON)
endmacro()
macro(check_exists file)
message(STATUS "Checking for ${file}")
if (NOT EXISTS ${file})
mark_failed("File \"${file}\" does not exist")
endif()
endmacro()
check_exists(${BINARY_DIR}/ECMPoQm/fr/only-process.qm)
set(exp_files
"share/locale/fr/LC_MESSAGES/process-and-install.qm"
"share/locale/es/LC_MESSAGES/install-test.qm"
"share/locale/fr/LC_MESSAGES/install-test.qm"
"share/locale/en/LC_MESSAGES/catalog.qm"
"share/locale/en/LC_MESSAGES/catalog2.qm"
"share/locale/de/LC_MESSAGES/catalog.qm"
"share/locale/de/LC_MESSAGES/catalog2.qm"
"custom-dir1/es/LC_MESSAGES/custom-dir1-install-test.qm"
"custom-dir1/fr/LC_MESSAGES/custom-dir1-install-test.qm"
"custom-dir2/es/LC_MESSAGES/custom-dir2-install-test.qm"
"custom-dir2/fr/LC_MESSAGES/custom-dir2-install-test.qm"
)
file(GLOB_RECURSE actual_files RELATIVE "${ACTUAL_TREE}" "${ACTUAL_TREE}/*")
list(SORT exp_files)
list(SORT actual_files)
if(NOT exp_files STREQUAL actual_files)
foreach(f ${exp_files})
list(FIND actual_files "${f}" result)
if(result EQUAL -1)
message(WARNING "${f} was expected, but not found")
set(fail ON)
endif()
endforeach()
foreach(f ${actual_files})
list(FIND exp_files "${f}" result)
if(result EQUAL -1)
message(WARNING "${f} was found, but not expected")
set(fail ON)
endif()
endforeach()
else()
message(STATUS "Installed translations in expected locations")
endif()
# we know we can modify the executable environment on Linux
if("@CMAKE_SYSTEM_NAME@" STREQUAL "Linux")
set(exp_output_catalog_en "english text:english plural form 5")
set(exp_output_catalog_de "german text:german plural form 5")
# no de_AT translation -> should fall back to de
set(exp_output_catalog_de_AT "${exp_output_catalog_de}")
# no french translation provided -> english fallback
set(exp_output_catalog_fr "${exp_output_catalog_en}")
set(exp_output_catalog2_en "2nd english text:2nd english plural form 5")
set(exp_output_catalog2_de "2nd german text:2nd german plural form 5")
# no de_AT translation -> should fall back to de
set(exp_output_catalog2_de_AT "${exp_output_catalog2_de}")
# no french translation provided -> english fallback
set(exp_output_catalog2_fr "${exp_output_catalog2_en}")
function(check_translations name exec catalog_name)
foreach(lang en de de_AT fr)
execute_process(
COMMAND "${CMAKE_COMMAND}" -E env "XDG_DATA_DIRS=${ACTUAL_TREE}/share"
LANG=${lang} "${exec}"
OUTPUT_VARIABLE output
)
string(STRIP "${output}" stripped_output)
if(NOT stripped_output STREQUAL exp_output_${catalog_name}_${lang})
message(WARNING "${name}[${lang}] output was \"${stripped_output}\", but expected \"${exp_output_${catalog_name}_${lang}}\"")
set(fail ON PARENT_SCOPE)
else()
message(STATUS "${name}[${lang}] output was \"${stripped_output}\", as expected")
endif()
endforeach()
endfunction()
function(check_translations_multilang name exec catalog_name langs expected_lang)
execute_process(
COMMAND "${CMAKE_COMMAND}" -E env "XDG_DATA_DIRS=${ACTUAL_TREE}/share"
LANGUAGE=${langs} "${exec}"
OUTPUT_VARIABLE output
)
string(STRIP "${output}" stripped_output)
if(NOT stripped_output STREQUAL exp_output_${catalog_name}_${expected_lang})
message(WARNING "${name}[${langs}] output was \"${stripped_output}\", but expected \"${exp_output_${catalog_name}_${expected_lang}}\"")
set(fail ON PARENT_SCOPE)
else()
message(STATUS "${name}[${langs}] output was \"${stripped_output}\", as expected")
endif()
endfunction()
check_translations(TR_TEST "${TR_TEST_EXEC}" catalog)
check_translations(TR_TEST_TARGET "${TR_TEST_TARGET_EXEC}" catalog)
check_translations(TR_TEST_2 "${TR_TEST_2_EXEC}" catalog2)
check_translations(TR_TEST_SUBDIR "${TR_TEST_SUBDIR_EXEC}" catalog)
if (TR_THREAD_TEST)
check_translations(TR_THREAD_TEST "${TR_THREAD_TEST_EXEC}" catalog)
endif()
if (TR_THREAD_TEST_2)
check_translations(TR_THREAD_TEST_2 "${TR_THREAD_TEST_2_EXEC}" catalog2)
endif()
check_translations_multilang(TR_TEST "${TR_TEST_EXEC}" catalog "de_AT:fr_FR" "de")
check_translations_multilang(TR_TEST "${TR_TEST_EXEC}" catalog "en_GB:de_DE" "en")
endif()
if (fail)
message(FATAL_ERROR "Test failed!")
endif()
@@ -0,0 +1,6 @@
set(TR_TEST_EXEC "$<TARGET_FILE:tr_test>")
set(TR_TEST_TARGET_EXEC "$<TARGET_FILE:tr_test_target>")
set(TR_TEST_2_EXEC "$<TARGET_FILE:tr_test_2>")
set(TR_TEST_SUBDIR_EXEC "$<TARGET_FILE:tr_test_subdir>")
set(TR_THREAD_TEST_EXEC "$<TARGET_FILE:tr_thread_test>")
set(TR_THREAD_TEST_2_EXEC "$<TARGET_FILE:tr_thread_test_2>")
@@ -0,0 +1,4 @@
set(TR_TEST_EXEC "$<TARGET_FILE:tr_test>")
set(TR_TEST_TARGET_EXEC "$<TARGET_FILE:tr_test_target>")
set(TR_TEST_2_EXEC "$<TARGET_FILE:tr_test_2>")
set(TR_TEST_SUBDIR_EXEC "$<TARGET_FILE:tr_test_subdir>")
@@ -0,0 +1,18 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Language: fr\n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Qt-Contexts: true\n"
"X-Generator: Poedit 1.5.4\n"
#: ../../home/aurelien/src/kf5/frameworks/kbookmarks/src/kbookmark.cpp:307
msgctxt "KBookmark|Bookmark separator"
msgid "--- separator ---"
msgstr ""
@@ -0,0 +1,18 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Language: fr\n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Qt-Contexts: true\n"
"X-Generator: Poedit 1.5.4\n"
#: ../../home/aurelien/src/kf5/frameworks/kbookmarks/src/kbookmark.cpp:307
msgctxt "KBookmark|Bookmark separator"
msgid "--- separator ---"
msgstr ""
@@ -0,0 +1,18 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Language: fr\n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Qt-Contexts: true\n"
"X-Generator: Poedit 1.5.4\n"
#: ../../home/aurelien/src/kf5/frameworks/kbookmarks/src/kbookmark.cpp:307
msgctxt "KBookmark|Bookmark separator"
msgid "--- separator ---"
msgstr ""
@@ -0,0 +1,18 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Language: fr\n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Qt-Contexts: true\n"
"X-Generator: Poedit 1.5.4\n"
#: ../../home/aurelien/src/kf5/frameworks/kbookmarks/src/kbookmark.cpp:307
msgctxt "KBookmark|Bookmark separator"
msgid "--- separator ---"
msgstr ""
@@ -0,0 +1,18 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Language: fr\n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Qt-Contexts: true\n"
"X-Generator: Poedit 1.5.4\n"
#: ../../home/aurelien/src/kf5/frameworks/kbookmarks/src/kbookmark.cpp:307
msgctxt "KBookmark|Bookmark separator"
msgid "--- separator ---"
msgstr ""
@@ -0,0 +1,18 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Language: fr\n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Qt-Contexts: true\n"
"X-Generator: Poedit 1.5.4\n"
#: ../../home/aurelien/src/kf5/frameworks/kbookmarks/src/kbookmark.cpp:307
msgctxt "KBookmark|Bookmark separator"
msgid "--- separator ---"
msgstr ""
@@ -0,0 +1,18 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Language: fr\n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Qt-Contexts: true\n"
"X-Generator: Poedit 1.5.4\n"
#: ../../home/aurelien/src/kf5/frameworks/kbookmarks/src/kbookmark.cpp:307
msgctxt "KBookmark|Bookmark separator"
msgid "--- separator ---"
msgstr ""
@@ -0,0 +1,18 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Language: fr\n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Qt-Contexts: true\n"
"X-Generator: Poedit 1.5.4\n"
#: ../../home/aurelien/src/kf5/frameworks/kbookmarks/src/kbookmark.cpp:307
msgctxt "KBookmark|Bookmark separator"
msgid "--- separator ---"
msgstr ""
@@ -0,0 +1,5 @@
# QMLOADER_FILES comes from parent CMakeLists.txt. This is not something we
# want people to do, but it's unfortunately something projects have done and we
# need to keep them building
add_executable(tr_test_subdir ../tr_test.cpp ${QMLOADER_FILES})
target_link_libraries(tr_test_subdir PRIVATE Qt${QT_MAJOR_VERSION}::Core)
@@ -0,0 +1,22 @@
msgid ""
msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Language: de\n"
"X-Qt-Contexts: true\n"
#: main.cpp:12
msgctxt "testcontext|"
msgid "test string"
msgstr "german text"
#: main.cpp:13
#, qt-format
#| msgid "test plural"
msgctxt "testcontext|"
msgid "test plural %n"
msgid_plural "test plural %n"
msgstr[0] "german singular form %n"
msgstr[1] "german plural form %n"
@@ -0,0 +1,22 @@
msgid ""
msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Language: de\n"
"X-Qt-Contexts: true\n"
#: main.cpp:12
msgctxt "testcontext|"
msgid "test string"
msgstr "2nd german text"
#: main.cpp:13
#, qt-format
#| msgid "test plural"
msgctxt "testcontext|"
msgid "test plural %n"
msgid_plural "test plural %n"
msgstr[0] "2nd german singular form %n"
msgstr[1] "2nd german plural form %n"
@@ -0,0 +1,22 @@
msgid ""
msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Language: en\n"
"X-Qt-Contexts: true\n"
#: main.cpp:12
msgctxt "testcontext|"
msgid "test string"
msgstr "english text"
#: main.cpp:13
#, qt-format
#| msgid "test plural"
msgctxt "testcontext|"
msgid "test plural %n"
msgid_plural "test plural %n"
msgstr[0] "english singular form %n"
msgstr[1] "english plural form %n"
@@ -0,0 +1,22 @@
msgid ""
msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Language: en\n"
"X-Qt-Contexts: true\n"
#: main.cpp:12
msgctxt "testcontext|"
msgid "test string"
msgstr "2nd english text"
#: main.cpp:13
#, qt-format
#| msgid "test plural"
msgctxt "testcontext|"
msgid "test plural %n"
msgid_plural "test plural %n"
msgstr[0] "2nd english singular form %n"
msgstr[1] "2nd english plural form %n"
@@ -0,0 +1,22 @@
/*
SPDX-FileCopyrightText: 2015 Alex Merry <alex.merry@kde.org>
SPDX-License-Identifier: BSD-3-Clause
*/
#include <QCoreApplication>
#include <QTextStream>
#include <stdio.h>
int main(int argc, char** argv)
{
QCoreApplication app(argc, argv);
QTextStream output(stdout);
output << QCoreApplication::translate("testcontext", "test string") << ":";
output << QCoreApplication::translate("testcontext", "test plural %n", 0, 5) << '\n';
return 0;
}
@@ -0,0 +1,77 @@
/*
SPDX-FileCopyrightText: 2015 Alex Merry <alex.merry@kde.org>
SPDX-License-Identifier: BSD-3-Clause
*/
#include <QCoreApplication>
#include <QLibrary>
#include <QMetaObject>
#include <QThread>
class Thread : public QThread
{
Q_OBJECT
QLibrary *m_lib;
public:
Thread()
: m_lib(0)
{}
~Thread()
{
delete m_lib;
}
Q_SIGNALS:
void libraryLoaded();
public Q_SLOTS:
void printStrings()
{
Q_ASSERT(QThread::currentThread() == QCoreApplication::instance()->thread());
QFunctionPointer print_strings = m_lib->resolve("print_strings");
if (print_strings) {
print_strings();
} else {
qFatal("Could not resolve print_strings: %s", qPrintable(m_lib->errorString()));
}
QCoreApplication::instance()->quit();
}
protected:
void run()
{
m_lib = new QLibrary(MODULE_PATH);
if (!m_lib->load()) {
qFatal("Could not load module: %s", m_lib->errorString().toUtf8().data());
}
// Queue a call to printStrings() on the main event loop (giving the
// translations a chance to be loaded).
QMetaObject::invokeMethod(this, "printStrings", Qt::QueuedConnection);
}
};
int main(int argc, char** argv)
{
QCoreApplication app(argc, argv);
Thread thread;
// Start the thread *after* QCoreApplication is started (otherwise the
// plugin's startup function won't be run on the Thread, and we won't test
// what we wanted to test).
QMetaObject::invokeMethod(&thread, "start", Qt::QueuedConnection);
app.exec();
thread.wait();
return 0;
}
#include "tr_thread_test.moc"
@@ -0,0 +1,18 @@
/*
SPDX-FileCopyrightText: 2015 Alex Merry <alex.merry@kde.org>
SPDX-License-Identifier: BSD-3-Clause
*/
#include <QCoreApplication>
#include <QTextStream>
#include <stdio.h>
extern "C" Q_DECL_EXPORT void print_strings()
{
QTextStream output(stdout);
output << QCoreApplication::translate("testcontext", "test string") << ":";
output << QCoreApplication::translate("testcontext", "test plural %n", 0, 5) << '\n';
}