• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KDECore

klocale.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 
00008    This library is free software; you can redistribute it and/or
00009    modify it under the terms of the GNU Library General Public
00010    License as published by the Free Software Foundation; either
00011    version 2 of the License, or (at your option) any later version.
00012 
00013    This library is distributed in the hope that it will be useful,
00014    but WITHOUT ANY WARRANTY; without even the implied warranty of
00015    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016    Library General Public License for more details.
00017 
00018    You should have received a copy of the GNU Library General Public License
00019    along with this library; see the file COPYING.LIB.  If not, write to
00020    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00021    Boston, MA 02110-1301, USA.
00022 */
00023 
00024 #include "klocale.h"
00025 #include "klocale_p.h"
00026 
00027 #include <config.h>
00028 
00029 #ifdef HAVE_SYS_TIME_H
00030 #include <sys/time.h>
00031 #endif
00032 #ifdef HAVE_TIME_H
00033 #include <time.h>
00034 #endif
00035 #ifdef HAVE_LANGINFO_H
00036 #include <langinfo.h>
00037 #endif
00038 
00039 #include <QtCore/QTextCodec>
00040 #include <QtCore/QFile>
00041 #include <QtGui/QPrinter>
00042 #include <QtCore/QFileInfo>
00043 #include <QtCore/QRegExp>
00044 #include <QtCore/QLocale>
00045 #include <QtCore/QHash>
00046 #include <QtCore/QMutexLocker>
00047 
00048 #include "kcatalog_p.h"
00049 #include "kglobal.h"
00050 #include "kstandarddirs.h"
00051 #include "kconfig.h"
00052 #include "kcomponentdata.h"
00053 #include "kdebug.h"
00054 #include "kdatetime.h"
00055 #include "kcalendarsystem.h"
00056 #include "klocalizedstring.h"
00057 #include "ktranslit_p.h"
00058 #include "kconfiggroup.h"
00059 #include "kcatalogname_p.h"
00060 #include "common_helpers_p.h"
00061 
00062 #ifdef Q_WS_WIN
00063 #include <windows.h>
00064 #endif
00065 
00066 class KLocaleStaticData
00067 {
00068     public:
00069 
00070     KLocaleStaticData ();
00071 
00072     QString maincatalog;
00073 
00074     // FIXME: Temporary until full language-sensitivity implemented.
00075     QHash<KLocale::DigitSet, QStringList> languagesUsingDigitSet;
00076 };
00077 
00078 KLocaleStaticData::KLocaleStaticData ()
00079 {
00080     // Languages using non-Western Arabic digit sets.
00081     // FIXME: Temporary until full language-sensitivity implemented.
00082     #define SET_LANGS_FOR_DS(ds, langs) do { \
00083         languagesUsingDigitSet[KLocale::ds] = QStringList(); \
00084         languagesUsingDigitSet[KLocale::ds] << langs; \
00085     } while (0)
00086     SET_LANGS_FOR_DS(ArabicIndicDigits, "ar" << "ps");
00087     SET_LANGS_FOR_DS(EasternArabicIndicDigits, "fa" << "ur");
00088     SET_LANGS_FOR_DS(DevenagariDigits, "hi" << "ne");
00089 }
00090 
00091 K_GLOBAL_STATIC(KLocaleStaticData, staticData)
00092 
00093 
00094 QDebug operator<<(QDebug debug, const KCatalogName &cn)
00095 {
00096     return debug << cn.name << cn.loadCount;
00097 }
00098 
00099 class KLocalePrivate
00100 {
00101 public:
00102     KLocalePrivate(const QString& catalog, KConfig *config, const QString &language_ = QString(), const QString &country_ = QString());
00106   void initMainCatalogs();
00107 
00116   void initLanguageList(KConfig *config, bool useEnv);
00117 
00121   void initEncoding();
00122 
00127   void initFileNameEncoding();
00128 
00132   static QByteArray encodeFileNameUTF8( const QString & fileName );
00133 
00137   static QString decodeFileNameUTF8( const QByteArray & localFileName );
00138 
00142   void initFormat(KConfig *config);
00143 
00147   bool setLanguage(const QString & _language, KConfig *config);
00148 
00152   bool setLanguage(const QStringList & languages);
00153 
00157   bool setEncoding(int mibEnum);
00158 
00162   void translate_priv(const char *msgctxt,
00163                       const char *msgid,
00164                       const char *msgid_plural = 0,
00165                       unsigned long n = 0,
00166                       QString *language = 0,
00167                       QString *translation = 0) const;
00168 
00172   bool useDefaultLanguage() const;
00173 
00180   void updateCatalogs( );
00181 
00187   bool isApplicationTranslatedInto( const QString & language);
00188 
00192   static QString formatDateTime(const KLocale *locale, const QDateTime &dateTime,
00193                                 KLocale::DateFormat, bool includeSeconds, int daysToNow);
00197   static QString fancyDate(const KLocale *locale, const QDate &date, int daysToNow);
00198 
00199   enum DurationType {
00200       DaysDurationType = 0,
00201       HoursDurationType,
00202       MinutesDurationType,
00203       SecondsDurationType
00204   };
00208   static QString formatSingleDuration( DurationType durationType, int n );
00209 
00210   // Numbers and money
00211   QString decimalSymbol;
00212   QString thousandsSeparator;
00213   QString currencySymbol;
00214   QString monetaryDecimalSymbol;
00215   QString monetaryThousandsSeparator;
00216   QString positiveSign;
00217   QString negativeSign;
00218   KLocale::DigitSet digitSet;
00219   int fracDigits;
00220   KLocale::SignPosition positiveMonetarySignPosition;
00221   KLocale::SignPosition negativeMonetarySignPosition;
00222   bool positivePrefixCurrencySymbol : 1;
00223   bool negativePrefixCurrencySymbol : 1;
00224   KLocale::DigitSet monetaryDigitSet;
00225 
00226   // Date and time
00227   QString timeFormat;
00228   QString dateFormat;
00229   QString dateFormatShort;
00230   int weekStartDay;
00231   int workingWeekStartDay;
00232   int workingWeekEndDay;
00233   int weekDayOfPray;
00234   KLocale::DigitSet dateTimeDigitSet;
00235 
00236   // FIXME: Temporary until full language-sensitivity implemented.
00237   bool languageSensitiveDigits;
00238 
00239   // Locale
00240   QString language;
00241   QString country;
00242 
00243   // Handling of translation catalogs
00244   QStringList languageList;
00245 
00246   QList<KCatalogName> catalogNames; // list of all catalogs (regardless of language)
00247   QList<KCatalog> catalogs; // list of all found catalogs, one instance per catalog name and language
00248   int numberOfSysCatalogs; // number of catalogs that each app draws from
00249   bool useTranscript; // indicates if scripted messages are to be executed
00250 
00251   // Misc
00252   QString encoding;
00253   QTextCodec * codecForEncoding;
00254   //KSharedConfig::Ptr config;
00255   int pageSize;
00256   KLocale::MeasureSystem measureSystem;
00257   KConfig * languages;
00258 
00259   QString calendarType;
00260   KCalendarSystem * calendar;
00261   QString catalogName; // catalogName ("app name") used by this KLocale object
00262   bool nounDeclension:1;
00263   bool dateMonthNamePossessive:1;
00264   bool utf8FileEncoding:1;
00265 #ifdef Q_WS_WIN
00266   char win32SystemEncoding[3+7]; //"cp " + lang ID
00267 #endif
00268 
00269   // Performance stuff.
00270   enum ByteSizeFmt {
00271     TiB, GiB, MiB, KiB, B
00272   };
00273   QList<QString> byteSizeFmt;
00274 };
00275 
00276 KLocalePrivate::KLocalePrivate(const QString& catalog, KConfig *config, const QString &language_, const QString &country_)
00277     : language(language_),
00278       country(country_),
00279       useTranscript(false),
00280       codecForEncoding(0),
00281       languages(0), calendar(0),
00282       catalogName(catalog)
00283 {
00284     initEncoding();
00285     initFileNameEncoding();
00286 
00287     if (config) {
00288         initLanguageList(config, false);
00289     }
00290     else {
00291         config = KGlobal::config().data();
00292         initLanguageList(config, true);
00293     }
00294 
00295     initMainCatalogs();
00296 
00297     initFormat(config);
00298 }
00299 
00300 KLocale::KLocale( const QString & catalog, KSharedConfig::Ptr config )
00301     : d(new KLocalePrivate(catalog, config.data()))
00302 {
00303 }
00304 
00305 KLocale::KLocale(const QString& catalog, const QString &language, const QString &country, KConfig *config)
00306     : d(new KLocalePrivate(catalog, config, language, country))
00307 {
00308 }
00309 
00310 void KLocalePrivate::initMainCatalogs()
00311 {
00312   KLocaleStaticData *s = staticData;
00313   QMutexLocker lock(kLocaleMutex());
00314 
00315   if (!s->maincatalog.isEmpty()) {
00316       // If setMainCatalog was called, then we use that (e.g. korgac calls setMainCatalog("korganizer") to use korganizer.po)
00317       catalogName = s->maincatalog;
00318   }
00319 
00320   if (catalogName.isEmpty()) {
00321     kDebug(173) << "KLocale instance created called without valid "
00322                  << "catalog! Give an argument or call setMainCatalog "
00323                  << "before init" << endl;
00324   }
00325   else {
00326     // do not use insertCatalog here, that would already trigger updateCatalogs
00327     catalogNames.append(KCatalogName(catalogName));   // application catalog
00328 
00329     // catalogs from which each application can draw translations
00330     numberOfSysCatalogs = 4;
00331     catalogNames.append(KCatalogName("libphonon"));
00332     catalogNames.append(KCatalogName("kio4"));
00333     catalogNames.append(KCatalogName("kdelibs4"));
00334     catalogNames.append(KCatalogName("kdeqt"));
00335 
00336     updateCatalogs(); // evaluate this for all languages
00337   }
00338 }
00339 
00340 static inline void getLanguagesFromVariable(QStringList& list, const char* variable)
00341 {
00342   QByteArray var( qgetenv(variable) );
00343   if (!var.isEmpty())
00344     list += QFile::decodeName(var).split(':');
00345 }
00346 
00347 void KLocalePrivate::initLanguageList(KConfig *config, bool useEnv)
00348 {
00349   KConfigGroup cg(config, "Locale");
00350 
00351   // Set the country as specified by the KDE config or use default,
00352   // do not consider environment variables.
00353   if (country.isEmpty())
00354     country = cg.readEntry("Country");
00355   if (country.isEmpty())
00356       country = KLocale::defaultCountry();
00357 
00358   // Collect possible languages by decreasing priority.
00359   // The priority is as follows:
00360   // - KDE_LANG environment variable (can be a list)
00361   // - KDE configuration (can be a list)
00362   // - environment variables considered by gettext(3)
00363   // The environment variables are not considered if useEnv is false.
00364   QStringList list;
00365 
00366   if (!language.isEmpty())
00367       list.append(language);
00368 
00369   // Collect languages set by KDE_LANG.
00370   if (useEnv)
00371     getLanguagesFromVariable(list, "KDE_LANG");
00372 
00373   // Collect languages set by KDE config.
00374   QString languages(cg.readEntry("Language", QString()));
00375   if (!languages.isEmpty())
00376     list += languages.split(':');
00377 
00378   // Collect languages read from environment variables by gettext(3).
00379   QStringList rawList;
00380   if (useEnv) {
00381     // Collect by same order of priority as for gettext(3).
00382 
00383     // LANGUAGE should contain colon-separated list of exact language codes,
00384     // so add them directly.
00385     getLanguagesFromVariable(list, "LANGUAGE");
00386 
00387     // Other environment variables contain locale string, which should
00388     // be checked for all combinations yielding language codes.
00389     getLanguagesFromVariable(rawList, "LC_ALL");
00390     getLanguagesFromVariable(rawList, "LC_MESSAGES");
00391     getLanguagesFromVariable(rawList, "LANG");
00392   }
00393 #ifdef Q_WS_WIN // how about Mac?
00394   rawList += QLocale::system().name(); // fall back to the system language
00395 #endif
00396 
00397 #ifndef Q_WS_WIN
00398   if (useEnv) // Collect languages  - continued...
00399 #endif
00400   {
00401     // Process the raw list to create possible combinations.
00402     foreach (const QString &ln, rawList) {
00403       QString lang, ctry, modf, cset;
00404       KLocale::splitLocale(ln, lang, ctry, modf, cset);
00405 
00406       if (!ctry.isEmpty() && !modf.isEmpty()) {
00407         list += lang + '_' + ctry + '@' + modf;
00408       }
00409       // NOTE: The priority is tricky in case both ctry and modf are present.
00410       // Should really lang@modf be of higher priority than lang_ctry?
00411       // For at least one case (Serbian language), it is better this way.
00412       if (!modf.isEmpty()) {
00413         list += lang + '@' + modf;
00414       }
00415       if (!ctry.isEmpty()) {
00416         list += lang + '_' + ctry;
00417       }
00418       list += lang;
00419     }
00420   }
00421 
00422   // Send the list to filter for really present languages on the system.
00423   setLanguage(list);
00424 }
00425 
00426 void KLocalePrivate::initFormat(KConfig *config)
00427 {
00428   Q_ASSERT(config);
00429 
00430   //kDebug(173) << "KLocalePrivate::KLocalePrivate";
00431 
00432   config->setLocale(language);
00433 
00434   KConfigGroup cg(config, "Locale");
00435 
00436   KConfig entryFile(KStandardDirs::locate("locale",
00437                                            QString::fromLatin1("l10n/%1/entry.desktop")
00438                                            .arg(country)));
00439   entryFile.setLocale(language);
00440   KConfigGroup entry(&entryFile, "KCM Locale");
00441 
00442   // Numeric
00443 #define readConfigEntry(key, default, save) \
00444   save = entry.readEntry(key, default); \
00445   save = cg.readEntry(key, save);
00446 
00447 #define readConfigNumEntry(key, default, save, type) \
00448   save = (type)entry.readEntry(key, int(default)); \
00449   save = (type)cg.readEntry(key, int(save));
00450 
00451   readConfigEntry("DecimalSymbol", ".", decimalSymbol);
00452   readConfigEntry("ThousandsSeparator", ",", thousandsSeparator);
00453   thousandsSeparator.remove( QString::fromLatin1("$0") );
00454   //kDebug(173) << "thousandsSeparator=" << thousandsSeparator;
00455 
00456   readConfigEntry("PositiveSign", "", positiveSign);
00457   readConfigEntry("NegativeSign", "-", negativeSign);
00458 
00459   readConfigNumEntry("DigitSet", KLocale::ArabicDigits,
00460                      digitSet, KLocale::DigitSet);
00461   // FIXME: Temporary until full language-sensitivity implemented.
00462   readConfigEntry("LanguageSensitiveDigits", true,
00463                   languageSensitiveDigits);
00464 
00465   // Monetary
00466   readConfigEntry("CurrencySymbol", "$", currencySymbol);
00467   readConfigEntry("MonetaryDecimalSymbol", ".", monetaryDecimalSymbol);
00468   readConfigEntry("MonetaryThousandsSeparator", ",",
00469           monetaryThousandsSeparator);
00470   monetaryThousandsSeparator.remove(QString::fromLatin1("$0"));
00471 
00472   readConfigNumEntry("FracDigits", 2, fracDigits, int);
00473   readConfigEntry("PositivePrefixCurrencySymbol", true,
00474               positivePrefixCurrencySymbol);
00475   readConfigEntry("NegativePrefixCurrencySymbol", true,
00476               negativePrefixCurrencySymbol);
00477   readConfigNumEntry("PositiveMonetarySignPosition", KLocale::BeforeQuantityMoney,
00478              positiveMonetarySignPosition, KLocale::SignPosition);
00479   readConfigNumEntry("NegativeMonetarySignPosition", KLocale::ParensAround,
00480              negativeMonetarySignPosition, KLocale::SignPosition);
00481 
00482   readConfigNumEntry("MonetaryDigitSet", KLocale::ArabicDigits,
00483                      monetaryDigitSet, KLocale::DigitSet);
00484 
00485   // Date and time
00486   readConfigEntry("TimeFormat", "%H:%M:%S", timeFormat);
00487   readConfigEntry("DateFormat", "%A %d %B %Y", dateFormat);
00488   readConfigEntry("DateFormatShort", "%Y-%m-%d", dateFormatShort);
00489   readConfigNumEntry("WeekStartDay", 1, weekStartDay, int);                //default to Monday
00490   readConfigNumEntry("WorkingWeekStartDay", 1, workingWeekStartDay, int);  //default to Monday
00491   readConfigNumEntry("WorkingWeekEndDay", 5, workingWeekEndDay, int);      //default to Friday
00492   readConfigNumEntry("WeekDayOfPray", 7, weekDayOfPray, int);              //default to Sunday
00493   readConfigNumEntry("DateTimeDigitSet", KLocale::ArabicDigits,
00494                      dateTimeDigitSet, KLocale::DigitSet);
00495 
00496   // other
00497   readConfigNumEntry("PageSize", QPrinter::A4, pageSize,
00498              QPrinter::PageSize);
00499   readConfigNumEntry("MeasureSystem", KLocale::Metric,
00500              measureSystem, KLocale::MeasureSystem);
00501   readConfigEntry("CalendarSystem", "gregorian", calendarType);
00502   delete calendar;
00503   calendar = 0; // ### HPB Is this the correct place?
00504 
00505   readConfigEntry("Transcript", true, useTranscript);
00506 
00507   //Grammatical
00508   //Precedence here is l10n / i18n / config file
00509   KConfig langCfg(KStandardDirs::locate("locale",
00510                                         QString::fromLatin1("%1/entry.desktop")
00511                                         .arg(language)));
00512   KConfigGroup lang(&langCfg,"KCM Locale");
00513 #define read3ConfigBoolEntry(key, default, save) \
00514   save = entry.readEntry(key, default); \
00515   save = lang.readEntry(key, save); \
00516   save = cg.readEntry(key, save);
00517 
00518   read3ConfigBoolEntry("NounDeclension", false, nounDeclension);
00519   read3ConfigBoolEntry("DateMonthNamePossessive", false,
00520                dateMonthNamePossessive);
00521 }
00522 
00523 bool KLocale::setCountry(const QString & aCountry, KConfig *config)
00524 {
00525   // Check if the file exists too??
00526   if ( aCountry.isEmpty() )
00527     return false;
00528 
00529   d->country = aCountry;
00530 
00531   d->initFormat(config);
00532 
00533   return true;
00534 }
00535 
00536 bool KLocale::setLanguage(const QString & language, KConfig *config)
00537 {
00538   return d->setLanguage(language, config);
00539 }
00540 
00541 bool KLocalePrivate::setLanguage(const QString & _language, KConfig *config)
00542 {
00543   QMutexLocker lock(kLocaleMutex());
00544   languageList.removeAll( _language );
00545   languageList.prepend( _language ); // let us consider this language to be the most important one
00546 
00547   language = _language; // remember main language for shortcut evaluation
00548 
00549   // important when called from the outside and harmless when called before
00550   // populating the catalog name list
00551   updateCatalogs();
00552 
00553   initFormat(config);
00554 
00555   return true; // Maybe the mo-files for this language are empty, but in principle we can speak all languages
00556 }
00557 
00558 bool KLocale::setLanguage(const QStringList & languages)
00559 {
00560   return d->setLanguage(languages);
00561 }
00562 
00563 bool KLocalePrivate::setLanguage(const QStringList & languages)
00564 {
00565   QMutexLocker lock(kLocaleMutex());
00566   // This list might contain
00567   // 1) some empty strings that we have to eliminate
00568   // 2) duplicate entries like in de:fr:de, where we have to keep the first occurrence of a language in order
00569   //    to preserve the order of precenence of the user
00570   // 3) languages into which the application is not translated. For those languages we should not even load kdelibs.mo or kio.po.
00571   //    these languages have to be dropped. Otherwise we get strange side effects, e.g. with Hebrew:
00572   //    the right/left switch for languages that write from
00573   //    right to left (like Hebrew or Arabic) is set in kdelibs.mo. If you only have kdelibs.mo
00574   //    but nothing from appname.mo, you get a mostly English app with layout from right to left.
00575   //    That was considered to be a bug by the Hebrew translators.
00576   QStringList list;
00577   foreach (const QString &language, languages) {
00578       if (!language.isEmpty() && !list.contains(language) && isApplicationTranslatedInto(language)) {
00579           list.append(language);
00580       }
00581   }
00582 
00583   if ( !list.contains( KLocale::defaultLanguage() ) ) {
00584     // English should always be added as final possibility; this is important
00585     // for proper initialization of message text post-processors which are
00586     // needed for English too, like semantic to visual formatting, etc.
00587     list.append( KLocale::defaultLanguage() );
00588   }
00589 
00590   language = list.first(); // keep this for shortcut evaluations
00591 
00592   languageList = list; // keep this new list of languages to use
00593 
00594   // important when called from the outside and harmless when called before populating the
00595   // catalog name list
00596   updateCatalogs();
00597 
00598   return true; // we found something. Maybe it's only English, but we found something
00599 }
00600 
00601 bool KLocale::isApplicationTranslatedInto( const QString & lang)
00602 {
00603   return d->isApplicationTranslatedInto( lang );
00604 }
00605 
00606 bool KLocalePrivate::isApplicationTranslatedInto( const QString & lang)
00607 {
00608   if ( lang.isEmpty() ) {
00609     return false;
00610   }
00611 
00612   if ( lang == KLocale::defaultLanguage() ) {
00613     // en_us is always "installed"
00614     return true;
00615   }
00616 
00617   if (catalogName.isEmpty()) {
00618       kDebug() << "no appName!";
00619       return false;
00620   }
00621 
00622   // Check for this language and possible transliteration fallbacks.
00623   QStringList possibles;
00624   possibles += lang;
00625   possibles += KTranslit::fallbackList(lang);
00626   foreach (const QString &lang, possibles) {
00627     if ( ! KCatalog::catalogLocaleDir( catalogName, lang ).isEmpty() ) {
00628         return true;
00629     }
00630   }
00631   return false;
00632 }
00633 
00634 void KLocale::splitLocale(const QString &aLocale,
00635                           QString &language,
00636                           QString &country,
00637                           QString &modifier,
00638                           QString &charset)
00639 {
00640   QString locale = aLocale;
00641 
00642   language.clear();
00643   country.clear();
00644   modifier.clear();
00645   charset.clear();
00646 
00647   // In case there are several concatenated locale specifications,
00648   // truncate all but first.
00649   int f = locale.indexOf(':');
00650   if (f >= 0) {
00651     locale.truncate(f);
00652   }
00653 
00654   f = locale.indexOf('.');
00655   if (f >= 0) {
00656     charset = locale.mid(f + 1);
00657     locale.truncate(f);
00658   }
00659 
00660   f = locale.indexOf('@');
00661   if (f >= 0) {
00662     modifier = locale.mid(f + 1);
00663     locale.truncate(f);
00664   }
00665 
00666   f = locale.indexOf('_');
00667   if (f >= 0) {
00668     country = locale.mid(f + 1);
00669     locale.truncate(f);
00670   }
00671 
00672   language = locale;
00673 }
00674 
00675 QString KLocale::language() const
00676 {
00677   return d->language;
00678 }
00679 
00680 QString KLocale::country() const
00681 {
00682   return d->country;
00683 }
00684 
00685 void KLocale::insertCatalog( const QString & catalog )
00686 {
00687   QMutexLocker lock(kLocaleMutex());
00688     int pos = d->catalogNames.indexOf(KCatalogName(catalog));
00689     if (pos != -1) {
00690         ++d->catalogNames[pos].loadCount;
00691         return;
00692     }
00693 
00694     // Insert new catalog just before system catalogs, to preserve the
00695     // lowest priority of system catalogs.
00696     d->catalogNames.insert(d->catalogNames.size() - d->numberOfSysCatalogs,
00697                            KCatalogName(catalog));
00698     d->updateCatalogs(); // evaluate the changed list and generate the necessary KCatalog objects
00699 }
00700 
00701 void KLocalePrivate::updateCatalogs( )
00702 {
00703   // some changes have occurred. Maybe we have learned or forgotten some languages.
00704   // Maybe the language precedence has changed.
00705   // Maybe we have learned or forgotten some catalog names.
00706 
00707   QList<KCatalog> newCatalogs;
00708 
00709   // Insert possible transliteration fallbacks after each set language.
00710   QStringList languageListFB;
00711   foreach (const QString &lang, languageList) {
00712     languageListFB += lang;
00713     languageListFB += KTranslit::fallbackList(lang);
00714   }
00715 
00716   // now iterate over all languages and all wanted catalog names and append or create them in the right order
00717   // the sequence must be e.g. nds/appname nds/kdelibs nds/kio de/appname de/kdelibs de/kio etc.
00718   // and not nds/appname de/appname nds/kdelibs de/kdelibs etc. Otherwise we would be in trouble with a language
00719   // sequende nds,en_US, de. In this case en_US must hide everything below in the language list.
00720   foreach ( const QString &lang, languageListFB ) {
00721     foreach ( const KCatalogName &name, catalogNames ) {
00722       // create and add catalog for this name and language if it exists
00723       if ( ! KCatalog::catalogLocaleDir( name.name, lang ).isEmpty() )
00724       {
00725         newCatalogs.append( KCatalog( name.name, lang ) );
00726         //kDebug(173) << "Catalog: " << name << ":" << lang;
00727       }
00728     }
00729   }
00730 
00731   // notify KLocalizedString of catalog update.
00732   catalogs = newCatalogs;
00733   KLocalizedString::notifyCatalogsUpdated(languageListFB, catalogNames);
00734 }
00735 
00736 void KLocale::removeCatalog(const QString &catalog)
00737 {
00738     QMutexLocker lock(kLocaleMutex());
00739     int pos = d->catalogNames.indexOf(KCatalogName(catalog));
00740     if (pos == -1)
00741         return;
00742     if (--d->catalogNames[pos].loadCount > 0)
00743         return;
00744     d->catalogNames.removeAt(pos);
00745     if (KGlobal::hasMainComponent())
00746         d->updateCatalogs();  // walk through the KCatalog instances and weed out everything we no longer need
00747 }
00748 
00749 void KLocale::setActiveCatalog(const QString &catalog)
00750 {
00751     QMutexLocker lock(kLocaleMutex());
00752     int pos = d->catalogNames.indexOf(KCatalogName(catalog));
00753     if (pos == -1)
00754         return;
00755     d->catalogNames.move(pos, 0);
00756     d->updateCatalogs();  // walk through the KCatalog instances and adapt to the new order
00757 }
00758 
00759 KLocale::~KLocale()
00760 {
00761     delete d->calendar;
00762     delete d->languages;
00763     delete d;
00764 }
00765 
00766 void KLocalePrivate::translate_priv(const char *msgctxt,
00767                                     const char *msgid,
00768                                     const char *msgid_plural,
00769                                     unsigned long n,
00770                                     QString *language,
00771                                     QString *translation) const
00772 {
00773   if ( !msgid || !msgid[0] ) {
00774     kDebug(173) << "KLocale: trying to look up \"\" in catalog. "
00775                 << "Fix the program" << endl;
00776     language->clear();
00777     translation->clear();
00778     return;
00779   }
00780   if ( msgctxt && !msgctxt[0] ) {
00781     kDebug(173) << "KLocale: trying to use \"\" as context to message. "
00782                 << "Fix the program" << endl;
00783   }
00784   if ( msgid_plural && !msgid_plural[0] ) {
00785     kDebug(173) << "KLocale: trying to use \"\" as plural message. "
00786                 << "Fix the program" << endl;
00787   }
00788 
00789   QMutexLocker locker(kLocaleMutex());
00790   // determine the fallback string
00791   QString fallback;
00792   if ( msgid_plural == NULL )
00793     fallback = QString::fromUtf8( msgid );
00794   else {
00795     if ( n == 1 )
00796       fallback = QString::fromUtf8( msgid );
00797     else
00798       fallback = QString::fromUtf8( msgid_plural );
00799   }
00800   if ( language )
00801     *language = KLocale::defaultLanguage();
00802   if ( translation )
00803     *translation = fallback;
00804 
00805   // shortcut evaluation if en_US is main language: do not consult the catalogs
00806   if ( useDefaultLanguage() )
00807     return;
00808 
00809   QList<KCatalog> catalogList = catalogs;
00810   for ( QList<KCatalog>::ConstIterator it = catalogList.constBegin();
00811         it != catalogList.constEnd();
00812         ++it )
00813   {
00814     // shortcut evaluation: once we have arrived at en_US (default language) we cannot consult
00815     // the catalog as it will not have an assiciated mo-file. For this default language we can
00816     // immediately pick the fallback string.
00817     if ( (*it).language() == KLocale::defaultLanguage() )
00818       return;
00819 
00820     QString text;
00821     if ( msgctxt != NULL && msgid_plural != NULL )
00822         text = (*it).translateStrict( msgctxt, msgid, msgid_plural, n );
00823     else if ( msgid_plural != NULL )
00824         text = (*it).translateStrict( msgid, msgid_plural, n );
00825     else if ( msgctxt != NULL )
00826         text = (*it).translateStrict( msgctxt, msgid );
00827     else
00828         text = (*it).translateStrict( msgid );
00829 
00830     if ( !text.isEmpty() ) {
00831       // we found it
00832       if ( language )
00833         *language = (*it).language();
00834       if ( translation )
00835         *translation = text;
00836       return;
00837     }
00838   }
00839 }
00840 
00841 void KLocale::translateRaw(const char* msg,
00842                            QString *lang, QString *trans) const
00843 {
00844   d->translate_priv(0, msg, 0, 0, lang, trans);
00845 }
00846 
00847 void KLocale::translateRaw(const char *ctxt, const char *msg,
00848                            QString *lang, QString *trans) const
00849 {
00850   d->translate_priv(ctxt, msg, 0, 0, lang, trans);
00851 }
00852 
00853 void KLocale::translateRaw(const char *singular, const char *plural, unsigned long n,
00854                            QString *lang, QString *trans) const
00855 {
00856   d->translate_priv(0, singular, plural, n, lang, trans);
00857 }
00858 
00859 void KLocale::translateRaw(const char *ctxt, const char *singular, const char *plural,
00860                            unsigned long n,
00861                            QString *lang, QString *trans) const
00862 {
00863   d->translate_priv(ctxt, singular, plural, n, lang, trans);
00864 }
00865 
00866 QString KLocale::translateQt(const char *context, const char *sourceText,
00867                              const char *comment) const
00868 {
00869   // Qt's context is normally the name of the class of the method which makes
00870   // the tr(sourceText) call. However, it can also be manually supplied via
00871   // translate(context, sourceText) call.
00872   //
00873   // Qt's sourceText is the actual message displayed to the user.
00874   //
00875   // Qt's comment is an optional argument of tr() and translate(), like
00876   // tr(sourceText, comment) and translate(context, sourceText, comment).
00877   //
00878   // We handle this in the following way:
00879   //
00880   // If the comment is given, then it is considered gettext's msgctxt, so a
00881   // context call is made.
00882   //
00883   // If the comment is not given, but context is given, then we treat it as
00884   // msgctxt only if it was manually supplied (the one in translate()) -- but
00885   // we don't know this, so we first try a context call, and if translation
00886   // is not found, we fallback to ordinary call.
00887   //
00888   // If neither comment nor context are given, it's just an ordinary call
00889   // on sourceText.
00890 
00891   if (!sourceText || !sourceText[0]) {
00892     kDebug(173) << "KLocale: trying to look up \"\" in catalog. "
00893                 << "Fix the program" << endl;
00894     return QString();
00895   }
00896 
00897   if (d->useDefaultLanguage()) {
00898     return QString();
00899   }
00900 
00901   QString translation;
00902   QString language;
00903 
00904   // NOTE: Condition (language != defaultLanguage()) means that translation
00905   // was found, otherwise we got the original string back as translation.
00906 
00907   if (comment && comment[0]) {
00908     // Comment given, go for context call.
00909     d->translate_priv(comment, sourceText, 0, 0, &language, &translation);
00910   }
00911   else {
00912     // Comment not given, go for try-fallback with context.
00913     if (context && context[0]) {
00914       d->translate_priv(context, sourceText, 0, 0, &language, &translation);
00915     }
00916     if (language.isEmpty() || language == defaultLanguage()) {
00917       d->translate_priv(0, sourceText, 0, 0, &language, &translation);
00918     }
00919   }
00920 
00921   if (language != defaultLanguage()) {
00922     return translation;
00923   }
00924 
00925   // No proper translation found, return empty according to Qt's expectation.
00926   return QString();
00927 }
00928 
00929 QList<KLocale::DigitSet> KLocale::allDigitSetsList () const
00930 {
00931     QList<DigitSet> digitSets;
00932     digitSets.append(ArabicDigits);
00933     digitSets.append(ArabicIndicDigits);
00934     digitSets.append(EasternArabicIndicDigits);
00935     digitSets.append(DevenagariDigits);
00936     return digitSets;
00937 }
00938 
00939 static QString digitSetString (KLocale::DigitSet digitSet)
00940 {
00941     switch (digitSet) {
00942     case KLocale::ArabicIndicDigits:
00943         return QString::fromUtf8("٠١٢٣٤٥٦٧٨٩");
00944     case KLocale::EasternArabicIndicDigits:
00945         return QString::fromUtf8("Û°Û±Û²Û³Û´ÛµÛ¶Û·Û¸Û¹");
00946     case KLocale::DevenagariDigits:
00947         return QString::fromUtf8("०१२३४५६७८९");
00948     default:
00949         return QString::fromUtf8("0123456789");
00950     }
00951 }
00952 
00953 QString KLocale::digitSetToName (DigitSet digitSet, bool withDigits) const
00954 {
00955     QString name;
00956     switch (digitSet) {
00957     case KLocale::ArabicIndicDigits:
00958         name = i18nc("digit set", "Arabic-Indic");
00959         break;
00960     case KLocale::EasternArabicIndicDigits:
00961         name = i18nc("digit set", "Eastern Arabic-Indic");
00962         break;
00963     case KLocale::DevenagariDigits:
00964         name = i18nc("digit set", "Devenagari");
00965         break;
00966     default:
00967         name = i18nc("digit set", "Arabic");
00968     }
00969     if (withDigits) {
00970         QString digits = digitSetString(digitSet);
00971         QString nameWithDigits = i18nc("name of digit set with digit string, "
00972                                        "e.g. 'Arabic (0123456789)'",
00973                                        "%1 (%2)", name, digits);
00974         return nameWithDigits;
00975     } else {
00976         return name;
00977     }
00978 }
00979 
00980 QString KLocale::convertDigits (const QString &str, DigitSet digitSet,
00981                                 bool ignoreContext) const
00982 {
00983     if (!ignoreContext) {
00984         // Fall back to Western Arabic digits if requested digit set
00985         // is not appropriate for current application language.
00986         // FIXME: Temporary until full language-sensitivity implemented.
00987         KLocaleStaticData *s = staticData;
00988         if (   d->languageSensitiveDigits
00989             && !s->languagesUsingDigitSet[digitSet].contains(d->language))
00990         {
00991             digitSet = KLocale::ArabicDigits;
00992         }
00993     }
00994 
00995     QString nstr;
00996     QString digitDraw = digitSetString(digitSet);
00997     foreach (const QChar &c, str) {
00998         if (c.isDigit()) {
00999             nstr += digitDraw[c.digitValue()];
01000         } else {
01001             nstr += c;
01002         }
01003     }
01004     return nstr;
01005 }
01006 
01007 static QString toArabicDigits(const QString &str)
01008 {
01009     QString nstr;
01010     foreach (const QChar &c, str) {
01011         if (c.isDigit()) {
01012             nstr += QChar('0' + c.digitValue());
01013         } else {
01014             nstr += c;
01015         }
01016     }
01017     return nstr;
01018 }
01019 
01020 bool KLocale::nounDeclension() const
01021 {
01022   return d->nounDeclension;
01023 }
01024 
01025 bool KLocale::dateMonthNamePossessive() const
01026 {
01027   return d->dateMonthNamePossessive;
01028 }
01029 
01030 int KLocale::weekStartDay() const
01031 {
01032   return d->weekStartDay;
01033 }
01034 
01035 int KLocale::workingWeekStartDay() const
01036 {
01037   return d->workingWeekStartDay;
01038 }
01039 
01040 int KLocale::workingWeekEndDay() const
01041 {
01042   return d->workingWeekEndDay;
01043 }
01044 
01045 int KLocale::weekDayOfPray() const
01046 {
01047   return d->weekDayOfPray;
01048 }
01049 
01050 
01051 QString KLocale::decimalSymbol() const
01052 {
01053   return d->decimalSymbol;
01054 }
01055 
01056 QString KLocale::thousandsSeparator() const
01057 {
01058   return d->thousandsSeparator;
01059 }
01060 
01061 QString KLocale::currencySymbol() const
01062 {
01063   return d->currencySymbol;
01064 }
01065 
01066 QString KLocale::monetaryDecimalSymbol() const
01067 {
01068   return d->monetaryDecimalSymbol;
01069 }
01070 
01071 QString KLocale::monetaryThousandsSeparator() const
01072 {
01073   return d->monetaryThousandsSeparator;
01074 }
01075 
01076 QString KLocale::positiveSign() const
01077 {
01078   return d->positiveSign;
01079 }
01080 
01081 QString KLocale::negativeSign() const
01082 {
01083   return d->negativeSign;
01084 }
01085 
01086 int KLocale::fracDigits() const
01087 {
01088   return d->fracDigits;
01089 }
01090 
01091 bool KLocale::positivePrefixCurrencySymbol() const
01092 {
01093   return d->positivePrefixCurrencySymbol;
01094 }
01095 
01096 bool KLocale::negativePrefixCurrencySymbol() const
01097 {
01098   return d->negativePrefixCurrencySymbol;
01099 }
01100 
01101 KLocale::SignPosition KLocale::positiveMonetarySignPosition() const
01102 {
01103   return d->positiveMonetarySignPosition;
01104 }
01105 
01106 KLocale::SignPosition KLocale::negativeMonetarySignPosition() const
01107 {
01108   return d->negativeMonetarySignPosition;
01109 }
01110 
01111 static inline void put_it_in( QChar *buffer, int& index, const QString &s )
01112 {
01113   for ( int l = 0; l < s.length(); l++ )
01114     buffer[index++] = s.at( l );
01115 }
01116 
01117 static inline void put_it_in( QChar *buffer, int& index, int number )
01118 {
01119   buffer[index++] = number / 10 + '0';
01120   buffer[index++] = number % 10 + '0';
01121 }
01122 
01123 // insert (thousands)-"separator"s into the non-fractional part of str
01124 static void _insertSeparator(QString &str, const QString &separator,
01125                  const QString &decimalSymbol)
01126 {
01127   // leave fractional part untouched
01128   const int decimalSymbolPos = str.indexOf(decimalSymbol);
01129   const int start = decimalSymbolPos == -1 ? str.length() : decimalSymbolPos;
01130   for (int pos = start - 3; pos > 0; pos -= 3)
01131     str.insert(pos, separator);
01132 }
01133 
01134 QString KLocale::formatMoney(double num,
01135                  const QString & symbol,
01136                  int precision) const
01137 {
01138   // some defaults
01139   QString currency = symbol.isNull()
01140     ? currencySymbol()
01141     : symbol;
01142   if (precision < 0) precision = fracDigits();
01143 
01144   // the number itself
01145   bool neg = num < 0;
01146   QString res = QString::number(neg?-num:num, 'f', precision);
01147 
01148   // Replace dot with locale decimal separator
01149   res.replace(QChar('.'), monetaryDecimalSymbol());
01150 
01151   // Insert the thousand separators
01152   _insertSeparator(res, monetaryThousandsSeparator(), monetaryDecimalSymbol());
01153 
01154   // set some variables we need later
01155   int signpos = neg
01156     ? negativeMonetarySignPosition()
01157     : positiveMonetarySignPosition();
01158   QString sign = neg
01159     ? negativeSign()
01160     : positiveSign();
01161 
01162   switch (signpos)
01163     {
01164     case ParensAround:
01165       res.prepend(QLatin1Char('('));
01166       res.append (QLatin1Char(')'));
01167       break;
01168     case BeforeQuantityMoney:
01169       res.prepend(sign);
01170       break;
01171     case AfterQuantityMoney:
01172       res.append(sign);
01173       break;
01174     case BeforeMoney:
01175       currency.prepend(sign);
01176       break;
01177     case AfterMoney:
01178       currency.append(sign);
01179       break;
01180     }
01181 
01182   if (neg?negativePrefixCurrencySymbol():
01183       positivePrefixCurrencySymbol())
01184     {
01185       res.prepend(QLatin1Char(' '));
01186       res.prepend(currency);
01187     } else {
01188       res.append (QLatin1Char(' '));
01189       res.append (currency);
01190     }
01191 
01192   // Convert to target digit set.
01193   res = convertDigits(res, d->monetaryDigitSet);
01194 
01195   return res;
01196 }
01197 
01198 
01199 QString KLocale::formatNumber(double num, int precision) const
01200 {
01201   if (precision == -1) precision = 2;
01202   // no need to round since QString::number does this for us
01203   return formatNumber(QString::number(num, 'f', precision), false, 0);
01204 }
01205 
01206 QString KLocale::formatLong(long num) const
01207 {
01208   return formatNumber((double)num, 0);
01209 }
01210 
01211 
01212 // increase the digit at 'position' by one
01213 static void _inc_by_one(QString &str, int position)
01214 {
01215   for (int i = position; i >= 0; i--)
01216     {
01217       char last_char = str[i].toLatin1();
01218       switch(last_char)
01219     {
01220     case '0':
01221       str[i] = '1';
01222       break;
01223     case '1':
01224       str[i] = '2';
01225       break;
01226     case '2':
01227       str[i] = '3';
01228       break;
01229     case '3':
01230       str[i] = '4';
01231       break;
01232     case '4':
01233       str[i] = '5';
01234       break;
01235     case '5':
01236       str[i] = '6';
01237       break;
01238     case '6':
01239       str[i] = '7';
01240       break;
01241     case '7':
01242       str[i] = '8';
01243       break;
01244     case '8':
01245       str[i] = '9';
01246       break;
01247     case '9':
01248       str[i] = '0';
01249       if (i == 0) str.prepend('1');
01250       continue;
01251     case '.':
01252       continue;
01253     }
01254       break;
01255     }
01256 }
01257 
01258 // Cut off if more digits in fractional part than 'precision'
01259 static void _round(QString &str, int precision)
01260 {
01261   int decimalSymbolPos = str.indexOf('.');
01262 
01263   if (decimalSymbolPos == -1) {
01264     if (precision == 0)  return;
01265     else if (precision > 0) // add dot if missing (and needed)
01266       {
01267     str.append('.');
01268     decimalSymbolPos = str.length() - 1;
01269       }
01270   }
01271   // fill up with more than enough zeroes (in case fractional part too short)
01272   str.reserve(str.length() + precision);
01273   for (int i = 0; i < precision; ++i)
01274     str.append('0');
01275 
01276   // Now decide whether to round up or down
01277   char last_char = str[decimalSymbolPos + precision + 1].toLatin1();
01278   switch (last_char)
01279     {
01280     case '0':
01281     case '1':
01282     case '2':
01283     case '3':
01284     case '4':
01285       // nothing to do, rounding down
01286       break;
01287     case '5':
01288     case '6':
01289     case '7':
01290     case '8':
01291     case '9':
01292       _inc_by_one(str, decimalSymbolPos + precision);
01293       break;
01294     default:
01295       break;
01296     }
01297 
01298   decimalSymbolPos = str.indexOf('.');
01299   str.truncate(decimalSymbolPos + precision + 1);
01300 
01301   // if precision == 0 delete also '.'
01302   if (precision == 0) str = str.left(decimalSymbolPos);
01303 
01304   str.squeeze();
01305 }
01306 
01307 QString KLocale::formatNumber(const QString &numStr, bool round,
01308                   int precision) const
01309 {
01310   QString tmpString = numStr;
01311   if (round && precision < 0)
01312     return numStr;
01313 
01314   // Skip the sign (for now)
01315   const bool neg = (tmpString[0] == '-');
01316   if (neg || tmpString[0] == '+') tmpString.remove(0, 1);
01317 
01318   //kDebug(173)<<"tmpString:"<<tmpString;
01319 
01320   // Split off exponential part (including 'e'-symbol)
01321   const int expPos = tmpString.indexOf('e'); // -1 if not found
01322   QString mantString = tmpString.left(expPos); // entire string if no 'e' found
01323   QString expString;
01324   if (expPos > -1) {
01325     expString = tmpString.mid(expPos); // includes the 'e', or empty if no 'e'
01326     if (expString.length() == 1)
01327       expString.clear();
01328   }
01329 
01330   //kDebug(173)<<"mantString:"<<mantString;
01331   //kDebug(173)<<"expString:"<<expString;
01332   if (mantString.isEmpty() || !mantString[0].isDigit()) // invalid number
01333     mantString = "0";
01334 
01335   if (round)
01336     _round(mantString, precision);
01337 
01338   // Replace dot with locale decimal separator
01339   mantString.replace(QChar('.'), decimalSymbol());
01340 
01341   // Insert the thousand separators
01342   _insertSeparator(mantString, thousandsSeparator(), decimalSymbol());
01343 
01344   // How can we know where we should put the sign?
01345   mantString.prepend(neg?negativeSign():positiveSign());
01346 
01347   // Convert to target digit set.
01348   if (d->digitSet != KLocale::ArabicDigits) {
01349       mantString = convertDigits(mantString, d->digitSet);
01350       expString = convertDigits(expString, d->digitSet);
01351   }
01352 
01353   return mantString + expString;
01354 }
01355 
01356 // If someone wants the SI-standard prefixes kB/MB/GB/TB, I would recommend
01357 // a hidden kconfig option and getting the code from #57240 into the same
01358 // method, so that all KDE apps use the same unit, instead of letting each app decide.
01359 
01360 QString KLocale::formatByteSize( double size ) const
01361 {
01362     // Per IEC 60027-2
01363 
01364     // Binary prefixes
01365     //Tebi-byte             TiB             2^40    1,099,511,627,776 bytes
01366     //Gibi-byte             GiB             2^30    1,073,741,824 bytes
01367     //Mebi-byte             MiB             2^20    1,048,576 bytes
01368     //Kibi-byte             KiB             2^10    1,024 bytes
01369 
01370     if (d->byteSizeFmt.size() == 0) {
01371         QMutexLocker lock(kLocaleMutex());
01372         // Pretranslated format strings for byte sizes.
01373         #define CACHEBYTEFMT(x) { \
01374             QString s; \
01375             translateRaw(x, 0, &s); \
01376             d->byteSizeFmt.append(s); \
01377         } while(0)
01378         // i18n: Dumb message, avoid any markup or scripting.
01379         CACHEBYTEFMT(I18N_NOOP("%1 TiB"));
01380         // i18n: Dumb message, avoid any markup or scripting.
01381         CACHEBYTEFMT(I18N_NOOP("%1 GiB"));
01382         // i18n: Dumb message, avoid any markup or scripting.
01383         CACHEBYTEFMT(I18N_NOOP("%1 MiB"));
01384         // i18n: Dumb message, avoid any markup or scripting.
01385         CACHEBYTEFMT(I18N_NOOP("%1 KiB"));
01386         // i18n: Dumb message, avoid any markup or scripting.
01387         CACHEBYTEFMT(I18N_NOOP("%1 B"));
01388     }
01389 
01390     #define BYTEFMT(unit, n) (d->byteSizeFmt[KLocalePrivate::unit].arg(n))
01391     QString s;
01392     if (size >= 1073741824.0) {
01393         size /= 1073741824.0;
01394         if (size > 1024.0) {
01395             s = BYTEFMT(TiB, formatNumber(size / 1024.0, 1));
01396         } else {
01397             s = BYTEFMT(GiB, formatNumber(size, 1));
01398         }
01399     } else if (size >= 1048576.0) {
01400         size /= 1048576.0;
01401         s = BYTEFMT(MiB, formatNumber(size, 1));
01402     } else if (size >= 1024.0) {
01403         size /= 1024.0;
01404         s = BYTEFMT(KiB, formatNumber(size, 1));
01405     } else {
01406         s = BYTEFMT(B, formatNumber(size, 0));
01407     }
01408     return s;
01409 }
01410 
01411 QString KLocale::formatDuration( unsigned long mSec) const
01412 {
01413    if( mSec >= 24*3600000) {
01414       return i18n( "%1 days", formatNumber( mSec/(24*3600000), 3));
01415    } else if(mSec >= 3600000)
01416    {
01417       return i18n( "%1 hours", formatNumber( mSec/3600000.0, 2));
01418    } else if(mSec >= 60000)
01419    {
01420       return i18n( "%1 minutes", formatNumber( mSec/60000.0, 2));
01421    } else if(mSec >= 1000)
01422    {
01423       return i18n( "%1 seconds", formatNumber( mSec/1000.0, 2));
01424    }
01425 
01426    return i18n( "%1 milliseconds", formatNumber(mSec, 0));
01427 }
01428 
01429 QString KLocalePrivate::formatSingleDuration( DurationType durationType, int n )
01430 {
01431     switch (durationType) {
01432         case DaysDurationType:
01433             return i18ncp("@item:intext", "1 day", "%1 days", n);
01434         case HoursDurationType:
01435             return i18ncp("@item:intext", "1 hour", "%1 hours", n);
01436         case MinutesDurationType:
01437             return i18ncp("@item:intext", "1 minute", "%1 minutes", n);
01438         case SecondsDurationType:
01439             return i18ncp("@item:intext", "1 second", "%1 seconds", n);
01440     }
01441     return QString();
01442 }
01443 
01444 QString KLocale::prettyFormatDuration( unsigned long mSec ) const
01445 {
01446     unsigned long ms = mSec;
01447     int days = ms/(24*3600000);
01448     ms = ms%(24*3600000);
01449     int hours = ms/3600000;
01450     ms = ms%3600000;
01451     int minutes = ms/60000;
01452     ms = ms%60000;
01453     int seconds = qRound(ms/1000.0);
01454 
01455     // Handle correctly problematic case #1 (look at KLocaleTest::prettyFormatDuration()
01456     // at klocaletest.cpp)
01457     if (seconds == 60) {
01458         return prettyFormatDuration(mSec - ms + 60000);
01459     }
01460 
01461     if (days && hours) {
01462         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", "%1 and %2", d->formatSingleDuration(KLocalePrivate::DaysDurationType, days), d->formatSingleDuration(KLocalePrivate::HoursDurationType, hours));
01463     } else if (days) {
01464         return d->formatSingleDuration(KLocalePrivate::DaysDurationType, days);
01465     } else if (hours && minutes) {
01466         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", "%1 and %2", d->formatSingleDuration(KLocalePrivate::HoursDurationType, hours), d->formatSingleDuration(KLocalePrivate::MinutesDurationType, minutes));
01467     } else if (hours) {
01468         return d->formatSingleDuration(KLocalePrivate::HoursDurationType, hours);
01469     } else if (minutes && seconds) {
01470         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", "%1 and %2", d->formatSingleDuration(KLocalePrivate::MinutesDurationType, minutes), d->formatSingleDuration(KLocalePrivate::SecondsDurationType, seconds));
01471     } else if (minutes) {
01472         return d->formatSingleDuration(KLocalePrivate::MinutesDurationType, minutes);
01473     } else {
01474         return d->formatSingleDuration(KLocalePrivate::SecondsDurationType, seconds);
01475     }
01476 }
01477 
01478 QString KLocale::formatDate(const QDate &pDate, DateFormat format) const
01479 {
01480   if (format == FancyShortDate || format == FancyLongDate)
01481   {
01482     int days = pDate.daysTo(QDate::currentDate());
01483     if (days >= 0 && days < 7)
01484       return KLocalePrivate::fancyDate(this, pDate, days);
01485     format = (format == FancyShortDate) ? ShortDate : LongDate;
01486   }
01487   const QString rst = (format == ShortDate) ? dateFormatShort() : dateFormat();
01488 
01489   QString buffer;
01490 
01491   if ( ! pDate.isValid() ) return buffer;
01492 
01493   bool escape = false;
01494 
01495   int year = calendar()->year(pDate);
01496   int month = calendar()->month(pDate);
01497 
01498   for ( int format_index = 0; format_index < rst.length(); ++format_index )
01499     {
01500       if ( !escape )
01501     {
01502       if ( rst.at( format_index ).unicode() == '%' )
01503         escape = true;
01504       else
01505         buffer.append(rst.at(format_index));
01506     }
01507       else
01508     {
01509       switch ( rst.at( format_index ).unicode() )
01510         {
01511         case '%':
01512           buffer.append(QLatin1Char('%'));
01513           break;
01514         case 'Y':  //Long year numeric
01515           buffer.append(calendar()->yearString(pDate, KCalendarSystem::LongFormat));
01516           break;
01517         case 'y':  //Short year numeric
01518           buffer.append(calendar()->yearString(pDate, KCalendarSystem::ShortFormat));
01519           break;
01520         case 'n':  //Short month numeric
01521               buffer.append(calendar()->monthString(pDate, KCalendarSystem::ShortFormat));
01522           break;
01523         case 'e':  //Short day numeric
01524               buffer.append(calendar()->dayString(pDate, KCalendarSystem::ShortFormat));
01525           break;
01526         case 'm':  //Long month numeric
01527               buffer.append(calendar()->monthString(pDate, KCalendarSystem::LongFormat));
01528           break;
01529         case 'b':  //Short month name
01530           if (d->dateMonthNamePossessive)
01531         buffer.append(calendar()->monthName(month, year, KCalendarSystem::ShortNamePossessive));
01532           else
01533         buffer.append(calendar()->monthName(month, year, KCalendarSystem::ShortName));
01534           break;
01535         case 'B':  //Long month name
01536           if (d->dateMonthNamePossessive)
01537         buffer.append(calendar()->monthName(month, year, KCalendarSystem::LongNamePossessive));
01538           else
01539         buffer.append(calendar()->monthName(month, year, KCalendarSystem::LongName));
01540           break;
01541         case 'd':  //Long day numeric
01542               buffer.append(calendar()->dayString(pDate, KCalendarSystem::LongFormat));
01543           break;
01544         case 'a':  //Short weekday name
01545           buffer.append(calendar()->weekDayName(pDate, KCalendarSystem::ShortDayName));
01546           break;
01547         case 'A':  //Long weekday name
01548           buffer.append(calendar()->weekDayName(pDate, KCalendarSystem::LongDayName));
01549           break;
01550         default:
01551           buffer.append(rst.at(format_index));
01552           break;
01553         }
01554       escape = false;
01555     }
01556     }
01557   buffer = convertDigits(buffer, d->dateTimeDigitSet);
01558   return buffer;
01559 }
01560 
01561 QString KLocalePrivate::fancyDate(const KLocale *locale, const QDate &date, int days)
01562 {
01563   switch (days)
01564   {
01565     case 0:
01566       return i18n("Today");
01567     case 1:
01568       return i18n("Yesterday");
01569     default:
01570       return locale->calendar()->weekDayName(date);
01571   }
01572 }
01573 
01574 void KLocale::setMainCatalog(const char *catalog)
01575 {
01576   KLocaleStaticData *s = staticData;
01577   s->maincatalog = QString::fromUtf8(catalog);
01578 }
01579 
01580 double KLocale::readNumber(const QString &_str, bool * ok) const
01581 {
01582   QString str = _str.trimmed();
01583   bool neg = str.indexOf(negativeSign()) == 0;
01584   if (neg)
01585     str.remove( 0, negativeSign().length() );
01586 
01587   /* will hold the scientific notation portion of the number.
01588      Example, with 2.34E+23, exponentialPart == "E+23"
01589   */
01590   QString exponentialPart;
01591   int EPos;
01592 
01593   EPos = str.indexOf('E', 0, Qt::CaseInsensitive);
01594 
01595   if (EPos != -1)
01596   {
01597     exponentialPart = str.mid(EPos);
01598     str = str.left(EPos);
01599   }
01600 
01601   int pos = str.indexOf(decimalSymbol());
01602   QString major;
01603   QString minor;
01604   if ( pos == -1 )
01605     major = str;
01606   else
01607     {
01608       major = str.left(pos);
01609       minor = str.mid(pos + decimalSymbol().length());
01610     }
01611 
01612   // Remove thousand separators
01613   int thlen = thousandsSeparator().length();
01614   int lastpos = 0;
01615   while ( ( pos = major.indexOf( thousandsSeparator() ) ) > 0 )
01616   {
01617     // e.g. 12,,345,,678,,922 Acceptable positions (from the end) are 5, 10, 15... i.e. (3+thlen)*N
01618     int fromEnd = major.length() - pos;
01619     if ( fromEnd % (3+thlen) != 0 // Needs to be a multiple, otherwise it's an error
01620         || pos - lastpos > 3 // More than 3 digits between two separators -> error
01621         || pos == 0          // Can't start with a separator
01622         || (lastpos>0 && pos-lastpos!=3))   // Must have exactly 3 digits between two separators
01623     {
01624       if (ok) *ok = false;
01625       return 0.0;
01626     }
01627 
01628     lastpos = pos;
01629     major.remove( pos, thlen );
01630   }
01631   if (lastpos>0 && major.length()-lastpos!=3)   // Must have exactly 3 digits after the last separator
01632   {
01633     if (ok) *ok = false;
01634     return 0.0;
01635   }
01636 
01637   QString tot;
01638   if (neg) tot = '-';
01639 
01640   tot += major + '.' + minor + exponentialPart;
01641 
01642   tot = toArabicDigits(tot);
01643 
01644   return tot.toDouble(ok);
01645 }
01646 
01647 double KLocale::readMoney(const QString &_str, bool * ok) const
01648 {
01649   QString str = _str.trimmed();
01650   bool neg = false;
01651   bool currencyFound = false;
01652   QString symbol = currencySymbol();
01653 
01654   // First try removing currency symbol from either end
01655   int pos = str.indexOf(symbol);
01656   if ( pos == 0 || pos == (int) str.length()-symbol.length() )
01657     {
01658       str.remove(pos,symbol.length());
01659       str = str.trimmed();
01660       currencyFound = true;
01661     }
01662   if (str.isEmpty())
01663     {
01664       if (ok) *ok = false;
01665       return 0;
01666     }
01667   // Then try removing negative sign from either end
01668   // (with a special case for parenthesis)
01669   if (negativeMonetarySignPosition() == ParensAround)
01670     {
01671       if (str[0] == '(' && str[str.length()-1] == ')')
01672         {
01673       neg = true;
01674       str.remove(str.length()-1,1);
01675       str.remove(0,1);
01676         }
01677     }
01678   else
01679     {
01680       int i1 = str.indexOf(negativeSign());
01681       if ( i1 == 0 || i1 == (int) str.length()-1 )
01682         {
01683       neg = true;
01684       str.remove(i1,negativeSign().length());
01685         }
01686     }
01687   if (neg) str = str.trimmed();
01688 
01689   // Finally try again for the currency symbol, if we didn't find
01690   // it already (because of the negative sign being in the way).
01691   if ( !currencyFound )
01692     {
01693       pos = str.indexOf(symbol);
01694       if ( pos == 0 || pos == (int) str.length()-symbol.length() )
01695         {
01696       str.remove(pos,symbol.length());
01697       str = str.trimmed();
01698         }
01699     }
01700 
01701   // And parse the rest as a number
01702   pos = str.indexOf(monetaryDecimalSymbol());
01703   QString major;
01704   QString minior;
01705   if (pos == -1)
01706     major = str;
01707   else
01708     {
01709       major = str.left(pos);
01710       minior = str.mid(pos + monetaryDecimalSymbol().length());
01711     }
01712 
01713   // Remove thousand separators
01714   int thlen = monetaryThousandsSeparator().length();
01715   int lastpos = 0;
01716   while ( ( pos = major.indexOf( monetaryThousandsSeparator() ) ) > 0 )
01717   {
01718     // e.g. 12,,345,,678,,922 Acceptable positions (from the end) are 5, 10, 15... i.e. (3+thlen)*N
01719     int fromEnd = major.length() - pos;
01720     if ( fromEnd % (3+thlen) != 0 // Needs to be a multiple, otherwise it's an error
01721         || pos - lastpos > 3 // More than 3 digits between two separators -> error
01722         || pos == 0          // Can't start with a separator
01723         || (lastpos>0 && pos-lastpos!=3))   // Must have exactly 3 digits between two separators
01724     {
01725       if (ok) *ok = false;
01726       return 0.0;
01727     }
01728     lastpos = pos;
01729     major.remove( pos, thlen );
01730   }
01731   if (lastpos>0 && major.length()-lastpos!=3)   // Must have exactly 3 digits after the last separator
01732   {
01733     if (ok) *ok = false;
01734     return 0.0;
01735   }
01736 
01737   QString tot;
01738   if (neg) tot = '-';
01739   tot += major + '.' + minior;
01740   tot = toArabicDigits(tot);
01741   return tot.toDouble(ok);
01742 }
01743 
01750 static int readInt(const QString &str, int &pos)
01751 {
01752   if (!str.at(pos).isDigit()) return -1;
01753   int result = 0;
01754   for (; str.length() > pos && str.at(pos).isDigit(); ++pos)
01755     {
01756       result *= 10;
01757       result += str.at(pos).digitValue();
01758     }
01759 
01760   return result;
01761 }
01762 
01763 QDate KLocale::readDate(const QString &intstr, bool* ok) const
01764 {
01765   QDate date;
01766   date = readDate(intstr, ShortFormat, ok);
01767   if (date.isValid()) return date;
01768   return readDate(intstr, NormalFormat, ok);
01769 }
01770 
01771 QDate KLocale::readDate(const QString &intstr, ReadDateFlags flags, bool* ok) const
01772 {
01773   QString fmt = ((flags & ShortFormat) ? dateFormatShort() : dateFormat()).simplified();
01774   return readDate( intstr, fmt, ok );
01775 }
01776 
01777 QDate KLocale::readDate(const QString &intstr, const QString &fmt, bool* ok) const
01778 {
01779     //kDebug(173) << "KLocale::readDate intstr=" << intstr << " fmt=" << fmt;
01780     QString str = intstr.simplified().toLower();
01781     int day = -1, month = -1;
01782     // allow the year to be omitted if not in the format
01783     int year = calendar()->year(QDate::currentDate());
01784     int strpos = 0;
01785     int fmtpos = 0;
01786 
01787     int iLength; // Temporary variable used when reading input
01788 
01789     bool error = false;
01790 
01791     while (fmt.length() > fmtpos && str.length() > strpos && !error) {
01792 
01793         QChar c = fmt.at(fmtpos++);
01794         if (c != '%') {
01795             if (c.isSpace() && str.at(strpos).isSpace())
01796                 strpos++;
01797             else if (c != str.at(strpos++))
01798                 error = true;
01799         } else {
01800             int j;
01801             // remove space at the beginning
01802             if (str.length() > strpos && str.at(strpos).isSpace())
01803                 strpos++;
01804 
01805             c = fmt.at(fmtpos++);
01806             switch (c.unicode())
01807             {
01808             case 'a':
01809             case 'A':
01810                 error = true;
01811                 j = 1;
01812                 while (error && (j < 8)) {
01813                     QString s;
01814                     if ( c == 'a') {
01815                         s = calendar()->weekDayName(j, KCalendarSystem::ShortDayName).toLower();
01816                     } else {
01817                         s = calendar()->weekDayName(j, KCalendarSystem::LongDayName).toLower();
01818                     }
01819                     int len = s.length();
01820                     if (str.mid(strpos, len) == s)
01821                     {
01822                         strpos += len;
01823                         error = false;
01824                     }
01825                     j++;
01826                 }
01827                 break;
01828             case 'b':
01829             case 'B':
01830                 error = true;
01831                 if (d->dateMonthNamePossessive) {
01832                     j = 1;
01833                     while (error && (j < 13)) {
01834                         QString s;
01835                         if ( c == 'b' ) {
01836                             s = calendar()->monthName(j, year, KCalendarSystem::ShortNamePossessive).toLower();
01837                         } else {
01838                             s = calendar()->monthName(j, year, KCalendarSystem::LongNamePossessive).toLower();
01839                         }
01840                         int len = s.length();
01841                         if (str.mid(strpos, len) == s) {
01842                             month = j;
01843                             strpos += len;
01844                             error = false;
01845                         }
01846                         j++;
01847                     }
01848                 }
01849                 j = 1;
01850                 while (error && (j < 13)) {
01851                     QString s;
01852                     if ( c == 'b' ) {
01853                         s = calendar()->monthName(j, year, KCalendarSystem::ShortName).toLower();
01854                     } else {
01855                         s = calendar()->monthName(j, year, KCalendarSystem::LongName).toLower();
01856                     }
01857                     int len = s.length();
01858                     if (str.mid(strpos, len) == s) {
01859                         month = j;
01860                         strpos += len;
01861                         error = false;
01862                     }
01863                     j++;
01864                 }
01865                 break;
01866             case 'd':
01867             case 'e':
01868                 day = calendar()->dayStringToInteger(str.mid(strpos), iLength);
01869                 strpos += iLength;
01870 
01871                 error = iLength <= 0;
01872                 break;
01873 
01874             case 'n':
01875             case 'm':
01876                 month = calendar()->monthStringToInteger(str.mid(strpos), iLength);
01877                 strpos += iLength;
01878 
01879                 error = iLength <= 0;
01880                 break;
01881 
01882             case 'Y':
01883             case 'y':
01884                 year = calendar()->yearStringToInteger(str.mid(strpos), iLength);
01885                 strpos += iLength;
01886                 if (c == 'y' && year < 100)
01887                     year += 2000; // QDate assumes 19xx by default, but this isn't what users want...
01888 
01889                 error = iLength <= 0;
01890                 break;
01891             }
01892         }
01893     }
01894 
01895     /* for a match, we should reach the end of both strings, not just one of
01896        them */
01897     if (fmt.length() > fmtpos || str.length() > strpos) {
01898         error = true;
01899     }
01900 
01901     //kDebug(173) << "KLocale::readDate day=" << day << " month=" << month << " year=" << year;
01902     if (year != -1 && month != -1 && day != -1 && !error) {
01903         if (ok) *ok = true;
01904 
01905         QDate result;
01906         calendar()->setYMD(result, year, month, day);
01907 
01908         return result;
01909     }
01910 
01911     if (ok) *ok = false;
01912     return QDate(); // invalid date
01913 }
01914 
01915 QTime KLocale::readTime(const QString &intstr, bool *ok) const
01916 {
01917   QTime _time;
01918   _time = readTime(intstr, WithSeconds, ok);
01919   if (_time.isValid()) return _time;
01920   return readTime(intstr, WithoutSeconds, ok);
01921 }
01922 
01923 QTime KLocale::readTime(const QString &intstr, ReadTimeFlags flags, bool *ok) const
01924 {
01925     QString str = intstr.simplified().toLower();
01926     QString Format = timeFormat().simplified();
01927     if (flags & WithoutSeconds)
01928         Format.remove(QRegExp(".%S"));
01929 
01930     int hour = -1, minute = -1;
01931     int second = ( (flags & WithoutSeconds) == 0 ) ? -1 : 0; // don't require seconds
01932     bool g_12h = false;
01933     bool pm = false;
01934     int strpos = 0;
01935     int Formatpos = 0;
01936 
01937     while (Format.length() > Formatpos || str.length() > strpos) {
01938         if ( !(Format.length() > Formatpos && str.length() > strpos) )
01939             goto error;
01940 
01941         QChar c = Format.at(Formatpos++);
01942 
01943         if (c != '%') {
01944             if (c.isSpace())
01945                 strpos++;
01946             else if (c != str.at(strpos++))
01947                 goto error;
01948             continue;
01949     }
01950 
01951         // remove space at the beginning
01952         if (str.length() > strpos && str.at(strpos).isSpace())
01953             strpos++;
01954 
01955         c = Format.at(Formatpos++);
01956         switch (c.unicode()) {
01957     case 'p':
01958         {
01959         QString s = i18n("pm").toLower();
01960         int len = s.length();
01961         if (str.mid(strpos, len) == s) {
01962         pm = true;
01963         strpos += len;
01964             } else {
01965         s = i18n("am").toLower();
01966         len = s.length();
01967         if (str.mid(strpos, len) == s) {
01968                     pm = false;
01969                     strpos += len;
01970         } else
01971                     goto error;
01972             }
01973         }
01974         break;
01975 
01976     case 'k':
01977     case 'H':
01978             g_12h = false;
01979             hour = readInt(str, strpos);
01980             if (hour < 0 || hour > 23)
01981                 goto error;
01982 
01983             break;
01984 
01985     case 'l':
01986     case 'I':
01987             g_12h = true;
01988             hour = readInt(str, strpos);
01989             if (hour < 1 || hour > 12)
01990                 goto error;
01991 
01992             break;
01993 
01994     case 'M':
01995             minute = readInt(str, strpos);
01996             if (minute < 0 || minute > 59)
01997                 goto error;
01998 
01999             break;
02000 
02001     case 'S':
02002             second = readInt(str, strpos);
02003             if (second < 0 || second > 59)
02004                 goto error;
02005 
02006             break;
02007     }
02008     }
02009     if (g_12h) {
02010         hour %= 12;
02011         if (pm) hour += 12;
02012     }
02013 
02014     if (ok) *ok = true;
02015     return QTime(hour, minute, second);
02016 
02017 error:
02018     if (ok) *ok = false;
02019     return QTime(); // return invalid date if it didn't work
02020 }
02021 
02022 QString KLocale::formatTime(const QTime &pTime, bool includeSecs, bool isDuration) const
02023 {
02024   const QString rst = timeFormat();
02025 
02026   // only "pm/am" here can grow, the rest shrinks, but
02027   // I'm rather safe than sorry
02028   QChar *buffer = new QChar[rst.length() * 3 / 2 + 30];
02029 
02030   int index = 0;
02031   bool escape = false;
02032   int number = 0;
02033 
02034   for ( int format_index = 0; format_index < rst.length(); format_index++ )
02035     {
02036       if ( !escape )
02037     {
02038       if ( rst.at( format_index ).unicode() == '%' )
02039         escape = true;
02040       else
02041         buffer[index++] = rst.at( format_index );
02042     }
02043       else
02044     {
02045       switch ( rst.at( format_index ).unicode() )
02046         {
02047         case '%':
02048           buffer[index++] = '%';
02049           break;
02050         case 'H':
02051           put_it_in( buffer, index, pTime.hour() );
02052           break;
02053         case 'I':
02054           if ( isDuration )
02055               put_it_in( buffer, index, pTime.hour() );
02056           else
02057               put_it_in( buffer, index, ( pTime.hour() + 11) % 12 + 1 );
02058           break;
02059         case 'M':
02060           put_it_in( buffer, index, pTime.minute() );
02061           break;
02062         case 'S':
02063           if (includeSecs)
02064         put_it_in( buffer, index, pTime.second() );
02065           else if (index > 0) {
02066                   // remove spaces (#164813)
02067                   while(index > 0 && buffer[index-1].isSpace())
02068                     --index;
02069           // we remove the separator sign before the seconds and
02070           // assume that works everywhere
02071           --index;
02072                   // remove spaces (#164813)
02073                   while(index > 0 && buffer[index-1].isSpace())
02074                     --index;
02075           break;
02076         }
02077           break;
02078         case 'k':
02079           number = pTime.hour();
02080         case 'l':
02081           // to share the code
02082           if ( rst.at( format_index ).unicode() == 'l' )
02083         number = isDuration ? pTime.hour() : (pTime.hour() + 11) % 12 + 1;
02084           if ( number / 10 )
02085         buffer[index++] = number / 10 + '0';
02086           buffer[index++] = number % 10 + '0';
02087           break;
02088         case 'p':
02089           if ( !isDuration )
02090           {
02091         QString s;
02092         if ( pTime.hour() >= 12 )
02093           put_it_in( buffer, index, i18n("pm") );
02094         else
02095           put_it_in( buffer, index, i18n("am") );
02096           }
02097           break;
02098         default:
02099           buffer[index++] = rst.at( format_index );
02100           break;
02101         }
02102       escape = false;
02103     }
02104     }
02105   QString ret( buffer, index );
02106   delete [] buffer;
02107   ret = convertDigits(ret, d->dateTimeDigitSet);
02108   if ( isDuration ) // eliminate trailing-space due to " %p"
02109     return ret.trimmed();
02110   else
02111     return ret;
02112 }
02113 
02114 bool KLocale::use12Clock() const
02115 {
02116   if ((timeFormat().contains(QString::fromLatin1("%I")) > 0) ||
02117       (timeFormat().contains(QString::fromLatin1("%l")) > 0))
02118     return true;
02119   else
02120     return false;
02121 }
02122 
02123 QStringList KLocale::languageList() const
02124 {
02125   return d->languageList;
02126 }
02127 
02128 QString KLocalePrivate::formatDateTime(const KLocale *locale, const QDateTime &dateTime,
02129                                 KLocale::DateFormat format, bool includeSeconds, int daysTo)
02130 {
02131   QString dateStr = (format == KLocale::FancyShortDate || format == KLocale::FancyLongDate)
02132                   ? KLocalePrivate::fancyDate(locale, dateTime.date(), daysTo)
02133                   : locale->formatDate(dateTime.date(), format);
02134   return i18nc("concatenation of dates and time", "%1 %2",
02135                dateStr, locale->formatTime(dateTime.time(), includeSeconds));
02136 }
02137 
02138 QString KLocale::formatDateTime(const QDateTime &dateTime, DateFormat format,
02139                                 bool includeSeconds) const
02140 {
02141   QString dateStr;
02142   int days = -1;
02143   if (format == FancyShortDate || format == FancyLongDate)
02144   {
02145     QDateTime now = QDateTime::currentDateTime();
02146     days = dateTime.date().daysTo(now.date());
02147     if ((days == 0 && now.secsTo(dateTime) <= 3600)   // not more than an hour in the future
02148     ||  (days > 0 && days < 7))
02149       ;  // use fancy date format
02150     else
02151       format = (format == FancyShortDate) ? ShortDate : LongDate;  // fancy date not applicable
02152   }
02153   return KLocalePrivate::formatDateTime(this, dateTime, format, includeSeconds, days);
02154 }
02155 
02156 QString KLocale::formatDateTime(const KDateTime &dateTime, DateFormat format,
02157                                 DateTimeFormatOptions options) const
02158 {
02159   QString dt;
02160   if (dateTime.isDateOnly())
02161     dt = formatDate( dateTime.date(), format );
02162   else
02163   {
02164     int days = -1;
02165     if (format == FancyShortDate || format == FancyLongDate)
02166     {
02167       // Use the time specification (i.e. time zone, etc.) of 'dateTime' to
02168       // check whether it's less than a week ago.
02169       KDateTime now = KDateTime::currentDateTime(dateTime.timeSpec());
02170       days = dateTime.date().daysTo(now.date());
02171       if ((days == 0 && now.secsTo(dateTime) <= 3600)   // not more than an hour in the future
02172       ||  (days > 0 && days < 7))
02173         ;  // use fancy date format
02174       else
02175         format = (format == FancyShortDate) ? ShortDate : LongDate;  // fancy date not applicable
02176     }
02177     dt = KLocalePrivate::formatDateTime(this, dateTime.dateTime(), format, (options & Seconds), days);
02178   }
02179   if (options & TimeZone)
02180   {
02181     QString tz;
02182     switch (dateTime.timeType())
02183     {
02184       case KDateTime::OffsetFromUTC:
02185         tz = i18n(dateTime.toString("%z").toUtf8());
02186         break;
02187       case KDateTime::UTC:
02188       case KDateTime::TimeZone:
02189         tz = i18n(dateTime.toString((format == ShortDate) ? "%Z" : "%:Z").toUtf8());
02190         break;
02191       case KDateTime::ClockTime:
02192       default:
02193         break;
02194     }
02195     return i18nc( "concatenation of date/time and time zone", "%1 %2", dt, tz );
02196   }
02197   else
02198     return dt;
02199 }
02200 
02201 QString KLocale::langLookup(const QString &fname, const char *rtype)
02202 {
02203   QStringList search;
02204 
02205   // assemble the local search paths
02206   const QStringList localDoc = KGlobal::dirs()->resourceDirs(rtype);
02207 
02208   // look up the different languages
02209   for (int id=localDoc.count()-1; id >= 0; --id)
02210     {
02211       QStringList langs = KGlobal::locale()->languageList();
02212       langs.replaceInStrings("en_US", "en");
02213       langs.append("en");
02214       Q_FOREACH(const QString &lang, langs)
02215         search.append(QString("%1%2/%3").arg(localDoc[id]).arg(lang).arg(fname));
02216     }
02217 
02218   // try to locate the file
02219   Q_FOREACH (const QString &file, search)
02220     {
02221       kDebug(173) << "Looking for help in: " << file;
02222 
02223       QFileInfo info(file);
02224       if (info.exists() && info.isFile() && info.isReadable())
02225         return file;
02226     }
02227 
02228   return QString();
02229 }
02230 
02231 bool KLocalePrivate::useDefaultLanguage() const
02232 {
02233   return language == KLocale::defaultLanguage();
02234 }
02235 
02236 void KLocalePrivate::initEncoding()
02237 {
02238   codecForEncoding = 0;
02239 
02240   // This all made more sense when we still had the EncodingEnum config key.
02241 #if defined(HAVE_LANGINFO_H) && !defined(Q_OS_WIN)
02242   // Qt since 4.2 always returns 'System' as codecForLocale and KDE (for example KEncodingFileDialog)
02243   // expects real encoding name. So on systems that have langinfo.h use nl_langinfo instead,
02244   // just like Qt compiled without iconv does. Windows already has its own workaround
02245 
02246   QByteArray systemLocale = nl_langinfo(CODESET);
02247 #if defined(Q_OS_MAC)
02248   // Mac OX X is UTF-8, always.
02249   systemLocale = "UTF-8";
02250 #endif
02251 
02252   if (systemLocale == "ANSI_X3.4-1968") // means ascii, "C"; QTextCodec doesn't know, so avoid warning
02253     systemLocale = "ISO-8859-1";
02254 
02255   QTextCodec* codec = QTextCodec::codecForName(systemLocale);
02256   if ( codec )
02257     setEncoding( codec->mibEnum() );
02258 #else
02259   setEncoding( QTextCodec::codecForLocale()->mibEnum() );
02260 #endif
02261 
02262   if ( !codecForEncoding ) {
02263       kWarning() << "Cannot resolve system encoding, defaulting to ISO 8859-1.";
02264       const int mibDefault = 4; // ISO 8859-1
02265       setEncoding(mibDefault);
02266     }
02267 
02268   Q_ASSERT( codecForEncoding );
02269 }
02270 
02271 void KLocalePrivate::initFileNameEncoding()
02272 {
02273   // If the following environment variable is set, assume all filenames
02274   // are in UTF-8 regardless of the current C locale.
02275   utf8FileEncoding = !qgetenv("KDE_UTF8_FILENAMES").isEmpty();
02276   if (utf8FileEncoding)
02277   {
02278     QFile::setEncodingFunction(KLocalePrivate::encodeFileNameUTF8);
02279     QFile::setDecodingFunction(KLocalePrivate::decodeFileNameUTF8);
02280   }
02281   // Otherwise, stay with QFile's default filename encoding functions
02282   // which, on Unix platforms, use the locale's codec.
02283 }
02284 
02285 QByteArray KLocalePrivate::encodeFileNameUTF8( const QString & fileName )
02286 {
02287   return fileName.toUtf8();
02288 }
02289 
02290 QString KLocalePrivate::decodeFileNameUTF8( const QByteArray & localFileName )
02291 {
02292   return QString::fromUtf8(localFileName);
02293 }
02294 
02295 void KLocale::setDateFormat(const QString & format)
02296 {
02297   d->dateFormat = format.trimmed();
02298 }
02299 
02300 void KLocale::setDateFormatShort(const QString & format)
02301 {
02302   d->dateFormatShort = format.trimmed();
02303 }
02304 
02305 void KLocale::setDateMonthNamePossessive(bool possessive)
02306 {
02307   d->dateMonthNamePossessive = possessive;
02308 }
02309 
02310 void KLocale::setTimeFormat(const QString & format)
02311 {
02312   d->timeFormat = format.trimmed();
02313 }
02314 
02315 void KLocale::setWeekStartDay(int day)
02316 {
02317   if (day >= 1 && day <= calendar()->daysInWeek(QDate()))
02318     d->weekStartDay = day;
02319 }
02320 
02321 void KLocale::setWorkingWeekStartDay(int day)
02322 {
02323   if (day >= 1 && day <= calendar()->daysInWeek(QDate()))
02324     d->workingWeekStartDay = day;
02325 }
02326 
02327 void KLocale::setWorkingWeekEndDay(int day)
02328 {
02329   if (day >= 1 && day <= calendar()->daysInWeek(QDate()))
02330     d->workingWeekEndDay = day;
02331 }
02332 
02333 void KLocale::setWeekDayOfPray(int day)
02334 {
02335   if (day >= 0 && day <= calendar()->daysInWeek(QDate()))  // 0 = None
02336     d->weekDayOfPray = day;
02337 }
02338 
02339 QString KLocale::dateFormat() const
02340 {
02341   return d->dateFormat;
02342 }
02343 
02344 QString KLocale::dateFormatShort() const
02345 {
02346   return d->dateFormatShort;
02347 }
02348 
02349 QString KLocale::timeFormat() const
02350 {
02351   return d->timeFormat;
02352 }
02353 
02354 void KLocale::setDecimalSymbol(const QString & symbol)
02355 {
02356   d->decimalSymbol = symbol.trimmed();
02357 }
02358 
02359 void KLocale::setThousandsSeparator(const QString & separator)
02360 {
02361   // allow spaces here
02362   d->thousandsSeparator = separator;
02363 }
02364 
02365 void KLocale::setPositiveSign(const QString & sign)
02366 {
02367   d->positiveSign = sign.trimmed();
02368 }
02369 
02370 void KLocale::setNegativeSign(const QString & sign)
02371 {
02372   d->negativeSign = sign.trimmed();
02373 }
02374 
02375 void KLocale::setPositiveMonetarySignPosition(SignPosition signpos)
02376 {
02377   d->positiveMonetarySignPosition = signpos;
02378 }
02379 
02380 void KLocale::setNegativeMonetarySignPosition(SignPosition signpos)
02381 {
02382   d->negativeMonetarySignPosition = signpos;
02383 }
02384 
02385 void KLocale::setPositivePrefixCurrencySymbol(bool prefix)
02386 {
02387   d->positivePrefixCurrencySymbol = prefix;
02388 }
02389 
02390 void KLocale::setNegativePrefixCurrencySymbol(bool prefix)
02391 {
02392   d->negativePrefixCurrencySymbol = prefix;
02393 }
02394 
02395 void KLocale::setFracDigits(int digits)
02396 {
02397   d->fracDigits = digits;
02398 }
02399 
02400 void KLocale::setMonetaryThousandsSeparator(const QString & separator)
02401 {
02402   // allow spaces here
02403   d->monetaryThousandsSeparator = separator;
02404 }
02405 
02406 void KLocale::setMonetaryDecimalSymbol(const QString & symbol)
02407 {
02408   d->monetaryDecimalSymbol = symbol.trimmed();
02409 }
02410 
02411 void KLocale::setCurrencySymbol(const QString & symbol)
02412 {
02413   d->currencySymbol = symbol.trimmed();
02414 }
02415 
02416 int KLocale::pageSize() const
02417 {
02418   return d->pageSize;
02419 }
02420 
02421 void KLocale::setPageSize(int size)
02422 {
02423   // #### check if it's in range??
02424   d->pageSize = size;
02425 }
02426 
02427 KLocale::MeasureSystem KLocale::measureSystem() const
02428 {
02429   return d->measureSystem;
02430 }
02431 
02432 void KLocale::setMeasureSystem(MeasureSystem value)
02433 {
02434   d->measureSystem = value;
02435 }
02436 
02437 QString KLocale::defaultLanguage()
02438 {
02439   return QString::fromLatin1("en_US");
02440 }
02441 
02442 QString KLocale::defaultCountry()
02443 {
02444   return QString::fromLatin1("C");
02445 }
02446 
02447 bool KLocale::useTranscript() const
02448 {
02449   return d->useTranscript;
02450 }
02451 
02452 const QByteArray KLocale::encoding() const
02453 {
02454 #ifdef Q_WS_WIN
02455   if (0==qstrcmp("System", codecForEncoding()->name()))
02456   {
02457     //win32 returns "System" codec name here but KDE apps expect a real name:
02458     strcpy(d->win32SystemEncoding, "cp ");
02459     if (GetLocaleInfoA( MAKELCID(MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), SORT_DEFAULT),
02460       LOCALE_IDEFAULTANSICODEPAGE, d->win32SystemEncoding+3, sizeof(d->win32SystemEncoding)-3-1 ))
02461     {
02462       return d->win32SystemEncoding;
02463     }
02464   }
02465 #endif
02466   return codecForEncoding()->name();
02467 }
02468 
02469 int KLocale::encodingMib() const
02470 {
02471   return codecForEncoding()->mibEnum();
02472 }
02473 
02474 int KLocale::fileEncodingMib() const
02475 {
02476   if (d->utf8FileEncoding)
02477      return 106;
02478   return codecForEncoding()->mibEnum();
02479 }
02480 
02481 QTextCodec * KLocale::codecForEncoding() const
02482 {
02483   return d->codecForEncoding;
02484 }
02485 
02486 bool KLocale::setEncoding(int mibEnum)
02487 {
02488   return d->setEncoding(mibEnum);
02489 }
02490 
02491 bool KLocalePrivate::setEncoding(int mibEnum)
02492 {
02493   QTextCodec * codec = QTextCodec::codecForMib(mibEnum);
02494   if (codec)
02495     codecForEncoding = codec;
02496 
02497   return codec != 0;
02498 }
02499 
02500 QStringList KLocale::allLanguagesList() const
02501 {
02502   if (!d->languages)
02503     d->languages = new KConfig("all_languages", KConfig::NoGlobals, "locale");
02504 
02505   return d->languages->groupList();
02506 }
02507 
02508 QString KLocale::languageCodeToName(const QString &language) const
02509 {
02510   if (!d->languages)
02511     d->languages = new KConfig("all_languages", KConfig::NoGlobals, "locale");
02512 
02513   KConfigGroup cg(d->languages, language);
02514   return cg.readEntry("Name");
02515 }
02516 
02517 QStringList KLocale::allCountriesList() const
02518 {
02519   QStringList countries;
02520   const QStringList paths = KGlobal::dirs()->findAllResources("locale", "l10n/*/entry.desktop");
02521   for(QStringList::ConstIterator it = paths.begin();
02522       it != paths.end(); ++it)
02523   {
02524     QString code = (*it).mid((*it).length()-16, 2);
02525     if (code != "/C")
02526        countries.append(code);
02527   }
02528   return countries;
02529 }
02530 
02531 QString KLocale::countryCodeToName(const QString &country) const
02532 {
02533   QString countryName;
02534   QString entryFile = KStandardDirs::locate("locale", "l10n/"+country.toLower()+"/entry.desktop");
02535   if (!entryFile.isEmpty()) {
02536     KConfig cfg(entryFile);
02537     KConfigGroup cg(&cfg, "KCM Locale");
02538     countryName = cg.readEntry("Name");
02539   }
02540   return countryName;
02541 }
02542 
02543 void KLocale::setCalendar(const QString & calType)
02544 {
02545   d->calendarType = calType;
02546 
02547   delete d->calendar;
02548   d->calendar = 0;
02549 }
02550 
02551 QString KLocale::calendarType() const
02552 {
02553   return d->calendarType;
02554 }
02555 
02556 const KCalendarSystem * KLocale::calendar() const
02557 {
02558   // Check if it's the correct calendar?!?
02559   if ( !d->calendar )
02560     d->calendar = KCalendarSystem::create( d->calendarType, this );
02561 
02562   return d->calendar;
02563 }
02564 
02565 KLocale::KLocale(const KLocale & rhs) : d(new KLocalePrivate(*rhs.d))
02566 {
02567   d->languages = 0; // Don't copy languages
02568   d->calendar = 0; // Don't copy the calendar
02569 }
02570 
02571 KLocale & KLocale::operator=(const KLocale & rhs)
02572 {
02573   // the assignment operator works here
02574   *d = *rhs.d;
02575   d->languages = 0; // Don't copy languages
02576   d->calendar = 0; // Don't copy the calendar
02577 
02578   return *this;
02579 }
02580 
02581 void KLocale::copyCatalogsTo(KLocale *locale)
02582 {
02583     QMutexLocker lock(kLocaleMutex());
02584     locale->d->catalogNames = d->catalogNames;
02585     locale->d->updateCatalogs();
02586 }
02587 
02588 QString KLocale::localizedFilePath(const QString &filePath) const
02589 {
02590     // Stop here if the default language is primary.
02591     if (d->useDefaultLanguage()) {
02592         return filePath;
02593     }
02594 
02595     // Check if l10n sudir is present, stop if not.
02596     QFileInfo fileInfo(filePath);
02597     QString locDirPath = fileInfo.path() + "/l10n";
02598     QFileInfo locDirInfo(locDirPath);
02599     if (!locDirInfo.isDir()) {
02600         return filePath;
02601     }
02602 
02603     // Go through possible localized paths by priority of languages,
02604     // return first that exists.
02605     QString fileName = fileInfo.fileName();
02606     foreach (const QString &lang, d->languageList) {
02607         // Stop when the default language is reached.
02608         if (lang == KLocale::defaultLanguage()) {
02609             return filePath;
02610         }
02611         QString locFilePath = locDirPath + '/' + lang + '/' + fileName;
02612         QFileInfo locFileInfo(locFilePath);
02613         if (locFileInfo.isFile() && locFileInfo.isReadable()) {
02614             return locFilePath;
02615         }
02616     }
02617 
02618     return filePath;
02619 }
02620 
02621 QString KLocale::removeAcceleratorMarker(const QString &label) const
02622 {
02623     return ::removeAcceleratorMarker(label);
02624 }
02625 
02626 void KLocale::setDigitSet (DigitSet digitSet)
02627 {
02628     d->digitSet = digitSet;
02629 }
02630 
02631 KLocale::DigitSet KLocale::digitSet () const
02632 {
02633     return d->digitSet;
02634 }
02635 
02636 void KLocale::setMonetaryDigitSet (DigitSet digitSet)
02637 {
02638     d->monetaryDigitSet = digitSet;
02639 }
02640 
02641 KLocale::DigitSet KLocale::monetaryDigitSet () const
02642 {
02643     return d->monetaryDigitSet;
02644 }
02645 
02646 void KLocale::setDateTimeDigitSet (DigitSet digitSet)
02647 {
02648     d->dateTimeDigitSet = digitSet;
02649 }
02650 
02651 KLocale::DigitSet KLocale::dateTimeDigitSet () const
02652 {
02653     return d->dateTimeDigitSet;
02654 }
02655 
02656 Q_GLOBAL_STATIC_WITH_ARGS(QMutex, s_kLocaleMutex, (QMutex::Recursive))
02657 QMutex* kLocaleMutex()
02658 {
02659     return s_kLocaleMutex();
02660 }

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
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
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