KTextTemplate

qtlocalizer.cpp
1/*
2 This file is part of the KTextTemplate library
3
4 SPDX-FileCopyrightText: 2010 Stephen Kelly <steveire@gmail.com>
5
6 SPDX-License-Identifier: LGPL-2.1-or-later
7
8*/
9
10#include "qtlocalizer.h"
11
12#include <QCoreApplication>
13#include <QDateTime>
14#include <QLibraryInfo>
15#include <QList>
16#include <QTranslator>
17
18#include <QLoggingCategory>
19
20Q_LOGGING_CATEGORY(KTEXTTEMPLATE_LOCALIZER, "kf.texttemplate.qtlocalizer")
21
22struct Locale {
23 explicit Locale(const QLocale &_locale)
24 : locale(_locale)
25 {
26 }
27
28 ~Locale()
29 {
30 qDeleteAll(systemTranslators);
31 qDeleteAll(themeTranslators);
32 }
33
34 const QLocale locale;
35 QList<QTranslator *> externalSystemTranslators; // Not owned by us!
36 QList<QTranslator *> systemTranslators;
37 QList<QTranslator *> themeTranslators;
38};
39
40namespace KTextTemplate
41{
42
43class QtLocalizerPrivate
44{
45 QtLocalizerPrivate(QtLocalizer *qq, const QLocale &locale)
46 : q_ptr(qq)
47 {
48 auto localeStruct = new Locale(locale);
49 m_availableLocales.insert(locale.name(), localeStruct);
50 m_locales.push_back(localeStruct);
51 }
52
53 ~QtLocalizerPrivate()
54 {
55 m_locales.clear();
56 qDeleteAll(m_availableLocales);
57 }
58
59 QLocale currentLocale() const
60 {
61 Q_ASSERT(!m_locales.isEmpty());
62 if (m_locales.isEmpty()) {
63 qCWarning(KTEXTTEMPLATE_LOCALIZER) << "Invalid Locale";
64 return {};
65 }
66 return m_locales.last()->locale;
67 }
68
69 Q_DECLARE_PUBLIC(QtLocalizer)
70 QtLocalizer *const q_ptr;
71
72 QString translate(const QString &input, const QString &context, int count = -1) const;
73
74 QHash<QString, Locale *> m_availableLocales;
75
76 QList<Locale *> m_locales;
77 QString m_appTranslatorPath;
78 QString m_appTranslatorPrefix;
79};
80}
81
82using namespace KTextTemplate;
83
84static void replacePercentN(QString *result, int n)
85{
86 if (n >= 0) {
87 auto percentPos = 0;
88 auto len = 0;
89 while ((percentPos = result->indexOf(QLatin1Char('%'), percentPos + len)) != -1) {
90 len = 1;
91 QString fmt;
92 if (result->at(percentPos + len) == QLatin1Char('L')) {
93 ++len;
94 fmt = QStringLiteral("%L1");
95 } else {
96 fmt = QStringLiteral("%1");
97 }
98 if (result->at(percentPos + len) == QLatin1Char('n')) {
99 fmt = fmt.arg(n);
100 ++len;
101 result->replace(percentPos, len, fmt);
102 len = fmt.length();
103 }
104 }
105 }
106}
107
108QString QtLocalizerPrivate::translate(const QString &input, const QString &context, int count) const
109{
110 QString result;
111
112 if (m_locales.isEmpty()) {
113 result = input;
114 replacePercentN(&result, count);
115 return result;
116 }
117
118 auto locale = m_locales.last();
119 for (QTranslator *translator : std::as_const(locale->themeTranslators)) {
120 result = translator->translate("GR_FILENAME", input.toUtf8().constData(), context.toUtf8().constData(), count);
121 }
122 if (result.isEmpty()) {
123 auto translators = locale->externalSystemTranslators + locale->systemTranslators;
124 if (translators.isEmpty())
125 return QCoreApplication::translate("GR_FILENAME", input.toUtf8().constData(), context.toUtf8().constData(), count);
126 for (QTranslator *translator : std::as_const(translators)) {
127 result = translator->translate("GR_FILENAME", input.toUtf8().constData(), context.toUtf8().constData(), count);
128 if (!result.isEmpty())
129 break;
130 }
131 }
132 if (!result.isEmpty()) {
133 replacePercentN(&result, count);
134 return result;
135 }
136 auto fallback = input;
137 replacePercentN(&fallback, count);
138 return fallback;
139}
140
142 : d_ptr(new QtLocalizerPrivate(this, locale))
143{
144}
145
147{
148 delete d_ptr;
149}
150
152{
154 d->m_appTranslatorPath = path;
155}
156
158{
160 d->m_appTranslatorPrefix = prefix;
161}
162
163void QtLocalizer::installTranslator(QTranslator *translator, const QString &localeName)
164{
166 if (!d->m_availableLocales.contains(localeName)) {
167 const QLocale namedLocale(localeName);
168 d->m_availableLocales.insert(localeName, new Locale(namedLocale));
169 }
170 d->m_availableLocales[localeName]->externalSystemTranslators.prepend(translator);
171}
172
174{
175 Q_D(const QtLocalizer);
176 return d->currentLocale().toString(date, formatType);
177}
178
180{
181 Q_D(const QtLocalizer);
182 return d->currentLocale().toString(time, formatType);
183}
184
186{
187 Q_D(const QtLocalizer);
188 return d->currentLocale().toString(dateTime, formatType);
189}
190
192{
193 Q_D(const QtLocalizer);
194 return d->currentLocale().toString(number);
195}
196
198{
199 Q_D(const QtLocalizer);
200 return d->currentLocale().toString(number, 'f', 2);
201}
202
203QString QtLocalizer::localizeMonetaryValue(qreal value, const QString &currencyCode) const
204{
205 Q_D(const QtLocalizer);
206 auto currencySymbol = QStringLiteral("$");
207 if (currencyCode == QStringLiteral("EUR")) {
208 currencySymbol = QChar(0x20AC);
209 } else if (currencyCode == QStringLiteral("GBP")) {
210 currencySymbol = QStringLiteral("£");
211 } else {
212 currencySymbol = currencyCode;
213 }
214 return currencySymbol + QLatin1Char(' ') + d->currentLocale().toString(value, 'f', 2);
215}
216
217static QString substituteArguments(const QString &input, const QVariantList &arguments)
218{
219 auto string = input;
220 for (const QVariant &arg : arguments) {
221 if (arg.userType() == qMetaTypeId<int>())
222 string = string.arg(arg.value<int>());
223 else if (arg.userType() == qMetaTypeId<double>())
224 string = string.arg(arg.value<double>());
225 else if (arg.userType() == qMetaTypeId<QDateTime>())
226 string = string.arg(arg.value<QDateTime>().toString());
227 else
228 string = string.arg(arg.value<QString>());
229 }
230 return string;
231}
232
233QString QtLocalizer::localizeContextString(const QString &string, const QString &context, const QVariantList &arguments) const
234{
235 Q_D(const QtLocalizer);
236 const auto translated = d->translate(string, context);
237 return substituteArguments(translated, arguments);
238}
239
240QString QtLocalizer::localizeString(const QString &string, const QVariantList &arguments) const
241{
242 Q_D(const QtLocalizer);
243 const auto translated = d->translate(string, QString());
244 return substituteArguments(translated, arguments);
245}
246
247QString QtLocalizer::localizePluralContextString(const QString &string, const QString &pluralForm, const QString &context, const QVariantList &_arguments) const
248{
249 Q_UNUSED(pluralForm)
250 Q_D(const QtLocalizer);
251 auto arguments = _arguments;
252 const auto N = arguments.takeFirst().toInt();
253 const auto translated = d->translate(string, context, N);
254 return substituteArguments(translated, arguments);
255}
256
257QString QtLocalizer::localizePluralString(const QString &string, const QString &pluralForm, const QVariantList &_arguments) const
258{
259 Q_UNUSED(pluralForm)
260 Q_D(const QtLocalizer);
261 auto arguments = _arguments;
262 const auto N = arguments.takeFirst().toInt();
263 const auto translated = d->translate(string, QString(), N);
264 return substituteArguments(translated, arguments);
265}
266
268{
269 Q_D(const QtLocalizer);
270 return d->currentLocale().name();
271}
272
273void QtLocalizer::pushLocale(const QString &localeName)
274{
276 Locale *localeStruct = nullptr;
277 if (!d->m_availableLocales.contains(localeName)) {
278 localeStruct = new Locale(QLocale(localeName));
279 auto qtTranslator = new QTranslator;
280 (void)qtTranslator->load(QStringLiteral("qt_") + localeName, QLibraryInfo::path(QLibraryInfo::TranslationsPath));
281 localeStruct->systemTranslators.append(qtTranslator);
282 auto appTranslator = new QTranslator;
283 (void)appTranslator->load(d->m_appTranslatorPrefix + localeName, d->m_appTranslatorPath);
284 localeStruct->systemTranslators.append(appTranslator);
285 d->m_availableLocales.insert(localeName, localeStruct);
286 } else {
287 localeStruct = d->m_availableLocales[localeName];
288 }
289 Q_ASSERT(localeStruct);
290 d->m_locales.push_back(localeStruct);
291}
292
294{
296 Q_ASSERT(!d->m_locales.isEmpty());
297 d->m_locales.takeLast();
298}
299
300void QtLocalizer::loadCatalog(const QString &path, const QString &catalog)
301{
303 auto it = d->m_availableLocales.constBegin();
304 const auto end = d->m_availableLocales.constEnd();
305 for (; it != end; ++it) {
306 auto translator = new QTranslator();
307 const auto loaded = translator->load(it.key() + QLatin1Char('/') + catalog, path);
308 if (!loaded)
309 continue;
310
311 translator->setObjectName(catalog);
312
313 it.value()->themeTranslators.prepend(translator);
314 }
315}
316
318{
320 auto it = d->m_availableLocales.constBegin();
321 const auto end = d->m_availableLocales.constEnd();
322 for (; it != end; ++it) {
323 auto tranIt = (*it)->themeTranslators.begin();
324 while (tranIt != (*it)->themeTranslators.end()) {
325 if ((*tranIt)->objectName() == catalog) {
326 delete *tranIt;
327 tranIt = (*it)->themeTranslators.erase(tranIt);
328 } else {
329 ++tranIt;
330 }
331 }
332 }
333}
Provides internationalization based on QLocale and QTranslator.
Definition qtlocalizer.h:47
QString localizePluralString(const QString &string, const QString &pluralForm, const QVariantList &arguments={}) const override
Localizes string or its pluralForm with the specified arguments for substitution.
QString localizeDate(const QDate &date, QLocale::FormatType formatType=QLocale::ShortFormat) const override
Localizes date with the specified formatType.
void installTranslator(QTranslator *translator, const QString &localeName=QLocale::system().name())
Install a translator to use for a particular localeName.
QString localizeContextString(const QString &string, const QString &context, const QVariantList &arguments={}) const override
Localizes string, disambiguated by context with the specified arguments for substitution.
QString currentLocale() const override
Returns the current locale as a string.
void setAppTranslatorPath(const QString &path)
Set the path to look for translations of the application strings.
QString localizeNumber(int number) const override
Localizes number.
QString localizeMonetaryValue(qreal value, const QString &currencyCode={}) const override
Localizes value as a monetary value in the currency specified by currencyCode.
void popLocale() override
Makes this localizer use the previous locale for output.
void loadCatalog(const QString &path, const QString &catalog) override
Loads the catalog from path.
QString localizeTime(const QTime &time, QLocale::FormatType formatType=QLocale::ShortFormat) const override
Localizes time with the specified formatType.
QString localizeDateTime(const QDateTime &dateTime, QLocale::FormatType formatType=QLocale::ShortFormat) const override
Localizes dateTime with the specified formatType.
~QtLocalizer() override
Destructor.
QtLocalizer(const QLocale &locale=QLocale::system())
Constructs a new QtLocalizer using the locale.
QString localizePluralContextString(const QString &string, const QString &pluralForm, const QString &context, const QVariantList &arguments={}) const override
Localizes string or its pluralForm, disambiguated by context with the specified arguments for substit...
void pushLocale(const QString &localeName) override
Makes this localizer use the locale specified by localeName for output.
QString localizeString(const QString &string, const QVariantList &arguments={}) const override
Localizes string with the specified arguments for substitution.
void setAppTranslatorPrefix(const QString &prefix)
Set the prefix of translation files.
void unloadCatalog(const QString &catalog) override
Unloads the catalog.
The KTextTemplate namespace holds all public KTextTemplate API.
Definition Mainpage.dox:8
const char * constData() const const
QString translate(const char *context, const char *sourceText, const char *disambiguation, int n)
QString toString(QStringView format, QCalendar cal) const const
iterator insert(const Key &key, const T &value)
QString path(LibraryPath p)
void append(QList< T > &&value)
void clear()
bool isEmpty() const const
T & last()
void push_back(parameter_type value)
QString name() const const
QString arg(Args &&... args) const const
const QChar at(qsizetype position) const const
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
qsizetype length() const const
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QByteArray toUtf8() const const
bool load(const QLocale &locale, const QString &filename, const QString &prefix, const QString &directory, const QString &suffix)
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:19:42 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.