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