File indexing completed on 2025-02-09 04:28:39
0001 /* 0002 This file is part of the KTextTemplate library 0003 0004 SPDX-FileCopyrightText: 2010 Stephen Kelly <steveire@gmail.com> 0005 0006 SPDX-License-Identifier: LGPL-2.1-or-later 0007 0008 */ 0009 0010 #include "qtlocalizer.h" 0011 0012 #include <QCoreApplication> 0013 #include <QDateTime> 0014 #include <QLibraryInfo> 0015 #include <QList> 0016 #include <QTranslator> 0017 0018 #include <QLoggingCategory> 0019 0020 Q_LOGGING_CATEGORY(KTEXTTEMPLATE_LOCALIZER, "kf.texttemplate.qtlocalizer") 0021 0022 struct Locale { 0023 explicit Locale(const QLocale &_locale) 0024 : locale(_locale) 0025 { 0026 } 0027 0028 ~Locale() 0029 { 0030 qDeleteAll(systemTranslators); 0031 qDeleteAll(themeTranslators); 0032 } 0033 0034 const QLocale locale; 0035 QList<QTranslator *> externalSystemTranslators; // Not owned by us! 0036 QList<QTranslator *> systemTranslators; 0037 QList<QTranslator *> themeTranslators; 0038 }; 0039 0040 namespace KTextTemplate 0041 { 0042 0043 class QtLocalizerPrivate 0044 { 0045 QtLocalizerPrivate(QtLocalizer *qq, const QLocale &locale) 0046 : q_ptr(qq) 0047 { 0048 auto localeStruct = new Locale(locale); 0049 m_availableLocales.insert(locale.name(), localeStruct); 0050 m_locales.push_back(localeStruct); 0051 } 0052 0053 ~QtLocalizerPrivate() 0054 { 0055 m_locales.clear(); 0056 qDeleteAll(m_availableLocales); 0057 } 0058 0059 QLocale currentLocale() const 0060 { 0061 Q_ASSERT(!m_locales.isEmpty()); 0062 if (m_locales.isEmpty()) { 0063 qCWarning(KTEXTTEMPLATE_LOCALIZER) << "Invalid Locale"; 0064 return {}; 0065 } 0066 return m_locales.last()->locale; 0067 } 0068 0069 Q_DECLARE_PUBLIC(QtLocalizer) 0070 QtLocalizer *const q_ptr; 0071 0072 QString translate(const QString &input, const QString &context, int count = -1) const; 0073 0074 QHash<QString, Locale *> m_availableLocales; 0075 0076 QList<Locale *> m_locales; 0077 QString m_appTranslatorPath; 0078 QString m_appTranslatorPrefix; 0079 }; 0080 } 0081 0082 using namespace KTextTemplate; 0083 0084 static void replacePercentN(QString *result, int n) 0085 { 0086 if (n >= 0) { 0087 auto percentPos = 0; 0088 auto len = 0; 0089 while ((percentPos = result->indexOf(QLatin1Char('%'), percentPos + len)) != -1) { 0090 len = 1; 0091 QString fmt; 0092 if (result->at(percentPos + len) == QLatin1Char('L')) { 0093 ++len; 0094 fmt = QStringLiteral("%L1"); 0095 } else { 0096 fmt = QStringLiteral("%1"); 0097 } 0098 if (result->at(percentPos + len) == QLatin1Char('n')) { 0099 fmt = fmt.arg(n); 0100 ++len; 0101 result->replace(percentPos, len, fmt); 0102 len = fmt.length(); 0103 } 0104 } 0105 } 0106 } 0107 0108 QString QtLocalizerPrivate::translate(const QString &input, const QString &context, int count) const 0109 { 0110 QString result; 0111 0112 if (m_locales.isEmpty()) { 0113 result = input; 0114 replacePercentN(&result, count); 0115 return result; 0116 } 0117 0118 auto locale = m_locales.last(); 0119 for (QTranslator *translator : std::as_const(locale->themeTranslators)) { 0120 result = translator->translate("GR_FILENAME", input.toUtf8().constData(), context.toUtf8().constData(), count); 0121 } 0122 if (result.isEmpty()) { 0123 auto translators = locale->externalSystemTranslators + locale->systemTranslators; 0124 if (translators.isEmpty()) 0125 return QCoreApplication::translate("GR_FILENAME", input.toUtf8().constData(), context.toUtf8().constData(), count); 0126 for (QTranslator *translator : std::as_const(translators)) { 0127 result = translator->translate("GR_FILENAME", input.toUtf8().constData(), context.toUtf8().constData(), count); 0128 if (!result.isEmpty()) 0129 break; 0130 } 0131 } 0132 if (!result.isEmpty()) { 0133 replacePercentN(&result, count); 0134 return result; 0135 } 0136 auto fallback = input; 0137 replacePercentN(&fallback, count); 0138 return fallback; 0139 } 0140 0141 QtLocalizer::QtLocalizer(const QLocale &locale) 0142 : d_ptr(new QtLocalizerPrivate(this, locale)) 0143 { 0144 } 0145 0146 QtLocalizer::~QtLocalizer() 0147 { 0148 delete d_ptr; 0149 } 0150 0151 void QtLocalizer::setAppTranslatorPath(const QString &path) 0152 { 0153 Q_D(QtLocalizer); 0154 d->m_appTranslatorPath = path; 0155 } 0156 0157 void QtLocalizer::setAppTranslatorPrefix(const QString &prefix) 0158 { 0159 Q_D(QtLocalizer); 0160 d->m_appTranslatorPrefix = prefix; 0161 } 0162 0163 void QtLocalizer::installTranslator(QTranslator *translator, const QString &localeName) 0164 { 0165 Q_D(QtLocalizer); 0166 if (!d->m_availableLocales.contains(localeName)) { 0167 const QLocale namedLocale(localeName); 0168 d->m_availableLocales.insert(localeName, new Locale(namedLocale)); 0169 } 0170 d->m_availableLocales[localeName]->externalSystemTranslators.prepend(translator); 0171 } 0172 0173 QString QtLocalizer::localizeDate(const QDate &date, QLocale::FormatType formatType) const 0174 { 0175 Q_D(const QtLocalizer); 0176 return d->currentLocale().toString(date, formatType); 0177 } 0178 0179 QString QtLocalizer::localizeTime(const QTime &time, QLocale::FormatType formatType) const 0180 { 0181 Q_D(const QtLocalizer); 0182 return d->currentLocale().toString(time, formatType); 0183 } 0184 0185 QString QtLocalizer::localizeDateTime(const QDateTime &dateTime, QLocale::FormatType formatType) const 0186 { 0187 Q_D(const QtLocalizer); 0188 return d->currentLocale().toString(dateTime, formatType); 0189 } 0190 0191 QString QtLocalizer::localizeNumber(int number) const 0192 { 0193 Q_D(const QtLocalizer); 0194 return d->currentLocale().toString(number); 0195 } 0196 0197 QString QtLocalizer::localizeNumber(qreal number) const 0198 { 0199 Q_D(const QtLocalizer); 0200 return d->currentLocale().toString(number, 'f', 2); 0201 } 0202 0203 QString QtLocalizer::localizeMonetaryValue(qreal value, const QString ¤cyCode) const 0204 { 0205 Q_D(const QtLocalizer); 0206 auto currencySymbol = QStringLiteral("$"); 0207 if (currencyCode == QStringLiteral("EUR")) { 0208 currencySymbol = QChar(0x20AC); 0209 } else if (currencyCode == QStringLiteral("GBP")) { 0210 currencySymbol = QStringLiteral("£"); 0211 } else { 0212 currencySymbol = currencyCode; 0213 } 0214 return currencySymbol + QLatin1Char(' ') + d->currentLocale().toString(value, 'f', 2); 0215 } 0216 0217 static QString substituteArguments(const QString &input, const QVariantList &arguments) 0218 { 0219 auto string = input; 0220 for (const QVariant &arg : arguments) { 0221 if (arg.userType() == qMetaTypeId<int>()) 0222 string = string.arg(arg.value<int>()); 0223 else if (arg.userType() == qMetaTypeId<double>()) 0224 string = string.arg(arg.value<double>()); 0225 else if (arg.userType() == qMetaTypeId<QDateTime>()) 0226 string = string.arg(arg.value<QDateTime>().toString()); 0227 else 0228 string = string.arg(arg.value<QString>()); 0229 } 0230 return string; 0231 } 0232 0233 QString QtLocalizer::localizeContextString(const QString &string, const QString &context, const QVariantList &arguments) const 0234 { 0235 Q_D(const QtLocalizer); 0236 const auto translated = d->translate(string, context); 0237 return substituteArguments(translated, arguments); 0238 } 0239 0240 QString QtLocalizer::localizeString(const QString &string, const QVariantList &arguments) const 0241 { 0242 Q_D(const QtLocalizer); 0243 const auto translated = d->translate(string, QString()); 0244 return substituteArguments(translated, arguments); 0245 } 0246 0247 QString QtLocalizer::localizePluralContextString(const QString &string, const QString &pluralForm, const QString &context, const QVariantList &_arguments) const 0248 { 0249 Q_UNUSED(pluralForm) 0250 Q_D(const QtLocalizer); 0251 auto arguments = _arguments; 0252 const auto N = arguments.takeFirst().toInt(); 0253 const auto translated = d->translate(string, context, N); 0254 return substituteArguments(translated, arguments); 0255 } 0256 0257 QString QtLocalizer::localizePluralString(const QString &string, const QString &pluralForm, const QVariantList &_arguments) const 0258 { 0259 Q_UNUSED(pluralForm) 0260 Q_D(const QtLocalizer); 0261 auto arguments = _arguments; 0262 const auto N = arguments.takeFirst().toInt(); 0263 const auto translated = d->translate(string, QString(), N); 0264 return substituteArguments(translated, arguments); 0265 } 0266 0267 QString QtLocalizer::currentLocale() const 0268 { 0269 Q_D(const QtLocalizer); 0270 return d->currentLocale().name(); 0271 } 0272 0273 void QtLocalizer::pushLocale(const QString &localeName) 0274 { 0275 Q_D(QtLocalizer); 0276 Locale *localeStruct = nullptr; 0277 if (!d->m_availableLocales.contains(localeName)) { 0278 localeStruct = new Locale(QLocale(localeName)); 0279 auto qtTranslator = new QTranslator; 0280 (void)qtTranslator->load(QStringLiteral("qt_") + localeName, QLibraryInfo::path(QLibraryInfo::TranslationsPath)); 0281 localeStruct->systemTranslators.append(qtTranslator); 0282 auto appTranslator = new QTranslator; 0283 (void)appTranslator->load(d->m_appTranslatorPrefix + localeName, d->m_appTranslatorPath); 0284 localeStruct->systemTranslators.append(appTranslator); 0285 d->m_availableLocales.insert(localeName, localeStruct); 0286 } else { 0287 localeStruct = d->m_availableLocales[localeName]; 0288 } 0289 Q_ASSERT(localeStruct); 0290 d->m_locales.push_back(localeStruct); 0291 } 0292 0293 void QtLocalizer::popLocale() 0294 { 0295 Q_D(QtLocalizer); 0296 Q_ASSERT(!d->m_locales.isEmpty()); 0297 d->m_locales.takeLast(); 0298 } 0299 0300 void QtLocalizer::loadCatalog(const QString &path, const QString &catalog) 0301 { 0302 Q_D(QtLocalizer); 0303 auto it = d->m_availableLocales.constBegin(); 0304 const auto end = d->m_availableLocales.constEnd(); 0305 for (; it != end; ++it) { 0306 auto translator = new QTranslator(); 0307 const auto loaded = translator->load(it.key() + QLatin1Char('/') + catalog, path); 0308 if (!loaded) 0309 continue; 0310 0311 translator->setObjectName(catalog); 0312 0313 it.value()->themeTranslators.prepend(translator); 0314 } 0315 } 0316 0317 void QtLocalizer::unloadCatalog(const QString &catalog) 0318 { 0319 Q_D(QtLocalizer); 0320 auto it = d->m_availableLocales.constBegin(); 0321 const auto end = d->m_availableLocales.constEnd(); 0322 for (; it != end; ++it) { 0323 auto tranIt = (*it)->themeTranslators.begin(); 0324 while (tranIt != (*it)->themeTranslators.end()) { 0325 if ((*tranIt)->objectName() == catalog) { 0326 delete *tranIt; 0327 tranIt = (*it)->themeTranslators.erase(tranIt); 0328 } else { 0329 ++tranIt; 0330 } 0331 } 0332 } 0333 }