• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

KDECore

klocale_kde.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE libraries
00002    Copyright (c) 1997,2001 Stephan Kulow <coolo@kde.org>
00003    Copyright (c) 1999 Preston Brown <pbrown@kde.org>
00004    Copyright (c) 1999-2002 Hans Petter Bieker <bieker@kde.org>
00005    Copyright (c) 2002 Lukas Tinkl <lukas@kde.org>
00006    Copyright (C) 2007 Bernhard Loos <nhuh.put@web.de>
00007    Copyright (C) 2009, 2010 John Layt <john@layt.net>
00008 
00009    This library is free software; you can redistribute it and/or
00010    modify it under the terms of the GNU Library General Public
00011    License as published by the Free Software Foundation; either
00012    version 2 of the License, or (at your option) any later version.
00013 
00014    This library is distributed in the hope that it will be useful,
00015    but WITHOUT ANY WARRANTY; without even the implied warranty of
00016    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017    Library General Public License for more details.
00018 
00019    You should have received a copy of the GNU Library General Public License
00020    along with this library; see the file COPYING.LIB.  If not, write to
00021    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00022    Boston, MA 02110-1301, USA.
00023 */
00024 
00025 #include "klocale_p.h"
00026 
00027 #include <config.h>
00028 
00029 #include <math.h>
00030 
00031 #ifdef HAVE_SYS_TIME_H
00032 #include <sys/time.h>
00033 #endif
00034 #ifdef HAVE_TIME_H
00035 #include <time.h>
00036 #endif
00037 #ifdef HAVE_LANGINFO_H
00038 #include <langinfo.h>
00039 #endif
00040 
00041 #include <QtCore/QTextCodec>
00042 #include <QtCore/QFile>
00043 #include <QtGui/QPrinter>
00044 #include <QtCore/QFileInfo>
00045 #include <QtCore/QRegExp>
00046 #include <QtCore/QLocale>
00047 #include <QtCore/QHash>
00048 #include <QtCore/QMutexLocker>
00049 
00050 #include "kcatalog_p.h"
00051 #include "kglobal.h"
00052 #include "kstandarddirs.h"
00053 #include "kconfig.h"
00054 #include "kcomponentdata.h"
00055 #include "kdebug.h"
00056 #include "kdatetime.h"
00057 #include "kcalendarsystem.h"
00058 #include "kcurrencycode.h"
00059 #include "klocalizedstring.h"
00060 #include "kconfiggroup.h"
00061 #include "kcatalogname_p.h"
00062 #include "common_helpers_p.h"
00063 #include "kdayperiod_p.h"
00064 
00065 class KLocaleStaticData
00066 {
00067 public:
00068 
00069     KLocaleStaticData();
00070 
00071     QString maincatalog;
00072 
00073     // FIXME: Temporary until full language-sensitivity implemented.
00074     QHash<KLocale::DigitSet, QStringList> languagesUsingDigitSet;
00075 };
00076 
00077 KLocaleStaticData::KLocaleStaticData()
00078 {
00079     // Languages using non-Western Arabic digit sets.
00080     // FIXME: Temporary until full language-sensitivity implemented.
00081     languagesUsingDigitSet.insert(KLocale::ArabicIndicDigits, QStringList() << QString::fromLatin1("ar") << QString::fromLatin1("ps"));
00082     languagesUsingDigitSet.insert(KLocale::BengaliDigits, QStringList() << QString::fromLatin1("bn") << QString::fromLatin1("as") );
00083     languagesUsingDigitSet.insert(KLocale::DevenagariDigits, QStringList() << QString::fromLatin1("hi") << QString::fromLatin1("ne"));
00084     languagesUsingDigitSet.insert(KLocale::EasternArabicIndicDigits, QStringList() << QString::fromLatin1("fa") << QString::fromLatin1("ur"));
00085     languagesUsingDigitSet.insert(KLocale::GujaratiDigits, QStringList() << QString::fromLatin1("gu") );
00086     languagesUsingDigitSet.insert(KLocale::GurmukhiDigits, QStringList() << QString::fromLatin1("pa") );
00087     languagesUsingDigitSet.insert(KLocale::KannadaDigits, QStringList() << QString::fromLatin1("kn") );
00088     languagesUsingDigitSet.insert(KLocale::KhmerDigits, QStringList() << QString::fromLatin1("km") );
00089     languagesUsingDigitSet.insert(KLocale::MalayalamDigits, QStringList() << QString::fromLatin1("ml") );
00090     languagesUsingDigitSet.insert(KLocale::OriyaDigits, QStringList() << QString::fromLatin1("or") );
00091     languagesUsingDigitSet.insert(KLocale::TamilDigits, QStringList() << QString::fromLatin1("ta") );
00092     languagesUsingDigitSet.insert(KLocale::TeluguDigits, QStringList() << QString::fromLatin1("te") );
00093     languagesUsingDigitSet.insert(KLocale::ThaiDigits, QStringList() << QString::fromLatin1("th"));
00094 }
00095 
00096 K_GLOBAL_STATIC(KLocaleStaticData, staticData)
00097 
00098 
00099 QDebug operator<<(QDebug debug, const KCatalogName &cn)
00100 {
00101     return debug << cn.name << cn.loadCount;
00102 }
00103 
00104 KLocalePrivate::KLocalePrivate(KLocale *q_ptr)
00105                : q(q_ptr),
00106                  m_config(KSharedConfig::Ptr()),
00107                  m_country(QString()),
00108                  m_language(QString()),
00109                  m_languages(0),
00110                  m_catalogName(QString()),
00111                  m_calendar(0),
00112                  m_currency(0),
00113                  m_codecForEncoding(0)
00114 {
00115 }
00116 
00117 KLocalePrivate::KLocalePrivate(const KLocalePrivate &rhs)
00118 {
00119     copy(rhs);
00120 }
00121 
00122 KLocalePrivate &KLocalePrivate::operator=(const KLocalePrivate &rhs)
00123 {
00124     copy(rhs);
00125     return *this;
00126 }
00127 
00128 KConfig *KLocalePrivate::config()
00129 {
00130     if (m_config != KSharedConfig::Ptr()) {
00131         return m_config.data();
00132     } else {
00133         return KGlobal::config().data();
00134     }
00135 }
00136 
00137 void KLocalePrivate::copy(const KLocalePrivate &rhs)
00138 {
00139     // Parent KLocale
00140     q = 0;
00141 
00142     // Config
00143     m_config = rhs.m_config;
00144 
00145     // Country settings
00146     m_country = rhs.m_country;
00147     m_countryDivisionCode = rhs.m_countryDivisionCode;
00148 
00149     // Language settings
00150     m_language = rhs.m_language;
00151     m_languages = 0;
00152     m_languageList = rhs.m_languageList;
00153     m_languageSensitiveDigits = rhs.m_languageSensitiveDigits;
00154     m_nounDeclension = rhs.m_nounDeclension;
00155 
00156     // Catalog settings
00157     m_catalogName = rhs.m_catalogName;
00158     m_catalogNames = rhs.m_catalogNames;
00159     m_catalogs = rhs.m_catalogs;
00160     m_numberOfSysCatalogs = rhs.m_numberOfSysCatalogs;
00161     m_useTranscript = rhs.m_useTranscript;
00162 
00163     // Calendar settings
00164     m_calendarSystem = rhs.m_calendarSystem;
00165     m_calendar = 0;
00166     m_weekStartDay = rhs.m_weekStartDay;
00167     m_workingWeekStartDay = rhs.m_workingWeekStartDay;
00168     m_workingWeekEndDay = rhs.m_workingWeekEndDay;
00169     m_weekDayOfPray = rhs.m_weekDayOfPray;
00170 
00171     // Date/Time settings
00172     m_dateFormat = rhs.m_dateFormat;
00173     m_dateFormatShort = rhs.m_dateFormatShort;
00174     m_timeFormat = rhs.m_timeFormat;
00175     m_dateTimeDigitSet = rhs.m_dateTimeDigitSet;
00176     m_dateMonthNamePossessive = rhs.m_dateMonthNamePossessive;
00177     m_dayPeriods = rhs.m_dayPeriods;
00178 
00179     // Number settings
00180     m_decimalPlaces = rhs.m_decimalPlaces;
00181     m_decimalSymbol = rhs.m_decimalSymbol;
00182     m_thousandsSeparator = rhs.m_thousandsSeparator;
00183     m_positiveSign = rhs.m_positiveSign;
00184     m_negativeSign = rhs.m_negativeSign;
00185     m_digitSet = rhs.m_digitSet;
00186 
00187     // Currency settings
00188     m_currencyCode = rhs.m_currencyCode;
00189     m_currency = 0;
00190     m_currencyCodeList = rhs.m_currencyCodeList;
00191 
00192     // Money settings
00193     m_currencySymbol = rhs.m_currencySymbol;
00194     m_monetaryDecimalSymbol = rhs.m_monetaryDecimalSymbol;
00195     m_monetaryThousandsSeparator = rhs.m_monetaryThousandsSeparator;
00196     m_monetaryDecimalPlaces = rhs.m_monetaryDecimalPlaces;
00197     m_positiveMonetarySignPosition = rhs.m_positiveMonetarySignPosition;
00198     m_negativeMonetarySignPosition = rhs.m_negativeMonetarySignPosition;
00199     m_positivePrefixCurrencySymbol = rhs.m_positivePrefixCurrencySymbol;
00200     m_negativePrefixCurrencySymbol = rhs.m_negativePrefixCurrencySymbol;
00201     m_monetaryDigitSet = rhs.m_monetaryDigitSet;
00202 
00203     // Units settings
00204     m_binaryUnitDialect = rhs.m_binaryUnitDialect;
00205     m_byteSizeFmt = rhs.m_byteSizeFmt;
00206     m_pageSize = rhs.m_pageSize;
00207     m_measureSystem = rhs.m_measureSystem;
00208 
00209     // Encoding settings
00210     m_encoding = rhs.m_encoding;
00211     m_codecForEncoding = rhs.m_codecForEncoding;
00212     m_utf8FileEncoding = rhs.m_utf8FileEncoding;
00213 }
00214 
00215 KLocalePrivate::~KLocalePrivate()
00216 {
00217     delete m_currency;
00218     delete m_calendar;
00219     delete m_languages;
00220 }
00221 
00222 // init only called from platform specific constructor, so set everything up
00223 // Will be given a persistantConfig or a tempConfig or neither, but never both
00224 void KLocalePrivate::init(const QString& catalogName, const QString &language, const QString &country,
00225                           KSharedConfig::Ptr persistantConfig, KConfig *tempConfig)
00226 {
00227     m_catalogName = catalogName;
00228 
00229     // Only keep the persistant config if it is not the global
00230     if (persistantConfig != KSharedConfig::Ptr() && persistantConfig != KGlobal::config()) {
00231         m_config = persistantConfig;
00232     }
00233 
00234     KConfigGroup cg;
00235     bool useEnvironmentVariables;
00236 
00237     // We can't read the formats from the config until we know what locale to read in, but we need
00238     // to read the config to find out the locale.  The Country and Language settings should never
00239     // be localized in the config, so we can read a temp copy of them to get us started.
00240 
00241     // If no config given, use the global config and include envvars, otherwise use only the config.
00242     if (m_config != KSharedConfig::Ptr()) {
00243         cg = m_config->group(QLatin1String("Locale"));
00244         useEnvironmentVariables = false;
00245     } else if (tempConfig == 0 || tempConfig == KGlobal::config().data()) {
00246         cg = KGlobal::config()->group(QLatin1String("Locale"));
00247         useEnvironmentVariables = true;
00248     } else {
00249         cg = tempConfig->group(QLatin1String("Locale"));
00250         useEnvironmentVariables = false;
00251     }
00252 
00253     initEncoding();
00254     initFileNameEncoding();
00255     initCountry(country, cg.readEntry(QLatin1String("Country")));
00256     initLanguageList(language, cg.readEntry(QLatin1String("Language")), useEnvironmentVariables);
00257     // Now that we have a language, we can set up the config which uses it to setLocale()
00258     initConfig(tempConfig);
00259     initMainCatalogs();
00260     initFormat();
00261 }
00262 
00263 // Init the config, this is called during construction and by later setCountry/setLanguage calls.
00264 // You _must_ have the m_language set to a valid language or en_US before calling this so a
00265 // setLocale can be applied to the config
00266 void KLocalePrivate::initConfig(KConfig *config)
00267 {
00268     // * If we were constructed with a KSharedConfig it means the user gave it to us
00269     //   to use for the life of the KLocale, so just keep using it after a setLocale
00270     // * If passed in KConfig is null or the global config then use the global, but
00271     //   do the setLocale first.
00272     // * If we have a KConfig we need to use that, but due to keeping old behaviour
00273     //   of not requiring access to it for life we can't keep a reference so instead
00274     //   take a copy and use that, but do setLocale first.
00275 
00276     if (m_config != KSharedConfig::Ptr()) {
00277         m_config->setLocale(m_language);
00278     } else {
00279         // If no config given then use the global
00280         if (config == 0 || config == KGlobal::config().data()) {
00281             KGlobal::config()->setLocale(m_language);
00282         } else {
00283             config->setLocale(m_language);
00284             m_config = KSharedConfig::openConfig();
00285             config->copyTo(QString(), m_config.data());
00286             m_config->markAsClean();
00287         }
00288     }
00289 }
00290 
00291 void KLocalePrivate::initMainCatalogs()
00292 {
00293     KLocaleStaticData *s = staticData;
00294     QMutexLocker lock(kLocaleMutex());
00295 
00296     if (!s->maincatalog.isEmpty()) {
00297         // If setMainCatalog was called, then we use that
00298         // (e.g. korgac calls setMainCatalog("korganizer") to use korganizer.po)
00299         m_catalogName = s->maincatalog;
00300     }
00301 
00302     if (m_catalogName.isEmpty()) {
00303         kDebug(173) << "KLocale instance created called without valid "
00304                     << "catalog! Give an argument or call setMainCatalog "
00305                     << "before init" << endl;
00306     } else {
00307         // do not use insertCatalog here, that would already trigger updateCatalogs
00308         m_catalogNames.append(KCatalogName(m_catalogName));   // application catalog
00309 
00310         // catalogs from which each application can draw translations
00311         const int numberOfCatalogs = m_catalogNames.size();
00312         m_catalogNames.append(KCatalogName(QString::fromLatin1("libphonon")));
00313         m_catalogNames.append(KCatalogName(QString::fromLatin1("kio4")));
00314         m_catalogNames.append(KCatalogName(QString::fromLatin1("kdelibs4")));
00315         m_catalogNames.append(KCatalogName(QString::fromLatin1("kdeqt")));
00316         m_catalogNames.append(KCatalogName(QString::fromLatin1("solid_qt")));
00317         m_catalogNames.append(KCatalogName(QString::fromLatin1("kdecalendarsystems")));
00318         m_numberOfSysCatalogs = m_catalogNames.size() - numberOfCatalogs;
00319 
00320         updateCatalogs(); // evaluate this for all languages
00321     }
00322 }
00323 
00324 void KLocalePrivate::getLanguagesFromVariable(QStringList &list, const char *variable, bool isLanguageList)
00325 {
00326     QByteArray var(qgetenv(variable));
00327     if (!var.isEmpty()) {
00328         QString value = QFile::decodeName(var);
00329         if (isLanguageList) {
00330             list += value.split(QLatin1Char(':'));
00331         } else {
00332             // Process the value to create possible combinations.
00333             QString lang, ctry, modf, cset;
00334             KLocale::splitLocale(value, lang, ctry, modf, cset);
00335 
00336             if (!ctry.isEmpty() && !modf.isEmpty()) {
00337                 list += lang + QLatin1Char('_') + ctry + QLatin1Char('@') + modf;
00338             }
00339             // NOTE: The priority is tricky in case both ctry and modf are present.
00340             // Should really lang@modf be of higher priority than lang_ctry?
00341             // For at least one case (Serbian language), it is better this way.
00342             if (!modf.isEmpty()) {
00343                 list += lang + QLatin1Char('@') + modf;
00344             }
00345             if (!ctry.isEmpty()) {
00346                 list += lang + QLatin1Char('_') + ctry;
00347             }
00348             list += lang;
00349         }
00350     }
00351 }
00352 
00353 // init the country at construction only, will ensure we always have a country set
00354 void KLocalePrivate::initCountry(const QString &country, const QString &configCountry)
00355 {
00356     // Cache the valid countries list and add the default C as it is valid to use
00357     QStringList validCountries = allCountriesList();
00358     validCountries.append( defaultCountry() );
00359 
00360     // First check if the constructor passed in a value and if so if it is valid
00361     QString putativeCountry = country;
00362 
00363     if ( putativeCountry.isEmpty() || !validCountries.contains( putativeCountry, Qt::CaseInsensitive ) ) {
00364 
00365         // If the requested country is not valid, try the country as set in the config:
00366         putativeCountry = configCountry;
00367 
00368         if ( putativeCountry.isEmpty() || !validCountries.contains( putativeCountry, Qt::CaseInsensitive ) ) {
00369 
00370             // If the config country is not valid try the current host system country
00371             putativeCountry = systemCountry();
00372 
00373             if ( putativeCountry.isEmpty() || !validCountries.contains( putativeCountry, Qt::CaseInsensitive ) ) {
00374                 // Only if no other option, resort to the default C
00375                 putativeCountry = defaultCountry();
00376             }
00377         }
00378     }
00379 
00380     // Always save as lowercase, unless it's C when we want it uppercase
00381     if ( putativeCountry.toLower() == defaultCountry().toLower() ) {
00382         m_country = defaultCountry();
00383     } else {
00384         m_country = putativeCountry.toLower();
00385     }
00386 }
00387 
00388 QString KLocalePrivate::systemCountry() const
00389 {
00390     // Use QLocale for now as it supposedly provides a sensible default most times,
00391     // e.g. if locale is only "de" it is assumed to mean country of "DE"
00392     QString systemCountry, s1, s2, s3;
00393     splitLocale( QLocale::system().name(), s1, systemCountry, s2, s3 );
00394     return systemCountry.toLower();
00395 }
00396 
00397 void KLocalePrivate::initLanguageList(const QString &language, const QString &configLanguages,
00398                                       bool useEnvironmentVariables)
00399 {
00400     m_language = language;
00401 
00402     // Collect possible languages by decreasing priority.
00403     // The priority is as follows:
00404     // - the internally set language, if any
00405     // - KDE_LANG environment variable (can be a list)
00406     // - KDE configuration (can be a list)
00407     // - environment variables considered by gettext(3)
00408     // The environment variables are not considered if useEnvironmentVariables is false.
00409     QStringList list;
00410     if (!m_language.isEmpty()) {
00411         list += m_language;
00412     }
00413 
00414     // If the Locale object was created with a specific config file, then do not use the
00415     // environmental variables.  If the locale object was created with the global config, then
00416     // do use the environmental variables.
00417     if (useEnvironmentVariables) {
00418         // KDE_LANG contains list of language codes, not locale string.
00419         getLanguagesFromVariable(list, "KDE_LANG", true);
00420     }
00421 
00422     if (!configLanguages.isEmpty()) {
00423         list += configLanguages.split(QLatin1Char(':'));
00424     }
00425 
00426     if (useEnvironmentVariables) {
00427         // Collect languages by same order of priority as for gettext(3).
00428         // LANGUAGE contains list of language codes, not locale string.
00429         getLanguagesFromVariable(list, "LANGUAGE", true);
00430         getLanguagesFromVariable(list, "LC_ALL");
00431         getLanguagesFromVariable(list, "LC_MESSAGES");
00432         getLanguagesFromVariable(list, "LANG");
00433     }
00434 
00435     // fall back to the system language
00436     list += systemLanguageList();
00437 
00438     // Send the list to filter for really present languages on the system.
00439     setLanguage(list);
00440 }
00441 
00442 QStringList KLocalePrivate::systemLanguageList() const
00443 {
00444     return QStringList();
00445 }
00446 
00447 void KLocalePrivate::initFormat()
00448 {
00449     KConfigGroup cg(config(), "Locale");
00450 
00451     KConfig entryFile(KStandardDirs::locate("locale", QString::fromLatin1("l10n/%1/entry.desktop").arg(m_country)));
00452     entryFile.setLocale(m_language);
00453     KConfigGroup entry(&entryFile, "KCM Locale");
00454 
00455     //One-time conversion in 4.4 from FracDigits to DecimalPlaces and MonetaryDecimalPlaces
00456     //If user has personal setting for FracDigits then use it for both Decimal Places
00457     //TODO: Possible to do with kconf_update
00458     if (cg.hasKey("FracDigits")) {
00459         QString fracDigits = cg.readEntry("FracDigits", "");
00460         if (!fracDigits.isEmpty()) {
00461             cg.writeEntry("DecimalPlaces", fracDigits);
00462             cg.writeEntry("MonetaryDecimalPlaces", fracDigits);
00463         }
00464         cg.deleteEntry("FracDigits");
00465         cg.config()->sync();
00466     }
00467 
00468     // Numeric
00469 #define readConfigEntry(key, default, save) \
00470         save = entry.readEntry(key, default); \
00471         save = cg.readEntry(key, save);
00472 
00473 #define readConfigNumEntry(key, default, save, type) \
00474         save = (type)entry.readEntry(key, int(default)); \
00475         save = (type)cg.readEntry(key, int(save));
00476 
00477     // Country settings
00478     readConfigEntry("CountryDivisionCode", QString(), m_countryDivisionCode);
00479 
00480     // Numeric formats
00481     readConfigNumEntry("DecimalPlaces", 2, m_decimalPlaces, int);
00482 
00483     readConfigEntry("DecimalSymbol", ".", m_decimalSymbol);
00484     readConfigEntry("ThousandsSeparator", ",", m_thousandsSeparator);
00485     m_thousandsSeparator.remove(QString::fromLatin1("$0"));
00486 
00487     readConfigEntry("PositiveSign", "", m_positiveSign);
00488     readConfigEntry("NegativeSign", "-", m_negativeSign);
00489 
00490     readConfigNumEntry("DigitSet", KLocale::ArabicDigits, m_digitSet, KLocale::DigitSet);
00491     // FIXME: Temporary until full language-sensitivity implemented.
00492     readConfigEntry("LanguageSensitiveDigits", true, m_languageSensitiveDigits);
00493 
00494     // Currency
00495     readConfigEntry("CurrencyCode", "USD", m_currencyCode);
00496     initCurrency();
00497     readConfigEntry("CurrencySymbol", m_currency->defaultSymbol(), m_currencySymbol);
00498     readConfigEntry("CurrencyCodesInUse", QStringList(m_currencyCode), m_currencyCodeList);
00499 
00500     // Monetary formats
00501     readConfigNumEntry("MonetaryDecimalPlaces", m_currency->decimalPlaces(), m_monetaryDecimalPlaces, int);
00502 
00503     readConfigEntry("MonetaryDecimalSymbol", ".", m_monetaryDecimalSymbol);
00504     readConfigEntry("MonetaryThousandsSeparator", ",", m_monetaryThousandsSeparator);
00505     m_monetaryThousandsSeparator.remove(QString::fromLatin1("$0"));
00506 
00507     readConfigEntry("PositivePrefixCurrencySymbol", true, m_positivePrefixCurrencySymbol);
00508     readConfigEntry("NegativePrefixCurrencySymbol", true, m_negativePrefixCurrencySymbol);
00509     readConfigNumEntry("PositiveMonetarySignPosition", KLocale::BeforeQuantityMoney,
00510                        m_positiveMonetarySignPosition, KLocale::SignPosition);
00511     readConfigNumEntry("NegativeMonetarySignPosition", KLocale::ParensAround,
00512                        m_negativeMonetarySignPosition, KLocale::SignPosition);
00513 
00514     readConfigNumEntry("MonetaryDigitSet", KLocale::ArabicDigits,
00515                        m_monetaryDigitSet, KLocale::DigitSet);
00516     readConfigNumEntry("BinaryUnitDialect", KLocale::IECBinaryDialect,
00517                        m_binaryUnitDialect, KLocale::BinaryUnitDialect);
00518 
00519     // Date and time
00520     readConfigEntry("TimeFormat", "%H:%M:%S", m_timeFormat);
00521     readConfigEntry("DateFormat", "%A %d %B %Y", m_dateFormat);
00522     readConfigEntry("DateFormatShort", "%Y-%m-%d", m_dateFormatShort);
00523     readConfigNumEntry("WeekStartDay", 1, m_weekStartDay, int);                //default to Monday
00524     readConfigNumEntry("WorkingWeekStartDay", 1, m_workingWeekStartDay, int);  //default to Monday
00525     readConfigNumEntry("WorkingWeekEndDay", 5, m_workingWeekEndDay, int);      //default to Friday
00526     readConfigNumEntry("WeekDayOfPray", 7, m_weekDayOfPray, int);              //default to Sunday
00527     readConfigNumEntry("DateTimeDigitSet", KLocale::ArabicDigits,
00528                        m_dateTimeDigitSet, KLocale::DigitSet);
00529 
00530     // other
00531 #ifndef QT_NO_PRINTER
00532     readConfigNumEntry("PageSize", QPrinter::A4, m_pageSize, QPrinter::PageSize);
00533 #endif
00534     readConfigNumEntry("MeasureSystem", KLocale::Metric, m_measureSystem, KLocale::MeasureSystem);
00535     QString calendarType;
00536     readConfigEntry("CalendarSystem", "gregorian", calendarType);
00537     setCalendar(calendarType);
00538 
00539     readConfigEntry("Transcript", true, m_useTranscript);
00540 
00541     //Grammatical
00542     //Precedence here is l10n / i18n / config file
00543     KConfig langCfg(KStandardDirs::locate("locale", QString::fromLatin1("%1/entry.desktop").arg(m_language)));
00544     KConfigGroup lang(&langCfg, "KCM Locale");
00545 #define read3ConfigBoolEntry(key, default, save) \
00546         save = entry.readEntry(key, default); \
00547         save = lang.readEntry(key, save); \
00548         save = cg.readEntry(key, save);
00549 
00550     read3ConfigBoolEntry("NounDeclension", false, m_nounDeclension);
00551     read3ConfigBoolEntry("DateMonthNamePossessive", false, m_dateMonthNamePossessive);
00552 
00553     initDayPeriods(cg);
00554 }
00555 
00556 void KLocalePrivate::initDayPeriods(const KConfigGroup &cg)
00557 {
00558     // Prefer any l10n file value for country/language,
00559     // otherwise default to language only value which will be filled in later when i18n available
00560 
00561     //Day Period are stored in config as one QStringList entry per Day Period
00562     //PeriodCode,LongName,ShortName,NarrowName,StartTime,EndTime,Offset,OffsetIfZero
00563     //where start and end time are in the format HH:MM:SS.MMM
00564 
00565     m_dayPeriods.clear();
00566     QString periodKey = QString::fromLatin1("DayPeriod1");
00567     int i = 1;
00568     while (cg.hasKey(periodKey)) {
00569         QStringList period = cg.readEntry(periodKey, QStringList());
00570         if (period.count() == 8) {
00571             m_dayPeriods.append(KDayPeriod(period[0], period[1], period[2], period[3],
00572                                            QTime::fromString(period[4], QString::fromLatin1("HH:mm:ss.zzz")),
00573                                            QTime::fromString(period[5], QString::fromLatin1("HH:mm:ss.zzz")),
00574                                            period[6].toInt(), period[7].toInt()));
00575         }
00576         i = i + 1;
00577         periodKey = QString::fromLatin1("DayPeriod%1").arg(i);
00578     }
00579 }
00580 
00581 bool KLocalePrivate::setCountry(const QString &country, KConfig *newConfig)
00582 {
00583     // Cache the valid countries list and add the default C as it is valid to use
00584     QStringList validCountries = allCountriesList();
00585     validCountries.append(defaultCountry());
00586 
00587     QString putativeCountry = country;
00588 
00589     if (putativeCountry.isEmpty()) {
00590         // An empty string means to use the system country
00591         putativeCountry = systemCountry();
00592         if (putativeCountry.isEmpty() || !validCountries.contains(putativeCountry, Qt::CaseInsensitive)) {
00593             // If the system country is not valid, use the default
00594             putativeCountry = defaultCountry();
00595         }
00596     } else if (!validCountries.contains(putativeCountry, Qt::CaseInsensitive)) {
00597         return false;
00598     }
00599 
00600     // Always save as lowercase, unless it's C when we want it uppercase
00601     if (putativeCountry.toLower() == defaultCountry().toLower()) {
00602         m_country = defaultCountry();
00603     } else {
00604         m_country = putativeCountry.toLower();
00605     }
00606 
00607     // Get rid of the old config, start again with the new
00608     m_config = KSharedConfig::Ptr();
00609     initConfig(newConfig);
00610 
00611     // Init all the settings
00612     initFormat();
00613 
00614     return true;
00615 }
00616 
00617 bool KLocalePrivate::setCountryDivisionCode(const QString &countryDivisionCode)
00618 {
00619     m_countryDivisionCode = countryDivisionCode;
00620     return true;
00621 }
00622 
00623 bool KLocalePrivate::setLanguage(const QString &language, KConfig *config)
00624 {
00625     QMutexLocker lock(kLocaleMutex());
00626     m_languageList.removeAll(language);
00627     m_languageList.prepend(language);   // let us consider this language to be the most important one
00628 
00629     m_language = language; // remember main language for shortcut evaluation
00630 
00631     // important when called from the outside and harmless when called before
00632     // populating the catalog name list
00633     updateCatalogs();
00634 
00635     // Get rid of the old config, start again with the new
00636     m_config = KSharedConfig::Ptr();
00637     initConfig(config);
00638 
00639     // Init the new format settings
00640     initFormat();
00641 
00642     // Maybe the mo-files for this language are empty, but in principle we can speak all languages
00643     return true;
00644 }
00645 
00646 // KDE5 Unlike the other setLanguage call this does not reparse the config so the localized config
00647 // settings for the new primary language will _not_ be loaded.  In KDE5 always keep the original
00648 // config so this can be reparsed when required.
00649 bool KLocalePrivate::setLanguage(const QStringList &languages)
00650 {
00651     QMutexLocker lock(kLocaleMutex());
00652     // This list might contain
00653     // 1) some empty strings that we have to eliminate
00654     // 2) duplicate entries like in de:fr:de, where we have to keep the first occurrence of a
00655     //    language in order to preserve the order of precenence of the user
00656     // 3) languages into which the application is not translated. For those languages we should not
00657     //    even load kdelibs.mo or kio.po. these languages have to be dropped. Otherwise we get
00658     //    strange side effects, e.g. with Hebrew: the right/left switch for languages that write
00659     //    from right to left (like Hebrew or Arabic) is set in kdelibs.mo. If you only have
00660     //    kdelibs.mo but nothing from appname.mo, you get a mostly English app with layout from
00661     //    right to left. That was considered to be a bug by the Hebrew translators.
00662     QStringList list;
00663     foreach(const QString &language, languages) {
00664         if (!language.isEmpty() && !list.contains(language) && isApplicationTranslatedInto(language)) {
00665             list.append(language);
00666         }
00667     }
00668 
00669     if (!list.contains(KLocale::defaultLanguage())) {
00670         // English should always be added as final possibility; this is important
00671         // for proper initialization of message text post-processors which are
00672         // needed for English too, like semantic to visual formatting, etc.
00673         list.append(KLocale::defaultLanguage());
00674     }
00675 
00676     m_language = list.first(); // keep this for shortcut evaluations
00677 
00678     m_languageList = list; // keep this new list of languages to use
00679 
00680     // important when called from the outside and harmless when called before populating the
00681     // catalog name list
00682     updateCatalogs();
00683 
00684     return true; // we found something. Maybe it's only English, but we found something
00685 }
00686 
00687 void KLocalePrivate::initCurrency()
00688 {
00689     if (m_currencyCode.isEmpty() || !KCurrencyCode::isValid(m_currencyCode)) {
00690         m_currencyCode = KLocale::defaultCurrencyCode();
00691     }
00692 
00693     if (!m_currency || m_currencyCode != m_currency->isoCurrencyCode() || !m_currency->isValid()) {
00694         delete m_currency;
00695         m_currency = new KCurrencyCode(m_currencyCode, m_language);
00696     }
00697 }
00698 
00699 void KLocalePrivate::setCurrencyCode(const QString &newCurrencyCode)
00700 {
00701     if (!newCurrencyCode.isEmpty() && newCurrencyCode != m_currency->isoCurrencyCode() &&
00702         KCurrencyCode::isValid(newCurrencyCode)) {
00703         m_currencyCode = newCurrencyCode;
00704         initCurrency();
00705     }
00706 }
00707 
00708 bool KLocalePrivate::isApplicationTranslatedInto(const QString &lang)
00709 {
00710     if (lang.isEmpty()) {
00711         return false;
00712     }
00713 
00714     if (lang == KLocale::defaultLanguage()) {
00715         // default language is always "installed"
00716         return true;
00717     }
00718 
00719     if (m_catalogName.isEmpty()) {
00720         kDebug() << "no appName!";
00721         return false;
00722     }
00723 
00724     if (!KCatalog::catalogLocaleDir(m_catalogName, lang).isEmpty()) {
00725         return true;
00726     }
00727     return false;
00728 }
00729 
00730 void KLocalePrivate::splitLocale(const QString &aLocale, QString &language, QString &country,
00731                                  QString &modifier, QString &charset)
00732 {
00733     QString locale = aLocale;
00734 
00735     language.clear();
00736     country.clear();
00737     modifier.clear();
00738     charset.clear();
00739 
00740     // In case there are several concatenated locale specifications,
00741     // truncate all but first.
00742     int f = locale.indexOf(QLatin1Char(':'));
00743     if (f >= 0) {
00744         locale.truncate(f);
00745     }
00746 
00747     f = locale.indexOf(QLatin1Char('.'));
00748     if (f >= 0) {
00749         charset = locale.mid(f + 1);
00750         locale.truncate(f);
00751     }
00752 
00753     f = locale.indexOf(QLatin1Char('@'));
00754     if (f >= 0) {
00755         modifier = locale.mid(f + 1);
00756         locale.truncate(f);
00757     }
00758 
00759     f = locale.indexOf(QLatin1Char('_'));
00760     if (f >= 0) {
00761         country = locale.mid(f + 1);
00762         locale.truncate(f);
00763     }
00764 
00765     language = locale;
00766 }
00767 
00768 QString KLocalePrivate::language() const
00769 {
00770     return m_language;
00771 }
00772 
00773 QString KLocalePrivate::country() const
00774 {
00775     return m_country;
00776 }
00777 
00778 QString KLocalePrivate::countryDivisionCode() const
00779 {
00780     if (m_countryDivisionCode.isEmpty()) {
00781         return country().toUpper();
00782     } else {
00783         return m_countryDivisionCode;
00784     }
00785 }
00786 
00787 KCurrencyCode *KLocalePrivate::currency()
00788 {
00789     if (!m_currency) {
00790         initCurrency();
00791     }
00792     return m_currency;
00793 }
00794 
00795 QString KLocalePrivate::currencyCode() const
00796 {
00797     return m_currencyCode;
00798 }
00799 
00800 void KLocalePrivate::insertCatalog(const QString &catalog)
00801 {
00802     QMutexLocker lock(kLocaleMutex());
00803     int pos = m_catalogNames.indexOf(KCatalogName(catalog));
00804     if (pos != -1) {
00805         ++m_catalogNames[pos].loadCount;
00806         return;
00807     }
00808 
00809     // Insert new catalog just before system catalogs, to preserve the
00810     // lowest priority of system catalogs.
00811     m_catalogNames.insert(m_catalogNames.size() - m_numberOfSysCatalogs, KCatalogName(catalog));
00812     updateCatalogs(); // evaluate the changed list and generate the necessary KCatalog objects
00813 }
00814 
00815 void KLocalePrivate::updateCatalogs()
00816 {
00817     // some changes have occurred. Maybe we have learned or forgotten some languages.
00818     // Maybe the language precedence has changed.
00819     // Maybe we have learned or forgotten some catalog names.
00820 
00821     QList<KCatalog> newCatalogs;
00822 
00823     // now iterate over all languages and all wanted catalog names and append or create them in the
00824     // right order the sequence must be e.g. nds/appname nds/kdelibs nds/kio de/appname de/kdelibs
00825     // de/kio etc. and not nds/appname de/appname nds/kdelibs de/kdelibs etc. Otherwise we would be
00826     // in trouble with a language sequende nds,<default>,de. In this case <default> must hide
00827     // everything after itself in the language list.
00828     foreach(const QString &lang, m_languageList) {
00829         if (lang == KLocale::defaultLanguage()) {
00830             // Default language has no catalogs (messages from the code),
00831             // so loading catalogs for languages below the default
00832             // would later confuse the fallback resolution.
00833             break;
00834         }
00835         foreach(const KCatalogName &name, m_catalogNames) {
00836             // create and add catalog for this name and language if it exists
00837             if (! KCatalog::catalogLocaleDir(name.name, lang).isEmpty()) {
00838                 newCatalogs.append(KCatalog(name.name, lang));
00839                 //kDebug(173) << "Catalog: " << name << ":" << lang;
00840             }
00841         }
00842     }
00843 
00844     // notify KLocalizedString of catalog update.
00845     m_catalogs = newCatalogs;
00846     KLocalizedString::notifyCatalogsUpdated(m_languageList, m_catalogNames);
00847 }
00848 
00849 void KLocalePrivate::removeCatalog(const QString &catalog)
00850 {
00851     QMutexLocker lock(kLocaleMutex());
00852     int pos = m_catalogNames.indexOf(KCatalogName(catalog));
00853     if (pos == -1) {
00854         return;
00855     }
00856     if (--m_catalogNames[pos].loadCount > 0) {
00857         return;
00858     }
00859     m_catalogNames.removeAt(pos);
00860     if (KGlobal::hasMainComponent()) {
00861         // walk through the KCatalog instances and weed out everything we no longer need
00862         updateCatalogs();
00863     }
00864 }
00865 
00866 void KLocalePrivate::setActiveCatalog(const QString &catalog)
00867 {
00868     QMutexLocker lock(kLocaleMutex());
00869     int pos = m_catalogNames.indexOf(KCatalogName(catalog));
00870     if (pos == -1) {
00871         return;
00872     }
00873     m_catalogNames.move(pos, 0);
00874     // walk through the KCatalog instances and adapt to the new order
00875     updateCatalogs();
00876 }
00877 
00878 void KLocalePrivate::translateRawFrom(const char *catname, const char *msgctxt, const char *msgid, const char *msgid_plural,
00879                                       unsigned long n, QString *language, QString *translation) const
00880 {
00881     if (!msgid || !msgid[0]) {
00882         kDebug(173) << "KLocale: trying to look up \"\" in catalog. "
00883         << "Fix the program" << endl;
00884         language->clear();
00885         translation->clear();
00886         return;
00887     }
00888     if (msgctxt && !msgctxt[0]) {
00889         kDebug(173) << "KLocale: trying to use \"\" as context to message. "
00890         << "Fix the program" << endl;
00891     }
00892     if (msgid_plural && !msgid_plural[0]) {
00893         kDebug(173) << "KLocale: trying to use \"\" as plural message. "
00894         << "Fix the program" << endl;
00895     }
00896 
00897     QMutexLocker locker(kLocaleMutex());
00898     // determine the fallback string
00899     QString fallback;
00900     if (msgid_plural == NULL) {
00901         fallback = QString::fromUtf8(msgid);
00902     } else {
00903         if (n == 1) {
00904             fallback = QString::fromUtf8(msgid);
00905         } else {
00906             fallback = QString::fromUtf8(msgid_plural);
00907         }
00908     }
00909     if (language) {
00910         *language = KLocale::defaultLanguage();
00911     }
00912     if (translation) {
00913         *translation = fallback;
00914     }
00915 
00916     // shortcut evaluation if default language is main language: do not consult the catalogs
00917     if (useDefaultLanguage()) {
00918         return;
00919     }
00920 
00921     const QList<KCatalog> catalogList = m_catalogs;
00922     QString catNameDecoded;
00923     if (catname != NULL) {
00924         catNameDecoded = QString::fromUtf8(catname);
00925     }
00926     for (QList<KCatalog>::ConstIterator it = catalogList.constBegin(); it != catalogList.constEnd();
00927          ++it) {
00928         // shortcut evaluation: once we have arrived at default language, we cannot consult
00929         // the catalog as it will not have an assiciated mo-file. For this default language we can
00930         // immediately pick the fallback string.
00931         if ((*it).language() == KLocale::defaultLanguage()) {
00932             return;
00933         }
00934 
00935         if (catNameDecoded.isEmpty() || catNameDecoded == (*it).name()) {
00936             QString text;
00937             if (msgctxt != NULL && msgid_plural != NULL) {
00938                 text = (*it).translateStrict(msgctxt, msgid, msgid_plural, n);
00939             } else if (msgid_plural != NULL) {
00940                 text = (*it).translateStrict(msgid, msgid_plural, n);
00941             } else if (msgctxt != NULL) {
00942                 text = (*it).translateStrict(msgctxt, msgid);
00943             } else {
00944                 text = (*it).translateStrict(msgid);
00945             }
00946 
00947             if (!text.isEmpty()) {
00948                 // we found it
00949                 if (language) {
00950                     *language = (*it).language();
00951                 }
00952                 if (translation) {
00953                     *translation = text;
00954                 }
00955                 return;
00956             }
00957         }
00958     }
00959 }
00960 
00961 QString KLocalePrivate::translateQt(const char *context, const char *sourceText, const char *comment) const
00962 {
00963     // Qt's context is normally the name of the class of the method which makes
00964     // the tr(sourceText) call. However, it can also be manually supplied via
00965     // translate(context, sourceText) call.
00966     //
00967     // Qt's sourceText is the actual message displayed to the user.
00968     //
00969     // Qt's comment is an optional argument of tr() and translate(), like
00970     // tr(sourceText, comment) and translate(context, sourceText, comment).
00971     //
00972     // We handle this in the following way:
00973     //
00974     // If the comment is given, then it is considered gettext's msgctxt, so a
00975     // context call is made.
00976     //
00977     // If the comment is not given, but context is given, then we treat it as
00978     // msgctxt only if it was manually supplied (the one in translate()) -- but
00979     // we don't know this, so we first try a context call, and if translation
00980     // is not found, we fallback to ordinary call.
00981     //
00982     // If neither comment nor context are given, it's just an ordinary call
00983     // on sourceText.
00984 
00985     if (!sourceText || !sourceText[0]) {
00986         kDebug(173) << "KLocale: trying to look up \"\" in catalog. "
00987         << "Fix the program" << endl;
00988         return QString();
00989     }
00990 
00991     if (useDefaultLanguage()) {
00992         return QString();
00993     }
00994 
00995     QString translation;
00996     QString language;
00997 
00998     // NOTE: Condition (language != defaultLanguage()) means that translation
00999     // was found, otherwise we got the original string back as translation.
01000 
01001     if (comment && comment[0]) {
01002         // Comment given, go for context call.
01003         translateRawFrom(0, comment, sourceText, 0, 0, &language, &translation);
01004     } else {
01005         // Comment not given, go for try-fallback with context.
01006         if (context && context[0]) {
01007             translateRawFrom(0, context, sourceText, 0, 0, &language, &translation);
01008         }
01009         if (language.isEmpty() || language == defaultLanguage()) {
01010             translateRawFrom(0, 0, sourceText, 0, 0, &language, &translation);
01011         }
01012     }
01013 
01014     if (language != defaultLanguage()) {
01015         return translation;
01016     }
01017 
01018     // No proper translation found, return empty according to Qt's expectation.
01019     return QString();
01020 }
01021 
01022 QList<KLocale::DigitSet> KLocalePrivate::allDigitSetsList() const
01023 {
01024     QList<KLocale::DigitSet> digitSets;
01025     digitSets.append(KLocale::ArabicDigits);
01026     digitSets.append(KLocale::ArabicIndicDigits);
01027     digitSets.append(KLocale::BengaliDigits);
01028     digitSets.append(KLocale::DevenagariDigits);
01029     digitSets.append(KLocale::EasternArabicIndicDigits);
01030     digitSets.append(KLocale::GujaratiDigits);
01031     digitSets.append(KLocale::GurmukhiDigits);
01032     digitSets.append(KLocale::KannadaDigits);
01033     digitSets.append(KLocale::KhmerDigits);
01034     digitSets.append(KLocale::MalayalamDigits);
01035     digitSets.append(KLocale::OriyaDigits);
01036     digitSets.append(KLocale::TamilDigits);
01037     digitSets.append(KLocale::TeluguDigits);
01038     digitSets.append(KLocale::ThaiDigits);
01039     qSort(digitSets);
01040     return digitSets;
01041 }
01042 
01043 QString KLocalePrivate::digitSetString(KLocale::DigitSet digitSet)
01044 {
01045     switch (digitSet) {
01046     case KLocale::ArabicIndicDigits:
01047         return QString::fromUtf8("٠١٢٣٤٥٦٧٨٩");
01048     case KLocale::BengaliDigits:
01049         return QString::fromUtf8("০১২৩৪৫৬৭৮৯");
01050     case KLocale::DevenagariDigits:
01051         return QString::fromUtf8("०१२३४५६७८९");
01052     case KLocale::EasternArabicIndicDigits:
01053         return QString::fromUtf8("۰۱۲۳۴۵۶۷۸۹");
01054     case KLocale::GujaratiDigits:
01055         return QString::fromUtf8("૦૧૨૩૪૫૬૭૮૯");
01056     case KLocale::GurmukhiDigits:
01057         return QString::fromUtf8("੦੧੨੩੪੫੬੭੮੯");
01058     case KLocale::KannadaDigits:
01059         return QString::fromUtf8("೦೧೨೩೪೫೬೭೮೯");
01060     case KLocale::KhmerDigits:
01061         return QString::fromUtf8("០១២៣៤៥៦៧៨៩");
01062     case KLocale::MalayalamDigits:
01063         return QString::fromUtf8("൦൧൨൩൪൫൬൭൮൯");
01064     case KLocale::OriyaDigits:
01065         return QString::fromUtf8("୦୧୨୩୪୫୬୭୮୯");
01066     case KLocale::TamilDigits:
01067         return QString::fromUtf8("௦௧௨௩௪௫௬௭௮");
01068     case KLocale::TeluguDigits:
01069         return QString::fromUtf8("౦౧౨౩౪౫౬౭౯");
01070     case KLocale::ThaiDigits:
01071         return QString::fromUtf8("๐๑๒๓๔๕๖๗๘๙");
01072     default:
01073         return QString::fromUtf8("0123456789");
01074     }
01075 }
01076 
01077 QString KLocalePrivate::digitSetToName(KLocale::DigitSet digitSet, bool withDigits) const
01078 {
01079     QString name;
01080     switch (digitSet) {
01081     case KLocale::ArabicIndicDigits:
01082         name = i18nc("digit set", "Arabic-Indic");
01083         break;
01084     case KLocale::BengaliDigits:
01085         name = i18nc("digit set", "Bengali");
01086         break;
01087     case KLocale::DevenagariDigits:
01088         name = i18nc("digit set", "Devanagari");
01089         break;
01090     case KLocale::EasternArabicIndicDigits:
01091         name = i18nc("digit set", "Eastern Arabic-Indic");
01092         break;
01093     case KLocale::GujaratiDigits:
01094         name = i18nc("digit set", "Gujarati");
01095         break;
01096     case KLocale::GurmukhiDigits:
01097         name = i18nc("digit set", "Gurmukhi");
01098         break;
01099     case KLocale::KannadaDigits:
01100         name = i18nc("digit set", "Kannada");
01101         break;
01102     case KLocale::KhmerDigits:
01103         name = i18nc("digit set", "Khmer");
01104         break;
01105     case KLocale::MalayalamDigits:
01106         name = i18nc("digit set", "Malayalam");
01107         break;
01108     case KLocale::OriyaDigits:
01109         name = i18nc("digit set", "Oriya");
01110         break;
01111     case KLocale::TamilDigits:
01112         name = i18nc("digit set", "Tamil");
01113         break;
01114     case KLocale::TeluguDigits:
01115         name = i18nc("digit set", "Telugu");
01116         break;
01117     case KLocale::ThaiDigits:
01118         name = i18nc("digit set", "Thai");
01119         break;
01120     default:
01121         name = i18nc("digit set", "Arabic");
01122     }
01123     if (withDigits) {
01124         QString digits = digitSetString(digitSet);
01125         QString nameWithDigits = i18nc("name of digit set with digit string, "
01126                                        "e.g. 'Arabic (0123456789)'", "%1 (%2)", name, digits);
01127         return nameWithDigits;
01128     } else {
01129         return name;
01130     }
01131 }
01132 
01133 QString KLocalePrivate::convertDigits(const QString &str, KLocale::DigitSet digitSet, bool ignoreContext) const
01134 {
01135     if (!ignoreContext) {
01136         // Fall back to Western Arabic digits if requested digit set
01137         // is not appropriate for current application language.
01138         // FIXME: Temporary until full language-sensitivity implemented.
01139         KLocaleStaticData *s = staticData;
01140         if (m_languageSensitiveDigits && !s->languagesUsingDigitSet[digitSet].contains(m_language)) {
01141             digitSet = KLocale::ArabicDigits;
01142         }
01143     }
01144 
01145     QString nstr;
01146     QString digitDraw = digitSetString(digitSet);
01147     foreach(const QChar &c, str) {
01148         if (c.isDigit()) {
01149             nstr += digitDraw[c.digitValue()];
01150         } else {
01151             nstr += c;
01152         }
01153     }
01154     return nstr;
01155 }
01156 
01157 QString KLocalePrivate::toArabicDigits(const QString &str)
01158 {
01159     QString nstr;
01160     foreach(const QChar &c, str) {
01161         if (c.isDigit()) {
01162             nstr += QChar('0' + c.digitValue());
01163         } else {
01164             nstr += c;
01165         }
01166     }
01167     return nstr;
01168 }
01169 
01170 bool KLocalePrivate::nounDeclension() const
01171 {
01172     return m_nounDeclension;
01173 }
01174 
01175 bool KLocalePrivate::dateMonthNamePossessive() const
01176 {
01177     return m_dateMonthNamePossessive;
01178 }
01179 
01180 int KLocalePrivate::weekStartDay() const
01181 {
01182     return m_weekStartDay;
01183 }
01184 
01185 int KLocalePrivate::workingWeekStartDay() const
01186 {
01187     return m_workingWeekStartDay;
01188 }
01189 
01190 int KLocalePrivate::workingWeekEndDay() const
01191 {
01192     return m_workingWeekEndDay;
01193 }
01194 
01195 int KLocalePrivate::weekDayOfPray() const
01196 {
01197     return m_weekDayOfPray;
01198 }
01199 
01200 int KLocalePrivate::decimalPlaces() const
01201 {
01202     return m_decimalPlaces;
01203 }
01204 
01205 QString KLocalePrivate::decimalSymbol() const
01206 {
01207     return m_decimalSymbol;
01208 }
01209 
01210 QString KLocalePrivate::thousandsSeparator() const
01211 {
01212     return m_thousandsSeparator;
01213 }
01214 
01215 QString KLocalePrivate::currencySymbol() const
01216 {
01217     return m_currencySymbol;
01218 }
01219 
01220 QString KLocalePrivate::monetaryDecimalSymbol() const
01221 {
01222     return m_monetaryDecimalSymbol;
01223 }
01224 
01225 QString KLocalePrivate::monetaryThousandsSeparator() const
01226 {
01227     return m_monetaryThousandsSeparator;
01228 }
01229 
01230 QString KLocalePrivate::positiveSign() const
01231 {
01232     return m_positiveSign;
01233 }
01234 
01235 QString KLocalePrivate::negativeSign() const
01236 {
01237     return m_negativeSign;
01238 }
01239 
01240 /* Just copy to keep the diff looking clean, delete later
01241 int KLocale::fracDigits() const
01242 {
01243     return monetaryDecimalPlaces();
01244 }
01245 */
01246 
01247 int KLocalePrivate::monetaryDecimalPlaces() const
01248 {
01249     return m_monetaryDecimalPlaces;
01250 }
01251 
01252 bool KLocalePrivate::positivePrefixCurrencySymbol() const
01253 {
01254     return m_positivePrefixCurrencySymbol;
01255 }
01256 
01257 bool KLocalePrivate::negativePrefixCurrencySymbol() const
01258 {
01259     return m_negativePrefixCurrencySymbol;
01260 }
01261 
01262 KLocale::SignPosition KLocalePrivate::positiveMonetarySignPosition() const
01263 {
01264     return m_positiveMonetarySignPosition;
01265 }
01266 
01267 KLocale::SignPosition KLocalePrivate::negativeMonetarySignPosition() const
01268 {
01269     return m_negativeMonetarySignPosition;
01270 }
01271 
01272 static inline void put_it_in(QChar *buffer, int &index, const QString &s)
01273 {
01274     for (int l = 0; l < s.length(); l++) {
01275         buffer[index++] = s.at(l);
01276     }
01277 }
01278 
01279 static inline void put_it_in(QChar *buffer, int &index, int number)
01280 {
01281     buffer[index++] = number / 10 + '0';
01282     buffer[index++] = number % 10 + '0';
01283 }
01284 
01285 // insert (thousands)-"separator"s into the non-fractional part of str
01286 static void _insertSeparator(QString &str, const QString &separator, const QString &decimalSymbol)
01287 {
01288     // leave fractional part untouched
01289     const int decimalSymbolPos = str.indexOf(decimalSymbol);
01290     const int start = decimalSymbolPos == -1 ? str.length() : decimalSymbolPos;
01291     for (int pos = start - 3; pos > 0; pos -= 3) {
01292         str.insert(pos, separator);
01293     }
01294 }
01295 
01296 QString KLocalePrivate::formatMoney(double num, const QString &symbol, int precision) const
01297 {
01298     // some defaults
01299     QString currencyString = symbol;
01300     if (symbol.isNull()) {
01301         currencyString = currencySymbol();
01302     }
01303     if (precision < 0) {
01304         precision = monetaryDecimalPlaces();
01305     }
01306 
01307     // the number itself
01308     bool neg = num < 0;
01309     QString res = QString::number(neg ? -num : num, 'f', precision);
01310 
01311     // Replace dot with locale decimal separator
01312     res.replace(QLatin1Char('.'), monetaryDecimalSymbol());
01313 
01314     // Insert the thousand separators
01315     _insertSeparator(res, monetaryThousandsSeparator(), monetaryDecimalSymbol());
01316 
01317     // set some variables we need later
01318     int signpos = neg
01319                   ? negativeMonetarySignPosition()
01320                   : positiveMonetarySignPosition();
01321     QString sign = neg
01322                    ? negativeSign()
01323                    : positiveSign();
01324 
01325     switch (signpos) {
01326     case KLocale::ParensAround:
01327         res.prepend(QLatin1Char('('));
01328         res.append(QLatin1Char(')'));
01329         break;
01330     case KLocale::BeforeQuantityMoney:
01331         res.prepend(sign);
01332         break;
01333     case KLocale::AfterQuantityMoney:
01334         res.append(sign);
01335         break;
01336     case KLocale::BeforeMoney:
01337         currencyString.prepend(sign);
01338         break;
01339     case KLocale::AfterMoney:
01340         currencyString.append(sign);
01341         break;
01342     }
01343 
01344     if (neg ? negativePrefixCurrencySymbol() :
01345             positivePrefixCurrencySymbol()) {
01346         res.prepend(QLatin1Char(' '));
01347         res.prepend(currencyString);
01348     } else {
01349         res.append(QLatin1Char(' '));
01350         res.append(currencyString);
01351     }
01352 
01353     // Convert to target digit set.
01354     res = convertDigits(res, m_monetaryDigitSet);
01355 
01356     return res;
01357 }
01358 
01359 
01360 QString KLocalePrivate::formatNumber(double num, int precision) const
01361 {
01362     if (precision < 0) {
01363         precision = decimalPlaces();
01364     }
01365     // no need to round since QString::number does this for us
01366     return formatNumber(QString::number(num, 'f', precision), false, 0);
01367 }
01368 
01369 QString KLocalePrivate::formatLong(long num) const
01370 {
01371     return formatNumber((double)num, 0);
01372 }
01373 
01374 // increase the digit at 'position' by one
01375 static void _inc_by_one(QString &str, int position)
01376 {
01377     for (int i = position; i >= 0; i--) {
01378         char last_char = str[i].toLatin1();
01379         switch (last_char) {
01380         case '0':
01381             str[i] = '1';
01382             break;
01383         case '1':
01384             str[i] = '2';
01385             break;
01386         case '2':
01387             str[i] = '3';
01388             break;
01389         case '3':
01390             str[i] = '4';
01391             break;
01392         case '4':
01393             str[i] = '5';
01394             break;
01395         case '5':
01396             str[i] = '6';
01397             break;
01398         case '6':
01399             str[i] = '7';
01400             break;
01401         case '7':
01402             str[i] = '8';
01403             break;
01404         case '8':
01405             str[i] = '9';
01406             break;
01407         case '9':
01408             str[i] = '0';
01409             if (i == 0) str.prepend(QLatin1Char('1'));
01410             continue;
01411         case '.':
01412             continue;
01413         }
01414         break;
01415     }
01416 }
01417 
01418 // Cut off if more digits in fractional part than 'precision'
01419 static void _round(QString &str, int precision)
01420 {
01421     int decimalSymbolPos = str.indexOf(QLatin1Char('.'));
01422 
01423     if (decimalSymbolPos == -1) {
01424         if (precision == 0)  return;
01425         else if (precision > 0) { // add dot if missing (and needed)
01426             str.append(QLatin1Char('.'));
01427             decimalSymbolPos = str.length() - 1;
01428         }
01429     }
01430     // fill up with more than enough zeroes (in case fractional part too short)
01431     str.reserve(str.length() + precision);
01432     for (int i = 0; i < precision; ++i)
01433         str.append(QLatin1Char('0'));
01434 
01435     // Now decide whether to round up or down
01436     char last_char = str[decimalSymbolPos + precision + 1].toLatin1();
01437     switch (last_char) {
01438     case '0':
01439     case '1':
01440     case '2':
01441     case '3':
01442     case '4':
01443         // nothing to do, rounding down
01444         break;
01445     case '5':
01446     case '6':
01447     case '7':
01448     case '8':
01449     case '9':
01450         _inc_by_one(str, decimalSymbolPos + precision);
01451         break;
01452     default:
01453         break;
01454     }
01455 
01456     decimalSymbolPos = str.indexOf(QLatin1Char('.'));
01457     str.truncate(decimalSymbolPos + precision + 1);
01458 
01459     // if precision == 0 delete also '.'
01460     if (precision == 0) {
01461         str = str.left(decimalSymbolPos);
01462     }
01463 
01464     str.squeeze();
01465 }
01466 
01467 QString KLocalePrivate::formatNumber(const QString &numStr, bool round, int precision) const
01468 {
01469     QString tmpString = numStr;
01470 
01471     if (precision < 0) {
01472         precision = decimalPlaces();
01473     }
01474 
01475     // Skip the sign (for now)
01476     const bool neg = (tmpString[0] == QLatin1Char('-'));
01477     if (neg || tmpString[0] == QLatin1Char('+')) {
01478         tmpString.remove(0, 1);
01479     }
01480 
01481     //kDebug(173)<<"tmpString:"<<tmpString;
01482 
01483     // Split off exponential part (including 'e'-symbol)
01484     const int expPos = tmpString.indexOf(QLatin1Char('e')); // -1 if not found
01485     QString mantString = tmpString.left(expPos); // entire string if no 'e' found
01486     QString expString;
01487     if (expPos > -1) {
01488         expString = tmpString.mid(expPos); // includes the 'e', or empty if no 'e'
01489         if (expString.length() == 1) {
01490             expString.clear();
01491         }
01492     }
01493 
01494     //kDebug(173)<<"mantString:"<<mantString;
01495     //kDebug(173)<<"expString:"<<expString;
01496     if (mantString.isEmpty() || !mantString[0].isDigit()) {// invalid number
01497         mantString = QLatin1Char('0');
01498     }
01499 
01500     if (round) {
01501         _round(mantString, precision);
01502     }
01503 
01504     // Replace dot with locale decimal separator
01505     mantString.replace(QLatin1Char('.'), decimalSymbol());
01506 
01507     // Insert the thousand separators
01508     _insertSeparator(mantString, thousandsSeparator(), decimalSymbol());
01509 
01510     // How can we know where we should put the sign?
01511     mantString.prepend(neg ? negativeSign() : positiveSign());
01512 
01513     // Convert to target digit set.
01514     if (digitSet() != KLocale::ArabicDigits) {
01515         mantString = convertDigits(mantString, digitSet());
01516         expString = convertDigits(expString, digitSet());
01517     }
01518 
01519     return mantString + expString;
01520 }
01521 
01522 // Returns a list of already translated units to use later in formatByteSize
01523 // and friends.  Account for every unit in KLocale::BinarySizeUnits
01524 QList<QString> KLocalePrivate::dialectUnitsList(KLocale::BinaryUnitDialect dialect)
01525 {
01526     QList<QString> binaryUnits;
01527     QString s; // Used in CACHE_BYTE_FMT macro defined shortly
01528 
01529     // Adds a given translation to the binaryUnits list.
01530 #define CACHE_BYTE_FMT(ctxt_text) \
01531         translateRawFrom(0, ctxt_text, 0, 0, 0, &s); \
01532         binaryUnits.append(s);
01533 
01534     // Do not remove i18n: comments below, they are used by the
01535     // translators.
01536 
01537     // This prefix is shared by all current dialects.
01538     // i18n: Dumb message, avoid any markup or scripting.
01539     CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in bytes", "%1 B"));
01540 
01541     switch (dialect) {
01542     case KLocale::MetricBinaryDialect:
01543         // i18n: Dumb message, avoid any markup or scripting.
01544         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 1000 bytes", "%1 kB"));
01545         // i18n: Dumb message, avoid any markup or scripting.
01546         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 10^6 bytes", "%1 MB"));
01547         // i18n: Dumb message, avoid any markup or scripting.
01548         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 10^9 bytes", "%1 GB"));
01549         // i18n: Dumb message, avoid any markup or scripting.
01550         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 10^12 bytes", "%1 TB"));
01551         // i18n: Dumb message, avoid any markup or scripting.
01552         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 10^15 bytes", "%1 PB"));
01553         // i18n: Dumb message, avoid any markup or scripting.
01554         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 10^18 bytes", "%1 EB"));
01555         // i18n: Dumb message, avoid any markup or scripting.
01556         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 10^21 bytes", "%1 ZB"));
01557         // i18n: Dumb message, avoid any markup or scripting.
01558         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 10^24 bytes", "%1 YB"));
01559         break;
01560 
01561     case KLocale::JEDECBinaryDialect:
01562         // i18n: Dumb message, avoid any markup or scripting.
01563         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 1024 bytes", "%1 KB"));
01564         // i18n: Dumb message, avoid any markup or scripting.
01565         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 2^20 bytes", "%1 MB"));
01566         // i18n: Dumb message, avoid any markup or scripting.
01567         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 2^30 bytes", "%1 GB"));
01568         // i18n: Dumb message, avoid any markup or scripting.
01569         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 2^40 bytes", "%1 TB"));
01570         // i18n: Dumb message, avoid any markup or scripting.
01571         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 2^50 bytes", "%1 PB"));
01572         // i18n: Dumb message, avoid any markup or scripting.
01573         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 2^60 bytes", "%1 EB"));
01574         // i18n: Dumb message, avoid any markup or scripting.
01575         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 2^70 bytes", "%1 ZB"));
01576         // i18n: Dumb message, avoid any markup or scripting.
01577         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 2^80 bytes", "%1 YB"));
01578         break;
01579 
01580     case KLocale::IECBinaryDialect:
01581     default:
01582         // i18n: Dumb message, avoid any markup or scripting.
01583         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 1024 bytes", "%1 KiB"));
01584         // i18n: Dumb message, avoid any markup or scripting.
01585         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 2^20 bytes", "%1 MiB"));
01586         // i18n: Dumb message, avoid any markup or scripting.
01587         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 2^30 bytes", "%1 GiB"));
01588         // i18n: Dumb message, avoid any markup or scripting.
01589         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 2^40 bytes", "%1 TiB"));
01590         // i18n: Dumb message, avoid any markup or scripting.
01591         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 2^50 bytes", "%1 PiB"));
01592         // i18n: Dumb message, avoid any markup or scripting.
01593         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 2^60 bytes", "%1 EiB"));
01594         // i18n: Dumb message, avoid any markup or scripting.
01595         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 2^70 bytes", "%1 ZiB"));
01596         // i18n: Dumb message, avoid any markup or scripting.
01597         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 2^80 bytes", "%1 YiB"));
01598         break;
01599     }
01600 
01601     return binaryUnits;
01602 }
01603 
01604 QString KLocalePrivate::formatByteSize(double size, int precision, KLocale::BinaryUnitDialect dialect,
01605                                        KLocale::BinarySizeUnits specificUnit)
01606 {
01607     // Error checking
01608     if (dialect <= KLocale::DefaultBinaryDialect || dialect > KLocale::LastBinaryDialect) {
01609         dialect = m_binaryUnitDialect;
01610     }
01611 
01612     if (specificUnit < KLocale::DefaultBinaryUnits || specificUnit > KLocale::UnitLastUnit) {
01613         specificUnit = KLocale::DefaultBinaryUnits;
01614     }
01615 
01616     // Choose appropriate units.
01617     QList<QString> dialectUnits;
01618     if (dialect == m_binaryUnitDialect) {
01619         // Cache default units for speed
01620         if (m_byteSizeFmt.size() == 0) {
01621             QMutexLocker lock(kLocaleMutex());
01622 
01623             // We only cache the user's default dialect.
01624             m_byteSizeFmt = dialectUnitsList(m_binaryUnitDialect);
01625         }
01626 
01627         dialectUnits = m_byteSizeFmt;
01628     } else {
01629         dialectUnits = dialectUnitsList(dialect);
01630     }
01631 
01632     int unit = 0; // Selects what unit to use from cached list
01633     double multiplier = 1024.0;
01634 
01635     if (dialect == KLocale::MetricBinaryDialect) {
01636         multiplier = 1000.0;
01637     }
01638 
01639     // If a specific unit conversion is given, use it directly.  Otherwise
01640     // search until the result is in [0, multiplier) (or out of our range).
01641     if (specificUnit == KLocale::DefaultBinaryUnits) {
01642         while (size >= multiplier && unit < (int) KLocale::UnitYottaByte) {
01643             size /= multiplier;
01644             unit++;
01645         }
01646     } else {
01647         // A specific unit is in use
01648         unit = static_cast<int>(specificUnit);
01649         if (unit > 0) {
01650             size /= pow(multiplier, unit);
01651         }
01652     }
01653 
01654     if (unit == 0) {
01655         // Bytes, no rounding
01656         return dialectUnits[unit].arg(formatNumber(size, 0));
01657     }
01658 
01659     return dialectUnits[unit].arg(formatNumber(size, precision));
01660 }
01661 
01662 QString KLocalePrivate::formatByteSize(double size)
01663 {
01664     return formatByteSize(size, 1);
01665 }
01666 
01667 KLocale::BinaryUnitDialect KLocalePrivate::binaryUnitDialect() const
01668 {
01669     return m_binaryUnitDialect;
01670 }
01671 
01672 void KLocalePrivate::setBinaryUnitDialect(KLocale::BinaryUnitDialect newDialect)
01673 {
01674     if (newDialect > KLocale::DefaultBinaryDialect && newDialect <= KLocale::LastBinaryDialect) {
01675         QMutexLocker lock(kLocaleMutex());
01676         m_binaryUnitDialect = newDialect;
01677         m_byteSizeFmt.clear(); // Reset cached translations.
01678     }
01679 }
01680 
01681 QString KLocalePrivate::formatDuration(unsigned long mSec) const
01682 {
01683     if (mSec >= 24*3600000) {
01684         return i18nc("@item:intext %1 is a real number, e.g. 1.23 days", "%1 days",
01685                      formatNumber(mSec / (24 * 3600000.0), 2));
01686     } else if (mSec >= 3600000) {
01687         return i18nc("@item:intext %1 is a real number, e.g. 1.23 hours", "%1 hours",
01688                      formatNumber(mSec / 3600000.0, 2));
01689     } else if (mSec >= 60000) {
01690         return i18nc("@item:intext %1 is a real number, e.g. 1.23 minutes", "%1 minutes",
01691                      formatNumber(mSec / 60000.0, 2));
01692     } else if (mSec >= 1000) {
01693         return i18nc("@item:intext %1 is a real number, e.g. 1.23 seconds", "%1 seconds",
01694                      formatNumber(mSec / 1000.0, 2));
01695     }
01696     return i18ncp("@item:intext", "%1 millisecond", "%1 milliseconds", mSec);
01697 }
01698 
01699 QString KLocalePrivate::formatSingleDuration(KLocalePrivate::DurationType durationType, int n)
01700 {
01701     switch (durationType) {
01702     case KLocalePrivate::DaysDurationType:
01703         return i18ncp("@item:intext", "1 day", "%1 days", n);
01704     case KLocalePrivate::HoursDurationType:
01705         return i18ncp("@item:intext", "1 hour", "%1 hours", n);
01706     case KLocalePrivate::MinutesDurationType:
01707         return i18ncp("@item:intext", "1 minute", "%1 minutes", n);
01708     case KLocalePrivate::SecondsDurationType:
01709         return i18ncp("@item:intext", "1 second", "%1 seconds", n);
01710     }
01711     return QString();
01712 }
01713 
01714 QString KLocalePrivate::prettyFormatDuration(unsigned long mSec) const
01715 {
01716     unsigned long ms = mSec;
01717     int days = ms / (24 * 3600000);
01718     ms = ms % (24 * 3600000);
01719     int hours = ms / 3600000;
01720     ms = ms % 3600000;
01721     int minutes = ms / 60000;
01722     ms = ms % 60000;
01723     int seconds = qRound(ms / 1000.0);
01724 
01725     // Handle correctly problematic case #1 (look at KLocaleTest::prettyFormatDuration()
01726     // at klocaletest.cpp)
01727     if (seconds == 60) {
01728         return prettyFormatDuration(mSec - ms + 60000);
01729     }
01730 
01731     if (days && hours) {
01732         return i18nc("@item:intext days and hours. This uses the previous item:intext messages. If this does not fit the grammar of your language please contact the i18n team to solve the problem",
01733                      "%1 and %2", formatSingleDuration(KLocalePrivate::DaysDurationType, days),
01734                      formatSingleDuration(KLocalePrivate::HoursDurationType, hours));
01735     } else if (days) {
01736         return formatSingleDuration(KLocalePrivate::DaysDurationType, days);
01737     } else if (hours && minutes) {
01738         return i18nc("@item:intext hours and minutes. This uses the previous item:intext messages. If this does not fit the grammar of your language please contact the i18n team to solve the problem",
01739                      "%1 and %2",
01740                      formatSingleDuration(KLocalePrivate::HoursDurationType, hours),
01741                      formatSingleDuration(KLocalePrivate::MinutesDurationType, minutes));
01742     } else if (hours) {
01743         return formatSingleDuration(KLocalePrivate::HoursDurationType, hours);
01744     } else if (minutes && seconds) {
01745         return i18nc("@item:intext minutes and seconds. This uses the previous item:intext messages. If this does not fit the grammar of your language please contact the i18n team to solve the problem",
01746                      "%1 and %2",
01747                      formatSingleDuration(KLocalePrivate::MinutesDurationType, minutes),
01748                      formatSingleDuration(KLocalePrivate::SecondsDurationType, seconds));
01749     } else if (minutes) {
01750         return formatSingleDuration(KLocalePrivate::MinutesDurationType, minutes);
01751     } else {
01752         return formatSingleDuration(KLocalePrivate::SecondsDurationType, seconds);
01753     }
01754 }
01755 
01756 QString KLocalePrivate::formatDate(const QDate &date, KLocale::DateFormat format)
01757 {
01758     return calendar()->formatDate(date, format);
01759 }
01760 
01761 void KLocalePrivate::setMainCatalog(const char *catalog)
01762 {
01763     KLocaleStaticData *s = staticData;
01764     s->maincatalog = QString::fromUtf8(catalog);
01765 }
01766 
01767 double KLocalePrivate::readNumber(const QString &_str, bool * ok) const
01768 {
01769     QString str = _str.trimmed();
01770     bool neg = str.indexOf(negativeSign()) == 0;
01771     if (neg) {
01772         str.remove(0, negativeSign().length());
01773     }
01774 
01775     /* will hold the scientific notation portion of the number.
01776        Example, with 2.34E+23, exponentialPart == "E+23"
01777     */
01778     QString exponentialPart;
01779     int EPos;
01780 
01781     EPos = str.indexOf(QLatin1Char('E'), 0, Qt::CaseInsensitive);
01782 
01783     if (EPos != -1) {
01784         exponentialPart = str.mid(EPos);
01785         str = str.left(EPos);
01786     }
01787 
01788     int pos = str.indexOf(decimalSymbol());
01789     QString major;
01790     QString minor;
01791     if (pos == -1) {
01792         major = str;
01793     } else {
01794         major = str.left(pos);
01795         minor = str.mid(pos + decimalSymbol().length());
01796     }
01797 
01798     // Remove thousand separators
01799     int thlen = thousandsSeparator().length();
01800     int lastpos = 0;
01801     while ((pos = major.indexOf(thousandsSeparator())) > 0) {
01802         // e.g. 12,,345,,678,,922 Acceptable positions (from the end) are 5, 10, 15...
01803         // i.e. (3+thlen)*N
01804         int fromEnd = major.length() - pos;
01805         if (fromEnd % (3 + thlen) != 0 || // Needs to be a multiple, otherwise it's an error
01806             pos - lastpos > 3 ||          // More than 3 digits between two separators -> error
01807             pos == 0 ||                   // Can't start with a separator
01808             (lastpos > 0 && pos - lastpos != 3)) { // Must have exactly 3 digits between 2 separators
01809             if (ok) {
01810                 *ok = false;
01811             }
01812             return 0.0;
01813         }
01814 
01815         lastpos = pos;
01816         major.remove(pos, thlen);
01817     }
01818     // Must have exactly 3 digits after the last separator
01819     if (lastpos > 0 && major.length() - lastpos != 3) {
01820         if (ok) {
01821             *ok = false;
01822         }
01823         return 0.0;
01824     }
01825 
01826     QString tot;
01827     if (neg) {
01828         tot = QLatin1Char('-');
01829     }
01830 
01831     tot += major + QLatin1Char('.') + minor + exponentialPart;
01832 
01833     tot = toArabicDigits(tot);
01834 
01835     return tot.toDouble(ok);
01836 }
01837 
01838 double KLocalePrivate::readMoney(const QString &_str, bool *ok) const
01839 {
01840     QString str = _str.trimmed();
01841     bool neg = false;
01842     bool currencyFound = false;
01843     QString symbol = currencySymbol();
01844 
01845     // First try removing currency symbol from either end
01846     int pos = str.indexOf(symbol);
01847     if (pos == 0 || pos == (int) str.length() - symbol.length()) {
01848         str.remove(pos, symbol.length());
01849         str = str.trimmed();
01850         currencyFound = true;
01851     }
01852     if (str.isEmpty()) {
01853         if (ok) {
01854             *ok = false;
01855         }
01856         return 0;
01857     }
01858     // Then try removing negative sign from either end
01859     // (with a special case for parenthesis)
01860     if (negativeMonetarySignPosition() == KLocale::ParensAround) {
01861         if (str[0] == QLatin1Char('(') && str[str.length()-1] == QLatin1Char(')')) {
01862             neg = true;
01863             str.remove(str.length() - 1, 1);
01864             str.remove(0, 1);
01865         }
01866     } else {
01867         int i1 = str.indexOf(negativeSign());
01868         if (i1 == 0 || i1 == (int) str.length() - 1) {
01869             neg = true;
01870             str.remove(i1, negativeSign().length());
01871         }
01872     }
01873     if (neg) str = str.trimmed();
01874 
01875     // Finally try again for the currency symbol, if we didn't find
01876     // it already (because of the negative sign being in the way).
01877     if (!currencyFound) {
01878         pos = str.indexOf(symbol);
01879         if (pos == 0 || pos == (int) str.length() - symbol.length()) {
01880             str.remove(pos, symbol.length());
01881             str = str.trimmed();
01882         }
01883     }
01884 
01885     // And parse the rest as a number
01886     pos = str.indexOf(monetaryDecimalSymbol());
01887     QString major;
01888     QString minior;
01889     if (pos == -1) {
01890         major = str;
01891     } else {
01892         major = str.left(pos);
01893         minior = str.mid(pos + monetaryDecimalSymbol().length());
01894     }
01895 
01896     // Remove thousand separators
01897     int thlen = monetaryThousandsSeparator().length();
01898     int lastpos = 0;
01899     while ((pos = major.indexOf(monetaryThousandsSeparator())) > 0) {
01900         // e.g. 12,,345,,678,,922 Acceptable positions (from the end) are 5, 10, 15...
01901         // i.e. (3+thlen)*N
01902         int fromEnd = major.length() - pos;
01903         if (fromEnd % (3 + thlen) != 0 || // Needs to be a multiple, otherwise it's an error
01904             pos - lastpos > 3 ||          // More than 3 digits between two separators -> error
01905             pos == 0 ||                   // Can't start with a separator
01906             (lastpos > 0 && pos - lastpos != 3)) { // Must have exactly 3 digits between two separators
01907             if (ok) {
01908                 *ok = false;
01909             }
01910             return 0.0;
01911         }
01912         lastpos = pos;
01913         major.remove(pos, thlen);
01914     }
01915     // Must have exactly 3 digits after the last separator
01916     if (lastpos > 0 && major.length() - lastpos != 3) {
01917         if (ok) {
01918             *ok = false;
01919         }
01920         return 0.0;
01921     }
01922 
01923     QString tot;
01924     if (neg) {
01925         tot = QLatin1Char('-');
01926     }
01927     tot += major + QLatin1Char('.') + minior;
01928     tot = toArabicDigits(tot);
01929     return tot.toDouble(ok);
01930 }
01931 
01938 static int readInt(const QString &str, int &pos)
01939 {
01940     if (!str.at(pos).isDigit()) {
01941         return -1;
01942     }
01943     int result = 0;
01944     for (; str.length() > pos && str.at(pos).isDigit(); ++pos) {
01945         result *= 10;
01946         result += str.at(pos).digitValue();
01947     }
01948 
01949     return result;
01950 }
01951 
01952 QDate KLocalePrivate::readDate(const QString &intstr, bool *ok)
01953 {
01954     return calendar()->readDate(intstr, ok);
01955 }
01956 
01957 QDate KLocalePrivate::readDate(const QString &intstr, KLocale::ReadDateFlags flags, bool *ok)
01958 {
01959     return calendar()->readDate(intstr, flags, ok);
01960 }
01961 
01962 QDate KLocalePrivate::readDate(const QString &intstr, const QString &fmt, bool *ok)
01963 {
01964     return calendar()->readDate(intstr, fmt, ok);
01965 }
01966 
01967 QTime KLocalePrivate::readTime(const QString &intstr, bool *ok) const
01968 {
01969     QTime time = readLocaleTime(intstr, ok, KLocale::TimeDefault, KLocale::ProcessStrict);
01970     if (time.isValid()) {
01971         return time;
01972     }
01973     return readLocaleTime(intstr, ok, KLocale::TimeWithoutSeconds, KLocale::ProcessStrict);
01974 }
01975 
01976 QTime KLocalePrivate::readTime(const QString &intstr, KLocale::ReadTimeFlags flags, bool *ok) const
01977 {
01978     return readLocaleTime(intstr, ok, (flags == KLocale::WithSeconds) ? KLocale::TimeDefault : KLocale::TimeWithoutSeconds,
01979                           KLocale::ProcessStrict);
01980 }
01981 
01982 // remove the first occurrence of the 2-character string
01983 // strip2char from inout and if found, also remove one preceding
01984 // punctuation character and arbitrary number of spaces.
01985 static void stripStringAndPreceedingSeparator(QString &inout, const QLatin1String &strip2char)
01986 {
01987     int remPos = inout.indexOf(strip2char);
01988     if (remPos == -1) {
01989         return;
01990     }
01991     int endPos = remPos + 2;
01992     int curPos = remPos - 1;
01993     while (curPos >= 0 && inout.at(curPos).isSpace()) {
01994         curPos--;
01995     }
01996     // remove the separator sign before the seconds
01997     // and assume that works everywhere
01998     if (curPos >= 0 && inout.at(curPos).isPunct() && inout.at(curPos) != QLatin1Char('%')) {
01999         curPos--;
02000     }
02001     while (curPos >= 0 && inout.at(curPos).isSpace()) {
02002         curPos--;
02003     }
02004 
02005     remPos = qMax(curPos + 1, 0);
02006     inout.remove(remPos, endPos - remPos);
02007 }
02008 
02009 // remove the first occurrence of the 2-character string
02010 // strip2char from inout and if found, also remove one
02011 // succeeding punctuation character and arbitrary number of spaces.
02012 static void stripStringAndSucceedingSeparator(QString &inout, const QLatin1String &strip2char)
02013 {
02014     int remPos = inout.indexOf(strip2char);
02015     if (remPos == -1) {
02016         return;
02017     }
02018     int curPos = remPos + 2;
02019     while (curPos < inout.size() &&
02020            (inout.at(curPos).isSpace() ||
02021             (inout.at(curPos).isPunct() && inout.at(curPos) != QLatin1Char('%')))) {
02022         curPos++;
02023     }
02024     inout.remove(remPos, curPos - remPos);
02025 }
02026 
02027 // remove the first occurrence of "%p" from the inout.
02028 static void stripAmPmFormat(QString &inout)
02029 {
02030     // NOTE: this function assumes that %p - if it's present -
02031     //       is either the first or the last element of the format
02032     //       string. Either a succeeding or a preceding
02033     //       punctuation symbol is stripped.
02034     int length = inout.size();
02035     int ppos = inout.indexOf(QLatin1String("%p"));
02036     if (ppos == -1) {
02037         return;
02038     } else if (ppos == 0) {
02039         // first element, eat succeeding punctuation and spaces
02040         ppos = 2;
02041         while (ppos < length && (inout.at(ppos).isSpace() || inout.at(ppos).isPunct()) &&
02042                 inout.at(ppos) != QLatin1Char('%')) {
02043             ppos++;
02044         }
02045         inout = inout.mid(ppos);
02046     } else {
02047         stripStringAndPreceedingSeparator(inout, QLatin1String("%p"));
02048     }
02049 }
02050 
02051 QTime KLocalePrivate::readLocaleTime(const QString &intstr, bool *ok, KLocale::TimeFormatOptions options,
02052                                      KLocale::TimeProcessingOptions processing) const
02053 {
02054     QString str(intstr.simplified().toLower());
02055     QString format(timeFormat().simplified());
02056 
02057     int hour = -1;
02058     int minute = -1;
02059     int second = -1;
02060     bool useDayPeriod = false;
02061     KDayPeriod dayPeriod = dayPeriodForTime(QTime(0,0,0));
02062     int strpos = 0;
02063     int formatpos = 0;
02064     bool error = false;
02065 
02066     bool excludeSecs = ((options & KLocale::TimeWithoutSeconds) == KLocale::TimeWithoutSeconds);
02067     bool isDuration = ((options & KLocale::TimeDuration) == KLocale::TimeDuration);
02068     bool noAmPm = ((options & KLocale::TimeWithoutAmPm) == KLocale::TimeWithoutAmPm);
02069     bool foldHours = ((options & KLocale::TimeFoldHours) == KLocale::TimeFoldHours);
02070     bool strict = ((processing & KLocale::ProcessStrict) == KLocale::ProcessStrict);
02071 
02072     // if seconds aren't needed, strip them from the timeFormat
02073     if (excludeSecs) {
02074         stripStringAndPreceedingSeparator(format, QLatin1String("%S"));
02075         second = 0; // seconds are always 0
02076     }
02077 
02078     // if hours are folded, strip them from the timeFormat
02079     if (foldHours) {
02080         stripStringAndSucceedingSeparator(format, QLatin1String("%H"));
02081         stripStringAndSucceedingSeparator(format, QLatin1String("%k"));
02082         stripStringAndSucceedingSeparator(format, QLatin1String("%I"));
02083         stripStringAndSucceedingSeparator(format, QLatin1String("%l"));
02084     }
02085 
02086     // if am/pm isn't needed, strip it from the timeFormat
02087     if (noAmPm) {
02088         stripAmPmFormat(format);
02089     }
02090 
02091     while (!error && (format.length() > formatpos || str.length() > strpos)) {
02092         if (!(format.length() > formatpos && str.length() > strpos)) {
02093             error = true;
02094             break;
02095         }
02096 
02097         QChar c = format.at(formatpos++);
02098         if (c.isSpace()) {
02099             if (strict) { // strict processing: space is needed
02100                 if (!str.at(strpos).isSpace()) {
02101                     error = true;
02102                     break;
02103                 }
02104                 strpos++;
02105             } else { // lax processing: space in str not needed
02106                 // 1 space maximum as str is simplified
02107                 if (str.at(strpos).isSpace()) {
02108                     strpos++;
02109                 }
02110             }
02111             continue;
02112         }
02113 
02114         if (c != QLatin1Char('%')) {
02115             if (c != str.at(strpos++)) {
02116                 error = true;
02117                 break;
02118             }
02119             continue;
02120         }
02121 
02122         c = format.at(formatpos++);
02123         switch (c.unicode()) {
02124 
02125         case 'p': // Day Period, normally AM/PM
02126         case 'P': // Lowercase Day Period, normally am/pm
02127         {
02128             error = true;
02129             foreach (const KDayPeriod &testDayPeriod, dayPeriods()) {
02130                 QString dayPeriodText = testDayPeriod.periodName(KLocale::ShortName);
02131                 int len = dayPeriodText.length();
02132                 if (str.mid(strpos, len) == dayPeriodText.toLower()) {
02133                     dayPeriod = testDayPeriod;
02134                     strpos += len;
02135                     error = false;
02136                     useDayPeriod = true;
02137                     break;
02138                 }
02139             }
02140             break;
02141         }
02142 
02143         case 'k':  // 24h Hours Short Number
02144         case 'H':  // 24h Hours Long Number
02145             useDayPeriod = false;
02146             hour = readInt(str, strpos);
02147             break;
02148 
02149         case 'l': // 12h Hours Short Number
02150         case 'I': // 12h Hours Long Number
02151             useDayPeriod = !isDuration;
02152             hour = readInt(str, strpos);
02153             break;
02154 
02155         case 'M':
02156             minute = readInt(str, strpos);
02157             // minutes can be bigger than 59 if hours are folded
02158             if (foldHours) {
02159                 // if hours are folded, make sure minutes doesn't get bigger than 59.
02160                 hour = minute / 60;
02161                 minute = minute % 60;
02162             }
02163             break;
02164 
02165         case 'S':
02166             second = readInt(str, strpos);
02167             break;
02168         }
02169 
02170         // NOTE: if anything is performed inside this loop, be sure to
02171         //       check for error!
02172     }
02173 
02174     QTime returnTime;
02175     if (!error) {
02176         if (useDayPeriod) {
02177             returnTime = dayPeriod.time(hour, minute, second);
02178         } else {
02179             returnTime = QTime(hour, minute, second);
02180         }
02181     }
02182     if (ok) {
02183         *ok = returnTime.isValid();
02184     }
02185     return returnTime;
02186 }
02187 
02188 QString KLocalePrivate::formatTime(const QTime &time, bool includeSecs, bool isDuration) const
02189 {
02190     KLocale::TimeFormatOptions options = KLocale::TimeDefault;
02191     if (!includeSecs) {
02192         options |= KLocale::TimeWithoutSeconds;
02193     }
02194     if (isDuration) {
02195         options |= KLocale::TimeDuration;
02196     }
02197     return formatLocaleTime(time, options);
02198 }
02199 
02200 QString KLocalePrivate::formatLocaleTime(const QTime &time, KLocale::TimeFormatOptions options) const
02201 {
02202     QString rst(timeFormat());
02203 
02204     bool excludeSecs = ((options & KLocale::TimeWithoutSeconds) == KLocale::TimeWithoutSeconds);
02205     bool isDuration = ((options & KLocale::TimeDuration) == KLocale::TimeDuration);
02206     bool noAmPm = ((options & KLocale::TimeWithoutAmPm) == KLocale::TimeWithoutAmPm);
02207     bool foldHours = ((options & KLocale::TimeFoldHours) == KLocale::TimeFoldHours);
02208 
02209     // if seconds aren't needed, strip them from the timeFormat
02210     if (excludeSecs) {
02211         stripStringAndPreceedingSeparator(rst, QLatin1String("%S"));
02212     }
02213 
02214     // if hours should be folded, strip all hour symbols from the timeFormat
02215     if (foldHours) {
02216         stripStringAndSucceedingSeparator(rst, QLatin1String("%H"));
02217         stripStringAndSucceedingSeparator(rst, QLatin1String("%k"));
02218         stripStringAndSucceedingSeparator(rst, QLatin1String("%I"));
02219         stripStringAndSucceedingSeparator(rst, QLatin1String("%l"));
02220     }
02221 
02222     // if am/pm isn't needed, strip it from the timeFormat
02223     if (noAmPm) {
02224         stripAmPmFormat(rst);
02225     }
02226 
02227     // only "pm/am" and %M here can grow, the rest shrinks, but
02228     // I'm rather safe than sorry
02229     QChar *buffer = new QChar[rst.length() * 3 / 2 + 32];
02230 
02231     int index = 0;
02232     bool escape = false;
02233     int number = 0;
02234 
02235     for (int format_index = 0; format_index < rst.length(); format_index++) {
02236         if (!escape) {
02237             if (rst.at(format_index).unicode() == '%') {
02238                 escape = true;
02239             } else {
02240                 buffer[index++] = rst.at(format_index);
02241             }
02242         } else {
02243             switch (rst.at(format_index).unicode()) {
02244             case '%':
02245                 buffer[index++] = QLatin1Char('%');
02246                 break;
02247             case 'H':
02248                 put_it_in(buffer, index, time.hour());
02249                 break;
02250             case 'I':
02251                 if (isDuration) {
02252                     put_it_in(buffer, index, time.hour());
02253                 } else {
02254                     put_it_in(buffer, index, dayPeriodForTime(time).hourInPeriod(time));
02255                 }
02256                 break;
02257             case 'M':
02258                 if (foldHours) {
02259                     put_it_in(buffer, index, QString::number(time.hour() * 60 + time.minute()));
02260                 } else {
02261                     put_it_in(buffer, index, time.minute());
02262                 }
02263                 break;
02264             case 'S':
02265                 put_it_in(buffer, index, time.second());
02266                 break;
02267             case 'k':
02268             case 'l':
02269                 // to share the code
02270                 if (!isDuration && rst.at(format_index).unicode() == 'l') {
02271                     number = dayPeriodForTime(time).hourInPeriod(time);
02272                 } else {
02273                     number = time.hour();
02274                 }
02275                 if (number / 10) {
02276                     buffer[index++] = number / 10 + '0';
02277                 }
02278                 buffer[index++] = number % 10 + '0';
02279                 break;
02280             case 'p':
02281             {
02282                 put_it_in(buffer, index, dayPeriodForTime(time).periodName(KLocale::ShortName));
02283                 break;
02284             }
02285             default:
02286                 buffer[index++] = rst.at(format_index);
02287                 break;
02288             }
02289             escape = false;
02290         }
02291     }
02292     QString ret(buffer, index);
02293     delete [] buffer;
02294     ret = convertDigits(ret, dateTimeDigitSet());
02295     return ret.trimmed();
02296 }
02297 
02298 bool KLocalePrivate::use12Clock() const
02299 {
02300     if ((timeFormat().contains(QString::fromLatin1("%I")) > 0) ||
02301         (timeFormat().contains(QString::fromLatin1("%l")) > 0)) {
02302         return true;
02303     } else {
02304         return false;
02305     }
02306 }
02307 
02308 void KLocalePrivate::setDayPeriods(const QList<KDayPeriod> &dayPeriods)
02309 {
02310     if (dayPeriods.count() > 0) {
02311         foreach (const KDayPeriod &dayPeriod, dayPeriods) {
02312             if (!dayPeriod.isValid()) {
02313                 return;
02314             }
02315         }
02316         m_dayPeriods = dayPeriods;
02317     }
02318 }
02319 
02320 QList<KDayPeriod> KLocalePrivate::dayPeriods() const
02321 {
02322     // If no Day Periods currently loaded then it means there were no country specific ones defined
02323     // in the country l10n file, so default to standard AM/PM translations for the users language.
02324     // Note we couldn't do this in initDayPeriods() as i18n isn't available until we have a
02325     // valid loacle constructed.
02326     if (m_dayPeriods.isEmpty()) {
02327         m_dayPeriods.append(KDayPeriod(QString::fromLatin1("am"),
02328                                        i18nc( "Before Noon KLocale::LongName", "Ante Meridiem" ),
02329                                        i18nc( "Before Noon KLocale::ShortName", "AM" ),
02330                                        i18nc( "Before Noon KLocale::NarrowName", "A" ),
02331                                        QTime( 0, 0, 0 ), QTime( 11, 59, 59, 999 ), 0, 12 ));
02332         m_dayPeriods.append(KDayPeriod(QString::fromLatin1("pm"),
02333                                        i18nc( "After Noon KLocale::LongName", "Post Meridiem" ),
02334                                        i18nc( "After Noon KLocale::ShortName", "PM" ),
02335                                        i18nc( "After Noon KLocale::NarrowName", "P" ),
02336                                        QTime( 12, 0, 0 ), QTime( 23, 59, 59, 999 ), 0, 12 ));
02337     }
02338     return m_dayPeriods;
02339 }
02340 
02341 KDayPeriod KLocalePrivate::dayPeriodForTime(const QTime &time) const
02342 {
02343     if (time.isValid()) {
02344         foreach (const KDayPeriod &dayPeriod, dayPeriods()) {
02345             if (dayPeriod.isValid(time)) {
02346                 return dayPeriod;
02347             }
02348         }
02349     }
02350     return KDayPeriod();
02351 }
02352 
02353 QStringList KLocalePrivate::languageList() const
02354 {
02355     return m_languageList;
02356 }
02357 
02358 QStringList KLocalePrivate::currencyCodeList() const
02359 {
02360     return m_currencyCodeList;
02361 }
02362 
02363 QString KLocalePrivate::formatDateTime(const KLocale *locale, const QDateTime &dateTime, KLocale::DateFormat format,
02364                                        bool includeSeconds, int daysTo, int secsTo)
02365 {
02366     // Have to do Fancy Date formatting here rather than using normal KCalendarSystem::formatDate()
02367     // as daysTo is relative to the time spec which formatDate doesn't know about.  Needs to be
02368     // kept in sync with Fancy Date code in KCalendarSystem::formatDate().  Fix in KDE5.
02369 
02370     // Only do Fancy if less than an hour into the future or less than a week in the past
02371     if ((daysTo == 0 && secsTo > 3600) ||  daysTo < 0 || daysTo > 6) {
02372         if (format == KLocale::FancyShortDate) {
02373             format = KLocale::ShortDate;
02374         } else if (format == KLocale::FancyLongDate) {
02375             format = KLocale::LongDate;
02376         }
02377     }
02378 
02379     QString dateStr;
02380     if (format == KLocale::FancyShortDate || format == KLocale::FancyLongDate) {
02381         switch (daysTo) {
02382         case 0:
02383             dateStr = i18n("Today");
02384             break;
02385         case 1:
02386             dateStr = i18n("Yesterday");
02387             break;
02388         default:
02389             dateStr = locale->calendar()->weekDayName(dateTime.date());
02390         }
02391     } else {
02392         dateStr = locale->formatDate(dateTime.date(), format);
02393     }
02394 
02395     KLocale::TimeFormatOption timeFormat;
02396     if (includeSeconds) {
02397         timeFormat = KLocale::TimeDefault;
02398     } else {
02399         timeFormat = KLocale::TimeWithoutSeconds;
02400     }
02401 
02402     return i18nc("concatenation of dates and time", "%1 %2", dateStr,
02403                  locale->formatLocaleTime(dateTime.time(), timeFormat));
02404 }
02405 
02406 QString KLocalePrivate::formatDateTime(const QDateTime &dateTime, KLocale::DateFormat format, bool includeSeconds) const
02407 {
02408     QDateTime now = QDateTime::currentDateTime();
02409     int daysTo = dateTime.date().daysTo(now.date());
02410     int secsTo = now.secsTo(dateTime);
02411     return KLocalePrivate::formatDateTime(q, dateTime, format, includeSeconds, daysTo, secsTo);
02412 }
02413 
02414 QString KLocalePrivate::formatDateTime(const KDateTime &dateTime, KLocale::DateFormat format,
02415                                        KLocale::DateTimeFormatOptions options)
02416 {
02417     QString dt;
02418 
02419     if (dateTime.isDateOnly()) {
02420         dt = formatDate(dateTime.date(), format);
02421     } else {
02422         KDateTime now = KDateTime::currentDateTime(dateTime.timeSpec());
02423         int daysTo = dateTime.date().daysTo(now.date());
02424         int secsTo = now.secsTo(dateTime);
02425         dt = KLocalePrivate::formatDateTime(q, dateTime.dateTime(), format, (options & KLocale::Seconds), daysTo, secsTo);
02426     }
02427 
02428     if (options & KLocale::TimeZone) {
02429         QString tz;
02430         switch (dateTime.timeType()) {
02431         case KDateTime::OffsetFromUTC:
02432             tz = i18n(dateTime.toString(QString::fromLatin1("%z")).toUtf8());
02433             break;
02434         case KDateTime::UTC:
02435         case KDateTime::TimeZone:
02436             tz = i18n(dateTime.toString(QString::fromLatin1((format == KLocale::ShortDate) ? "%Z" : "%:Z")).toUtf8());
02437             break;
02438         case KDateTime::ClockTime:
02439         default:
02440             break;
02441         }
02442         return i18nc("concatenation of date/time and time zone", "%1 %2", dt, tz);
02443     }
02444 
02445     return dt;
02446 }
02447 
02448 QString KLocalePrivate::langLookup(const QString &fname, const char *rtype)
02449 {
02450     QStringList search;
02451 
02452     // assemble the local search paths
02453     const QStringList localDoc = KGlobal::dirs()->resourceDirs(rtype);
02454 
02455     // look up the different languages
02456     for (int id = localDoc.count() - 1; id >= 0; --id) {
02457         QStringList langs = KGlobal::locale()->languageList();
02458         // FIXME: KDE 4.5, change such that English is not assumed.
02459         langs.replaceInStrings(QLatin1String("en_US"), QLatin1String("en"));
02460         langs.append(QLatin1String("en"));
02461         Q_FOREACH(const QString &lang, langs)
02462             search.append(QString::fromLatin1("%1%2/%3").arg(localDoc[id]).arg(lang).arg(fname));
02463     }
02464 
02465     // try to locate the file
02466     Q_FOREACH(const QString &file, search) {
02467         kDebug(173) << "Looking for help in: " << file;
02468 
02469         QFileInfo info(file);
02470         if (info.exists() && info.isFile() && info.isReadable())
02471             return file;
02472     }
02473 
02474     return QString();
02475 }
02476 
02477 bool KLocalePrivate::useDefaultLanguage() const
02478 {
02479     return language() == KLocale::defaultLanguage();
02480 }
02481 
02482 void KLocalePrivate::initEncoding()
02483 {
02484     m_codecForEncoding = 0;
02485 
02486     // This all made more sense when we still had the EncodingEnum config key.
02487 
02488     QByteArray codeset = systemCodeset();
02489 
02490     if (!codeset.isEmpty()) {
02491         QTextCodec* codec = QTextCodec::codecForName(codeset);
02492         if (codec) {
02493             setEncoding(codec->mibEnum());
02494         }
02495     } else {
02496         setEncoding(QTextCodec::codecForLocale()->mibEnum());
02497     }
02498 
02499     if (!m_codecForEncoding) {
02500         kWarning() << "Cannot resolve system encoding, defaulting to ISO 8859-1.";
02501         const int mibDefault = 4; // ISO 8859-1
02502         setEncoding(mibDefault);
02503     }
02504 
02505     Q_ASSERT(m_codecForEncoding);
02506 }
02507 
02508 QByteArray KLocalePrivate::systemCodeset() const
02509 {
02510     QByteArray codeset;
02511 #if defined(HAVE_LANGINFO_H)
02512     // Qt since 4.2 always returns 'System' as codecForLocale and KDE (for example
02513     // KEncodingFileDialog) expects real encoding name. So on systems that have langinfo.h use
02514     // nl_langinfo instead, just like Qt compiled without iconv does. Windows already has its own
02515     // workaround
02516 
02517     codeset = nl_langinfo(CODESET);
02518 
02519     if ((codeset == "ANSI_X3.4-1968") || (codeset == "US-ASCII")) {
02520         // means ascii, "C"; QTextCodec doesn't know, so avoid warning
02521         codeset = "ISO-8859-1";
02522     }
02523 #endif
02524     return codeset;
02525 }
02526 
02527 void KLocalePrivate::initFileNameEncoding()
02528 {
02529     // If the following environment variable is set, assume all filenames
02530     // are in UTF-8 regardless of the current C locale.
02531     m_utf8FileEncoding = !qgetenv("KDE_UTF8_FILENAMES").isEmpty();
02532     if (m_utf8FileEncoding) {
02533         QFile::setEncodingFunction(KLocalePrivate::encodeFileNameUTF8);
02534         QFile::setDecodingFunction(KLocalePrivate::decodeFileNameUTF8);
02535     }
02536     // Otherwise, stay with QFile's default filename encoding functions
02537     // which, on Unix platforms, use the locale's codec.
02538 }
02539 
02540 QByteArray KLocalePrivate::encodeFileNameUTF8(const QString & fileName)
02541 {
02542     return fileName.toUtf8();
02543 }
02544 
02545 QString KLocalePrivate::decodeFileNameUTF8(const QByteArray &localFileName)
02546 {
02547     return QString::fromUtf8(localFileName);
02548 }
02549 
02550 void KLocalePrivate::setDateFormat(const QString &format)
02551 {
02552     m_dateFormat = format.trimmed();
02553 }
02554 
02555 void KLocalePrivate::setDateFormatShort(const QString &format)
02556 {
02557     m_dateFormatShort = format.trimmed();
02558 }
02559 
02560 void KLocalePrivate::setDateMonthNamePossessive(bool possessive)
02561 {
02562     m_dateMonthNamePossessive = possessive;
02563 }
02564 
02565 void KLocalePrivate::setTimeFormat(const QString &format)
02566 {
02567     m_timeFormat = format.trimmed();
02568 }
02569 
02570 void KLocalePrivate::setWeekStartDay(int day)
02571 {
02572     if (day >= 1 && day <= calendar()->daysInWeek(QDate())) {
02573         m_weekStartDay = day;
02574     }
02575 }
02576 
02577 void KLocalePrivate::setWorkingWeekStartDay(int day)
02578 {
02579     if (day >= 1 && day <= calendar()->daysInWeek(QDate())) {
02580         m_workingWeekStartDay = day;
02581     }
02582 }
02583 
02584 void KLocalePrivate::setWorkingWeekEndDay(int day)
02585 {
02586     if (day >= 1 && day <= calendar()->daysInWeek(QDate())) {
02587         m_workingWeekEndDay = day;
02588     }
02589 }
02590 
02591 void KLocalePrivate::setWeekDayOfPray(int day)
02592 {
02593     if (day >= 0 && day <= calendar()->daysInWeek(QDate())) { // 0 = None
02594         m_weekDayOfPray = day;
02595     }
02596 }
02597 
02598 QString KLocalePrivate::dateFormat() const
02599 {
02600     return m_dateFormat;
02601 }
02602 
02603 QString KLocalePrivate::dateFormatShort() const
02604 {
02605     return m_dateFormatShort;
02606 }
02607 
02608 QString KLocalePrivate::timeFormat() const
02609 {
02610     return m_timeFormat;
02611 }
02612 
02613 void KLocalePrivate::setDecimalPlaces(int digits)
02614 {
02615     m_decimalPlaces = digits;
02616 }
02617 
02618 void KLocalePrivate::setDecimalSymbol(const QString &symbol)
02619 {
02620     m_decimalSymbol = symbol.trimmed();
02621 }
02622 
02623 void KLocalePrivate::setThousandsSeparator(const QString &separator)
02624 {
02625     // allow spaces here
02626     m_thousandsSeparator = separator;
02627 }
02628 
02629 void KLocalePrivate::setPositiveSign(const QString &sign)
02630 {
02631     m_positiveSign = sign.trimmed();
02632 }
02633 
02634 void KLocalePrivate::setNegativeSign(const QString &sign)
02635 {
02636     m_negativeSign = sign.trimmed();
02637 }
02638 
02639 void KLocalePrivate::setPositiveMonetarySignPosition(KLocale::SignPosition signpos)
02640 {
02641     m_positiveMonetarySignPosition = signpos;
02642 }
02643 
02644 void KLocalePrivate::setNegativeMonetarySignPosition(KLocale::SignPosition signpos)
02645 {
02646     m_negativeMonetarySignPosition = signpos;
02647 }
02648 
02649 void KLocalePrivate::setPositivePrefixCurrencySymbol(bool prefix)
02650 {
02651     m_positivePrefixCurrencySymbol = prefix;
02652 }
02653 
02654 void KLocalePrivate::setNegativePrefixCurrencySymbol(bool prefix)
02655 {
02656     m_negativePrefixCurrencySymbol = prefix;
02657 }
02658 
02659 void KLocalePrivate::setMonetaryDecimalPlaces(int digits)
02660 {
02661     m_monetaryDecimalPlaces = digits;
02662 }
02663 
02664 void KLocalePrivate::setMonetaryThousandsSeparator(const QString &separator)
02665 {
02666     // allow spaces here
02667     m_monetaryThousandsSeparator = separator;
02668 }
02669 
02670 void KLocalePrivate::setMonetaryDecimalSymbol(const QString &symbol)
02671 {
02672     m_monetaryDecimalSymbol = symbol.trimmed();
02673 }
02674 
02675 void KLocalePrivate::setCurrencySymbol(const QString & symbol)
02676 {
02677     m_currencySymbol = symbol.trimmed();
02678 }
02679 
02680 int KLocalePrivate::pageSize() const
02681 {
02682     return m_pageSize;
02683 }
02684 
02685 void KLocalePrivate::setPageSize(int size)
02686 {
02687     // #### check if it's in range??
02688     m_pageSize = size;
02689 }
02690 
02691 KLocale::MeasureSystem KLocalePrivate::measureSystem() const
02692 {
02693     return m_measureSystem;
02694 }
02695 
02696 void KLocalePrivate::setMeasureSystem(KLocale::MeasureSystem value)
02697 {
02698     m_measureSystem = value;
02699 }
02700 
02701 QString KLocalePrivate::defaultLanguage()
02702 {
02703     static const QString en_US = QString::fromLatin1("en_US");
02704     return en_US;
02705 }
02706 
02707 QString KLocalePrivate::defaultCountry()
02708 {
02709     return QString::fromLatin1("C");
02710 }
02711 
02712 QString KLocalePrivate::defaultCurrencyCode()
02713 {
02714     return QString::fromLatin1("USD");
02715 }
02716 
02717 bool KLocalePrivate::useTranscript() const
02718 {
02719     return m_useTranscript;
02720 }
02721 
02722 const QByteArray KLocalePrivate::encoding()
02723 {
02724     return codecForEncoding()->name();
02725 }
02726 
02727 int KLocalePrivate::encodingMib() const
02728 {
02729     return codecForEncoding()->mibEnum();
02730 }
02731 
02732 int KLocalePrivate::fileEncodingMib() const
02733 {
02734     if (m_utf8FileEncoding) {
02735         return 106;
02736     }
02737     return codecForEncoding()->mibEnum();
02738 }
02739 
02740 QTextCodec *KLocalePrivate::codecForEncoding() const
02741 {
02742     return m_codecForEncoding;
02743 }
02744 
02745 bool KLocalePrivate::setEncoding(int mibEnum)
02746 {
02747     QTextCodec * codec = QTextCodec::codecForMib(mibEnum);
02748     if (codec) {
02749         m_codecForEncoding = codec;
02750     }
02751 
02752     return codec != 0;
02753 }
02754 
02755 QStringList KLocalePrivate::allLanguagesList()
02756 {
02757     if (!m_languages) {
02758         m_languages = new KConfig(QLatin1String("all_languages"), KConfig::NoGlobals, "locale");
02759     }
02760     return m_languages->groupList();
02761 }
02762 
02763 QStringList KLocalePrivate::installedLanguages()
02764 {
02765     QStringList languages;
02766     QStringList paths = KGlobal::dirs()->findAllResources("locale", QLatin1String("*/entry.desktop"));
02767     foreach (const QString &path, paths) {
02768         QString part = path.left(path.length() - 14);
02769         languages.append(part.mid(part.lastIndexOf(QLatin1Char('/')) + 1));
02770     }
02771     languages.sort();
02772     return languages;
02773 }
02774 
02775 QString KLocalePrivate::languageCodeToName(const QString &language)
02776 {
02777     if (!m_languages) {
02778         m_languages = new KConfig(QLatin1String("all_languages"), KConfig::NoGlobals, "locale");
02779     }
02780 
02781     KConfigGroup cg(m_languages, language);
02782     return cg.readEntry("Name");
02783 }
02784 
02785 QStringList KLocalePrivate::allCountriesList() const
02786 {
02787     QStringList countries;
02788     const QStringList paths = KGlobal::dirs()->findAllResources("locale", QLatin1String("l10n/*/entry.desktop"));
02789     for (QStringList::ConstIterator it = paths.begin(); it != paths.end(); ++it) {
02790         QString code = (*it).mid((*it).length() - 16, 2);
02791         if (code != QLatin1String("/C")) {
02792             countries.append(code);
02793         }
02794     }
02795     return countries;
02796 }
02797 
02798 QString KLocalePrivate::countryCodeToName(const QString &country) const
02799 {
02800     QString countryName;
02801     QString entryFile = KStandardDirs::locate("locale", QString::fromLatin1("l10n/") + country.toLower() + QLatin1String("/entry.desktop"));
02802     if (!entryFile.isEmpty()) {
02803         KConfig cfg(entryFile);
02804         KConfigGroup cg(&cfg, "KCM Locale");
02805         countryName = cg.readEntry("Name");
02806     }
02807     return countryName;
02808 }
02809 
02810 KLocale::CalendarSystem KLocalePrivate::calendarTypeToCalendarSystem(const QString &calendarType) const
02811 {
02812     if (calendarType == QLatin1String("coptic")) {
02813         return KLocale::CopticCalendar;
02814     } else if (calendarType == QLatin1String("ethiopian")) {
02815         return KLocale::EthiopianCalendar;
02816     } else if (calendarType == QLatin1String("gregorian")) {
02817         return KLocale::QDateCalendar;
02818     } else if (calendarType == QLatin1String("gregorian-proleptic")) {
02819         return KLocale::GregorianCalendar;
02820     } else if (calendarType == QLatin1String("hebrew")) {
02821         return KLocale::HebrewCalendar;
02822     } else if (calendarType == QLatin1String("hijri")) {
02823         return KLocale::IslamicCivilCalendar;
02824     } else if (calendarType == QLatin1String("indian-national")) {
02825         return KLocale::IndianNationalCalendar;
02826     } else if (calendarType == QLatin1String("jalali")) {
02827         return KLocale::JalaliCalendar;
02828     } else if (calendarType == QLatin1String("japanese")) {
02829         return KLocale::JapaneseCalendar;
02830     } else if (calendarType == QLatin1String("julian")) {
02831         return KLocale::JulianCalendar;
02832     } else if (calendarType == QLatin1String("minguo")) {
02833         return KLocale::MinguoCalendar;
02834     } else if (calendarType == QLatin1String("thai")) {
02835         return KLocale::ThaiCalendar;
02836     } else {
02837         return KLocale::QDateCalendar;
02838     }
02839 }
02840 
02841 QString KLocalePrivate::calendarSystemToCalendarType(KLocale::CalendarSystem calendarSystem) const
02842 {
02843     switch (calendarSystem) {
02844     case KLocale::QDateCalendar:
02845         return QLatin1String("gregorian");
02846     case KLocale::CopticCalendar:
02847         return QLatin1String("coptic");
02848     case KLocale::EthiopianCalendar:
02849         return QLatin1String("ethiopian");
02850     case KLocale::GregorianCalendar:
02851         return QLatin1String("gregorian-proleptic");
02852     case KLocale::HebrewCalendar:
02853         return QLatin1String("hebrew");
02854     case KLocale::IslamicCivilCalendar:
02855         return QLatin1String("hijri");
02856     case KLocale::IndianNationalCalendar:
02857         return QLatin1String("indian-national");
02858     case KLocale::JalaliCalendar:
02859         return QLatin1String("jalali");
02860     case KLocale::JapaneseCalendar:
02861         return QLatin1String("japanese");
02862     case KLocale::JulianCalendar:
02863         return QLatin1String("julian");
02864     case KLocale::MinguoCalendar:
02865         return QLatin1String("minguo");
02866     case KLocale::ThaiCalendar:
02867         return QLatin1String("thai");
02868     default:
02869         return QLatin1String("gregorian");
02870     }
02871 }
02872 
02873 void KLocalePrivate::setCalendar(const QString &calendarType)
02874 {
02875     setCalendarSystem(calendarTypeToCalendarSystem(calendarType));
02876 }
02877 
02878 void KLocalePrivate::setCalendarSystem(KLocale::CalendarSystem calendarSystem)
02879 {
02880     m_calendarSystem = calendarSystem;
02881     delete m_calendar;
02882     m_calendar = 0;
02883 }
02884 
02885 QString KLocalePrivate::calendarType() const
02886 {
02887     return calendarSystemToCalendarType(m_calendarSystem);
02888 }
02889 
02890 KLocale::CalendarSystem KLocalePrivate::calendarSystem() const
02891 {
02892     return m_calendarSystem;
02893 }
02894 
02895 const KCalendarSystem * KLocalePrivate::calendar()
02896 {
02897     if (!m_calendar) {
02898         m_calendar = KCalendarSystem::create(m_calendarSystem, m_config, q);
02899     }
02900 
02901     return m_calendar;
02902 }
02903 
02904 void KLocalePrivate::setWeekNumberSystem(KLocale::WeekNumberSystem weekNumberSystem)
02905 {
02906     m_weekNumberSystem = weekNumberSystem;
02907 }
02908 
02909 KLocale::WeekNumberSystem KLocalePrivate::weekNumberSystem()
02910 {
02911     return m_weekNumberSystem;
02912 }
02913 
02914 void KLocalePrivate::copyCatalogsTo(KLocale *locale)
02915 {
02916     QMutexLocker lock(kLocaleMutex());
02917     locale->d->m_catalogNames = m_catalogNames;
02918     locale->d->updateCatalogs();
02919 }
02920 
02921 QString KLocalePrivate::localizedFilePath(const QString &filePath) const
02922 {
02923     // Stop here if the default language is primary.
02924     if (useDefaultLanguage()) {
02925         return filePath;
02926     }
02927 
02928     // Check if l10n sudir is present, stop if not.
02929     QFileInfo fileInfo(filePath);
02930     QString locDirPath = fileInfo.path() + QLatin1String("/l10n");
02931     QFileInfo locDirInfo(locDirPath);
02932     if (!locDirInfo.isDir()) {
02933         return filePath;
02934     }
02935 
02936     // Go through possible localized paths by priority of languages,
02937     // return first that exists.
02938     QString fileName = fileInfo.fileName();
02939     foreach(const QString &lang, languageList()) {
02940         // Stop when the default language is reached.
02941         if (lang == KLocale::defaultLanguage()) {
02942             return filePath;
02943         }
02944         QString locFilePath = locDirPath + QLatin1Char('/') + lang + QLatin1Char('/') + fileName;
02945         QFileInfo locFileInfo(locFilePath);
02946         if (locFileInfo.isFile() && locFileInfo.isReadable()) {
02947             return locFilePath;
02948         }
02949     }
02950 
02951     return filePath;
02952 }
02953 
02954 QString KLocalePrivate::removeAcceleratorMarker(const QString &label) const
02955 {
02956     return ::removeAcceleratorMarker(label);
02957 }
02958 
02959 void KLocalePrivate::setDigitSet(KLocale::DigitSet digitSet)
02960 {
02961     m_digitSet = digitSet;
02962 }
02963 
02964 KLocale::DigitSet KLocalePrivate::digitSet() const
02965 {
02966     return m_digitSet;
02967 }
02968 
02969 void KLocalePrivate::setMonetaryDigitSet(KLocale::DigitSet digitSet)
02970 {
02971     m_monetaryDigitSet = digitSet;
02972 }
02973 
02974 KLocale::DigitSet KLocalePrivate::monetaryDigitSet() const
02975 {
02976     return m_monetaryDigitSet;
02977 }
02978 
02979 void KLocalePrivate::setDateTimeDigitSet(KLocale::DigitSet digitSet)
02980 {
02981     m_dateTimeDigitSet = digitSet;
02982 }
02983 
02984 KLocale::DigitSet KLocalePrivate::dateTimeDigitSet() const
02985 {
02986     return m_dateTimeDigitSet;
02987 }
02988 
02989 Q_GLOBAL_STATIC_WITH_ARGS(QMutex, s_kLocaleMutex, (QMutex::Recursive))
02990 
02991 QMutex *kLocaleMutex()
02992 {
02993     return s_kLocaleMutex();
02994 }

KDECore

Skip menu "KDECore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal