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
31Q_LOGGING_CATEGORY(CUTELEE_LOCALIZER, "cutelee.localizer")
32
33struct 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
48namespace 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
90using namespace Cutelee;
91
92static 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
117QString 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
156QtLocalizer::QtLocalizer(const QLocale &locale)
157 : AbstractLocalizer(), d_ptr(new QtLocalizerPrivate(this, locale))
158{
159}
160
161QtLocalizer::~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
175void 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
201QString 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
236static 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
300void 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
333void 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