diff options
Diffstat (limited to 'modules')
| -rw-r--r-- | modules/ECMPoQmTools.cmake | 17 | ||||
| -rw-r--r-- | modules/ECMQmLoader.cpp.in | 93 | 
2 files changed, 79 insertions, 31 deletions
diff --git a/modules/ECMPoQmTools.cmake b/modules/ECMPoQmTools.cmake index 0af5b12f..2547df1a 100644 --- a/modules/ECMPoQmTools.cmake +++ b/modules/ECMPoQmTools.cmake @@ -28,8 +28,8 @@  #  #   ecm_create_qm_loader(<source_files_var> <catalog_name>)  # -# Generates a C++ file which ensures translations are automatically loaded at -# startup. The path of the .cpp file is appended to ``<source_files_var>``. +# Generates C++ code which ensures translations are automatically loaded at +# startup. The generated files are appended to ``<source_files_var>``.  #  # It assumes that the .qm file for the language code ``<lang>`` is installed as  # ``<sharedir>/locale/<lang>/LC_MESSAGES/<catalog_name>.qm``, where @@ -99,9 +99,16 @@ endfunction()  function(ecm_create_qm_loader out_var catalog_name) -    # catalog_name is used in ECMQmLoader.cpp.in -    configure_file(${ECM_MODULE_DIR}/ECMQmLoader.cpp.in ECMQmLoader-${catalog_name}.cpp @ONLY) -    set(${out_var} ${${out_var}} ${CMAKE_CURRENT_BINARY_DIR}/ECMQmLoader-${catalog_name}.cpp PARENT_SCOPE) +    set(loader_base ${CMAKE_CURRENT_BINARY_DIR}/ECMQmLoader-${catalog_name}) + +    set(QM_LOADER_CATALOG_NAME "${catalog_name}") + +    configure_file( +        ${ECM_MODULE_DIR}/ECMQmLoader.cpp.in +        "${loader_base}.cpp" +        @ONLY +    ) +    set(${out_var} "${${out_var}}" "${loader_base}.cpp" PARENT_SCOPE)  endfunction() diff --git a/modules/ECMQmLoader.cpp.in b/modules/ECMQmLoader.cpp.in index 423d1c93..97c5c826 100644 --- a/modules/ECMQmLoader.cpp.in +++ b/modules/ECMQmLoader.cpp.in @@ -33,40 +33,81 @@  #include <QCoreApplication>  #include <QLocale>  #include <QStandardPaths> +#include <QThread>  #include <QTranslator> -#include <QDebug> +namespace { -static bool loadTranslation(const QString &localeDirName) -{ -    QString subPath = QStringLiteral("locale/") + localeDirName + QStringLiteral("/LC_MESSAGES/@catalog_name@.qm"); -    QString fullPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, subPath); -    if (fullPath.isEmpty()) { -        return false; +    bool loadTranslation(const QString &localeDirName) +    { +        QString subPath = QStringLiteral("locale/") + localeDirName + QStringLiteral("/LC_MESSAGES/@QM_LOADER_CATALOG_NAME@.qm"); +        QString fullPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, subPath); +        if (fullPath.isEmpty()) { +            return false; +        } +        QTranslator *translator = new QTranslator(QCoreApplication::instance()); +        if (!translator->load(fullPath)) { +            delete translator; +            return false; +        } +        QCoreApplication::instance()->installTranslator(translator); +        return true;      } -    QTranslator *translator = new QTranslator(QCoreApplication::instance()); -    if (!translator->load(fullPath)) { -        delete translator; -        return false; + +    void load() +    { +        // The way Qt translation system handles plural forms makes it necessary to +        // have a translation file which contains only plural forms for `en`. That's +        // why we load the `en` translation unconditionally, then load the +        // translation for the current locale to overload it. +        loadTranslation(QStringLiteral("en")); + +        QLocale locale = QLocale::system(); +        if (locale.name() != QStringLiteral("en")) { +            if (!loadTranslation(locale.name())) { +                loadTranslation(locale.bcp47Name()); +            } +        }      } -    QCoreApplication::instance()->installTranslator(translator); -    return true; -} -static void load() -{ -    // The way Qt translation system handles plural forms makes it necessary to -    // have a translation file which contains only plural forms for `en`. That's -    // why we load the `en` translation unconditionally, then load the -    // translation for the current locale to overload it. -    loadTranslation(QStringLiteral("en")); +    // Helper to call load() on the main thread. +    // +    // Calling functions on another thread without using moc is non-trivial in +    // Qt until 5.4 (when some useful QTimer::singleShot overloads were added). +    // +    // Instead, we have to use QEvents. Ideally, we'd use a custom QEvent, but +    // there's a chance this could cause trouble with applications that claim +    // QEvent codes themselves, but don't register them with Qt (and we also +    // want to avoid registering a new QEvent code for every plugin that might +    // be loaded). We use QTimer because it's unlikely to be filtered by +    // applications, and is also unlikely to cause Qt to do something it +    // shouldn't. +    class Loader : public QObject +    { +    protected: +        void timerEvent(QTimerEvent *) Q_DECL_OVERRIDE +        { +            load(); +            this->deleteLater(); +        } +    }; -    QLocale locale = QLocale::system(); -    if (locale.name() != QStringLiteral("en")) { -        if (!loadTranslation(locale.name())) { -            loadTranslation(locale.bcp47Name()); +    void loadOnMainThread() +    { +        // If this library is loaded after the QCoreApplication instance is +        // created (eg: because it is brought in by a plugin), there is no +        // guarantee this function will be called on the main thread. +        // QCoreApplication::installTranslator needs to be called on the main +        // thread, because it uses QCoreApplication::sendEvent. +        if (QThread::currentThread() == QCoreApplication::instance()->thread()) { +            load(); +        } else { +            // QObjects inherit their parent object's thread +            Loader *loader = new Loader(); +            loader->moveToThread(QCoreApplication::instance()->thread()); +            QCoreApplication::instance()->postEvent(loader, new QTimerEvent(0), Qt::HighEventPriority);          }      }  } -Q_COREAPP_STARTUP_FUNCTION(load) +Q_COREAPP_STARTUP_FUNCTION(loadOnMainThread)  | 
