1/* This file is part of the KDE libraries
2 SPDX-FileCopyrightText: 2015 Lukáš Tinkl <ltinkl@redhat.com>
3 SPDX-FileCopyrightText: 2021,2023 Ingo Klöcker <kloecker@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "ki18n_logging.h"
9
10#include <QCoreApplication>
11#include <QFile>
12#include <QLibraryInfo>
13#include <QLocale>
14#include <QThread>
15#include <QTranslator>
16
17#include <memory>
18
19//
20// NOTE when changing anything in here, also check whether ECMQmLoader.cpp.in in ECM
21// needs to be changed as well!
22//
23
24using namespace Qt::Literals;
25
26[[nodiscard]] static QString translationsPath()
27{
28#ifndef Q_OS_ANDROID
29 return QLibraryInfo::path(p: QLibraryInfo::TranslationsPath);
30#else
31 return u"assets://translations/"_s;
32#endif
33}
34
35static bool loadCatalog(QStringView catalog, QStringView language)
36{
37 Q_ASSERT(QThread::currentThread() == QCoreApplication::instance()->thread());
38 const QString fullPath = translationsPath() + '/'_L1 + catalog + language + ".qm"_L1;
39 if (!QFile::exists(fileName: fullPath)) {
40 return false;
41 }
42 auto translator = std::make_unique<QTranslator>(args: QCoreApplication::instance());
43 if (!translator->load(filename: fullPath)) {
44 qCDebug(KI18N) << "Loading catalog failed:" << fullPath;
45 return false;
46 }
47 QCoreApplication::instance()->installTranslator(messageFile: translator.release());
48 return true;
49}
50
51// load global Qt translation, needed in KDE e.g. by lots of builtin dialogs (QColorDialog, QFontDialog) that we use
52static bool loadTranslation(QStringView language)
53{
54 // first, try to load the qt_ meta catalog
55 if (loadCatalog(catalog: u"qt_", language)) {
56 return true;
57 }
58 // if loading the meta catalog failed, then try loading the catalogs
59 // it depends on, i.e. qtbase, qtmultimedia separately
60 const auto catalogs = {
61 u"qtbase_",
62 u"qtmultimedia_",
63 };
64 bool success = false;
65 for (const auto &catalog : catalogs) {
66 success |= loadCatalog(catalog, language);
67 }
68 return success;
69}
70
71static QStringList getSystemLanguages()
72{
73#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
74 // On Windows and Apple OSs, we cannot use QLocale::system() if an application-specific
75 // language was set by kxmlgui because Qt ignores LANGUAGE on Windows and Apple OSs.
76 // The following code is a simplified variant of QSystemLocale::fallbackUiLocale()
77 // (in qlocale_unix.cpp) ignoring LC_ALL, LC_MESSAGES, and LANG.
78 if (const auto languages = qEnvironmentVariable("LANGUAGE").split(':'_L1, Qt::SkipEmptyParts); !languages.isEmpty()) {
79 return languages;
80 }
81#endif
82 return QLocale::system().uiLanguages();
83}
84
85static void load()
86{
87 // The way Qt translation system handles plural forms makes it necessary to
88 // have a translation file which contains only plural forms for `en`. That's
89 // why we load the `en` translation unconditionally, then load the
90 // translation for the current locale to overload it.
91 QMetaObject::invokeMethod(object: QCoreApplication::instance(), function: [] {
92 loadCatalog(catalog: u"qt_", language: u"en");
93
94 auto languages = getSystemLanguages();
95 for (qsizetype i = 0; i < languages.size(); ++i) {
96 // normalize into the format used in Qt catalog suffixes
97 languages[i].replace(before: '-'_L1, after: '_'_L1);
98 // make sure we always also have the generic language variant
99 // depending on the platform that might not be in uiLanguages by default
100 // insert that after the last country-specific entry for the same language
101 const auto idx = languages[i].indexOf(ch: '_'_L1);
102 if (idx > 0) {
103 const QString genericLang = languages[i].left(n: idx);
104 qsizetype j = i + 1;
105 for (; j < languages.size(); ++j) {
106 if (!languages[j].startsWith(s: genericLang)) {
107 break;
108 }
109 }
110 if (languages[j - 1] != genericLang) {
111 languages.insert(i: j, t: genericLang);
112 }
113 }
114 }
115 languages.removeDuplicates();
116 for (const auto &language : std::as_const(t&: languages)) {
117 if (language == "en"_L1 || loadTranslation(language)) {
118 break;
119 }
120 }
121 });
122}
123
124Q_COREAPP_STARTUP_FUNCTION(load)
125

source code of ki18n/src/i18n/main.cpp