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