File indexing completed on 2023-09-24 04:04:52
0001 /* This file is part of the KDE libraries 0002 Copyright (c) 1997,2001 Stephan Kulow <coolo@kde.org> 0003 Copyright (c) 1999 Preston Brown <pbrown@kde.org> 0004 Copyright (c) 1999-2002 Hans Petter Bieker <bieker@kde.org> 0005 Copyright (c) 2002 Lukas Tinkl <lukas@kde.org> 0006 Copyright (C) 2007 Bernhard Loos <nhuh.put@web.de> 0007 Copyright (C) 2009, 2010 John Layt <john@layt.net> 0008 0009 This library is free software; you can redistribute it and/or 0010 modify it under the terms of the GNU Library General Public 0011 License as published by the Free Software Foundation; either 0012 version 2 of the License, or (at your option) any later version. 0013 0014 This library is distributed in the hope that it will be useful, 0015 but WITHOUT ANY WARRANTY; without even the implied warranty of 0016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0017 Library General Public License for more details. 0018 0019 You should have received a copy of the GNU Library General Public License 0020 along with this library; see the file COPYING.LIB. If not, write to 0021 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0022 Boston, MA 02110-1301, USA. 0023 */ 0024 0025 #include "klocale_p.h" 0026 0027 #include "config-localization.h" 0028 0029 #include <math.h> 0030 #include <locale.h> 0031 0032 #if HAVE_SYS_TIME_H 0033 #include <sys/time.h> 0034 #endif 0035 #if HAVE_TIME_H 0036 #include <time.h> 0037 #endif 0038 #if HAVE_LANGINFO_H 0039 #include <langinfo.h> 0040 #endif 0041 0042 #include <QTextCodec> 0043 #include <QFile> 0044 #include <QDebug> 0045 #include <QPrinter> 0046 #include <QRegExp> 0047 #include <QLocale> 0048 #include <QHash> 0049 #include <QMutexLocker> 0050 #include <QStringList> 0051 #include <QCoreApplication> 0052 #include <QDir> 0053 #include <qstandardpaths.h> 0054 0055 #include "kconfig.h" 0056 #include "kdatetime.h" 0057 #include "kcalendarsystem.h" 0058 #include "kcurrencycode.h" 0059 #include "klocalizedstring.h" 0060 #include "kconfiggroup.h" 0061 #include "kdayperiod_p.h" 0062 0063 class KLocaleStaticData 0064 { 0065 public: 0066 0067 KLocaleStaticData(); 0068 0069 // FIXME: Temporary until full language-sensitivity implemented. 0070 QHash<KLocale::DigitSet, QStringList> languagesUsingDigitSet; 0071 }; 0072 0073 KLocaleStaticData::KLocaleStaticData() 0074 { 0075 // Languages using non-Western Arabic digit sets. 0076 // FIXME: Temporary until full language-sensitivity implemented. 0077 languagesUsingDigitSet.insert(KLocale::ArabicIndicDigits, QStringList() << QString::fromLatin1("ar") << QString::fromLatin1("ps")); 0078 languagesUsingDigitSet.insert(KLocale::BengaliDigits, QStringList() << QString::fromLatin1("bn") << QString::fromLatin1("as")); 0079 languagesUsingDigitSet.insert(KLocale::DevenagariDigits, QStringList() << QString::fromLatin1("hi") << QString::fromLatin1("ne")); 0080 languagesUsingDigitSet.insert(KLocale::EasternArabicIndicDigits, QStringList() << QString::fromLatin1("fa") << QString::fromLatin1("ur")); 0081 languagesUsingDigitSet.insert(KLocale::GujaratiDigits, QStringList() << QString::fromLatin1("gu")); 0082 languagesUsingDigitSet.insert(KLocale::GurmukhiDigits, QStringList() << QString::fromLatin1("pa")); 0083 languagesUsingDigitSet.insert(KLocale::KannadaDigits, QStringList() << QString::fromLatin1("kn")); 0084 languagesUsingDigitSet.insert(KLocale::KhmerDigits, QStringList() << QString::fromLatin1("km")); 0085 languagesUsingDigitSet.insert(KLocale::MalayalamDigits, QStringList() << QString::fromLatin1("ml")); 0086 languagesUsingDigitSet.insert(KLocale::OriyaDigits, QStringList() << QString::fromLatin1("or")); 0087 languagesUsingDigitSet.insert(KLocale::TamilDigits, QStringList() << QString::fromLatin1("ta")); 0088 languagesUsingDigitSet.insert(KLocale::TeluguDigits, QStringList() << QString::fromLatin1("te")); 0089 languagesUsingDigitSet.insert(KLocale::ThaiDigits, QStringList() << QString::fromLatin1("th")); 0090 } 0091 0092 Q_GLOBAL_STATIC(KLocaleStaticData, staticData) 0093 0094 Q_GLOBAL_STATIC_WITH_ARGS(QMutex, s_kLocaleMutex, (QMutex::Recursive)) 0095 0096 KLocalePrivate::KLocalePrivate(KLocale *q_ptr) 0097 : q(q_ptr), 0098 m_config(KSharedConfig::Ptr()), 0099 m_country(QString()), 0100 m_language(QString()), 0101 m_languages(nullptr), 0102 m_calendar(nullptr), 0103 m_currency(nullptr), 0104 m_codecForEncoding(nullptr) 0105 { 0106 } 0107 0108 KLocalePrivate::KLocalePrivate(const KLocalePrivate &rhs) 0109 { 0110 copy(rhs); 0111 } 0112 0113 KLocalePrivate &KLocalePrivate::operator=(const KLocalePrivate &rhs) 0114 { 0115 copy(rhs); 0116 return *this; 0117 } 0118 0119 KSharedConfig::Ptr KLocalePrivate::config() 0120 { 0121 if (m_config) { 0122 return m_config; 0123 } else { 0124 return KSharedConfig::openConfig(); 0125 } 0126 } 0127 0128 void KLocalePrivate::copy(const KLocalePrivate &rhs) 0129 { 0130 // Parent KLocale 0131 q = nullptr; 0132 0133 // Config 0134 m_config = rhs.m_config; 0135 0136 // Country settings 0137 m_country = rhs.m_country; 0138 m_countryDivisionCode = rhs.m_countryDivisionCode; 0139 0140 // Language settings 0141 m_language = rhs.m_language; 0142 m_languages = nullptr; 0143 m_languageList = rhs.m_languageList; 0144 m_languageSensitiveDigits = rhs.m_languageSensitiveDigits; 0145 m_nounDeclension = rhs.m_nounDeclension; 0146 0147 // Calendar settings 0148 m_calendarSystem = rhs.m_calendarSystem; 0149 m_calendar = nullptr; 0150 m_weekStartDay = rhs.m_weekStartDay; 0151 m_workingWeekStartDay = rhs.m_workingWeekStartDay; 0152 m_workingWeekEndDay = rhs.m_workingWeekEndDay; 0153 m_weekDayOfPray = rhs.m_weekDayOfPray; 0154 0155 // Date/Time settings 0156 m_dateFormat = rhs.m_dateFormat; 0157 m_dateFormatShort = rhs.m_dateFormatShort; 0158 m_timeFormat = rhs.m_timeFormat; 0159 m_dateTimeDigitSet = rhs.m_dateTimeDigitSet; 0160 m_dateMonthNamePossessive = rhs.m_dateMonthNamePossessive; 0161 m_dayPeriods = rhs.m_dayPeriods; 0162 m_weekNumberSystem = rhs.m_weekNumberSystem; 0163 0164 // Number settings 0165 m_decimalPlaces = rhs.m_decimalPlaces; 0166 m_decimalSymbol = rhs.m_decimalSymbol; 0167 m_thousandsSeparator = rhs.m_thousandsSeparator; 0168 m_numericDigitGrouping = rhs.m_numericDigitGrouping; 0169 m_positiveSign = rhs.m_positiveSign; 0170 m_negativeSign = rhs.m_negativeSign; 0171 m_digitSet = rhs.m_digitSet; 0172 0173 // Currency settings 0174 m_currencyCode = rhs.m_currencyCode; 0175 m_currency = nullptr; 0176 m_currencyCodeList = rhs.m_currencyCodeList; 0177 0178 // Money settings 0179 m_currencySymbol = rhs.m_currencySymbol; 0180 m_monetaryDecimalSymbol = rhs.m_monetaryDecimalSymbol; 0181 m_monetaryThousandsSeparator = rhs.m_monetaryThousandsSeparator; 0182 m_monetaryDigitGrouping = rhs.m_monetaryDigitGrouping; 0183 m_monetaryDecimalPlaces = rhs.m_monetaryDecimalPlaces; 0184 m_positiveMonetarySignPosition = rhs.m_positiveMonetarySignPosition; 0185 m_negativeMonetarySignPosition = rhs.m_negativeMonetarySignPosition; 0186 m_positivePrefixCurrencySymbol = rhs.m_positivePrefixCurrencySymbol; 0187 m_negativePrefixCurrencySymbol = rhs.m_negativePrefixCurrencySymbol; 0188 m_monetaryDigitSet = rhs.m_monetaryDigitSet; 0189 0190 // Units settings 0191 m_binaryUnitDialect = rhs.m_binaryUnitDialect; 0192 m_byteSizeFmt = rhs.m_byteSizeFmt; 0193 m_pageSize = rhs.m_pageSize; 0194 m_measureSystem = rhs.m_measureSystem; 0195 0196 // Encoding settings 0197 m_encoding = rhs.m_encoding; 0198 m_codecForEncoding = rhs.m_codecForEncoding; 0199 m_utf8FileEncoding = rhs.m_utf8FileEncoding; 0200 } 0201 0202 KLocalePrivate::~KLocalePrivate() 0203 { 0204 delete m_currency; 0205 delete m_calendar; 0206 delete m_languages; 0207 } 0208 0209 // init only called from platform specific constructor, so set everything up 0210 // Will be given a persistantConfig or a tempConfig or neither, but never both 0211 void KLocalePrivate::init(const QString &language, const QString &country, 0212 KSharedConfig::Ptr persistantConfig, KConfig *tempConfig) 0213 { 0214 // Only keep the persistant config if it is not the global 0215 if (persistantConfig != KSharedConfig::Ptr() && persistantConfig != KSharedConfig::openConfig()) { 0216 m_config = persistantConfig; 0217 } 0218 0219 KConfigGroup cg; 0220 bool useEnvironmentVariables; 0221 0222 // We can't read the formats from the config until we know what locale to read in, but we need 0223 // to read the config to find out the locale. The Country and Language settings should never 0224 // be localized in the config, so we can read a temp copy of them to get us started. 0225 0226 // If no config given, use the global config and include envvars, otherwise use only the config. 0227 if (m_config != KSharedConfig::Ptr()) { 0228 cg = m_config->group(QLatin1String("Locale")); 0229 useEnvironmentVariables = false; 0230 } else if (tempConfig == nullptr || tempConfig == KSharedConfig::openConfig().data()) { 0231 cg = KSharedConfig::openConfig()->group(QLatin1String("Locale")); 0232 useEnvironmentVariables = true; 0233 } else { 0234 cg = tempConfig->group(QLatin1String("Locale")); 0235 useEnvironmentVariables = false; 0236 } 0237 0238 initEncoding(); 0239 initCountry(country, cg.readEntry(QLatin1String("Country"))); 0240 initLanguageList(language, cg.readEntry(QLatin1String("Language")), useEnvironmentVariables); 0241 // Now that we have a language, we can set up the config which uses it to setLocale() 0242 initConfig(tempConfig); 0243 initFormat(); 0244 } 0245 0246 // Init the config, this is called during construction and by later setCountry/setLanguage calls. 0247 // You _must_ have the m_language set to a valid language or en_US before calling this so a 0248 // setLocale can be applied to the config 0249 void KLocalePrivate::initConfig(KConfig *config) 0250 { 0251 // * If we were constructed with a KSharedConfig it means the user gave it to us 0252 // to use for the life of the KLocale, so just keep using it after a setLocale 0253 // * If passed in KConfig is null or the global config then use the global, but 0254 // do the setLocale first. 0255 // * If we have a KConfig we need to use that, but due to keeping old behaviour 0256 // of not requiring access to it for life we can't keep a reference so instead 0257 // take a copy and use that, but do setLocale first. 0258 0259 if (m_config != KSharedConfig::Ptr()) { 0260 m_config->setLocale(m_language); 0261 } else { 0262 // If no config given then use the global 0263 if (config == nullptr || config == KSharedConfig::openConfig().data()) { 0264 KSharedConfig::openConfig()->setLocale(m_language); 0265 } else { 0266 config->setLocale(m_language); 0267 m_config = KSharedConfig::openConfig(); 0268 config->copyTo(QString(), m_config.data()); 0269 m_config->markAsClean(); 0270 } 0271 } 0272 } 0273 0274 void KLocalePrivate::getLanguagesFromVariable(QStringList &list, const char *variable, bool isLanguageList) 0275 { 0276 QByteArray var(qgetenv(variable)); 0277 if (!var.isEmpty()) { 0278 QString value = QFile::decodeName(var); 0279 if (isLanguageList) { 0280 list += value.split(QLatin1Char(':')); 0281 } else { 0282 // Process the value to create possible combinations. 0283 QString lang, ctry, modf, cset; 0284 KLocale::splitLocale(value, lang, ctry, modf, cset); 0285 0286 if (!ctry.isEmpty() && !modf.isEmpty()) { 0287 list += lang + QLatin1Char('_') + ctry + QLatin1Char('@') + modf; 0288 } 0289 // NOTE: The priority is tricky in case both ctry and modf are present. 0290 // Should really lang@modf be of higher priority than lang_ctry? 0291 // For at least one case (Serbian language), it is better this way. 0292 if (!modf.isEmpty()) { 0293 list += lang + QLatin1Char('@') + modf; 0294 } 0295 if (!ctry.isEmpty()) { 0296 list += lang + QLatin1Char('_') + ctry; 0297 } 0298 list += lang; 0299 } 0300 } 0301 } 0302 0303 // init the country at construction only, will ensure we always have a country set 0304 void KLocalePrivate::initCountry(const QString &country, const QString &configCountry) 0305 { 0306 // Cache the valid countries list and add the default C as it is valid to use 0307 QStringList validCountries = allCountriesList(); 0308 validCountries.append(defaultCountry()); 0309 0310 // First check if the constructor passed in a value and if so if it is valid 0311 QString putativeCountry = country; 0312 0313 if (putativeCountry.isEmpty() || !validCountries.contains(putativeCountry, Qt::CaseInsensitive)) { 0314 0315 // If the requested country is not valid, try the country as set in the config: 0316 putativeCountry = configCountry; 0317 0318 if (putativeCountry.isEmpty() || !validCountries.contains(putativeCountry, Qt::CaseInsensitive)) { 0319 0320 // If the config country is not valid try the current host system country 0321 putativeCountry = systemCountry(); 0322 0323 if (putativeCountry.isEmpty() || !validCountries.contains(putativeCountry, Qt::CaseInsensitive)) { 0324 // Only if no other option, resort to the default C 0325 putativeCountry = defaultCountry(); 0326 } 0327 } 0328 } 0329 0330 // Always save as lowercase, unless it's C when we want it uppercase 0331 if (putativeCountry.toLower() == defaultCountry().toLower()) { 0332 m_country = defaultCountry(); 0333 } else { 0334 m_country = putativeCountry.toLower(); 0335 } 0336 } 0337 0338 QString KLocalePrivate::systemCountry() const 0339 { 0340 // Use QLocale for now as it supposedly provides a sensible default most times, 0341 // e.g. if locale is only "de" it is assumed to mean country of "DE" 0342 QString systemCountry, s1, s2, s3; 0343 splitLocale(QLocale::system().name(), s1, systemCountry, s2, s3); 0344 return systemCountry.toLower(); 0345 } 0346 0347 void KLocalePrivate::initLanguageList(const QString &language, const QString &configLanguages, 0348 bool useEnvironmentVariables) 0349 { 0350 m_language = language; 0351 0352 // Collect possible languages by decreasing priority. 0353 // The priority is as follows: 0354 // - the internally set language, if any 0355 // - KDE_LANG environment variable (can be a list) 0356 // - KDE configuration (can be a list) 0357 // - environment variables considered by gettext(3) 0358 // The environment variables are not considered if useEnvironmentVariables is false. 0359 QStringList list; 0360 if (!m_language.isEmpty()) { 0361 list += m_language; 0362 } 0363 0364 // If the Locale object was created with a specific config file, then do not use the 0365 // environmental variables. If the locale object was created with the global config, then 0366 // do use the environmental variables. 0367 if (useEnvironmentVariables) { 0368 // KDE_LANG contains list of language codes, not locale string. 0369 getLanguagesFromVariable(list, "KDE_LANG", true); 0370 } 0371 0372 if (!configLanguages.isEmpty()) { 0373 list += configLanguages.split(QLatin1Char(':')); 0374 } 0375 0376 if (useEnvironmentVariables) { 0377 // Collect languages by same order of priority as for gettext(3). 0378 // LANGUAGE contains list of language codes, not locale string. 0379 getLanguagesFromVariable(list, "LANGUAGE", true); 0380 getLanguagesFromVariable(list, "LC_ALL"); 0381 getLanguagesFromVariable(list, "LC_MESSAGES"); 0382 getLanguagesFromVariable(list, "LANG"); 0383 } 0384 0385 // fall back to the system language 0386 list += systemLanguageList(); 0387 0388 // Send the list to filter for really present languages on the system. 0389 setLanguage(list); 0390 } 0391 0392 QStringList KLocalePrivate::systemLanguageList() const 0393 { 0394 return QStringList(); 0395 } 0396 0397 void KLocalePrivate::initFormat() 0398 { 0399 KConfigGroup cg(config(), "Locale"); 0400 0401 KConfig entryFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("kf5/locale/countries/") + QString::fromLatin1("%1/country.desktop").arg(m_country))); 0402 entryFile.setLocale(m_language); 0403 KConfigGroup entry(&entryFile, "KCM Locale"); 0404 0405 //One-time conversion in 4.4 from FracDigits to DecimalPlaces and MonetaryDecimalPlaces 0406 //If user has personal setting for FracDigits then use it for both Decimal Places 0407 //TODO: Possible to do with kconf_update 0408 if (cg.hasKey("FracDigits")) { 0409 QString fracDigits = cg.readEntry("FracDigits", ""); 0410 if (!fracDigits.isEmpty()) { 0411 cg.writeEntry("DecimalPlaces", fracDigits); 0412 cg.writeEntry("MonetaryDecimalPlaces", fracDigits); 0413 } 0414 cg.deleteEntry("FracDigits"); 0415 cg.config()->sync(); 0416 } 0417 0418 // Numeric 0419 #define readConfigEntry(key, default, save) \ 0420 save = entry.readEntry(key, default); \ 0421 save = cg.readEntry(key, save); 0422 0423 #define readConfigNumEntry(key, default, save, type) \ 0424 save = (type)entry.readEntry(key, int(default)); \ 0425 save = (type)cg.readEntry(key, int(save)); 0426 0427 // Country settings 0428 readConfigEntry("CountryDivisionCode", QString(), m_countryDivisionCode); 0429 0430 // Numeric formats 0431 readConfigNumEntry("DecimalPlaces", 2, m_decimalPlaces, int); 0432 0433 readConfigEntry("DecimalSymbol", ".", m_decimalSymbol); 0434 readConfigEntry("ThousandsSeparator", ",", m_thousandsSeparator); 0435 m_thousandsSeparator.remove(QString::fromLatin1("$0")); 0436 QString digitGroupFormat; 0437 readConfigEntry("DigitGroupFormat", "3", digitGroupFormat); 0438 m_numericDigitGrouping = digitGroupFormatToList(digitGroupFormat); 0439 0440 readConfigEntry("PositiveSign", "", m_positiveSign); 0441 readConfigEntry("NegativeSign", "-", m_negativeSign); 0442 0443 readConfigNumEntry("DigitSet", KLocale::ArabicDigits, m_digitSet, KLocale::DigitSet); 0444 // FIXME: Temporary until full language-sensitivity implemented. 0445 readConfigEntry("LanguageSensitiveDigits", true, m_languageSensitiveDigits); 0446 0447 // Currency 0448 readConfigEntry("CurrencyCode", "USD", m_currencyCode); 0449 initCurrency(); 0450 readConfigEntry("CurrencySymbol", m_currency->defaultSymbol(), m_currencySymbol); 0451 readConfigEntry("CurrencyCodesInUse", QStringList(m_currencyCode), m_currencyCodeList); 0452 0453 // Monetary formats 0454 readConfigNumEntry("MonetaryDecimalPlaces", m_currency->decimalPlaces(), m_monetaryDecimalPlaces, int); 0455 0456 readConfigEntry("MonetaryDecimalSymbol", ".", m_monetaryDecimalSymbol); 0457 readConfigEntry("MonetaryThousandsSeparator", ",", m_monetaryThousandsSeparator); 0458 m_monetaryThousandsSeparator.remove(QString::fromLatin1("$0")); 0459 readConfigEntry("MonetaryDigitGroupFormat", "3", digitGroupFormat); 0460 m_monetaryDigitGrouping = digitGroupFormatToList(digitGroupFormat); 0461 0462 readConfigEntry("PositivePrefixCurrencySymbol", true, m_positivePrefixCurrencySymbol); 0463 readConfigEntry("NegativePrefixCurrencySymbol", true, m_negativePrefixCurrencySymbol); 0464 readConfigNumEntry("PositiveMonetarySignPosition", KLocale::BeforeQuantityMoney, 0465 m_positiveMonetarySignPosition, KLocale::SignPosition); 0466 readConfigNumEntry("NegativeMonetarySignPosition", KLocale::ParensAround, 0467 m_negativeMonetarySignPosition, KLocale::SignPosition); 0468 0469 readConfigNumEntry("MonetaryDigitSet", KLocale::ArabicDigits, 0470 m_monetaryDigitSet, KLocale::DigitSet); 0471 readConfigNumEntry("BinaryUnitDialect", KLocale::IECBinaryDialect, 0472 m_binaryUnitDialect, KLocale::BinaryUnitDialect); 0473 0474 // Date and time 0475 readConfigEntry("TimeFormat", "%H:%M:%S", m_timeFormat); 0476 readConfigEntry("DateFormat", "%A %d %B %Y", m_dateFormat); 0477 readConfigEntry("DateFormatShort", "%Y-%m-%d", m_dateFormatShort); 0478 readConfigNumEntry("WeekStartDay", 1, m_weekStartDay, int); //default to Monday 0479 readConfigNumEntry("WorkingWeekStartDay", 1, m_workingWeekStartDay, int); //default to Monday 0480 readConfigNumEntry("WorkingWeekEndDay", 5, m_workingWeekEndDay, int); //default to Friday 0481 readConfigNumEntry("WeekDayOfPray", 7, m_weekDayOfPray, int); //default to Sunday 0482 readConfigNumEntry("DateTimeDigitSet", KLocale::ArabicDigits, 0483 m_dateTimeDigitSet, KLocale::DigitSet); 0484 readConfigNumEntry("WeekNumberSystem", KLocale::IsoWeekNumber, 0485 m_weekNumberSystem, KLocale::WeekNumberSystem); 0486 0487 // other 0488 #ifndef QT_NO_PRINTER 0489 readConfigNumEntry("PageSize", QPrinter::A4, m_pageSize, QPrinter::PageSize); 0490 #endif 0491 readConfigNumEntry("MeasureSystem", KLocale::Metric, m_measureSystem, KLocale::MeasureSystem); 0492 QString calendarType; 0493 readConfigEntry("CalendarSystem", "gregorian", calendarType); 0494 setCalendar(calendarType); 0495 0496 //Grammatical 0497 //Precedence here is l10n / i18n / config file 0498 KConfig langCfg(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("locale/") + QString::fromLatin1("%1/kf5_entry.desktop").arg(m_language))); 0499 KConfigGroup lang(&langCfg, "KCM Locale"); 0500 #define read3ConfigBoolEntry(key, default, save) \ 0501 save = entry.readEntry(key, default); \ 0502 save = lang.readEntry(key, save); \ 0503 save = cg.readEntry(key, save); 0504 0505 read3ConfigBoolEntry("NounDeclension", false, m_nounDeclension); 0506 read3ConfigBoolEntry("DateMonthNamePossessive", false, m_dateMonthNamePossessive); 0507 0508 initDayPeriods(cg); 0509 } 0510 0511 void KLocalePrivate::initDayPeriods(const KConfigGroup &cg) 0512 { 0513 // Prefer any l10n file value for country/language, 0514 // otherwise default to language only value which will be filled in later when i18n available 0515 0516 //Day Period are stored in config as one QStringList entry per Day Period 0517 //PeriodCode,LongName,ShortName,NarrowName,StartTime,EndTime,Offset,OffsetIfZero 0518 //where start and end time are in the format HH:MM:SS.MMM 0519 0520 m_dayPeriods.clear(); 0521 QString periodKey = QString::fromLatin1("DayPeriod1"); 0522 int i = 1; 0523 while (cg.hasKey(periodKey)) { 0524 QStringList period = cg.readEntry(periodKey, QStringList()); 0525 if (period.count() == 8) { 0526 m_dayPeriods.append(KDayPeriod(period[0], period[1], period[2], period[3], 0527 QTime::fromString(period[4], QString::fromLatin1("HH:mm:ss.zzz")), 0528 QTime::fromString(period[5], QString::fromLatin1("HH:mm:ss.zzz")), 0529 period[6].toInt(), period[7].toInt())); 0530 } 0531 i = i + 1; 0532 periodKey = QString::fromLatin1("DayPeriod%1").arg(i); 0533 } 0534 } 0535 0536 bool KLocalePrivate::setCountry(const QString &country, KConfig *newConfig) 0537 { 0538 // Cache the valid countries list and add the default C as it is valid to use 0539 QStringList validCountries = allCountriesList(); 0540 validCountries.append(defaultCountry()); 0541 0542 QString putativeCountry = country; 0543 0544 if (putativeCountry.isEmpty()) { 0545 // An empty string means to use the system country 0546 putativeCountry = systemCountry(); 0547 if (putativeCountry.isEmpty() || !validCountries.contains(putativeCountry, Qt::CaseInsensitive)) { 0548 // If the system country is not valid, use the default 0549 putativeCountry = defaultCountry(); 0550 } 0551 } else if (!validCountries.contains(putativeCountry, Qt::CaseInsensitive)) { 0552 return false; 0553 } 0554 0555 // Always save as lowercase, unless it's C when we want it uppercase 0556 if (putativeCountry.toLower() == defaultCountry().toLower()) { 0557 m_country = defaultCountry(); 0558 } else { 0559 m_country = putativeCountry.toLower(); 0560 } 0561 0562 // Get rid of the old config, start again with the new 0563 m_config = KSharedConfig::Ptr(); 0564 initConfig(newConfig); 0565 0566 // Init all the settings 0567 initFormat(); 0568 0569 return true; 0570 } 0571 0572 bool KLocalePrivate::setCountryDivisionCode(const QString &countryDivisionCode) 0573 { 0574 m_countryDivisionCode = countryDivisionCode; 0575 return true; 0576 } 0577 0578 bool KLocalePrivate::setLanguage(const QString &language, KConfig *config) 0579 { 0580 QMutexLocker lock(s_kLocaleMutex()); 0581 m_languageList.removeAll(language); 0582 m_languageList.prepend(language); // let us consider this language to be the most important one 0583 0584 m_language = language; // remember main language for shortcut evaluation 0585 0586 // Get rid of the old config, start again with the new 0587 m_config = KSharedConfig::Ptr(); 0588 initConfig(config); 0589 0590 // Init the new format settings 0591 initFormat(); 0592 0593 // Maybe the mo-files for this language are empty, but in principle we can speak all languages 0594 return true; 0595 } 0596 0597 // KDE5 Unlike the other setLanguage call this does not reparse the config so the localized config 0598 // settings for the new primary language will _not_ be loaded. In KDE5 always keep the original 0599 // config so this can be reparsed when required. 0600 bool KLocalePrivate::setLanguage(const QStringList &languages) 0601 { 0602 QMutexLocker lock(s_kLocaleMutex()); 0603 // This list might contain 0604 // 1) some empty strings that we have to eliminate 0605 // 2) duplicate entries like in de:fr:de, where we have to keep the first occurrence of a 0606 // language in order to preserve the order of precenence of the user 0607 // 3) languages into which the application is not translated. For those languages we should not 0608 // even load kdelibs.mo or kio.po. these languages have to be dropped. Otherwise we get 0609 // strange side effects, e.g. with Hebrew: the right/left switch for languages that write 0610 // from right to left (like Hebrew or Arabic) is set in kdelibs.mo. If you only have 0611 // kdelibs.mo but nothing from appname.mo, you get a mostly English app with layout from 0612 // right to left. That was considered to be a bug by the Hebrew translators. 0613 QStringList list; 0614 foreach (const QString &language, languages) { 0615 if (!language.isEmpty() && !list.contains(language) && KLocalizedString::isApplicationTranslatedInto(language)) { 0616 list.append(language); 0617 } 0618 } 0619 0620 if (!list.contains(KLocale::defaultLanguage())) { 0621 // English should always be added as final possibility; this is important 0622 // for proper initialization of message text post-processors which are 0623 // needed for English too, like semantic to visual formatting, etc. 0624 list.append(KLocale::defaultLanguage()); 0625 } 0626 0627 m_language = list.first(); // keep this for shortcut evaluations 0628 0629 m_languageList = list; // keep this new list of languages to use 0630 0631 return true; // we found something. Maybe it's only English, but we found something 0632 } 0633 0634 void KLocalePrivate::initCurrency() 0635 { 0636 if (m_currencyCode.isEmpty() || !KCurrencyCode::isValid(m_currencyCode)) { 0637 m_currencyCode = KLocale::defaultCurrencyCode(); 0638 } 0639 0640 if (!m_currency || m_currencyCode != m_currency->isoCurrencyCode() || !m_currency->isValid()) { 0641 delete m_currency; 0642 m_currency = new KCurrencyCode(m_currencyCode, m_language); 0643 } 0644 } 0645 0646 void KLocalePrivate::setCurrencyCode(const QString &newCurrencyCode) 0647 { 0648 if (!newCurrencyCode.isEmpty() && newCurrencyCode != m_currency->isoCurrencyCode() && 0649 KCurrencyCode::isValid(newCurrencyCode)) { 0650 m_currencyCode = newCurrencyCode; 0651 initCurrency(); 0652 } 0653 } 0654 0655 void KLocalePrivate::splitLocale(const QString &aLocale, QString &language, QString &country, 0656 QString &modifier, QString &charset) 0657 { 0658 QString locale = aLocale; 0659 0660 language.clear(); 0661 country.clear(); 0662 modifier.clear(); 0663 charset.clear(); 0664 0665 // In case there are several concatenated locale specifications, 0666 // truncate all but first. 0667 int f = locale.indexOf(QLatin1Char(':')); 0668 if (f >= 0) { 0669 locale.truncate(f); 0670 } 0671 0672 f = locale.indexOf(QLatin1Char('.')); 0673 if (f >= 0) { 0674 charset = locale.mid(f + 1); 0675 locale.truncate(f); 0676 } 0677 0678 f = locale.indexOf(QLatin1Char('@')); 0679 if (f >= 0) { 0680 modifier = locale.mid(f + 1); 0681 locale.truncate(f); 0682 } 0683 0684 f = locale.indexOf(QLatin1Char('_')); 0685 if (f >= 0) { 0686 country = locale.mid(f + 1); 0687 locale.truncate(f); 0688 } 0689 0690 language = locale; 0691 } 0692 0693 QString KLocalePrivate::language() const 0694 { 0695 return m_language; 0696 } 0697 0698 QString KLocalePrivate::country() const 0699 { 0700 return m_country; 0701 } 0702 0703 QString KLocalePrivate::countryDivisionCode() const 0704 { 0705 if (m_countryDivisionCode.isEmpty()) { 0706 return country().toUpper(); 0707 } else { 0708 return m_countryDivisionCode; 0709 } 0710 } 0711 0712 KCurrencyCode *KLocalePrivate::currency() 0713 { 0714 if (!m_currency) { 0715 initCurrency(); 0716 } 0717 return m_currency; 0718 } 0719 0720 QString KLocalePrivate::currencyCode() const 0721 { 0722 return m_currencyCode; 0723 } 0724 0725 QList<KLocale::DigitSet> KLocalePrivate::allDigitSetsList() const 0726 { 0727 QList<KLocale::DigitSet> digitSets; 0728 digitSets.append(KLocale::ArabicDigits); 0729 digitSets.append(KLocale::ArabicIndicDigits); 0730 digitSets.append(KLocale::BengaliDigits); 0731 digitSets.append(KLocale::DevenagariDigits); 0732 digitSets.append(KLocale::EasternArabicIndicDigits); 0733 digitSets.append(KLocale::GujaratiDigits); 0734 digitSets.append(KLocale::GurmukhiDigits); 0735 digitSets.append(KLocale::KannadaDigits); 0736 digitSets.append(KLocale::KhmerDigits); 0737 digitSets.append(KLocale::MalayalamDigits); 0738 digitSets.append(KLocale::OriyaDigits); 0739 digitSets.append(KLocale::TamilDigits); 0740 digitSets.append(KLocale::TeluguDigits); 0741 digitSets.append(KLocale::ThaiDigits); 0742 std::sort(digitSets.begin(), digitSets.end()); 0743 return digitSets; 0744 } 0745 0746 QString KLocalePrivate::digitSetString(KLocale::DigitSet digitSet) 0747 { 0748 switch (digitSet) { 0749 case KLocale::ArabicIndicDigits: 0750 return QString::fromUtf8("٠١٢٣٤٥٦٧٨٩"); 0751 case KLocale::BengaliDigits: 0752 return QString::fromUtf8("০১২৩৪৫৬৭৮৯"); 0753 case KLocale::DevenagariDigits: 0754 return QString::fromUtf8("०१२३४५६७८९"); 0755 case KLocale::EasternArabicIndicDigits: 0756 return QString::fromUtf8("۰۱۲۳۴۵۶۷۸۹"); 0757 case KLocale::GujaratiDigits: 0758 return QString::fromUtf8("૦૧૨૩૪૫૬૭૮૯"); 0759 case KLocale::GurmukhiDigits: 0760 return QString::fromUtf8("੦੧੨੩੪੫੬੭੮੯"); 0761 case KLocale::KannadaDigits: 0762 return QString::fromUtf8("೦೧೨೩೪೫೬೭೮೯"); 0763 case KLocale::KhmerDigits: 0764 return QString::fromUtf8("០១២៣៤៥៦៧៨៩"); 0765 case KLocale::MalayalamDigits: 0766 return QString::fromUtf8("൦൧൨൩൪൫൬൭൮൯"); 0767 case KLocale::OriyaDigits: 0768 return QString::fromUtf8("୦୧୨୩୪୫୬୭୮୯"); 0769 case KLocale::TamilDigits: 0770 return QString::fromUtf8("௦௧௨௩௪௫௬௭௮"); 0771 case KLocale::TeluguDigits: 0772 return QString::fromUtf8("౦౧౨౩౪౫౬౭౯"); 0773 case KLocale::ThaiDigits: 0774 return QString::fromUtf8("๐๑๒๓๔๕๖๗๘๙"); 0775 default: 0776 return QString::fromUtf8("0123456789"); 0777 } 0778 } 0779 0780 QString KLocalePrivate::digitSetToName(KLocale::DigitSet digitSet, bool withDigits) const 0781 { 0782 QString name; 0783 switch (digitSet) { 0784 case KLocale::ArabicIndicDigits: 0785 name = i18nc("digit set", "Arabic-Indic"); 0786 break; 0787 case KLocale::BengaliDigits: 0788 name = i18nc("digit set", "Bengali"); 0789 break; 0790 case KLocale::DevenagariDigits: 0791 name = i18nc("digit set", "Devanagari"); 0792 break; 0793 case KLocale::EasternArabicIndicDigits: 0794 name = i18nc("digit set", "Eastern Arabic-Indic"); 0795 break; 0796 case KLocale::GujaratiDigits: 0797 name = i18nc("digit set", "Gujarati"); 0798 break; 0799 case KLocale::GurmukhiDigits: 0800 name = i18nc("digit set", "Gurmukhi"); 0801 break; 0802 case KLocale::KannadaDigits: 0803 name = i18nc("digit set", "Kannada"); 0804 break; 0805 case KLocale::KhmerDigits: 0806 name = i18nc("digit set", "Khmer"); 0807 break; 0808 case KLocale::MalayalamDigits: 0809 name = i18nc("digit set", "Malayalam"); 0810 break; 0811 case KLocale::OriyaDigits: 0812 name = i18nc("digit set", "Oriya"); 0813 break; 0814 case KLocale::TamilDigits: 0815 name = i18nc("digit set", "Tamil"); 0816 break; 0817 case KLocale::TeluguDigits: 0818 name = i18nc("digit set", "Telugu"); 0819 break; 0820 case KLocale::ThaiDigits: 0821 name = i18nc("digit set", "Thai"); 0822 break; 0823 default: 0824 name = i18nc("digit set", "Arabic"); 0825 } 0826 if (withDigits) { 0827 QString digits = digitSetString(digitSet); 0828 QString nameWithDigits = i18nc("name of digit set with digit string, " 0829 "e.g. 'Arabic (0123456789)'", "%1 (%2)", name, digits); 0830 return nameWithDigits; 0831 } else { 0832 return name; 0833 } 0834 } 0835 0836 QString KLocalePrivate::convertDigits(const QString &str, KLocale::DigitSet digitSet, bool ignoreContext) const 0837 { 0838 if (!ignoreContext) { 0839 // Fall back to Western Arabic digits if requested digit set 0840 // is not appropriate for current application language. 0841 // FIXME: Temporary until full language-sensitivity implemented. 0842 KLocaleStaticData *s = staticData(); 0843 if (m_languageSensitiveDigits && !s->languagesUsingDigitSet[digitSet].contains(m_language)) { 0844 digitSet = KLocale::ArabicDigits; 0845 } 0846 } 0847 0848 QString nstr; 0849 QString digitDraw = digitSetString(digitSet); 0850 foreach (const QChar &c, str) { 0851 if (c.isDigit()) { 0852 nstr += digitDraw[c.digitValue()]; 0853 } else { 0854 nstr += c; 0855 } 0856 } 0857 return nstr; 0858 } 0859 0860 QString KLocalePrivate::toArabicDigits(const QString &str) 0861 { 0862 QString nstr; 0863 foreach (const QChar &c, str) { 0864 if (c.isDigit()) { 0865 nstr += QChar('0' + c.digitValue()); 0866 } else { 0867 nstr += c; 0868 } 0869 } 0870 return nstr; 0871 } 0872 0873 bool KLocalePrivate::nounDeclension() const 0874 { 0875 return m_nounDeclension; 0876 } 0877 0878 bool KLocalePrivate::dateMonthNamePossessive() const 0879 { 0880 return m_dateMonthNamePossessive; 0881 } 0882 0883 int KLocalePrivate::weekStartDay() const 0884 { 0885 return m_weekStartDay; 0886 } 0887 0888 int KLocalePrivate::workingWeekStartDay() const 0889 { 0890 return m_workingWeekStartDay; 0891 } 0892 0893 int KLocalePrivate::workingWeekEndDay() const 0894 { 0895 return m_workingWeekEndDay; 0896 } 0897 0898 int KLocalePrivate::weekDayOfPray() const 0899 { 0900 return m_weekDayOfPray; 0901 } 0902 0903 int KLocalePrivate::decimalPlaces() const 0904 { 0905 return m_decimalPlaces; 0906 } 0907 0908 QString KLocalePrivate::decimalSymbol() const 0909 { 0910 return m_decimalSymbol; 0911 } 0912 0913 QString KLocalePrivate::thousandsSeparator() const 0914 { 0915 return m_thousandsSeparator; 0916 } 0917 0918 QList<int> KLocalePrivate::numericDigitGrouping() const 0919 { 0920 return m_numericDigitGrouping; 0921 } 0922 0923 QString KLocalePrivate::currencySymbol() const 0924 { 0925 return m_currencySymbol; 0926 } 0927 0928 QString KLocalePrivate::monetaryDecimalSymbol() const 0929 { 0930 return m_monetaryDecimalSymbol; 0931 } 0932 0933 QString KLocalePrivate::monetaryThousandsSeparator() const 0934 { 0935 return m_monetaryThousandsSeparator; 0936 } 0937 0938 QList<int> KLocalePrivate::monetaryDigitGrouping() const 0939 { 0940 return m_monetaryDigitGrouping; 0941 } 0942 0943 QString KLocalePrivate::positiveSign() const 0944 { 0945 return m_positiveSign; 0946 } 0947 0948 QString KLocalePrivate::negativeSign() const 0949 { 0950 return m_negativeSign; 0951 } 0952 0953 /* Just copy to keep the diff looking clean, delete later 0954 int KLocale::fracDigits() const 0955 { 0956 return monetaryDecimalPlaces(); 0957 } 0958 */ 0959 0960 int KLocalePrivate::monetaryDecimalPlaces() const 0961 { 0962 return m_monetaryDecimalPlaces; 0963 } 0964 0965 bool KLocalePrivate::positivePrefixCurrencySymbol() const 0966 { 0967 return m_positivePrefixCurrencySymbol; 0968 } 0969 0970 bool KLocalePrivate::negativePrefixCurrencySymbol() const 0971 { 0972 return m_negativePrefixCurrencySymbol; 0973 } 0974 0975 KLocale::SignPosition KLocalePrivate::positiveMonetarySignPosition() const 0976 { 0977 return m_positiveMonetarySignPosition; 0978 } 0979 0980 KLocale::SignPosition KLocalePrivate::negativeMonetarySignPosition() const 0981 { 0982 return m_negativeMonetarySignPosition; 0983 } 0984 0985 static inline void put_it_in(QChar *buffer, int &index, const QString &s) 0986 { 0987 for (int l = 0; l < s.length(); l++) { 0988 buffer[index++] = s.at(l); 0989 } 0990 } 0991 0992 static inline void put_it_in(QChar *buffer, int &index, int number) 0993 { 0994 buffer[index++] = number / 10 + '0'; 0995 buffer[index++] = number % 10 + '0'; 0996 } 0997 0998 // Convert POSIX Digit Group Format string into a Qlist<int>, e.g. "3;2" converts to (3,2) 0999 QList<int> KLocalePrivate::digitGroupFormatToList(const QString &digitGroupFormat) const 1000 { 1001 QList<int> groupList; 1002 QStringList stringList = digitGroupFormat.split(QLatin1Char(';')); 1003 foreach (const QString &size, stringList) { 1004 groupList.append(size.toInt()); 1005 } 1006 return groupList; 1007 } 1008 1009 // Inserts all required occurrences of the group separator into a number string. 1010 QString KLocalePrivate::formatDigitGroup(const QString &number, const QString &groupSeparator, const QString &decimalSeperator, QList<int> groupList) const 1011 { 1012 if (groupList.isEmpty() || groupSeparator.isEmpty()) { 1013 return number; 1014 } 1015 1016 QString num = number; 1017 int groupCount = groupList.count(); 1018 int groupAt = 0; 1019 int groupSize = groupList.at(groupAt); 1020 int pos = num.indexOf(decimalSeperator); 1021 if (pos == -1) { 1022 pos = num.length(); 1023 } 1024 pos = pos - groupSize; 1025 1026 while (pos > 0 && groupSize > 0) { 1027 num.insert(pos, groupSeparator); 1028 if (groupAt + 1 < groupCount) { 1029 ++groupAt; 1030 groupSize = groupList.at(groupAt); 1031 } 1032 pos = pos - groupSize; 1033 } 1034 1035 return num; 1036 } 1037 1038 // Strips all occurrences of the group separator from a number, returns ok if the separators were all in the valid positions 1039 QString KLocalePrivate::parseDigitGroup(const QString &number, const QString &groupSeparator, const QString &decimalSeparator, QList<int> groupList, bool *ok) const 1040 { 1041 QString num = number; 1042 bool valid = true; 1043 1044 if (!groupSeparator.isEmpty()) { 1045 if (!groupList.isEmpty()) { 1046 int separatorSize = groupSeparator.length(); 1047 int groupCount = groupList.count(); 1048 int groupAt = 0; 1049 int groupSize = groupList.at(groupAt); 1050 int pos = number.indexOf(decimalSeparator); 1051 if (pos == -1) { 1052 pos = number.length(); 1053 } 1054 pos = pos - groupSize - separatorSize; 1055 1056 while (pos > 0 && valid && groupSize > 0) { 1057 if (num.mid(pos, separatorSize) == groupSeparator) { 1058 num.remove(pos, separatorSize); 1059 if (groupAt + 1 < groupCount) { 1060 ++groupAt; 1061 groupSize = groupList.at(groupAt); 1062 } 1063 pos = pos - groupSize - separatorSize; 1064 } else { 1065 valid = false; 1066 } 1067 } 1068 } 1069 1070 if (num.contains(groupSeparator)) { 1071 valid = false; 1072 num = num.remove(groupSeparator); 1073 } 1074 } 1075 1076 if (ok) { 1077 *ok = valid; 1078 } 1079 1080 return num; 1081 } 1082 1083 QString KLocalePrivate::formatMoney(double num, const QString &symbol, int precision) const 1084 { 1085 // some defaults 1086 QString currencyString = symbol; 1087 if (symbol.isNull()) { 1088 currencyString = currencySymbol(); 1089 } 1090 if (precision < 0) { 1091 precision = monetaryDecimalPlaces(); 1092 } 1093 1094 // the number itself 1095 bool neg = num < 0; 1096 QString res = QString::number(neg ? -num : num, 'f', precision); 1097 1098 // Replace dot with locale decimal separator 1099 res.replace(QLatin1Char('.'), monetaryDecimalSymbol()); 1100 1101 // Insert the thousand separators 1102 res = formatDigitGroup(res, monetaryThousandsSeparator(), monetaryDecimalSymbol(), monetaryDigitGrouping()); 1103 1104 // set some variables we need later 1105 int signpos = neg 1106 ? negativeMonetarySignPosition() 1107 : positiveMonetarySignPosition(); 1108 QString sign = neg 1109 ? negativeSign() 1110 : positiveSign(); 1111 1112 switch (signpos) { 1113 case KLocale::ParensAround: 1114 res.prepend(QLatin1Char('(')); 1115 res.append(QLatin1Char(')')); 1116 break; 1117 case KLocale::BeforeQuantityMoney: 1118 res.prepend(sign); 1119 break; 1120 case KLocale::AfterQuantityMoney: 1121 res.append(sign); 1122 break; 1123 case KLocale::BeforeMoney: 1124 currencyString.prepend(sign); 1125 break; 1126 case KLocale::AfterMoney: 1127 currencyString.append(sign); 1128 break; 1129 } 1130 1131 if (neg ? negativePrefixCurrencySymbol() : 1132 positivePrefixCurrencySymbol()) { 1133 res.prepend(QLatin1Char(' ')); 1134 res.prepend(currencyString); 1135 } else { 1136 res.append(QLatin1Char(' ')); 1137 res.append(currencyString); 1138 } 1139 1140 // Convert to target digit set. 1141 res = convertDigits(res, m_monetaryDigitSet); 1142 1143 return res; 1144 } 1145 1146 QString KLocalePrivate::formatNumber(double num, int precision) const 1147 { 1148 if (precision < 0) { 1149 precision = decimalPlaces(); 1150 } 1151 // no need to round since QString::number does this for us 1152 return formatNumber(QString::number(num, 'f', precision), false, 0); 1153 } 1154 1155 QString KLocalePrivate::formatLong(long num) const 1156 { 1157 return formatNumber((double)num, 0); 1158 } 1159 1160 // increase the digit at 'position' by one 1161 static void _inc_by_one(QString &str, int position) 1162 { 1163 for (int i = position; i >= 0; i--) { 1164 char last_char = str[i].toLatin1(); 1165 switch (last_char) { 1166 case '0': 1167 str[i] = '1'; 1168 break; 1169 case '1': 1170 str[i] = '2'; 1171 break; 1172 case '2': 1173 str[i] = '3'; 1174 break; 1175 case '3': 1176 str[i] = '4'; 1177 break; 1178 case '4': 1179 str[i] = '5'; 1180 break; 1181 case '5': 1182 str[i] = '6'; 1183 break; 1184 case '6': 1185 str[i] = '7'; 1186 break; 1187 case '7': 1188 str[i] = '8'; 1189 break; 1190 case '8': 1191 str[i] = '9'; 1192 break; 1193 case '9': 1194 str[i] = '0'; 1195 if (i == 0) { 1196 str.prepend(QLatin1Char('1')); 1197 } 1198 continue; 1199 case '.': 1200 continue; 1201 } 1202 break; 1203 } 1204 } 1205 1206 // Cut off if more digits in fractional part than 'precision' 1207 static void _round(QString &str, int precision) 1208 { 1209 int decimalSymbolPos = str.indexOf(QLatin1Char('.')); 1210 1211 if (decimalSymbolPos == -1) { 1212 if (precision == 0) { 1213 return; 1214 } else if (precision > 0) { // add dot if missing (and needed) 1215 str.append(QLatin1Char('.')); 1216 decimalSymbolPos = str.length() - 1; 1217 } 1218 } 1219 // fill up with more than enough zeroes (in case fractional part too short) 1220 str.reserve(str.length() + precision); 1221 for (int i = 0; i < precision; ++i) { 1222 str.append(QLatin1Char('0')); 1223 } 1224 1225 // Now decide whether to round up or down 1226 char last_char = str[decimalSymbolPos + precision + 1].toLatin1(); 1227 switch (last_char) { 1228 case '0': 1229 case '1': 1230 case '2': 1231 case '3': 1232 case '4': 1233 // nothing to do, rounding down 1234 break; 1235 case '5': 1236 case '6': 1237 case '7': 1238 case '8': 1239 case '9': 1240 _inc_by_one(str, decimalSymbolPos + precision); 1241 break; 1242 default: 1243 break; 1244 } 1245 1246 decimalSymbolPos = str.indexOf(QLatin1Char('.')); 1247 str.truncate(decimalSymbolPos + precision + 1); 1248 1249 // if precision == 0 delete also '.' 1250 if (precision == 0) { 1251 str = str.left(decimalSymbolPos); 1252 } 1253 1254 str.squeeze(); 1255 } 1256 1257 QString KLocalePrivate::formatNumber(const QString &numStr, bool round, int precision) const 1258 { 1259 QString tmpString = numStr; 1260 1261 if (precision < 0) { 1262 precision = decimalPlaces(); 1263 } 1264 1265 // Skip the sign (for now) 1266 const bool neg = (tmpString[0] == QLatin1Char('-')); 1267 if (neg || tmpString[0] == QLatin1Char('+')) { 1268 tmpString.remove(0, 1); 1269 } 1270 1271 //qDebug()<<"tmpString:"<<tmpString; 1272 1273 // Split off exponential part (including 'e'-symbol) 1274 const int expPos = tmpString.indexOf(QLatin1Char('e')); // -1 if not found 1275 QString mantString = tmpString.left(expPos); // entire string if no 'e' found 1276 QString expString; 1277 if (expPos > -1) { 1278 expString = tmpString.mid(expPos); // includes the 'e', or empty if no 'e' 1279 if (expString.length() == 1) { 1280 expString.clear(); 1281 } 1282 } 1283 1284 //qDebug()<<"mantString:"<<mantString; 1285 //qDebug()<<"expString:"<<expString; 1286 if (mantString.isEmpty() || !mantString[0].isDigit()) {// invalid number 1287 mantString = QLatin1Char('0'); 1288 } 1289 1290 if (round) { 1291 _round(mantString, precision); 1292 } 1293 1294 // Replace dot with locale decimal separator 1295 mantString.replace(QLatin1Char('.'), decimalSymbol()); 1296 1297 // Insert the thousand separators 1298 mantString = formatDigitGroup(mantString, thousandsSeparator(), decimalSymbol(), numericDigitGrouping()); 1299 1300 // How can we know where we should put the sign? 1301 mantString.prepend(neg ? negativeSign() : positiveSign()); 1302 1303 // Convert to target digit set. 1304 if (digitSet() != KLocale::ArabicDigits) { 1305 mantString = convertDigits(mantString, digitSet()); 1306 expString = convertDigits(expString, digitSet()); 1307 } 1308 1309 return mantString + expString; 1310 } 1311 1312 // Returns a list of already translated units to use later in formatByteSize 1313 // and friends. Account for every unit in KLocale::BinarySizeUnits 1314 QList<QString> KLocalePrivate::dialectUnitsList(KLocale::BinaryUnitDialect dialect) 1315 { 1316 QList<QString> binaryUnits; 1317 1318 // Adds a given translation to the binaryUnits list. 1319 #define CACHE_BYTE_FMT(ctxt_text) \ 1320 binaryUnits.append(i18nc(ctxt_text, QLatin1String("%1"))); 1321 1322 // Do not remove i18n: comments below, they are used by the 1323 // translators. 1324 1325 // This prefix is shared by all current dialects. 1326 // i18n: Dumb message, avoid any markup or scripting. 1327 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in bytes", "%1 B")); 1328 1329 switch (dialect) { 1330 case KLocale::MetricBinaryDialect: 1331 // i18n: Dumb message, avoid any markup or scripting. 1332 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 1000 bytes", "%1 kB")); 1333 // i18n: Dumb message, avoid any markup or scripting. 1334 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 10^6 bytes", "%1 MB")); 1335 // i18n: Dumb message, avoid any markup or scripting. 1336 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 10^9 bytes", "%1 GB")); 1337 // i18n: Dumb message, avoid any markup or scripting. 1338 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 10^12 bytes", "%1 TB")); 1339 // i18n: Dumb message, avoid any markup or scripting. 1340 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 10^15 bytes", "%1 PB")); 1341 // i18n: Dumb message, avoid any markup or scripting. 1342 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 10^18 bytes", "%1 EB")); 1343 // i18n: Dumb message, avoid any markup or scripting. 1344 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 10^21 bytes", "%1 ZB")); 1345 // i18n: Dumb message, avoid any markup or scripting. 1346 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 10^24 bytes", "%1 YB")); 1347 break; 1348 1349 case KLocale::JEDECBinaryDialect: 1350 // i18n: Dumb message, avoid any markup or scripting. 1351 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 1024 bytes", "%1 KB")); 1352 // i18n: Dumb message, avoid any markup or scripting. 1353 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 2^20 bytes", "%1 MB")); 1354 // i18n: Dumb message, avoid any markup or scripting. 1355 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 2^30 bytes", "%1 GB")); 1356 // i18n: Dumb message, avoid any markup or scripting. 1357 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 2^40 bytes", "%1 TB")); 1358 // i18n: Dumb message, avoid any markup or scripting. 1359 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 2^50 bytes", "%1 PB")); 1360 // i18n: Dumb message, avoid any markup or scripting. 1361 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 2^60 bytes", "%1 EB")); 1362 // i18n: Dumb message, avoid any markup or scripting. 1363 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 2^70 bytes", "%1 ZB")); 1364 // i18n: Dumb message, avoid any markup or scripting. 1365 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 2^80 bytes", "%1 YB")); 1366 break; 1367 1368 case KLocale::IECBinaryDialect: 1369 default: 1370 // i18n: Dumb message, avoid any markup or scripting. 1371 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 1024 bytes", "%1 KiB")); 1372 // i18n: Dumb message, avoid any markup or scripting. 1373 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 2^20 bytes", "%1 MiB")); 1374 // i18n: Dumb message, avoid any markup or scripting. 1375 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 2^30 bytes", "%1 GiB")); 1376 // i18n: Dumb message, avoid any markup or scripting. 1377 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 2^40 bytes", "%1 TiB")); 1378 // i18n: Dumb message, avoid any markup or scripting. 1379 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 2^50 bytes", "%1 PiB")); 1380 // i18n: Dumb message, avoid any markup or scripting. 1381 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 2^60 bytes", "%1 EiB")); 1382 // i18n: Dumb message, avoid any markup or scripting. 1383 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 2^70 bytes", "%1 ZiB")); 1384 // i18n: Dumb message, avoid any markup or scripting. 1385 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 2^80 bytes", "%1 YiB")); 1386 break; 1387 } 1388 1389 return binaryUnits; 1390 } 1391 1392 QString KLocalePrivate::formatByteSize(double size, int precision, KLocale::BinaryUnitDialect dialect, 1393 KLocale::BinarySizeUnits specificUnit) 1394 { 1395 // Error checking 1396 if (dialect <= KLocale::DefaultBinaryDialect || dialect > KLocale::LastBinaryDialect) { 1397 dialect = m_binaryUnitDialect; 1398 } 1399 1400 if (specificUnit < KLocale::DefaultBinaryUnits || specificUnit > KLocale::UnitLastUnit) { 1401 specificUnit = KLocale::DefaultBinaryUnits; 1402 } 1403 1404 // Choose appropriate units. 1405 QList<QString> dialectUnits; 1406 if (dialect == m_binaryUnitDialect) { 1407 // Cache default units for speed 1408 if (m_byteSizeFmt.size() == 0) { 1409 QMutexLocker lock(s_kLocaleMutex()); 1410 1411 // We only cache the user's default dialect. 1412 m_byteSizeFmt = dialectUnitsList(m_binaryUnitDialect); 1413 } 1414 1415 dialectUnits = m_byteSizeFmt; 1416 } else { 1417 dialectUnits = dialectUnitsList(dialect); 1418 } 1419 1420 int unit = 0; // Selects what unit to use from cached list 1421 double multiplier = 1024.0; 1422 1423 if (dialect == KLocale::MetricBinaryDialect) { 1424 multiplier = 1000.0; 1425 } 1426 1427 // If a specific unit conversion is given, use it directly. Otherwise 1428 // search until the result is in [0, multiplier) (or out of our range). 1429 if (specificUnit == KLocale::DefaultBinaryUnits) { 1430 while (qAbs(size) >= multiplier && unit < (int) KLocale::UnitYottaByte) { 1431 size /= multiplier; 1432 unit++; 1433 } 1434 } else { 1435 // A specific unit is in use 1436 unit = static_cast<int>(specificUnit); 1437 if (unit > 0) { 1438 size /= pow(multiplier, unit); 1439 } 1440 } 1441 1442 if (unit == 0) { 1443 // Bytes, no rounding 1444 return dialectUnits[unit].arg(formatNumber(size, 0)); 1445 } 1446 1447 return dialectUnits[unit].arg(formatNumber(size, precision)); 1448 } 1449 1450 QString KLocalePrivate::formatByteSize(double size) 1451 { 1452 return formatByteSize(size, 1); 1453 } 1454 1455 KLocale::BinaryUnitDialect KLocalePrivate::binaryUnitDialect() const 1456 { 1457 return m_binaryUnitDialect; 1458 } 1459 1460 void KLocalePrivate::setBinaryUnitDialect(KLocale::BinaryUnitDialect newDialect) 1461 { 1462 if (newDialect > KLocale::DefaultBinaryDialect && newDialect <= KLocale::LastBinaryDialect) { 1463 QMutexLocker lock(s_kLocaleMutex()); 1464 m_binaryUnitDialect = newDialect; 1465 m_byteSizeFmt.clear(); // Reset cached translations. 1466 } 1467 } 1468 1469 QString KLocalePrivate::formatDuration(unsigned long mSec) const 1470 { 1471 if (mSec >= 24 * 3600000) { 1472 return i18nc("@item:intext %1 is a real number, e.g. 1.23 days", "%1 days", 1473 formatNumber(mSec / (24 * 3600000.0), 2)); 1474 } else if (mSec >= 3600000) { 1475 return i18nc("@item:intext %1 is a real number, e.g. 1.23 hours", "%1 hours", 1476 formatNumber(mSec / 3600000.0, 2)); 1477 } else if (mSec >= 60000) { 1478 return i18nc("@item:intext %1 is a real number, e.g. 1.23 minutes", "%1 minutes", 1479 formatNumber(mSec / 60000.0, 2)); 1480 } else if (mSec >= 1000) { 1481 return i18nc("@item:intext %1 is a real number, e.g. 1.23 seconds", "%1 seconds", 1482 formatNumber(mSec / 1000.0, 2)); 1483 } 1484 return i18ncp("@item:intext", "%1 millisecond", "%1 milliseconds", mSec); 1485 } 1486 1487 QString KLocalePrivate::formatSingleDuration(KLocalePrivate::DurationType durationType, int n) 1488 { 1489 switch (durationType) { 1490 case KLocalePrivate::DaysDurationType: 1491 return i18ncp("@item:intext", "1 day", "%1 days", n); 1492 case KLocalePrivate::HoursDurationType: 1493 return i18ncp("@item:intext", "1 hour", "%1 hours", n); 1494 case KLocalePrivate::MinutesDurationType: 1495 return i18ncp("@item:intext", "1 minute", "%1 minutes", n); 1496 case KLocalePrivate::SecondsDurationType: 1497 return i18ncp("@item:intext", "1 second", "%1 seconds", n); 1498 } 1499 return QString(); 1500 } 1501 1502 QString KLocalePrivate::prettyFormatDuration(unsigned long mSec) const 1503 { 1504 unsigned long ms = mSec; 1505 int days = ms / (24 * 3600000); 1506 ms = ms % (24 * 3600000); 1507 int hours = ms / 3600000; 1508 ms = ms % 3600000; 1509 int minutes = ms / 60000; 1510 ms = ms % 60000; 1511 int seconds = qRound(ms / 1000.0); 1512 1513 // Handle correctly problematic case #1 (look at KLocaleTest::prettyFormatDuration() 1514 // at klocaletest.cpp) 1515 if (seconds == 60) { 1516 return prettyFormatDuration(mSec - ms + 60000); 1517 } 1518 1519 if (days && hours) { 1520 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", 1521 "%1 and %2", formatSingleDuration(KLocalePrivate::DaysDurationType, days), 1522 formatSingleDuration(KLocalePrivate::HoursDurationType, hours)); 1523 } else if (days) { 1524 return formatSingleDuration(KLocalePrivate::DaysDurationType, days); 1525 } else if (hours && minutes) { 1526 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", 1527 "%1 and %2", 1528 formatSingleDuration(KLocalePrivate::HoursDurationType, hours), 1529 formatSingleDuration(KLocalePrivate::MinutesDurationType, minutes)); 1530 } else if (hours) { 1531 return formatSingleDuration(KLocalePrivate::HoursDurationType, hours); 1532 } else if (minutes && seconds) { 1533 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", 1534 "%1 and %2", 1535 formatSingleDuration(KLocalePrivate::MinutesDurationType, minutes), 1536 formatSingleDuration(KLocalePrivate::SecondsDurationType, seconds)); 1537 } else if (minutes) { 1538 return formatSingleDuration(KLocalePrivate::MinutesDurationType, minutes); 1539 } else { 1540 return formatSingleDuration(KLocalePrivate::SecondsDurationType, seconds); 1541 } 1542 } 1543 1544 QString KLocalePrivate::formatDate(const QDate &date, KLocale::DateFormat format) 1545 { 1546 return calendar()->formatDate(date, format); 1547 } 1548 1549 double KLocalePrivate::readNumber(const QString &_str, bool *ok) const 1550 { 1551 QString str = _str.trimmed(); 1552 bool neg = false; 1553 1554 // Check negative or positive signs 1555 // Assumes blank sign is positive even if pos sign set, unless already taken by negative 1556 if (!negativeSign().isEmpty() && str.indexOf(negativeSign()) == 0) { 1557 neg = true; 1558 str.remove(0, negativeSign().length()); 1559 str = str.trimmed(); 1560 } else if (!positiveSign().isEmpty() && str.indexOf(positiveSign()) == 0) { 1561 neg = false; 1562 str.remove(0, positiveSign().length()); 1563 str = str.trimmed(); 1564 } else if (negativeSign().isEmpty() && str[0].isDigit()) { 1565 neg = true; 1566 } 1567 1568 /* will hold the scientific notation portion of the number. 1569 Example, with 2.34E+23, exponentialPart == "E+23" 1570 */ 1571 QString exponentialPart; 1572 int EPos; 1573 1574 EPos = str.indexOf(QLatin1Char('E'), 0, Qt::CaseInsensitive); 1575 1576 if (EPos != -1) { 1577 exponentialPart = str.mid(EPos); 1578 str = str.left(EPos); 1579 str = str.trimmed(); 1580 } 1581 1582 // Remove group separators 1583 bool groupOk = true; 1584 if (str.contains(thousandsSeparator())) { 1585 str = parseDigitGroup(str, thousandsSeparator(), decimalSymbol(), 1586 numericDigitGrouping(), &groupOk); 1587 } 1588 1589 if (!groupOk) { 1590 if (ok) { 1591 *ok = false; 1592 } 1593 return 0.0; 1594 } 1595 1596 int pos = str.indexOf(decimalSymbol()); 1597 QString major; 1598 QString minor; 1599 if (pos == -1) { 1600 major = str; 1601 } else { 1602 major = str.left(pos); 1603 minor = str.mid(pos + decimalSymbol().length()); 1604 } 1605 1606 // Check the major and minor parts are only digits 1607 bool digitTest = true; 1608 foreach (const QChar &ch, major) { 1609 if (!ch.isDigit()) { 1610 digitTest = false; 1611 break; 1612 } 1613 } 1614 foreach (const QChar &ch, minor) { 1615 if (!ch.isDigit()) { 1616 digitTest = false; 1617 break; 1618 } 1619 } 1620 if (!digitTest) { 1621 if (ok) { 1622 *ok = false; 1623 } 1624 return 0.0; 1625 } 1626 1627 QString tot; 1628 if (neg) { 1629 tot = QLatin1Char('-'); 1630 } 1631 tot += major + QLatin1Char('.') + minor + exponentialPart; 1632 tot = toArabicDigits(tot); 1633 return tot.toDouble(ok); 1634 } 1635 1636 double KLocalePrivate::readMoney(const QString &_str, bool *ok) const 1637 { 1638 QString str = _str.trimmed(); 1639 bool neg = false; 1640 bool currencyFound = false; 1641 QString symbol = currencySymbol(); 1642 1643 // First try removing currency symbol from either end 1644 int pos = str.indexOf(symbol); 1645 if (pos == 0 || pos == (int) str.length() - symbol.length()) { 1646 str.remove(pos, symbol.length()); 1647 str = str.trimmed(); 1648 currencyFound = true; 1649 } 1650 if (str.isEmpty()) { 1651 if (ok) { 1652 *ok = false; 1653 } 1654 return 0; 1655 } 1656 1657 // Then try removing sign from either end (with a special case for parenthesis) 1658 if (str[0] == QLatin1Char('(') && str[str.length() - 1] == QLatin1Char(')')) { 1659 if (positiveMonetarySignPosition() != KLocale::ParensAround) { 1660 neg = true; 1661 } 1662 str.remove(str.length() - 1, 1); 1663 str.remove(0, 1); 1664 str = str.trimmed(); 1665 } else { 1666 int len = 0; 1667 QString sign; 1668 int negLen = negativeSign().length(); 1669 QString negSign = negativeSign(); 1670 if (!negSign.isEmpty() && (str.left(negLen) == negSign || str.right(negSign.length()) == negSign)) { 1671 neg = true; 1672 len = negLen; 1673 sign = negSign; 1674 } else { 1675 int posLen = positiveSign().length(); 1676 QString posSign = positiveSign(); 1677 if (!posSign.isEmpty() && (str.left(posLen) == posSign || str.right(posSign.length()) == posSign)) { 1678 len = posLen; 1679 sign = posSign; 1680 } else if (negSign.isEmpty() && str[0].isDigit() && str[str.length() - 1].isDigit()) { 1681 neg = true; 1682 } 1683 } 1684 if (!sign.isEmpty()) { 1685 if (str.left(len) == sign) { 1686 str.remove(0, len); 1687 } else { 1688 str.remove(str.length() - len, len); 1689 } 1690 str = str.trimmed(); 1691 } 1692 } 1693 1694 // Finally try again for the currency symbol, if we didn't find 1695 // it already (because of the negative sign being in the way). 1696 if (!currencyFound) { 1697 pos = str.indexOf(symbol); 1698 if (pos == 0 || pos == (int) str.length() - symbol.length()) { 1699 str.remove(pos, symbol.length()); 1700 str = str.trimmed(); 1701 } 1702 } 1703 1704 // Remove group separators 1705 bool groupOk = true; 1706 if (str.contains(monetaryThousandsSeparator())) { 1707 str = parseDigitGroup(str, monetaryThousandsSeparator(), monetaryDecimalSymbol(), 1708 monetaryDigitGrouping(), &groupOk); 1709 } 1710 1711 if (!groupOk) { 1712 if (ok) { 1713 *ok = false; 1714 } 1715 return 0.0; 1716 } 1717 1718 // And parse the rest as a number 1719 pos = str.indexOf(monetaryDecimalSymbol()); 1720 QString major; 1721 QString minor; 1722 if (pos == -1) { 1723 major = str; 1724 } else { 1725 major = str.left(pos); 1726 minor = str.mid(pos + monetaryDecimalSymbol().length()); 1727 } 1728 1729 // Check the major and minor parts are only digits 1730 bool digitTest = true; 1731 foreach (const QChar &ch, major) { 1732 if (!ch.isDigit()) { 1733 digitTest = false; 1734 break; 1735 } 1736 } 1737 foreach (const QChar &ch, minor) { 1738 if (!ch.isDigit()) { 1739 digitTest = false; 1740 break; 1741 } 1742 } 1743 if (!digitTest) { 1744 if (ok) { 1745 *ok = false; 1746 } 1747 return 0.0; 1748 } 1749 1750 QString tot; 1751 if (neg) { 1752 tot = QLatin1Char('-'); 1753 } 1754 tot += major + QLatin1Char('.') + minor; 1755 tot = toArabicDigits(tot); 1756 return tot.toDouble(ok); 1757 } 1758 1759 /** 1760 * helper function to read integers 1761 * @param str 1762 * @param pos the position to start at. It will be updated when we parse it. 1763 * @return the integer read in the string, or -1 if no string 1764 */ 1765 static int readInt(const QString &str, int &pos) 1766 { 1767 if (!str.at(pos).isDigit()) { 1768 return -1; 1769 } 1770 int result = 0; 1771 for (; str.length() > pos && str.at(pos).isDigit(); ++pos) { 1772 result *= 10; 1773 result += str.at(pos).digitValue(); 1774 } 1775 1776 return result; 1777 } 1778 1779 QDate KLocalePrivate::readDate(const QString &intstr, bool *ok) 1780 { 1781 return calendar()->readDate(intstr, ok); 1782 } 1783 1784 QDate KLocalePrivate::readDate(const QString &intstr, KLocale::ReadDateFlags flags, bool *ok) 1785 { 1786 return calendar()->readDate(intstr, flags, ok); 1787 } 1788 1789 QDate KLocalePrivate::readDate(const QString &intstr, const QString &fmt, bool *ok) 1790 { 1791 return calendar()->readDate(intstr, fmt, ok); 1792 } 1793 1794 QTime KLocalePrivate::readTime(const QString &intstr, bool *ok) const 1795 { 1796 QTime time = readLocaleTime(intstr, ok, KLocale::TimeDefault, KLocale::ProcessStrict); 1797 if (time.isValid()) { 1798 return time; 1799 } 1800 return readLocaleTime(intstr, ok, KLocale::TimeWithoutSeconds, KLocale::ProcessStrict); 1801 } 1802 1803 QTime KLocalePrivate::readTime(const QString &intstr, KLocale::ReadTimeFlags flags, bool *ok) const 1804 { 1805 return readLocaleTime(intstr, ok, (flags == KLocale::WithSeconds) ? KLocale::TimeDefault : KLocale::TimeWithoutSeconds, 1806 KLocale::ProcessStrict); 1807 } 1808 1809 // remove the first occurrence of the 2-character string 1810 // strip2char from inout and if found, also remove one preceding 1811 // punctuation character and arbitrary number of spaces. 1812 static void stripStringAndPreceedingSeparator(QString &inout, const QLatin1String &strip2char) 1813 { 1814 int remPos = inout.indexOf(strip2char); 1815 if (remPos == -1) { 1816 return; 1817 } 1818 int endPos = remPos + 2; 1819 int curPos = remPos - 1; 1820 while (curPos >= 0 && inout.at(curPos).isSpace()) { 1821 curPos--; 1822 } 1823 // remove the separator sign before the seconds 1824 // and assume that works everywhere 1825 if (curPos >= 0 && inout.at(curPos).isPunct() && inout.at(curPos) != QLatin1Char('%')) { 1826 curPos--; 1827 } 1828 while (curPos >= 0 && inout.at(curPos).isSpace()) { 1829 curPos--; 1830 } 1831 1832 remPos = qMax(curPos + 1, 0); 1833 inout.remove(remPos, endPos - remPos); 1834 } 1835 1836 // remove the first occurrence of the 2-character string 1837 // strip2char from inout and if found, also remove one 1838 // succeeding punctuation character and arbitrary number of spaces. 1839 static void stripStringAndSucceedingSeparator(QString &inout, const QLatin1String &strip2char) 1840 { 1841 int remPos = inout.indexOf(strip2char); 1842 if (remPos == -1) { 1843 return; 1844 } 1845 int curPos = remPos + 2; 1846 while (curPos < inout.size() && 1847 (inout.at(curPos).isSpace() || 1848 (inout.at(curPos).isPunct() && inout.at(curPos) != QLatin1Char('%')))) { 1849 curPos++; 1850 } 1851 inout.remove(remPos, curPos - remPos); 1852 } 1853 1854 // remove the first occurrence of "%p" from the inout. 1855 static void stripAmPmFormat(QString &inout) 1856 { 1857 // NOTE: this function assumes that %p - if it's present - 1858 // is either the first or the last element of the format 1859 // string. Either a succeeding or a preceding 1860 // punctuation symbol is stripped. 1861 int length = inout.size(); 1862 int ppos = inout.indexOf(QLatin1String("%p")); 1863 if (ppos == -1) { 1864 return; 1865 } else if (ppos == 0) { 1866 // first element, eat succeeding punctuation and spaces 1867 ppos = 2; 1868 while (ppos < length && (inout.at(ppos).isSpace() || inout.at(ppos).isPunct()) && 1869 inout.at(ppos) != QLatin1Char('%')) { 1870 ppos++; 1871 } 1872 inout = inout.mid(ppos); 1873 } else { 1874 stripStringAndPreceedingSeparator(inout, QLatin1String("%p")); 1875 } 1876 } 1877 1878 QTime KLocalePrivate::readLocaleTime(const QString &intstr, bool *ok, KLocale::TimeFormatOptions options, 1879 KLocale::TimeProcessingOptions processing) const 1880 { 1881 QString str(intstr.simplified().toLower()); 1882 QString format(timeFormat().simplified()); 1883 1884 int hour = -1; 1885 int minute = -1; 1886 int second = -1; 1887 bool useDayPeriod = false; 1888 KDayPeriod dayPeriod = dayPeriodForTime(QTime(0, 0, 0)); 1889 int strpos = 0; 1890 int formatpos = 0; 1891 bool error = false; 1892 1893 bool excludeSecs = ((options & KLocale::TimeWithoutSeconds) == KLocale::TimeWithoutSeconds); 1894 bool isDuration = ((options & KLocale::TimeDuration) == KLocale::TimeDuration); 1895 bool noAmPm = ((options & KLocale::TimeWithoutAmPm) == KLocale::TimeWithoutAmPm); 1896 bool foldHours = ((options & KLocale::TimeFoldHours) == KLocale::TimeFoldHours); 1897 bool strict = ((processing & KLocale::ProcessStrict) == KLocale::ProcessStrict); 1898 1899 // if seconds aren't needed, strip them from the timeFormat 1900 if (excludeSecs) { 1901 stripStringAndPreceedingSeparator(format, QLatin1String("%S")); 1902 second = 0; // seconds are always 0 1903 } 1904 1905 // if hours are folded, strip them from the timeFormat 1906 if (foldHours) { 1907 stripStringAndSucceedingSeparator(format, QLatin1String("%H")); 1908 stripStringAndSucceedingSeparator(format, QLatin1String("%k")); 1909 stripStringAndSucceedingSeparator(format, QLatin1String("%I")); 1910 stripStringAndSucceedingSeparator(format, QLatin1String("%l")); 1911 } 1912 1913 // if am/pm isn't needed, strip it from the timeFormat 1914 if (noAmPm) { 1915 stripAmPmFormat(format); 1916 } 1917 1918 while (!error && (format.length() > formatpos || str.length() > strpos)) { 1919 if (!(format.length() > formatpos && str.length() > strpos)) { 1920 error = true; 1921 break; 1922 } 1923 1924 QChar c = format.at(formatpos++); 1925 if (c.isSpace()) { 1926 if (strict) { // strict processing: space is needed 1927 if (!str.at(strpos).isSpace()) { 1928 error = true; 1929 break; 1930 } 1931 strpos++; 1932 } else { // lax processing: space in str not needed 1933 // 1 space maximum as str is simplified 1934 if (str.at(strpos).isSpace()) { 1935 strpos++; 1936 } 1937 } 1938 continue; 1939 } 1940 1941 if (c != QLatin1Char('%')) { 1942 if (c != str.at(strpos++)) { 1943 error = true; 1944 break; 1945 } 1946 continue; 1947 } 1948 1949 c = format.at(formatpos++); 1950 switch (c.unicode()) { 1951 1952 case 'p': // Day Period, normally AM/PM 1953 case 'P': { // Lowercase Day Period, normally am/pm 1954 error = true; 1955 foreach (const KDayPeriod &testDayPeriod, dayPeriods()) { 1956 QString dayPeriodText = testDayPeriod.periodName(KLocale::ShortName); 1957 int len = dayPeriodText.length(); 1958 if (str.mid(strpos, len) == dayPeriodText.toLower()) { 1959 dayPeriod = testDayPeriod; 1960 strpos += len; 1961 error = false; 1962 useDayPeriod = true; 1963 break; 1964 } 1965 } 1966 break; 1967 } 1968 1969 case 'k': // 24h Hours Short Number 1970 case 'H': // 24h Hours Long Number 1971 useDayPeriod = false; 1972 hour = readInt(str, strpos); 1973 break; 1974 1975 case 'l': // 12h Hours Short Number 1976 case 'I': // 12h Hours Long Number 1977 useDayPeriod = !isDuration; 1978 hour = readInt(str, strpos); 1979 break; 1980 1981 case 'M': 1982 minute = readInt(str, strpos); 1983 // minutes can be bigger than 59 if hours are folded 1984 if (foldHours) { 1985 // if hours are folded, make sure minutes doesn't get bigger than 59. 1986 hour = minute / 60; 1987 minute = minute % 60; 1988 } 1989 break; 1990 1991 case 'S': 1992 second = readInt(str, strpos); 1993 break; 1994 } 1995 1996 // NOTE: if anything is performed inside this loop, be sure to 1997 // check for error! 1998 } 1999 2000 QTime returnTime; 2001 if (!error) { 2002 if (useDayPeriod) { 2003 returnTime = dayPeriod.time(hour, minute, second); 2004 } else { 2005 returnTime = QTime(hour, minute, second); 2006 } 2007 } 2008 if (ok) { 2009 *ok = returnTime.isValid(); 2010 } 2011 return returnTime; 2012 } 2013 2014 QString KLocalePrivate::formatTime(const QTime &time, bool includeSecs, bool isDuration) const 2015 { 2016 KLocale::TimeFormatOptions options = KLocale::TimeDefault; 2017 if (!includeSecs) { 2018 options |= KLocale::TimeWithoutSeconds; 2019 } 2020 if (isDuration) { 2021 options |= KLocale::TimeDuration; 2022 } 2023 return formatLocaleTime(time, options); 2024 } 2025 2026 QString KLocalePrivate::formatLocaleTime(const QTime &time, KLocale::TimeFormatOptions options) const 2027 { 2028 QString rst(timeFormat()); 2029 2030 bool excludeSecs = ((options & KLocale::TimeWithoutSeconds) == KLocale::TimeWithoutSeconds); 2031 bool isDuration = ((options & KLocale::TimeDuration) == KLocale::TimeDuration); 2032 bool noAmPm = ((options & KLocale::TimeWithoutAmPm) == KLocale::TimeWithoutAmPm); 2033 bool foldHours = ((options & KLocale::TimeFoldHours) == KLocale::TimeFoldHours); 2034 2035 // if seconds aren't needed, strip them from the timeFormat 2036 if (excludeSecs) { 2037 stripStringAndPreceedingSeparator(rst, QLatin1String("%S")); 2038 } 2039 2040 // if hours should be folded, strip all hour symbols from the timeFormat 2041 if (foldHours) { 2042 stripStringAndSucceedingSeparator(rst, QLatin1String("%H")); 2043 stripStringAndSucceedingSeparator(rst, QLatin1String("%k")); 2044 stripStringAndSucceedingSeparator(rst, QLatin1String("%I")); 2045 stripStringAndSucceedingSeparator(rst, QLatin1String("%l")); 2046 } 2047 2048 // if am/pm isn't needed, strip it from the timeFormat 2049 if (noAmPm) { 2050 stripAmPmFormat(rst); 2051 } 2052 2053 // only "pm/am" and %M here can grow, the rest shrinks, but 2054 // I'm rather safe than sorry 2055 QChar *buffer = new QChar[rst.length() * 3 / 2 + 32]; 2056 2057 int index = 0; 2058 bool escape = false; 2059 int number = 0; 2060 2061 for (int format_index = 0; format_index < rst.length(); format_index++) { 2062 if (!escape) { 2063 if (rst.at(format_index).unicode() == '%') { 2064 escape = true; 2065 } else { 2066 buffer[index++] = rst.at(format_index); 2067 } 2068 } else { 2069 switch (rst.at(format_index).unicode()) { 2070 case '%': 2071 buffer[index++] = QLatin1Char('%'); 2072 break; 2073 case 'H': 2074 put_it_in(buffer, index, time.hour()); 2075 break; 2076 case 'I': 2077 if (isDuration) { 2078 put_it_in(buffer, index, time.hour()); 2079 } else { 2080 put_it_in(buffer, index, dayPeriodForTime(time).hourInPeriod(time)); 2081 } 2082 break; 2083 case 'M': 2084 if (foldHours) { 2085 put_it_in(buffer, index, QString::number(time.hour() * 60 + time.minute())); 2086 } else { 2087 put_it_in(buffer, index, time.minute()); 2088 } 2089 break; 2090 case 'S': 2091 put_it_in(buffer, index, time.second()); 2092 break; 2093 case 'k': 2094 case 'l': 2095 // to share the code 2096 if (!isDuration && rst.at(format_index).unicode() == 'l') { 2097 number = dayPeriodForTime(time).hourInPeriod(time); 2098 } else { 2099 number = time.hour(); 2100 } 2101 if (number / 10) { 2102 buffer[index++] = number / 10 + '0'; 2103 } 2104 buffer[index++] = number % 10 + '0'; 2105 break; 2106 case 'p': { 2107 put_it_in(buffer, index, dayPeriodForTime(time).periodName(KLocale::ShortName)); 2108 break; 2109 } 2110 default: 2111 buffer[index++] = rst.at(format_index); 2112 break; 2113 } 2114 escape = false; 2115 } 2116 } 2117 QString ret(buffer, index); 2118 delete [] buffer; 2119 ret = convertDigits(ret, dateTimeDigitSet()); 2120 return ret.trimmed(); 2121 } 2122 2123 bool KLocalePrivate::use12Clock() const 2124 { 2125 if ((timeFormat().contains(QString::fromLatin1("%I")) > 0) || 2126 (timeFormat().contains(QString::fromLatin1("%l")) > 0)) { 2127 return true; 2128 } else { 2129 return false; 2130 } 2131 } 2132 2133 void KLocalePrivate::setDayPeriods(const QList<KDayPeriod> &dayPeriods) 2134 { 2135 if (dayPeriods.count() > 0) { 2136 foreach (const KDayPeriod &dayPeriod, dayPeriods) { 2137 if (!dayPeriod.isValid()) { 2138 return; 2139 } 2140 } 2141 m_dayPeriods = dayPeriods; 2142 } 2143 } 2144 2145 QList<KDayPeriod> KLocalePrivate::dayPeriods() const 2146 { 2147 // If no Day Periods currently loaded then it means there were no country specific ones defined 2148 // in the country l10n file, so default to standard AM/PM translations for the users language. 2149 // Note we couldn't do this in initDayPeriods() as i18n isn't available until we have a 2150 // valid loacle constructed. 2151 if (m_dayPeriods.isEmpty()) { 2152 m_dayPeriods.append(KDayPeriod(QString::fromLatin1("am"), 2153 i18nc("Before Noon KLocale::LongName", "Ante Meridiem"), 2154 i18nc("Before Noon KLocale::ShortName", "AM"), 2155 i18nc("Before Noon KLocale::NarrowName", "A"), 2156 QTime(0, 0, 0), QTime(11, 59, 59, 999), 0, 12)); 2157 m_dayPeriods.append(KDayPeriod(QString::fromLatin1("pm"), 2158 i18nc("After Noon KLocale::LongName", "Post Meridiem"), 2159 i18nc("After Noon KLocale::ShortName", "PM"), 2160 i18nc("After Noon KLocale::NarrowName", "P"), 2161 QTime(12, 0, 0), QTime(23, 59, 59, 999), 0, 12)); 2162 } 2163 return m_dayPeriods; 2164 } 2165 2166 KDayPeriod KLocalePrivate::dayPeriodForTime(const QTime &time) const 2167 { 2168 if (time.isValid()) { 2169 foreach (const KDayPeriod &dayPeriod, dayPeriods()) { 2170 if (dayPeriod.isValid(time)) { 2171 return dayPeriod; 2172 } 2173 } 2174 } 2175 return KDayPeriod(); 2176 } 2177 2178 QStringList KLocalePrivate::languageList() const 2179 { 2180 return m_languageList; 2181 } 2182 2183 QStringList KLocalePrivate::currencyCodeList() const 2184 { 2185 return m_currencyCodeList; 2186 } 2187 2188 QString KLocalePrivate::formatDateTime(const KLocale *locale, const QDateTime &dateTime, KLocale::DateFormat format, 2189 bool includeSeconds, int daysTo, int secsTo) 2190 { 2191 // Have to do Fancy Date formatting here rather than using normal KCalendarSystem::formatDate() 2192 // as daysTo is relative to the time spec which formatDate doesn't know about. Needs to be 2193 // kept in sync with Fancy Date code in KCalendarSystem::formatDate(). Fix in KDE5. 2194 2195 // Only do Fancy if less than an hour into the future or less than a week in the past 2196 if ((daysTo == 0 && secsTo > 3600) || daysTo < 0 || daysTo > 6) { 2197 if (format == KLocale::FancyShortDate) { 2198 format = KLocale::ShortDate; 2199 } else if (format == KLocale::FancyLongDate) { 2200 format = KLocale::LongDate; 2201 } 2202 } 2203 2204 QString dateStr; 2205 if (format == KLocale::FancyShortDate || format == KLocale::FancyLongDate) { 2206 switch (daysTo) { 2207 case 0: 2208 dateStr = i18n("Today"); 2209 break; 2210 case 1: 2211 dateStr = i18n("Yesterday"); 2212 break; 2213 default: 2214 dateStr = locale->calendar()->weekDayName(dateTime.date()); 2215 } 2216 } else { 2217 dateStr = locale->formatDate(dateTime.date(), format); 2218 } 2219 2220 KLocale::TimeFormatOption timeFormat; 2221 if (includeSeconds) { 2222 timeFormat = KLocale::TimeDefault; 2223 } else { 2224 timeFormat = KLocale::TimeWithoutSeconds; 2225 } 2226 2227 return i18nc("concatenation of dates and time", "%1 %2", dateStr, 2228 locale->formatLocaleTime(dateTime.time(), timeFormat)); 2229 } 2230 2231 QString KLocalePrivate::formatDateTime(const QDateTime &dateTime, KLocale::DateFormat format, bool includeSeconds) const 2232 { 2233 QDateTime now = QDateTime::currentDateTime(); 2234 int daysTo = dateTime.date().daysTo(now.date()); 2235 int secsTo = now.secsTo(dateTime); 2236 return KLocalePrivate::formatDateTime(q, dateTime, format, includeSeconds, daysTo, secsTo); 2237 } 2238 2239 QString KLocalePrivate::formatDateTime(const KDateTime &dateTime, KLocale::DateFormat format, 2240 KLocale::DateTimeFormatOptions options) 2241 { 2242 QString dt; 2243 2244 if (dateTime.isDateOnly()) { 2245 dt = formatDate(dateTime.date(), format); 2246 } else { 2247 KDateTime now = KDateTime::currentDateTime(dateTime.timeSpec()); 2248 int daysTo = dateTime.date().daysTo(now.date()); 2249 int secsTo = now.secsTo(dateTime); 2250 dt = KLocalePrivate::formatDateTime(q, dateTime.dateTime(), format, (options & KLocale::Seconds), daysTo, secsTo); 2251 } 2252 2253 if (options & KLocale::TimeZone) { 2254 QString tz; 2255 switch (dateTime.timeType()) { 2256 case KDateTime::OffsetFromUTC: 2257 tz = i18n(dateTime.toString(QString::fromLatin1("%z")).toUtf8()); 2258 break; 2259 case KDateTime::UTC: 2260 case KDateTime::TimeZone: 2261 tz = i18n(dateTime.toString(QString::fromLatin1((format == KLocale::ShortDate) ? "%Z" : "%:Z")).toUtf8()); 2262 break; 2263 case KDateTime::ClockTime: 2264 default: 2265 break; 2266 } 2267 return i18nc("concatenation of date/time and time zone", "%1 %2", dt, tz); 2268 } 2269 2270 return dt; 2271 } 2272 2273 bool KLocalePrivate::useDefaultLanguage() const 2274 { 2275 return language() == KLocale::defaultLanguage(); 2276 } 2277 2278 void KLocalePrivate::initEncoding() 2279 { 2280 m_codecForEncoding = nullptr; 2281 2282 // This all made more sense when we still had the EncodingEnum config key. 2283 2284 QByteArray codeset = systemCodeset(); 2285 2286 if (!codeset.isEmpty()) { 2287 QTextCodec *codec = QTextCodec::codecForName(codeset); 2288 if (codec) { 2289 setEncoding(codec->mibEnum()); 2290 } 2291 } else { 2292 setEncoding(QTextCodec::codecForLocale()->mibEnum()); 2293 } 2294 2295 if (!m_codecForEncoding) { 2296 qWarning() << "Cannot resolve system encoding, defaulting to ISO 8859-1."; 2297 const int mibDefault = 4; // ISO 8859-1 2298 setEncoding(mibDefault); 2299 } 2300 2301 Q_ASSERT(m_codecForEncoding); 2302 } 2303 2304 QByteArray KLocalePrivate::systemCodeset() const 2305 { 2306 QByteArray codeset; 2307 #if HAVE_LANGINFO_H 2308 // Qt since 4.2 always returns 'System' as codecForLocale and KDE (for example 2309 // KEncodingFileDialog) expects real encoding name. So on systems that have langinfo.h use 2310 // nl_langinfo instead, just like Qt compiled without iconv does. Windows already has its own 2311 // workaround 2312 2313 codeset = nl_langinfo(CODESET); 2314 2315 if ((codeset == "ANSI_X3.4-1968") || (codeset == "US-ASCII")) { 2316 // means ascii, "C"; QTextCodec doesn't know, so avoid warning 2317 codeset = "ISO-8859-1"; 2318 } 2319 #endif 2320 return codeset; 2321 } 2322 2323 static inline bool isUnicodeNonCharacter(uint ucs4) 2324 { 2325 return (ucs4 & 0xfffe) == 0xfffe || (ucs4 - 0xfdd0U) < 16; 2326 } 2327 2328 QByteArray KLocalePrivate::encodeFileNameUTF8(const QString &fileName) 2329 { 2330 if (fileName.isNull()) { 2331 return QByteArray(); 2332 } 2333 int len = fileName.length(); 2334 const QChar *uc = fileName.constData(); 2335 2336 uchar replacement = '?'; 2337 int rlen = 3 * len; 2338 int surrogate_high = -1; 2339 2340 QByteArray rstr; 2341 rstr.resize(rlen); 2342 uchar *cursor = (uchar *)rstr.data(); 2343 const QChar *ch = uc; 2344 int invalid = 0; 2345 2346 const QChar *end = ch + len; 2347 while (ch < end) { 2348 uint u = ch->unicode(); 2349 if (surrogate_high >= 0) { 2350 if (ch->isLowSurrogate()) { 2351 u = QChar::surrogateToUcs4(surrogate_high, u); 2352 surrogate_high = -1; 2353 } else { 2354 // high surrogate without low 2355 *cursor = replacement; 2356 ++ch; 2357 ++invalid; 2358 surrogate_high = -1; 2359 continue; 2360 } 2361 } else if (ch->isLowSurrogate()) { 2362 // low surrogate without high 2363 *cursor = replacement; 2364 ++ch; 2365 ++invalid; 2366 continue; 2367 } else if (ch->isHighSurrogate()) { 2368 surrogate_high = u; 2369 ++ch; 2370 continue; 2371 } 2372 2373 if (u >= 0x10FE00 && u <= 0x10FE7F) { 2374 *cursor++ = (uchar)(u - 0x10FE00 + 128); 2375 } else if (u < 0x80) { 2376 *cursor++ = (uchar)u; 2377 } else { 2378 if (u < 0x0800) { 2379 *cursor++ = 0xc0 | ((uchar)(u >> 6)); 2380 } else { 2381 // is it one of the Unicode non-characters? 2382 if (isUnicodeNonCharacter(u)) { 2383 *cursor++ = replacement; 2384 ++ch; 2385 ++invalid; 2386 continue; 2387 } 2388 2389 if (u > 0xffff) { 2390 *cursor++ = 0xf0 | ((uchar)(u >> 18)); 2391 *cursor++ = 0x80 | (((uchar)(u >> 12)) & 0x3f); 2392 } else { 2393 *cursor++ = 0xe0 | (((uchar)(u >> 12)) & 0x3f); 2394 } 2395 *cursor++ = 0x80 | (((uchar)(u >> 6)) & 0x3f); 2396 } 2397 *cursor++ = 0x80 | ((uchar)(u & 0x3f)); 2398 } 2399 ++ch; 2400 } 2401 2402 rstr.resize(cursor - (const uchar *)rstr.constData()); 2403 return rstr; 2404 } 2405 2406 QString KLocalePrivate::decodeFileNameUTF8(const QByteArray &localFileName) 2407 { 2408 const char *chars = localFileName; 2409 int len = qstrlen(chars); 2410 int need = 0; 2411 uint uc = 0; 2412 uint min_uc = 0; 2413 2414 QString result(2 * (len + 1), Qt::Uninitialized); // worst case 2415 ushort *qch = (ushort *)result.unicode(); 2416 uchar ch; 2417 2418 for (int i = 0; i < len; ++i) { 2419 ch = chars[i]; 2420 if (need) { 2421 if ((ch & 0xc0) == 0x80) { 2422 uc = (uc << 6) | (ch & 0x3f); 2423 --need; 2424 if (!need) { 2425 bool nonCharacter; 2426 if (!(nonCharacter = isUnicodeNonCharacter(uc)) && uc > 0xffff && uc < 0x110000) { 2427 // surrogate pair 2428 Q_ASSERT((qch - (ushort *)result.unicode()) + 2 < result.length()); 2429 *qch++ = QChar::highSurrogate(uc); 2430 *qch++ = QChar::lowSurrogate(uc); 2431 } else if ((uc < min_uc) || (uc >= 0xd800 && uc <= 0xdfff) || nonCharacter || uc >= 0x110000) { 2432 // error: overlong sequence, UTF16 surrogate or non-character 2433 goto error; 2434 } else { 2435 *qch++ = uc; 2436 } 2437 } 2438 } else { 2439 goto error; 2440 } 2441 } else { 2442 if (ch < 128) { 2443 *qch++ = ushort(ch); 2444 } else if ((ch & 0xe0) == 0xc0) { 2445 uc = ch & 0x1f; 2446 need = 1; 2447 min_uc = 0x80; 2448 } else if ((ch & 0xf0) == 0xe0) { 2449 uc = ch & 0x0f; 2450 need = 2; 2451 min_uc = 0x800; 2452 } else if ((ch & 0xf8) == 0xf0) { 2453 uc = ch & 0x07; 2454 need = 3; 2455 min_uc = 0x10000; 2456 } else { 2457 goto error; 2458 } 2459 } 2460 } 2461 if (need > 0) { 2462 // unterminated UTF sequence 2463 goto error; 2464 } 2465 result.truncate(qch - (ushort *)result.unicode()); 2466 return result; 2467 2468 error: 2469 2470 qch = (ushort *)result.unicode(); 2471 for (int i = 0; i < len; ++i) { 2472 ch = chars[i]; 2473 if (ch < 128) { 2474 *qch++ = ushort(ch); 2475 } else { 2476 uint uc = ch - 128 + 0x10FE00; //U+10FE00-U+10FE7F 2477 *qch++ = QChar::highSurrogate(uc); 2478 *qch++ = QChar::lowSurrogate(uc); 2479 } 2480 } 2481 result.truncate(qch - (ushort *)result.unicode()); 2482 return result; 2483 } 2484 2485 void KLocalePrivate::setDateFormat(const QString &format) 2486 { 2487 m_dateFormat = format.trimmed(); 2488 } 2489 2490 void KLocalePrivate::setDateFormatShort(const QString &format) 2491 { 2492 m_dateFormatShort = format.trimmed(); 2493 } 2494 2495 void KLocalePrivate::setDateMonthNamePossessive(bool possessive) 2496 { 2497 m_dateMonthNamePossessive = possessive; 2498 } 2499 2500 void KLocalePrivate::setTimeFormat(const QString &format) 2501 { 2502 m_timeFormat = format.trimmed(); 2503 } 2504 2505 void KLocalePrivate::setWeekStartDay(int day) 2506 { 2507 if (day >= 1 && day <= calendar()->daysInWeek(QDate())) { 2508 m_weekStartDay = day; 2509 } 2510 } 2511 2512 void KLocalePrivate::setWorkingWeekStartDay(int day) 2513 { 2514 if (day >= 1 && day <= calendar()->daysInWeek(QDate())) { 2515 m_workingWeekStartDay = day; 2516 } 2517 } 2518 2519 void KLocalePrivate::setWorkingWeekEndDay(int day) 2520 { 2521 if (day >= 1 && day <= calendar()->daysInWeek(QDate())) { 2522 m_workingWeekEndDay = day; 2523 } 2524 } 2525 2526 void KLocalePrivate::setWeekDayOfPray(int day) 2527 { 2528 if (day >= 0 && day <= calendar()->daysInWeek(QDate())) { // 0 = None 2529 m_weekDayOfPray = day; 2530 } 2531 } 2532 2533 QString KLocalePrivate::dateFormat() const 2534 { 2535 return m_dateFormat; 2536 } 2537 2538 QString KLocalePrivate::dateFormatShort() const 2539 { 2540 return m_dateFormatShort; 2541 } 2542 2543 QString KLocalePrivate::timeFormat() const 2544 { 2545 return m_timeFormat; 2546 } 2547 2548 void KLocalePrivate::setDecimalPlaces(int digits) 2549 { 2550 m_decimalPlaces = digits; 2551 } 2552 2553 void KLocalePrivate::setDecimalSymbol(const QString &symbol) 2554 { 2555 m_decimalSymbol = symbol.trimmed(); 2556 } 2557 2558 void KLocalePrivate::setThousandsSeparator(const QString &separator) 2559 { 2560 // allow spaces here 2561 m_thousandsSeparator = separator; 2562 } 2563 2564 void KLocalePrivate::setNumericDigitGrouping(QList<int> groupList) 2565 { 2566 m_numericDigitGrouping = groupList; 2567 } 2568 2569 void KLocalePrivate::setPositiveSign(const QString &sign) 2570 { 2571 m_positiveSign = sign.trimmed(); 2572 } 2573 2574 void KLocalePrivate::setNegativeSign(const QString &sign) 2575 { 2576 m_negativeSign = sign.trimmed(); 2577 } 2578 2579 void KLocalePrivate::setPositiveMonetarySignPosition(KLocale::SignPosition signpos) 2580 { 2581 m_positiveMonetarySignPosition = signpos; 2582 } 2583 2584 void KLocalePrivate::setNegativeMonetarySignPosition(KLocale::SignPosition signpos) 2585 { 2586 m_negativeMonetarySignPosition = signpos; 2587 } 2588 2589 void KLocalePrivate::setPositivePrefixCurrencySymbol(bool prefix) 2590 { 2591 m_positivePrefixCurrencySymbol = prefix; 2592 } 2593 2594 void KLocalePrivate::setNegativePrefixCurrencySymbol(bool prefix) 2595 { 2596 m_negativePrefixCurrencySymbol = prefix; 2597 } 2598 2599 void KLocalePrivate::setMonetaryDecimalPlaces(int digits) 2600 { 2601 m_monetaryDecimalPlaces = digits; 2602 } 2603 2604 void KLocalePrivate::setMonetaryThousandsSeparator(const QString &separator) 2605 { 2606 // allow spaces here 2607 m_monetaryThousandsSeparator = separator; 2608 } 2609 2610 void KLocalePrivate::setMonetaryDigitGrouping(QList<int> groupList) 2611 { 2612 m_monetaryDigitGrouping = groupList; 2613 } 2614 2615 void KLocalePrivate::setMonetaryDecimalSymbol(const QString &symbol) 2616 { 2617 m_monetaryDecimalSymbol = symbol.trimmed(); 2618 } 2619 2620 void KLocalePrivate::setCurrencySymbol(const QString &symbol) 2621 { 2622 m_currencySymbol = symbol.trimmed(); 2623 } 2624 2625 int KLocalePrivate::pageSize() const 2626 { 2627 return m_pageSize; 2628 } 2629 2630 void KLocalePrivate::setPageSize(int size) 2631 { 2632 // #### check if it's in range?? 2633 m_pageSize = size; 2634 } 2635 2636 KLocale::MeasureSystem KLocalePrivate::measureSystem() const 2637 { 2638 return m_measureSystem; 2639 } 2640 2641 void KLocalePrivate::setMeasureSystem(KLocale::MeasureSystem value) 2642 { 2643 m_measureSystem = value; 2644 } 2645 2646 QString KLocalePrivate::defaultLanguage() 2647 { 2648 static const QString en_US = QString::fromLatin1("en_US"); 2649 return en_US; 2650 } 2651 2652 QString KLocalePrivate::defaultCountry() 2653 { 2654 return QString::fromLatin1("C"); 2655 } 2656 2657 QString KLocalePrivate::defaultCurrencyCode() 2658 { 2659 return QString::fromLatin1("USD"); 2660 } 2661 2662 const QByteArray KLocalePrivate::encoding() 2663 { 2664 return codecForEncoding()->name(); 2665 } 2666 2667 int KLocalePrivate::encodingMib() const 2668 { 2669 return codecForEncoding()->mibEnum(); 2670 } 2671 2672 int KLocalePrivate::fileEncodingMib() const 2673 { 2674 if (m_utf8FileEncoding) { 2675 return 106; 2676 } 2677 return codecForEncoding()->mibEnum(); 2678 } 2679 2680 QTextCodec *KLocalePrivate::codecForEncoding() const 2681 { 2682 return m_codecForEncoding; 2683 } 2684 2685 bool KLocalePrivate::setEncoding(int mibEnum) 2686 { 2687 QTextCodec *codec = QTextCodec::codecForMib(mibEnum); 2688 if (codec) { 2689 m_codecForEncoding = codec; 2690 } 2691 2692 return codec != nullptr; 2693 } 2694 2695 QStringList KLocalePrivate::allLanguagesList() 2696 { 2697 if (!m_languages) { 2698 m_languages = new KConfig(QLatin1String("locale/kf5_all_languages"), KConfig::NoGlobals, QStandardPaths::GenericDataLocation); 2699 } 2700 return m_languages->groupList(); 2701 } 2702 2703 QStringList KLocalePrivate::installedLanguages() 2704 { 2705 QStringList languages; 2706 const QStringList localeDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("locale"), QStandardPaths::LocateDirectory); 2707 Q_FOREACH (const QString &localeDir, localeDirs) { 2708 const QStringList entries = QDir(localeDir).entryList(QDir::Dirs); 2709 Q_FOREACH (const QString &d, entries) { 2710 if (QFile::exists(localeDir + QLatin1Char('/') + d + QLatin1String("/kf5_entry.desktop"))) { 2711 languages.append(d); 2712 } 2713 } 2714 } 2715 2716 languages.sort(); 2717 return languages; 2718 } 2719 2720 QString KLocalePrivate::languageCodeToName(const QString &language) 2721 { 2722 if (!m_languages) { 2723 m_languages = new KConfig(QLatin1String("locale/kf5_all_languages"), KConfig::NoGlobals, QStandardPaths::GenericDataLocation); 2724 } 2725 2726 KConfigGroup cg(m_languages, language); 2727 return cg.readEntry("Name"); 2728 } 2729 2730 QStringList KLocalePrivate::allCountriesList() const 2731 { 2732 QStringList countries; 2733 const QStringList localeDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("kf5/locale/countries/"), QStandardPaths::LocateDirectory); 2734 Q_FOREACH (const QString &localeDir, localeDirs) { 2735 const QStringList entries = QDir(localeDir).entryList(QDir::Dirs); 2736 Q_FOREACH (const QString &d, entries) { 2737 if (QFile::exists(localeDir + QLatin1Char('/') + d + QLatin1String("/country.desktop"))) { 2738 countries.append(d); 2739 } 2740 } 2741 } 2742 return countries; 2743 } 2744 2745 QString KLocalePrivate::countryCodeToName(const QString &country) const 2746 { 2747 QString countryName; 2748 QString entryFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("kf5/locale/countries/") + country.toLower() + QLatin1String("/country.desktop")); 2749 if (!entryFile.isEmpty()) { 2750 KConfig cfg(entryFile); 2751 KConfigGroup cg(&cfg, "KCM Locale"); 2752 countryName = cg.readEntry("Name"); 2753 } 2754 return countryName; 2755 } 2756 2757 KLocale::CalendarSystem KLocalePrivate::calendarTypeToCalendarSystem(const QString &calendarType) const 2758 { 2759 if (calendarType == QLatin1String("coptic")) { 2760 return KLocale::CopticCalendar; 2761 } else if (calendarType == QLatin1String("ethiopian")) { 2762 return KLocale::EthiopianCalendar; 2763 } else if (calendarType == QLatin1String("gregorian")) { 2764 return KLocale::QDateCalendar; 2765 } else if (calendarType == QLatin1String("gregorian-proleptic")) { 2766 return KLocale::GregorianCalendar; 2767 } else if (calendarType == QLatin1String("hebrew")) { 2768 return KLocale::HebrewCalendar; 2769 } else if (calendarType == QLatin1String("hijri")) { 2770 return KLocale::IslamicCivilCalendar; 2771 } else if (calendarType == QLatin1String("indian-national")) { 2772 return KLocale::IndianNationalCalendar; 2773 } else if (calendarType == QLatin1String("jalali")) { 2774 return KLocale::JalaliCalendar; 2775 } else if (calendarType == QLatin1String("japanese")) { 2776 return KLocale::JapaneseCalendar; 2777 } else if (calendarType == QLatin1String("julian")) { 2778 return KLocale::JulianCalendar; 2779 } else if (calendarType == QLatin1String("minguo")) { 2780 return KLocale::MinguoCalendar; 2781 } else if (calendarType == QLatin1String("thai")) { 2782 return KLocale::ThaiCalendar; 2783 } else { 2784 return KLocale::QDateCalendar; 2785 } 2786 } 2787 2788 QString KLocalePrivate::calendarSystemToCalendarType(KLocale::CalendarSystem calendarSystem) const 2789 { 2790 switch (calendarSystem) { 2791 case KLocale::GregorianCalendar: 2792 return QLatin1String("gregorian"); 2793 case KLocale::CopticCalendar: 2794 return QLatin1String("coptic"); 2795 case KLocale::EthiopianCalendar: 2796 return QLatin1String("ethiopian"); 2797 case KLocale::HebrewCalendar: 2798 return QLatin1String("hebrew"); 2799 case KLocale::IslamicCivilCalendar: 2800 return QLatin1String("hijri"); 2801 case KLocale::IndianNationalCalendar: 2802 return QLatin1String("indian-national"); 2803 case KLocale::JalaliCalendar: 2804 return QLatin1String("jalali"); 2805 case KLocale::JapaneseCalendar: 2806 return QLatin1String("japanese"); 2807 case KLocale::JulianCalendar: 2808 return QLatin1String("julian"); 2809 case KLocale::MinguoCalendar: 2810 return QLatin1String("minguo"); 2811 case KLocale::ThaiCalendar: 2812 return QLatin1String("thai"); 2813 default: 2814 return QLatin1String("gregorian"); 2815 } 2816 } 2817 2818 void KLocalePrivate::setCalendar(const QString &calendarType) 2819 { 2820 setCalendarSystem(calendarTypeToCalendarSystem(calendarType)); 2821 } 2822 2823 void KLocalePrivate::setCalendarSystem(KLocale::CalendarSystem calendarSystem) 2824 { 2825 m_calendarSystem = calendarSystem; 2826 delete m_calendar; 2827 m_calendar = nullptr; 2828 } 2829 2830 QString KLocalePrivate::calendarType() const 2831 { 2832 return calendarSystemToCalendarType(m_calendarSystem); 2833 } 2834 2835 KLocale::CalendarSystem KLocalePrivate::calendarSystem() const 2836 { 2837 return m_calendarSystem; 2838 } 2839 2840 const KCalendarSystem *KLocalePrivate::calendar() 2841 { 2842 if (!m_calendar) { 2843 m_calendar = KCalendarSystem::create(m_calendarSystem, m_config, q); 2844 } 2845 2846 return m_calendar; 2847 } 2848 2849 void KLocalePrivate::setWeekNumberSystem(KLocale::WeekNumberSystem weekNumberSystem) 2850 { 2851 m_weekNumberSystem = weekNumberSystem; 2852 } 2853 2854 KLocale::WeekNumberSystem KLocalePrivate::weekNumberSystem() 2855 { 2856 return m_weekNumberSystem; 2857 } 2858 2859 void KLocalePrivate::setDigitSet(KLocale::DigitSet digitSet) 2860 { 2861 m_digitSet = digitSet; 2862 } 2863 2864 KLocale::DigitSet KLocalePrivate::digitSet() const 2865 { 2866 return m_digitSet; 2867 } 2868 2869 void KLocalePrivate::setMonetaryDigitSet(KLocale::DigitSet digitSet) 2870 { 2871 m_monetaryDigitSet = digitSet; 2872 } 2873 2874 KLocale::DigitSet KLocalePrivate::monetaryDigitSet() const 2875 { 2876 return m_monetaryDigitSet; 2877 } 2878 2879 void KLocalePrivate::setDateTimeDigitSet(KLocale::DigitSet digitSet) 2880 { 2881 m_dateTimeDigitSet = digitSet; 2882 } 2883 2884 KLocale::DigitSet KLocalePrivate::dateTimeDigitSet() const 2885 { 2886 return m_dateTimeDigitSet; 2887 }