Cutelee  6.1.0
qtlocalizer.cpp
1 /*
2  This file is part of the Cutelee template system.
3 
4  Copyright (c) 2010 Stephen Kelly <steveire@gmail.com>
5 
6  This library is free software; you can redistribute it and/or
7  modify it under the terms of the GNU Lesser General Public
8  License as published by the Free Software Foundation; either version
9  2.1 of the Licence, or (at your option) any later version.
10 
11  This library is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  Lesser General Public License for more details.
15 
16  You should have received a copy of the GNU Lesser General Public
17  License along with this library. If not, see <http://www.gnu.org/licenses/>.
18 
19 */
20 
21 #include "qtlocalizer.h"
22 
23 #include <QtCore/QCoreApplication>
24 #include <QtCore/QDateTime>
25 #include <QtCore/QLibraryInfo>
26 #include <QtCore/QTranslator>
27 #include <QtCore/QVector>
28 
29 #include <QtCore/QLoggingCategory>
30 
31 Q_LOGGING_CATEGORY(CUTELEE_LOCALIZER, "cutelee.localizer")
32 
33 struct Locale {
34  explicit Locale(const QLocale &_locale) : locale(_locale) {}
35 
36  ~Locale()
37  {
38  qDeleteAll(systemTranslators);
39  qDeleteAll(themeTranslators);
40  }
41 
42  const QLocale locale;
43  QVector<QTranslator *> externalSystemTranslators; // Not owned by us!
44  QVector<QTranslator *> systemTranslators;
45  QVector<QTranslator *> themeTranslators;
46 };
47 
48 namespace Cutelee
49 {
50 
52 {
53  QtLocalizerPrivate(QtLocalizer *qq, const QLocale &locale) : q_ptr(qq)
54  {
55  auto localeStruct = new Locale(locale);
56  m_availableLocales.insert(locale.name(), localeStruct);
57  m_locales.push_back(localeStruct);
58  }
59 
61  {
62  m_locales.clear();
63  qDeleteAll(m_availableLocales);
64  }
65 
66  QLocale currentLocale() const
67  {
68  Q_ASSERT(!m_locales.isEmpty());
69  if (m_locales.isEmpty()) {
70  qCWarning(CUTELEE_LOCALIZER) << "Invalid Locale";
71  return QLocale();
72  }
73  return m_locales.last()->locale;
74  }
75 
76  Q_DECLARE_PUBLIC(QtLocalizer)
77  QtLocalizer *const q_ptr;
78 
79  QString translate(const QString &input, const QString &context,
80  int count = -1) const;
81 
82  QHash<QString, Locale *> m_availableLocales;
83 
84  QList<Locale *> m_locales;
85  QString m_appTranslatorPath;
86  QString m_appTranslatorPrefix;
87 };
88 }
89 
90 using namespace Cutelee;
91 
92 static void replacePercentN(QString *result, int n)
93 {
94  if (n >= 0) {
95  auto percentPos = 0;
96  auto len = 0;
97  while ((percentPos = result->indexOf(QLatin1Char('%'), percentPos + len))
98  != -1) {
99  len = 1;
100  QString fmt;
101  if (result->at(percentPos + len) == QLatin1Char('L')) {
102  ++len;
103  fmt = QStringLiteral("%L1");
104  } else {
105  fmt = QStringLiteral("%1");
106  }
107  if (result->at(percentPos + len) == QLatin1Char('n')) {
108  fmt = fmt.arg(n);
109  ++len;
110  result->replace(percentPos, len, fmt);
111  len = fmt.length();
112  }
113  }
114  }
115 }
116 
117 QString QtLocalizerPrivate::translate(const QString &input,
118  const QString &context, int count) const
119 {
120  QString result;
121 
122  if (m_locales.isEmpty()) {
123  result = input;
124  replacePercentN(&result, count);
125  return result;
126  }
127 
128  auto locale = m_locales.last();
129  Q_FOREACH (QTranslator *translator, locale->themeTranslators) {
130  result = translator->translate("GR_FILENAME", input.toUtf8().constData(),
131  context.toUtf8().constData(), count);
132  }
133  if (result.isEmpty()) {
134  auto translators
135  = locale->externalSystemTranslators + locale->systemTranslators;
136  if (translators.isEmpty())
137  return QCoreApplication::translate("GR_FILENAME",
138  input.toUtf8().constData(),
139  context.toUtf8().constData(), count);
140  Q_FOREACH (QTranslator *translator, translators) {
141  result = translator->translate("GR_FILENAME", input.toUtf8().constData(),
142  context.toUtf8().constData(), count);
143  if (!result.isEmpty())
144  break;
145  }
146  }
147  if (!result.isEmpty()) {
148  replacePercentN(&result, count);
149  return result;
150  }
151  auto fallback = input;
152  replacePercentN(&fallback, count);
153  return fallback;
154 }
155 
156 QtLocalizer::QtLocalizer(const QLocale &locale)
157  : AbstractLocalizer(), d_ptr(new QtLocalizerPrivate(this, locale))
158 {
159 }
160 
161 QtLocalizer::~QtLocalizer() { delete d_ptr; }
162 
164 {
165  Q_D(QtLocalizer);
166  d->m_appTranslatorPath = path;
167 }
168 
170 {
171  Q_D(QtLocalizer);
172  d->m_appTranslatorPrefix = prefix;
173 }
174 
175 void QtLocalizer::installTranslator(QTranslator *translator,
176  const QString &localeName)
177 {
178  Q_D(QtLocalizer);
179  if (!d->m_availableLocales.contains(localeName)) {
180  const QLocale namedLocale(localeName);
181  d->m_availableLocales.insert(localeName, new Locale(namedLocale));
182  }
183  d->m_availableLocales[localeName]->externalSystemTranslators.prepend(
184  translator);
185 }
186 
188  QLocale::FormatType formatType) const
189 {
190  Q_D(const QtLocalizer);
191  return d->currentLocale().toString(date, formatType);
192 }
193 
195  QLocale::FormatType formatType) const
196 {
197  Q_D(const QtLocalizer);
198  return d->currentLocale().toString(time, formatType);
199 }
200 
201 QString QtLocalizer::localizeDateTime(const QDateTime &dateTime,
202  QLocale::FormatType formatType) const
203 {
204  Q_D(const QtLocalizer);
205  return d->currentLocale().toString(dateTime, formatType);
206 }
207 
209 {
210  Q_D(const QtLocalizer);
211  return d->currentLocale().toString(number);
212 }
213 
215 {
216  Q_D(const QtLocalizer);
217  return d->currentLocale().toString(number, 'f', 2);
218 }
219 
221  const QString &currencyCode) const
222 {
223  Q_D(const QtLocalizer);
224  auto currencySymbol = QStringLiteral("$");
225  if (currencyCode == QStringLiteral("EUR")) {
226  currencySymbol = QChar(0x20AC);
227  } else if (currencyCode == QStringLiteral("GBP")) {
228  currencySymbol = QStringLiteral("£");
229  } else {
230  currencySymbol = currencyCode;
231  }
232  return currencySymbol + QLatin1Char(' ')
233  + d->currentLocale().toString(value, 'f', 2);
234 }
235 
236 static QString substituteArguments(const QString &input,
237  const QVariantList &arguments)
238 {
239  auto string = input;
240  Q_FOREACH (const QVariant &arg, arguments) {
241  if (arg.userType() == qMetaTypeId<int>())
242  string = string.arg(arg.value<int>());
243  else if (arg.userType() == qMetaTypeId<double>())
244  string = string.arg(arg.value<double>());
245  else if (arg.userType() == qMetaTypeId<QDateTime>())
246  string = string.arg(arg.toDateTime().toString());
247  else
248  string = string.arg(arg.toString());
249  }
250  return string;
251 }
252 
254  const QString &context,
255  const QVariantList &arguments) const
256 {
257  Q_D(const QtLocalizer);
258  const auto translated = d->translate(string, context);
259  return substituteArguments(translated, arguments);
260 }
261 
263  const QVariantList &arguments) const
264 {
265  Q_D(const QtLocalizer);
266  const auto translated = d->translate(string, QString());
267  return substituteArguments(translated, arguments);
268 }
269 
271  const QString &string, const QString &pluralForm, const QString &context,
272  const QVariantList &_arguments) const
273 {
274  Q_UNUSED(pluralForm)
275  Q_D(const QtLocalizer);
276  auto arguments = _arguments;
277  const auto N = arguments.takeFirst().toInt();
278  const auto translated = d->translate(string, context, N);
279  return substituteArguments(translated, arguments);
280 }
281 
283  const QString &pluralForm,
284  const QVariantList &_arguments) const
285 {
286  Q_UNUSED(pluralForm)
287  Q_D(const QtLocalizer);
288  auto arguments = _arguments;
289  const auto N = arguments.takeFirst().toInt();
290  const auto translated = d->translate(string, QString(), N);
291  return substituteArguments(translated, arguments);
292 }
293 
295 {
296  Q_D(const QtLocalizer);
297  return d->currentLocale().name();
298 }
299 
300 void QtLocalizer::pushLocale(const QString &localeName)
301 {
302  Q_D(QtLocalizer);
303  Locale *localeStruct = 0;
304  if (!d->m_availableLocales.contains(localeName)) {
305  localeStruct = new Locale(QLocale(localeName));
306  auto qtTranslator = new QTranslator;
307  qtTranslator->load(QStringLiteral("qt_") + localeName,
308 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
309  QLibraryInfo::location(QLibraryInfo::TranslationsPath));
310 #else
311  QLibraryInfo::path(QLibraryInfo::TranslationsPath));
312 #endif
313  localeStruct->systemTranslators.append(qtTranslator);
314  auto appTranslator = new QTranslator;
315  appTranslator->load(d->m_appTranslatorPrefix + localeName,
316  d->m_appTranslatorPath);
317  localeStruct->systemTranslators.append(appTranslator);
318  d->m_availableLocales.insert(localeName, localeStruct);
319  } else {
320  localeStruct = d->m_availableLocales[localeName];
321  }
322  Q_ASSERT(localeStruct);
323  d->m_locales.push_back(localeStruct);
324 }
325 
327 {
328  Q_D(QtLocalizer);
329  Q_ASSERT(!d->m_locales.isEmpty());
330  d->m_locales.takeLast();
331 }
332 
333 void QtLocalizer::loadCatalog(const QString &path, const QString &catalog)
334 {
335  Q_D(QtLocalizer);
336  auto it = d->m_availableLocales.constBegin();
337  const auto end = d->m_availableLocales.constEnd();
338  for (; it != end; ++it) {
339  auto translator = new QTranslator();
340  const auto loaded
341  = translator->load(it.key() + QLatin1Char('/') + catalog, path);
342  if (!loaded)
343  continue;
344 
345  translator->setObjectName(catalog);
346 
347  it.value()->themeTranslators.prepend(translator);
348  }
349 }
350 
352 {
353  Q_D(QtLocalizer);
354  auto it = d->m_availableLocales.constBegin();
355  const auto end = d->m_availableLocales.constEnd();
356  for (; it != end; ++it) {
357  auto tranIt = (*it)->themeTranslators.begin();
358  while (tranIt != (*it)->themeTranslators.end()) {
359  if ((*tranIt)->objectName() == catalog) {
360  delete *tranIt;
361  tranIt = (*it)->themeTranslators.erase(tranIt);
362  } else {
363  ++tranIt;
364  }
365  }
366  }
367 }
Interface for implementing an internationalization system.
Provides internationalization based on QLocale and QTranslator.
Definition: qtlocalizer.h:56
QString localizePluralString(const QString &string, const QString &pluralForm, const QVariantList &arguments={}) const override
QString localizeDate(const QDate &date, QLocale::FormatType formatType=QLocale::ShortFormat) const override
void installTranslator(QTranslator *translator, const QString &localeName=QLocale::system().name())
QString localizeContextString(const QString &string, const QString &context, const QVariantList &arguments={}) const override
QString currentLocale() const override
void setAppTranslatorPath(const QString &path)
QString localizeNumber(int number) const override
QString localizeMonetaryValue(qreal value, const QString &currencyCode={}) const override
void popLocale() override
void loadCatalog(const QString &path, const QString &catalog) override
QString localizeTime(const QTime &time, QLocale::FormatType formatType=QLocale::ShortFormat) const override
QString localizeDateTime(const QDateTime &dateTime, QLocale::FormatType formatType=QLocale::ShortFormat) const override
QtLocalizer(const QLocale &locale=QLocale::system())
QString localizePluralContextString(const QString &string, const QString &pluralForm, const QString &context, const QVariantList &arguments={}) const override
void pushLocale(const QString &localeName) override
QString localizeString(const QString &string, const QVariantList &arguments={}) const override
void setAppTranslatorPrefix(const QString &prefix)
void unloadCatalog(const QString &catalog) override
The Cutelee namespace holds all public Cutelee API.
Definition: Mainpage.dox:8