KSeExpr  4.0.4.0
ECMQmLoader-seexpr2_qt.cpp
Go to the documentation of this file.
1 /* This file was generated by ecm_create_qm_loader().
2  * It was edited for KSeExpr.
3  *
4  * Building this file in a library ensures translations are automatically loaded
5  * when an application makes use of the library.
6  *
7  *
8  * SPDX-FileCopyrightText: 2014 Aurélien Gâteau <agateau@kde.org>
9  * SPDX-FileCopyrightText: 2015 Alex Merry <alex.merry@kde.org>
10  * SPDX-FileCopyrightText: 2020-2021 L. E. Segovia <amy@amyspark.me>
11  *
12  * SPDX-License-Identifier: BSD-3-Clause
13  */
14 #include <Debug.h>
15 
16 #include <QCoreApplication>
17 #include <QDir>
18 #include <QLocale>
19 #include <QObject>
20 #include <QStandardPaths>
21 #include <QThread>
22 #include <QTranslator>
23 
24 #if defined(KSeExpr_HAVE_I18N_FALLBACK_LANGUAGES_DETECTION)
25 #include <KLocalizedString>
26 #include <algorithm>
27 #endif
28 
29 namespace KSeExpr {
30 
31  bool loadTranslation(const QString &localeDirName)
32  {
33  QString subPath = QStringLiteral("locale/") + localeDirName + QStringLiteral("/LC_MESSAGES/seexpr2_qt.qm");
34 
35  dbgSeExpr << "Attempting to load: " << subPath;
36 
37 #if defined(Q_OS_ANDROID)
38  const QString fullPath = QStringLiteral("assets:/share/") + subPath;
39  if (!QFile::exists(fullPath)) {
40  return false;
41  }
42 #else
43  // Use application's own data directory (e.g. Krita on AppImage, Windows)
44  QString fullPath = QStandardPaths::locate(QStandardPaths::DataLocation, subPath);
45  if (fullPath.isEmpty()) {
46  // Try falling back to stock folder
47  fullPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, subPath);
48  if (fullPath.isEmpty())
49  return false;
50  }
51 #endif
52  QTranslator *translator = new QTranslator(QCoreApplication::instance());
53  if (!translator->load(fullPath)) {
54  delete translator;
55  return false;
56  }
57 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
58  dbgSeExpr << "Installing translation for: " << fullPath << "(" << translator->language() << ")";
59 #else
60  dbgSeExpr << "Installing translation for: " << fullPath;
61 #endif
62  dbgSeExpr << "Test: " << translator->translate("ExprControlCollection", "Add new variable");
63 
64  QCoreApplication::instance()->installTranslator(translator);
65  return true;
66  }
67 
68  void load()
69  {
70 #if defined(Q_OS_ANDROID)
71  const auto paths = QStringLiteral("assets:/share/");
72 #else
73  auto paths = QStandardPaths::standardLocations(QStandardPaths::DataLocation);
74  paths << QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
75 #endif
76  dbgSeExpr << "Base paths for translations: " << paths;
77 
78 #if defined(KSeExpr_HAVE_I18N_FALLBACK_LANGUAGES_DETECTION)
79  dbgSeExpr << "Qt UI languages: " << KLocalizedString::languages() << QLocale().uiLanguages() << qgetenv("LANG");
80 #else
81  dbgSeExpr << "Qt UI languages: " << QLocale().uiLanguages() << qgetenv("LANG");
82 #endif
83 
84  // The way Qt translation system handles plural forms makes it necessary to
85  // have a translation file which contains only plural forms for `en`. That's
86  // why we load the `en` translation unconditionally, then load the
87  // translation for the current locale to overload it.
88  loadTranslation(QStringLiteral("en"));
89 
90  // Use the default locale and any available fallback languages.
91  // Fallback languages must be reverse iterated in order of priority.
92 #if defined(KSeExpr_HAVE_I18N_FALLBACK_LANGUAGES_DETECTION)
93  const auto fallbackLanguages { KLocalizedString::languages()};
94  for (auto it = fallbackLanguages.rbegin(); it != fallbackLanguages.rend(); ++it) {
95  if(!loadTranslation(*it)) {
96  const auto localeParts = it->split('@');
97  QLocale locale(localeParts.constFirst());
98  if (!loadTranslation(locale.name())) {
99  if (!loadTranslation(locale.bcp47Name())) {
100  const int i = locale.name().indexOf(QLatin1Char('_'));
101  if (i > 0) {
102  loadTranslation(locale.name().left(i));
103  }
104  }
105  }
106  }
107  }
108 #else
109  for (const auto &locale : { QLocale() }) {
110  dbgSeExpr << "Attempting to load translations for locale: " << locale.name();
111  if (!loadTranslation(locale.name())) {
112  if (!loadTranslation(locale.bcp47Name())) {
113  const int i = locale.name().indexOf(QLatin1Char('_'));
114  if (i > 0) {
115  loadTranslation(locale.name().left(i));
116  }
117  }
118  }
119  }
120 #endif
121 
122  dbgSeExpr << "Test: " << QCoreApplication::translate("ExprControlCollection", "Add new variable");
123  }
124 
125  // Helper to call load() on the main thread.
126  //
127  // Calling functions on another thread without using moc is non-trivial in
128  // Qt until 5.4 (when some useful QTimer::singleShot overloads were added).
129  //
130  // Instead, we have to use QEvents. Ideally, we'd use a custom QEvent, but
131  // there's a chance this could cause trouble with applications that claim
132  // QEvent codes themselves, but don't register them with Qt (and we also
133  // want to avoid registering a new QEvent code for every plugin that might
134  // be loaded). We use QTimer because it's unlikely to be filtered by
135  // applications, and is also unlikely to cause Qt to do something it
136  // shouldn't.
137  class Loader : public QObject
138  {
139  protected:
140  void timerEvent(QTimerEvent *) Q_DECL_OVERRIDE
141  {
142  load();
143  this->deleteLater();
144  }
145  };
146 
148  {
149  // If this library is loaded after the QCoreApplication instance is
150  // created (eg: because it is brought in by a plugin), there is no
151  // guarantee this function will be called on the main thread.
152  // QCoreApplication::installTranslator needs to be called on the main
153  // thread, because it uses QCoreApplication::sendEvent.
154  if (QThread::currentThread() == QCoreApplication::instance()->thread()) {
155  load();
156  } else {
157  // QObjects inherit their parent object's thread
158  Loader *loader = new Loader();
159  loader->moveToThread(QCoreApplication::instance()->thread());
160  QCoreApplication::instance()->postEvent(loader, new QTimerEvent(0), Qt::HighEventPriority);
161  }
162  }
163 
164  Q_COREAPP_STARTUP_FUNCTION(loadOnMainThread)
165 } // namespace KSeExpr
#define dbgSeExpr
Definition: Debug.h:17
void timerEvent(QTimerEvent *) Q_DECL_OVERRIDE
bool loadTranslation(const QString &localeDirName)