kdelibs/kdecore/localization/klocale.cpp
Ivailo Monev 2372a3991f kdecore: KLocale optimization
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
2024-03-16 19:41:34 +02:00

955 lines
31 KiB
C++

/* This file is part of the KDE libraries
Copyright (C) 2023 Ivailo Monev <xakepa10@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2, as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "klocale.h"
#include "kcatalog_p.h"
#include "kglobal.h"
#include "kconfig.h"
#include "kconfiggroup.h"
#include "kstandarddirs.h"
#include "kdebug.h"
#include "common_helpers_p.h"
#include <QMutex>
#include <QFileInfo>
#include <QCoreApplication>
#include <qmath.h>
#include <array>
// #define KLOCALE_DUMP
// #define KLOCALE_DUMP_UNTRANSLATED
enum KLocaleDuration
{
KDurationMillisecond = 0,
KDurationSecond = 1,
KDurationMinute = 2,
KDurationHour = 3
};
// catalogs from which each application can draw translations
static QStringList s_defaultcatalogs = QStringList()
<< QString::fromLatin1("kio4")
<< QString::fromLatin1("kdelibs4")
<< QString::fromLatin1("kdeqt");
static const QLatin1Char s_localeexponentc = QLatin1Char('e');
static const QLatin1String s_defaultlanguage = QLatin1String("en_US");
static QString kGetDuration(const KLocaleDuration which, const int duration)
{
switch (which) {
case KLocaleDuration::KDurationHour: {
return i18ncp("@item:intext", "1 hour", "%1 hours", duration);
}
case KLocaleDuration::KDurationMinute: {
return i18ncp("@item:intext", "1 minute", "%1 minutes", duration);
}
case KLocaleDuration::KDurationSecond: {
return i18ncp("@item:intext", "1 second", "%1 seconds", duration);
}
case KLocaleDuration::KDurationMillisecond: {
return i18ncp("@item:intext", "1 millisecond", "%1 milliseconds", duration);
}
}
Q_ASSERT(false);
return QString();
}
static QString kGetMultiDuration(const KLocaleDuration which, const int duration,
const KLocaleDuration which2, const int duration2)
{
return i18nc("@item:intext", "%1 and %2", kGetDuration(which, duration), kGetDuration(which2, duration2));
}
static QString kGetLanguage(const QString &language)
{
// NOTE: QLocale::name() always return "foo_BAR", unless the locale is "C" but KLocale uses
// KLocale::defaultLanguage() instead
const int underscoreindex = language.indexOf(QLatin1Char('_'));
Q_ASSERT(underscoreindex > 1);
return language.mid(0, underscoreindex);
}
class KLocalePrivate
{
public:
KLocalePrivate(const KLocalePrivate &other);
KLocalePrivate(const QString &catalog, KSharedConfig::Ptr config);
KLocalePrivate(const QString &catalog, const QString &language, KConfig *config);
~KLocalePrivate();
KLocale::BinaryUnitDialect binaryUnitDialect;
std::array<QString, 3> dateFormats;
std::array<QString, 3> timeFormats;
std::array<QString, 3> dateTimeFormats;
QLocale::MeasurementSystem measurementSystem;
bool isdefault;
bool immutable;
QLocale locale;
QString catalog;
QStringList languagelist;
QStringList manualcatalogs;
QList<KCatalog> catalogs;
KConfigGroup configgroup;
QMutex *mutex;
};
static bool kInsertCatalog(KLocalePrivate *locale, const QString &catalogname, const QString &cataloglanguage)
{
foreach (const KCatalog &catalog, locale->catalogs) {
if (catalog.name() == catalogname && catalog.language() == cataloglanguage) {
return false;
}
}
if (KCatalog::hasCatalog(catalogname, cataloglanguage)) {
locale->catalogs.append(KCatalog(catalogname, cataloglanguage));
return true;
}
return false;
}
#if defined(KLOCALE_DUMP) || defined(KLOCALE_DUMP_UNTRANSLATED)
static void dumpKLocaleCatalogs(const KLocalePrivate *locale)
{
qDebug() << "Catalogs for" << locale->locale.name() << locale->catalogs.size();
foreach (const KCatalog &catalog, locale->catalogs) {
qDebug() << "Catalog for" << locale->locale.name();
qDebug() << " -> name =" << catalog.name();
qDebug() << " -> language =" << catalog.language();
}
}
#endif
KLocalePrivate::KLocalePrivate(const KLocalePrivate &other)
: binaryUnitDialect(other.binaryUnitDialect),
dateFormats(other.dateFormats),
timeFormats(other.timeFormats),
dateTimeFormats(other.dateTimeFormats),
measurementSystem(other.measurementSystem),
isdefault(other.isdefault),
immutable(other.immutable),
locale(other.locale),
catalog(other.catalog),
languagelist(other.languagelist),
manualcatalogs(other.manualcatalogs),
catalogs(other.catalogs),
configgroup(other.configgroup),
mutex(new QMutex())
{
}
KLocalePrivate::KLocalePrivate(const QString &_catalog, KSharedConfig::Ptr config)
: binaryUnitDialect(KLocale::IECBinaryDialect),
measurementSystem(QLocale::MetricSystem),
isdefault(true),
immutable(false),
catalog(_catalog),
mutex(new QMutex())
{
if (config) {
configgroup = config->group("Locale");
} else {
configgroup = KGlobal::config()->group("Locale");
}
}
KLocalePrivate::KLocalePrivate(const QString &_catalog, const QString &language, KConfig *config)
: binaryUnitDialect(KLocale::IECBinaryDialect),
measurementSystem(QLocale::MetricSystem),
isdefault(true),
immutable(true),
catalog(_catalog),
mutex(new QMutex())
{
locale = QLocale(language);
// fallback to the default
if (locale.language() == QLocale::C) {
locale = QLocale(s_defaultlanguage);
}
if (config) {
configgroup = config->group("Locale");
} else {
configgroup = KGlobal::config()->group("Locale");
}
}
KLocalePrivate::~KLocalePrivate()
{
delete mutex;
}
KLocale::KLocale(const QString &catalog, KSharedConfig::Ptr config)
: d(new KLocalePrivate(catalog, config))
{
reparseConfiguration();
}
KLocale::KLocale(const QString &catalog, const QString &language, KConfig *config)
: d(new KLocalePrivate(catalog, language, config))
{
reparseConfiguration();
}
KLocale::~KLocale()
{
delete d;
}
KLocale::KLocale(const KLocale &rhs)
: d(new KLocalePrivate(*rhs.d))
{
}
KLocale & KLocale::operator=(const KLocale &rhs)
{
// the assignment operator works here
*d = *rhs.d;
return *this;
}
QString KLocale::language() const
{
if (d->isdefault) {
return d->locale.name();
}
return kGetLanguage(d->locale.name());
}
QStringList KLocale::languageList() const
{
return d->languagelist;
}
KLocale::BinaryUnitDialect KLocale::binaryUnitDialect() const
{
return d->binaryUnitDialect;
}
QString KLocale::dateFormat(QLocale::FormatType format) const
{
return d->dateFormats[format];
}
QString KLocale::timeFormat(QLocale::FormatType format) const
{
return d->timeFormats[format];
}
QString KLocale::dateTimeFormat(QLocale::FormatType format) const
{
return d->dateTimeFormats[format];
}
QLocale::MeasurementSystem KLocale::measureSystem() const
{
return d->measurementSystem;
}
QString KLocale::formatNumber(double num, int precision) const
{
if (precision <= 0) {
// precision is automatically chosen here for prettiness
return d->locale.toString(num, 'f', qRound(num) == num ? 0 : 2);
}
const QString cnum = QString::number(num);
if (cnum.indexOf(s_localeexponentc) > 0) {
// special case for number with exponent - no rounding but convert it to the locale
return d->locale.toString(cnum.toDouble());
}
return d->locale.toString(num, 'f', precision);
}
QString KLocale::formatNumber(const QString &numStr, bool round, int precision) const
{
if (numStr.indexOf(s_localeexponentc) > 0) {
// see above
return d->locale.toString(numStr.toDouble());
}
if (round) {
return formatNumber(qRound(numStr.toDouble()), 0);
}
return formatNumber(numStr.toDouble(), precision);
}
QString KLocale::formatLong(long num) const
{
return d->locale.toString(qlonglong(num));
}
QString KLocale::formatByteSize(double size, int precision,
KLocale::BinarySizeUnits specificUnit) const
{
double sizeinunit = size;
int sizeprecision = precision;
KLocale::BinarySizeUnits sizeunit = specificUnit;
double sizemultiplier = double(1024.0);
if (d->binaryUnitDialect == KLocale::MetricBinaryDialect) {
sizemultiplier = double(1000.0);
}
if (sizeunit == KLocale::DefaultBinaryUnits) {
Q_ASSERT(int(KLocale::UnitByte) == 0);
int counter = 0;
static const int s_lastunitasint = static_cast<int>(KLocale::LastBinaryUnit);
while (qAbs(sizeinunit) >= sizemultiplier && counter < s_lastunitasint) {
sizeinunit = (sizeinunit / sizemultiplier);
counter++;
}
sizeunit = static_cast<KLocale::BinarySizeUnits>(counter);
} else if (sizeunit != KLocale::UnitByte) {
sizeinunit = (sizeinunit / qPow(sizemultiplier, static_cast<int>(sizeunit)));
}
if (sizeinunit == double(0.0)) {
// 0.0 is zero
sizeprecision = 0;
}
QString sizetranslation;
switch (d->binaryUnitDialect) {
case KLocale::MetricBinaryDialect: {
switch (sizeunit) {
case KLocale::UnitByte: {
translateRaw("size in bytes", "%1 B", nullptr, &sizetranslation);
break;
}
case KLocale::UnitKiloByte: {
translateRaw("size in 1000 bytes", "%1 kB", nullptr, &sizetranslation);
break;
}
case KLocale::UnitMegaByte: {
translateRaw("size in 10^6 bytes", "%1 MB", nullptr, &sizetranslation);
break;
}
case KLocale::UnitGigaByte: {
translateRaw("size in 10^9 bytes", "%1 GB", nullptr, &sizetranslation);
break;
}
case KLocale::UnitTeraByte: {
translateRaw("size in 10^12 bytes", "%1 TB", nullptr, &sizetranslation);
break;
}
case KLocale::UnitPetaByte: {
translateRaw("size in 10^15 bytes", "%1 PB", nullptr, &sizetranslation);
break;
}
case KLocale::UnitExaByte: {
translateRaw("size in 10^18 bytes", "%1 EB", nullptr, &sizetranslation);
break;
}
case KLocale::UnitZettaByte: {
translateRaw("size in 10^21 bytes", "%1 ZB", nullptr, &sizetranslation);
break;
}
case KLocale::UnitYottaByte: {
translateRaw("size in 10^24 bytes", "%1 YB", nullptr, &sizetranslation);
break;
}
default: {
Q_ASSERT(false);
break;
}
}
}
case KLocale::JEDECBinaryDialect: {
switch (sizeunit) {
case KLocale::UnitByte: {
translateRaw("size in bytes", "%1 B", nullptr, &sizetranslation);
break;
}
case KLocale::UnitKiloByte: {
translateRaw("memory size in 1024 bytes", "%1 KB", nullptr, &sizetranslation);
break;
}
case KLocale::UnitMegaByte: {
translateRaw("memory size in 2^20 bytes", "%1 MB", nullptr, &sizetranslation);
break;
}
case KLocale::UnitGigaByte: {
translateRaw("memory size in 2^30 bytes", "%1 GB", nullptr, &sizetranslation);
break;
}
case KLocale::UnitTeraByte: {
translateRaw("memory size in 2^40 bytes", "%1 TB", nullptr, &sizetranslation);
break;
}
case KLocale::UnitPetaByte: {
translateRaw("memory size in 2^50 bytes", "%1 PB", nullptr, &sizetranslation);
break;
}
case KLocale::UnitExaByte: {
translateRaw("memory size in 2^60 bytes", "%1 EB", nullptr, &sizetranslation);
break;
}
case KLocale::UnitZettaByte: {
translateRaw("memory size in 2^70 bytes", "%1 ZB", nullptr, &sizetranslation);
break;
}
case KLocale::UnitYottaByte: {
translateRaw("memory size in 2^80 bytes", "%1 YB", nullptr, &sizetranslation);
break;
}
default: {
Q_ASSERT(false);
break;
}
}
}
case KLocale::IECBinaryDialect:
default: {
switch (sizeunit) {
case KLocale::UnitByte: {
translateRaw("size in bytes", "%1 B", nullptr, &sizetranslation);
break;
}
case KLocale::UnitKiloByte: {
translateRaw("size in 1024 bytes", "%1 KiB", nullptr, &sizetranslation);
break;
}
case KLocale::UnitMegaByte: {
translateRaw("size in 2^20 bytes", "%1 MiB", nullptr, &sizetranslation);
break;
}
case KLocale::UnitGigaByte: {
translateRaw("size in 2^30 bytes", "%1 GiB", nullptr, &sizetranslation);
break;
}
case KLocale::UnitTeraByte: {
translateRaw("size in 2^40 bytes", "%1 TiB", nullptr, &sizetranslation);
break;
}
case KLocale::UnitPetaByte: {
translateRaw("size in 2^50 bytes", "%1 PiB", nullptr, &sizetranslation);
break;
}
case KLocale::UnitExaByte: {
translateRaw("size in 2^60 bytes", "%1 EiB", nullptr, &sizetranslation);
break;
}
case KLocale::UnitZettaByte: {
translateRaw("size in 2^70 bytes", "%1 ZiB", nullptr, &sizetranslation);
break;
}
case KLocale::UnitYottaByte: {
translateRaw("size in 2^80 bytes", "%1 YiB", nullptr, &sizetranslation);
break;
}
default: {
Q_ASSERT(false);
break;
}
}
}
}
return sizetranslation.arg(formatNumber(sizeinunit, sizeprecision));
}
QString KLocale::formatByteSize(double size) const
{
return formatByteSize(size, 1, KLocale::DefaultBinaryUnits);
}
QString KLocale::formatDuration(unsigned long mSec) const
{
QTime durationtime;
durationtime = durationtime.addMSecs(mSec);
const int hours = durationtime.hour();
const int minutes = durationtime.minute();
const int seconds = durationtime.second();
const int milliseconds = durationtime.msec();
if (hours && minutes) {
return kGetMultiDuration(
KLocaleDuration::KDurationHour, hours,
KLocaleDuration::KDurationMinute, minutes
);
} else if (hours) {
return kGetDuration(
KLocaleDuration::KDurationHour, hours
);
} else if (minutes && seconds) {
return kGetMultiDuration(
KLocaleDuration::KDurationMinute, minutes,
KLocaleDuration::KDurationSecond, seconds
);
} else if (minutes) {
return kGetDuration(
KLocaleDuration::KDurationMinute, minutes
);
} else if (seconds && milliseconds) {
return kGetMultiDuration(
KLocaleDuration::KDurationSecond, seconds,
KLocaleDuration::KDurationMillisecond, milliseconds
);
} else if (seconds) {
return kGetDuration(
KLocaleDuration::KDurationSecond, seconds
);
}
return kGetDuration(
KLocaleDuration::KDurationMillisecond, milliseconds
);
}
QString KLocale::formatDate(const QDate &date, QLocale::FormatType format) const
{
return d->locale.toString(date, d->dateFormats[format]);
}
QString KLocale::formatTime(const QTime &time, QLocale::FormatType format) const
{
return d->locale.toString(time, d->timeFormats[format]);
}
QString KLocale::formatDateTime(const QDateTime &dateTime, QLocale::FormatType format) const
{
return d->locale.toString(dateTime, d->dateTimeFormats[format]);
}
double KLocale::readNumber(const QString &str, bool *ok) const
{
return d->locale.toDouble(str, ok);
}
QDate KLocale::readDate(const QString &intstr, QLocale::FormatType format) const
{
return d->locale.toDate(intstr, d->dateFormats[format]);
}
QTime KLocale::readTime(const QString &intstr, QLocale::FormatType format) const
{
return d->locale.toTime(intstr, d->timeFormats[format]);
}
void KLocale::insertCatalog(const QString &catalog)
{
bool updated = false;
const QStringList cataloglanguages = languageList();
{
QMutexLocker locker(d->mutex);
foreach (const QString &cataloglanguage, cataloglanguages) {
if (kInsertCatalog(d, catalog, cataloglanguage)) {
if (!d->manualcatalogs.contains(catalog)) {
d->manualcatalogs.append(catalog);
}
updated = true;
}
}
#ifdef KLOCALE_DUMP
dumpKLocaleCatalogs(d);
#endif
}
if (updated) {
KLocalizedString::notifyCatalogsUpdated(cataloglanguages);
}
}
void KLocale::removeCatalog(const QString &catalog)
{
bool updated = false;
const QStringList cataloglanguages = languageList();
{
QMutexLocker locker(d->mutex);
QMutableListIterator<KCatalog> catalogsiter(d->catalogs);
while (catalogsiter.hasNext()) {
const QString catalogname = catalogsiter.next().name();
if (catalogname == catalog) {
catalogsiter.remove();
d->manualcatalogs.removeAll(catalog);
updated = true;
}
}
#ifdef KLOCALE_DUMP
dumpKLocaleCatalogs(d);
#endif
}
if (updated) {
KLocalizedString::notifyCatalogsUpdated(cataloglanguages);
}
}
void KLocale::setActiveCatalog(const QString &catalog)
{
bool updated = false;
{
QMutexLocker locker(d->mutex);
for (int i = 1; i < d->catalogs.size(); i++) {
if (d->catalogs.at(i).name() == catalog) {
d->catalogs.move(i, 0);
i = 1;
updated = true;
}
}
}
if (updated) {
KLocalizedString::notifyCatalogsUpdated(languageList());
}
}
void KLocale::translateRaw(const char *ctxt, const char *msg, QString *lang, QString *trans) const
{
{
QMutexLocker locker(d->mutex);
foreach (const KCatalog &catalog, d->catalogs) {
const QString result = catalog.translateStrict(ctxt, msg);
if (!result.isEmpty()) {
*trans = result;
if (lang) {
*lang = catalog.language();
}
return;
}
}
}
#ifdef KLOCALE_DUMP_UNTRANSLATED
qDebug() << "No translation for" << ctxt << msg;
dumpKLocaleCatalogs(d);
#endif
if (lang) {
*lang = s_defaultlanguage;
}
*trans = QString::fromUtf8(msg);
}
void KLocale::translateRaw(const char *ctxt, const char *singular, const char *plural,
unsigned long n, QString *lang, QString *trans) const
{
{
QMutexLocker locker(d->mutex);
foreach (const KCatalog &catalog, d->catalogs) {
const QString result = catalog.translateStrict(ctxt, singular, plural, n);
if (!result.isEmpty()) {
*trans = result;
if (lang) {
*lang = catalog.language();
}
return;
}
}
}
#ifdef KLOCALE_DUMP_UNTRANSLATED
qDebug() << "No translation for" << ctxt << singular << plural;
dumpKLocaleCatalogs(d);
#endif
if (lang) {
*lang = s_defaultlanguage;
}
if (!plural || n == 1) {
*trans = QString::fromUtf8(singular);
} else {
*trans = QString::fromUtf8(plural);
}
}
QString KLocale::translateQt(const char *context, const char *sourceText) const
{
// return empty according to Katie's expectations
QString result;
if (d->isdefault) {
return result;
}
QMutexLocker locker(d->mutex);
foreach (const KCatalog &catalog, d->catalogs) {
result = catalog.translateStrict(context, sourceText);
if (!result.isEmpty()) {
break;
}
}
return result;
}
QString KLocale::languageCodeToName(const QString &language) const
{
QString result;
const QString entryfile = KStandardDirs::locate("locale", language + QLatin1String("/entry.desktop"));
const QStringList entrylanguages = languageList();
if (!entryfile.isEmpty()) {
KConfig entryconfig(entryfile);
foreach (const QString &lang, entrylanguages) {
entryconfig.setLocale(lang);
KConfigGroup entrygroup(&entryconfig, "KCM Locale");
result = entrygroup.readEntry("Name");
if (!result.isEmpty()) {
break;
}
}
}
if (result.isEmpty()) {
// in case the language is not installed
KConfig languagesconfig(QLatin1String("all_languages"), KConfig::NoGlobals, "locale");
foreach (const QString &lang, entrylanguages) {
languagesconfig.setLocale(lang);
KConfigGroup languagegroup(&languagesconfig, language);
result = languagegroup.readEntry("Name");
if (!result.isEmpty()) {
break;
}
}
}
if (result.isEmpty()) {
// not translated at all
QLocale locale(language);
result = QLocale::languageToString(locale.language());
}
return result;
}
QString KLocale::countryCodeToName(const QString &country) const
{
QString result;
const QString entryfile = KStandardDirs::locate("locale", QString::fromLatin1("l10n/") + country.toLower() + QLatin1String("/entry.desktop"));
const QStringList entrylanguages = languageList();
if (!entryfile.isEmpty()) {
KConfig entryconfig(entryfile);
foreach (const QString &lang, entrylanguages) {
entryconfig.setLocale(lang);
KConfigGroup entrygroup(&entryconfig, "KCM Locale");
result = entrygroup.readEntry("Name");
if (!result.isEmpty()) {
break;
}
}
}
if (result.isEmpty()) {
// not translated at all (e.g. 001 - world)
result = country;
}
return result;
}
void KLocale::copyCatalogsTo(KLocale *locale)
{
{
QMutexLocker locker(d->mutex);
d->manualcatalogs = locale->d->manualcatalogs;
d->catalogs = locale->d->catalogs;
}
KLocalizedString::notifyCatalogsUpdated(languageList());
}
QString KLocale::localizedFilePath(const QString &filePath) const
{
// Stop here if the default language is primary.
if (d->isdefault) {
return filePath;
}
// Check if l10n sudir is present, stop if not.
QFileInfo fileInfo(filePath);
const QString locDirPath = fileInfo.path() + QLatin1String("/l10n");
QFileInfo locDirInfo(locDirPath);
if (!locDirInfo.isDir()) {
return filePath;
}
// Go through possible localized paths by priority of languages, return first that exists.
const QString fileName = fileInfo.fileName();
foreach(const QString &lang, languageList()) {
const QString locFilePath = locDirPath + QLatin1Char('/') + lang + QLatin1Char('/') + fileName;
QFileInfo locFileInfo(locFilePath);
if (locFileInfo.isFile() && locFileInfo.isReadable()) {
return locFilePath;
}
}
return filePath;
}
QString KLocale::removeAcceleratorMarker(const QString &label) const
{
return ::removeAcceleratorMarker(label);
}
void KLocale::reparseConfiguration()
{
QMutexLocker locker(d->mutex);
d->configgroup.sync();
if (!d->immutable) {
// locale from the config overrides everything (not Unix-like but that's how it should be)
const QString configlanguage = d->configgroup.readEntry("Language", QString());
d->locale = QLocale(configlanguage);
// if no locale was specified or QLocale does not support the specified language use the
// system locale
if (d->locale.language() == QLocale::C) {
d->locale = QLocale::system();
}
// finally, if the locale is C for compat fallback to what KLocale::defaultLanguage()
// returns
if (d->locale.language() == QLocale::C) {
d->locale = QLocale(s_defaultlanguage);
}
}
d->isdefault = (d->locale.name() == s_defaultlanguage);
d->binaryUnitDialect = static_cast<KLocale::BinaryUnitDialect>(
d->configgroup.readEntry("BinaryUnitDialect", int(KLocale::IECBinaryDialect))
);
Q_ASSERT(int(QLocale::LongFormat) == 0);
Q_ASSERT(int(QLocale::NarrowFormat) == 2);
d->dateFormats[QLocale::LongFormat] = d->configgroup.readEntry("LongDateFormat", d->locale.dateFormat(QLocale::LongFormat));
d->dateFormats[QLocale::ShortFormat] = d->configgroup.readEntry("ShortDateFormat", d->locale.dateFormat(QLocale::ShortFormat));
d->dateFormats[QLocale::NarrowFormat] = d->configgroup.readEntry("NarrowDateFormat", d->locale.dateFormat(QLocale::NarrowFormat));
d->timeFormats[QLocale::LongFormat] = d->configgroup.readEntry("LongTimeFormat", d->locale.timeFormat(QLocale::LongFormat));
d->timeFormats[QLocale::ShortFormat] = d->configgroup.readEntry("ShortTimeFormat", d->locale.timeFormat(QLocale::ShortFormat));
d->timeFormats[QLocale::NarrowFormat] = d->configgroup.readEntry("NarrowTimeFormat", d->locale.timeFormat(QLocale::NarrowFormat));
d->dateTimeFormats[QLocale::LongFormat] = d->configgroup.readEntry("LongDateTimeFormat", d->locale.dateTimeFormat(QLocale::LongFormat));
d->dateTimeFormats[QLocale::ShortFormat] = d->configgroup.readEntry("ShortDateTimeFormat", d->locale.dateTimeFormat(QLocale::ShortFormat));
d->dateTimeFormats[QLocale::NarrowFormat] = d->configgroup.readEntry("NarrowDateTimeFormat", d->locale.dateTimeFormat(QLocale::NarrowFormat));
d->measurementSystem = static_cast<QLocale::MeasurementSystem>(
d->configgroup.readEntry("MeasurementSystem", int(d->locale.measurementSystem()))
);
d->languagelist.clear();
// translation languages in the config
const QStringList configtranslations = d->configgroup.readEntry("Translations", QStringList());
if (!configtranslations.isEmpty()) {
d->languagelist.append(configtranslations);
}
const QString localename = d->locale.name();
// the language only (e.g. "en")
d->languagelist.append(kGetLanguage(localename));
// the locale name itself (e.g. "en_US")
d->languagelist.append(localename);
// default as fallback, unless the locale language is the default
if (localename != s_defaultlanguage) {
d->languagelist.append(s_defaultlanguage);
}
// qDebug() << Q_FUNC_INFO << d->languagelist;
const QStringList cataloglanguages = languageList();
d->catalogs.clear();
foreach (const QString &cataloglanguage, cataloglanguages) {
kInsertCatalog(d, d->catalog, cataloglanguage);
foreach (const QString &defaultcatalog, s_defaultcatalogs) {
kInsertCatalog(d, defaultcatalog, cataloglanguage);
}
foreach (const QString &manualcatalog, d->manualcatalogs) {
kInsertCatalog(d, manualcatalog, cataloglanguage);
}
}
#ifdef KLOCALE_DUMP
dumpKLocaleCatalogs(d);
#endif
KLocalizedString::notifyCatalogsUpdated(cataloglanguages);
}
QLocale KLocale::toLocale() const
{
return d->locale;
}
QStringList KLocale::allLanguagesList()
{
QStringList result;
const QList<QLocale> alllocales = QLocale::matchingLocales(
QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry
);
result.reserve(alllocales.size());
foreach (const QLocale &locale, alllocales) {
result.append(locale.name());
}
// NOTE: the locale name does not include the script (it includes the ISO 639 language code
// and the ISO 3166 country code) so there are duplicates
result.removeDuplicates();
return result;
}
QStringList KLocale::installedLanguages()
{
QStringList result;
const QStringList paths = KGlobal::dirs()->findAllResources("locale", QLatin1String("*/entry.desktop"));
foreach (const QString &path, paths) {
const QString part = path.left(path.length() - 14);
result.append(part.mid(part.lastIndexOf(QLatin1Char('/')) + 1));
}
result.sort();
return result;
}
bool KLocale::isApplicationTranslatedInto(const QString &lang)
{
if (lang.isEmpty()) {
return false;
}
if (lang == s_defaultlanguage) {
// default language is always "installed"
return true;
}
// check if the application itself was translated
if (KCatalog::hasCatalog(QCoreApplication::applicationName(), lang)) {
return true;
}
// check for partial translations from one of the default catalogs
foreach (const QString &defaultcatalog, s_defaultcatalogs) {
if (KCatalog::hasCatalog(defaultcatalog, lang)) {
return true;
}
}
return false;
}
void KLocale::splitLocale(const QString &locale, QString &language, QString &country, QString &modifier,
QString &charset)
{
QString localecopy = locale;
language.clear();
country.clear();
modifier.clear();
charset.clear();
// In case there are several concatenated locale specifications,
// truncate all but first.
int f = localecopy.indexOf(QLatin1Char(':'));
if (f >= 0) {
localecopy.truncate(f);
}
f = localecopy.indexOf(QLatin1Char('.'));
if (f >= 0) {
charset = localecopy.mid(f + 1);
localecopy.truncate(f);
}
f = localecopy.indexOf(QLatin1Char('@'));
if (f >= 0) {
modifier = localecopy.mid(f + 1);
localecopy.truncate(f);
}
f = localecopy.indexOf(QLatin1Char('_'));
if (f >= 0) {
country = localecopy.mid(f + 1);
localecopy.truncate(f);
}
language = localecopy;
}
QString KLocale::defaultLanguage()
{
return s_defaultlanguage;
}