KDELibs4Support

klocale_kde.cpp
1 /* This file is part of the KDE libraries
2  Copyright (c) 1997,2001 Stephan Kulow <[email protected]>
3  Copyright (c) 1999 Preston Brown <[email protected]>
4  Copyright (c) 1999-2002 Hans Petter Bieker <[email protected]>
5  Copyright (c) 2002 Lukas Tinkl <[email protected]>
6  Copyright (C) 2007 Bernhard Loos <[email protected]>
7  Copyright (C) 2009, 2010 John Layt <[email protected]>
8 
9  This library is free software; you can redistribute it and/or
10  modify it under the terms of the GNU Library General Public
11  License as published by the Free Software Foundation; either
12  version 2 of the License, or (at your option) any later version.
13 
14  This library is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  Library General Public License for more details.
18 
19  You should have received a copy of the GNU Library General Public License
20  along with this library; see the file COPYING.LIB. If not, write to
21  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  Boston, MA 02110-1301, USA.
23 */
24 
25 #include "klocale_p.h"
26 
27 #include "config-localization.h"
28 
29 #include <math.h>
30 #include <locale.h>
31 
32 #if HAVE_SYS_TIME_H
33 #include <sys/time.h>
34 #endif
35 #if HAVE_TIME_H
36 #include <time.h>
37 #endif
38 #if HAVE_LANGINFO_H
39 #include <langinfo.h>
40 #endif
41 
42 #include <QTextCodec>
43 #include <QFile>
44 #include <QDebug>
45 #include <QPrinter>
46 #include <QRegExp>
47 #include <QLocale>
48 #include <QHash>
49 #include <QMutexLocker>
50 #include <QStringList>
51 #include <QCoreApplication>
52 #include <QDir>
53 #include <qstandardpaths.h>
54 
55 #include "kconfig.h"
56 #include "kdatetime.h"
57 #include "kcalendarsystem.h"
58 #include "kcurrencycode.h"
59 #include "klocalizedstring.h"
60 #include "kconfiggroup.h"
61 #include "kdayperiod_p.h"
62 
63 class KLocaleStaticData
64 {
65 public:
66 
67  KLocaleStaticData();
68 
69  // FIXME: Temporary until full language-sensitivity implemented.
70  QHash<KLocale::DigitSet, QStringList> languagesUsingDigitSet;
71 };
72 
73 KLocaleStaticData::KLocaleStaticData()
74 {
75  // Languages using non-Western Arabic digit sets.
76  // FIXME: Temporary until full language-sensitivity implemented.
77  languagesUsingDigitSet.insert(KLocale::ArabicIndicDigits, QStringList() << QString::fromLatin1("ar") << QString::fromLatin1("ps"));
78  languagesUsingDigitSet.insert(KLocale::BengaliDigits, QStringList() << QString::fromLatin1("bn") << QString::fromLatin1("as"));
79  languagesUsingDigitSet.insert(KLocale::DevenagariDigits, QStringList() << QString::fromLatin1("hi") << QString::fromLatin1("ne"));
80  languagesUsingDigitSet.insert(KLocale::EasternArabicIndicDigits, QStringList() << QString::fromLatin1("fa") << QString::fromLatin1("ur"));
81  languagesUsingDigitSet.insert(KLocale::GujaratiDigits, QStringList() << QString::fromLatin1("gu"));
82  languagesUsingDigitSet.insert(KLocale::GurmukhiDigits, QStringList() << QString::fromLatin1("pa"));
83  languagesUsingDigitSet.insert(KLocale::KannadaDigits, QStringList() << QString::fromLatin1("kn"));
84  languagesUsingDigitSet.insert(KLocale::KhmerDigits, QStringList() << QString::fromLatin1("km"));
85  languagesUsingDigitSet.insert(KLocale::MalayalamDigits, QStringList() << QString::fromLatin1("ml"));
86  languagesUsingDigitSet.insert(KLocale::OriyaDigits, QStringList() << QString::fromLatin1("or"));
87  languagesUsingDigitSet.insert(KLocale::TamilDigits, QStringList() << QString::fromLatin1("ta"));
88  languagesUsingDigitSet.insert(KLocale::TeluguDigits, QStringList() << QString::fromLatin1("te"));
89  languagesUsingDigitSet.insert(KLocale::ThaiDigits, QStringList() << QString::fromLatin1("th"));
90 }
91 
92 Q_GLOBAL_STATIC(KLocaleStaticData, staticData)
93 
94 Q_GLOBAL_STATIC_WITH_ARGS(QMutex, s_kLocaleMutex, (QMutex::Recursive))
95 
96 KLocalePrivate::KLocalePrivate(KLocale *q_ptr)
97  : q(q_ptr),
98  m_config(KSharedConfig::Ptr()),
99  m_country(QString()),
100  m_language(QString()),
101  m_languages(nullptr),
102  m_calendar(nullptr),
103  m_currency(nullptr),
104  m_codecForEncoding(nullptr)
105 {
106 }
107 
108 KLocalePrivate::KLocalePrivate(const KLocalePrivate &rhs)
109 {
110  copy(rhs);
111 }
112 
113 KLocalePrivate &KLocalePrivate::operator=(const KLocalePrivate &rhs)
114 {
115  copy(rhs);
116  return *this;
117 }
118 
119 KSharedConfig::Ptr KLocalePrivate::config()
120 {
121  if (m_config) {
122  return m_config;
123  } else {
124  return KSharedConfig::openConfig();
125  }
126 }
127 
128 void KLocalePrivate::copy(const KLocalePrivate &rhs)
129 {
130  // Parent KLocale
131  q = nullptr;
132 
133  // Config
134  m_config = rhs.m_config;
135 
136  // Country settings
137  m_country = rhs.m_country;
138  m_countryDivisionCode = rhs.m_countryDivisionCode;
139 
140  // Language settings
141  m_language = rhs.m_language;
142  m_languages = nullptr;
143  m_languageList = rhs.m_languageList;
144  m_languageSensitiveDigits = rhs.m_languageSensitiveDigits;
145  m_nounDeclension = rhs.m_nounDeclension;
146 
147  // Calendar settings
148  m_calendarSystem = rhs.m_calendarSystem;
149  m_calendar = nullptr;
150  m_weekStartDay = rhs.m_weekStartDay;
151  m_workingWeekStartDay = rhs.m_workingWeekStartDay;
152  m_workingWeekEndDay = rhs.m_workingWeekEndDay;
153  m_weekDayOfPray = rhs.m_weekDayOfPray;
154 
155  // Date/Time settings
156  m_dateFormat = rhs.m_dateFormat;
157  m_dateFormatShort = rhs.m_dateFormatShort;
158  m_timeFormat = rhs.m_timeFormat;
159  m_dateTimeDigitSet = rhs.m_dateTimeDigitSet;
160  m_dateMonthNamePossessive = rhs.m_dateMonthNamePossessive;
161  m_dayPeriods = rhs.m_dayPeriods;
162  m_weekNumberSystem = rhs.m_weekNumberSystem;
163 
164  // Number settings
165  m_decimalPlaces = rhs.m_decimalPlaces;
166  m_decimalSymbol = rhs.m_decimalSymbol;
167  m_thousandsSeparator = rhs.m_thousandsSeparator;
168  m_numericDigitGrouping = rhs.m_numericDigitGrouping;
169  m_positiveSign = rhs.m_positiveSign;
170  m_negativeSign = rhs.m_negativeSign;
171  m_digitSet = rhs.m_digitSet;
172 
173  // Currency settings
174  m_currencyCode = rhs.m_currencyCode;
175  m_currency = nullptr;
176  m_currencyCodeList = rhs.m_currencyCodeList;
177 
178  // Money settings
179  m_currencySymbol = rhs.m_currencySymbol;
180  m_monetaryDecimalSymbol = rhs.m_monetaryDecimalSymbol;
181  m_monetaryThousandsSeparator = rhs.m_monetaryThousandsSeparator;
182  m_monetaryDigitGrouping = rhs.m_monetaryDigitGrouping;
183  m_monetaryDecimalPlaces = rhs.m_monetaryDecimalPlaces;
184  m_positiveMonetarySignPosition = rhs.m_positiveMonetarySignPosition;
185  m_negativeMonetarySignPosition = rhs.m_negativeMonetarySignPosition;
186  m_positivePrefixCurrencySymbol = rhs.m_positivePrefixCurrencySymbol;
187  m_negativePrefixCurrencySymbol = rhs.m_negativePrefixCurrencySymbol;
188  m_monetaryDigitSet = rhs.m_monetaryDigitSet;
189 
190  // Units settings
191  m_binaryUnitDialect = rhs.m_binaryUnitDialect;
192  m_byteSizeFmt = rhs.m_byteSizeFmt;
193  m_pageSize = rhs.m_pageSize;
194  m_measureSystem = rhs.m_measureSystem;
195 
196  // Encoding settings
197  m_encoding = rhs.m_encoding;
198  m_codecForEncoding = rhs.m_codecForEncoding;
199  m_utf8FileEncoding = rhs.m_utf8FileEncoding;
200 }
201 
202 KLocalePrivate::~KLocalePrivate()
203 {
204  delete m_currency;
205  delete m_calendar;
206  delete m_languages;
207 }
208 
209 // init only called from platform specific constructor, so set everything up
210 // Will be given a persistantConfig or a tempConfig or neither, but never both
211 void KLocalePrivate::init(const QString &language, const QString &country,
212  KSharedConfig::Ptr persistantConfig, KConfig *tempConfig)
213 {
214  // Only keep the persistant config if it is not the global
215  if (persistantConfig != KSharedConfig::Ptr() && persistantConfig != KSharedConfig::openConfig()) {
216  m_config = persistantConfig;
217  }
218 
219  KConfigGroup cg;
220  bool useEnvironmentVariables;
221 
222  // We can't read the formats from the config until we know what locale to read in, but we need
223  // to read the config to find out the locale. The Country and Language settings should never
224  // be localized in the config, so we can read a temp copy of them to get us started.
225 
226  // If no config given, use the global config and include envvars, otherwise use only the config.
227  if (m_config != KSharedConfig::Ptr()) {
228  cg = m_config->group(QLatin1String("Locale"));
229  useEnvironmentVariables = false;
230  } else if (tempConfig == nullptr || tempConfig == KSharedConfig::openConfig().data()) {
231  cg = KSharedConfig::openConfig()->group(QLatin1String("Locale"));
232  useEnvironmentVariables = true;
233  } else {
234  cg = tempConfig->group(QLatin1String("Locale"));
235  useEnvironmentVariables = false;
236  }
237 
238  initEncoding();
239  initCountry(country, cg.readEntry(QLatin1String("Country")));
240  initLanguageList(language, cg.readEntry(QLatin1String("Language")), useEnvironmentVariables);
241  // Now that we have a language, we can set up the config which uses it to setLocale()
242  initConfig(tempConfig);
243  initFormat();
244 }
245 
246 // Init the config, this is called during construction and by later setCountry/setLanguage calls.
247 // You _must_ have the m_language set to a valid language or en_US before calling this so a
248 // setLocale can be applied to the config
249 void KLocalePrivate::initConfig(KConfig *config)
250 {
251  // * If we were constructed with a KSharedConfig it means the user gave it to us
252  // to use for the life of the KLocale, so just keep using it after a setLocale
253  // * If passed in KConfig is null or the global config then use the global, but
254  // do the setLocale first.
255  // * If we have a KConfig we need to use that, but due to keeping old behaviour
256  // of not requiring access to it for life we can't keep a reference so instead
257  // take a copy and use that, but do setLocale first.
258 
259  if (m_config != KSharedConfig::Ptr()) {
260  m_config->setLocale(m_language);
261  } else {
262  // If no config given then use the global
263  if (config == nullptr || config == KSharedConfig::openConfig().data()) {
264  KSharedConfig::openConfig()->setLocale(m_language);
265  } else {
266  config->setLocale(m_language);
267  m_config = KSharedConfig::openConfig();
268  config->copyTo(QString(), m_config.data());
269  m_config->markAsClean();
270  }
271  }
272 }
273 
274 void KLocalePrivate::getLanguagesFromVariable(QStringList &list, const char *variable, bool isLanguageList)
275 {
276  QByteArray var(qgetenv(variable));
277  if (!var.isEmpty()) {
278  QString value = QFile::decodeName(var);
279  if (isLanguageList) {
280  list += value.split(QLatin1Char(':'));
281  } else {
282  // Process the value to create possible combinations.
283  QString lang, ctry, modf, cset;
284  KLocale::splitLocale(value, lang, ctry, modf, cset);
285 
286  if (!ctry.isEmpty() && !modf.isEmpty()) {
287  list += lang + QLatin1Char('_') + ctry + QLatin1Char('@') + modf;
288  }
289  // NOTE: The priority is tricky in case both ctry and modf are present.
290  // Should really [email protected] be of higher priority than lang_ctry?
291  // For at least one case (Serbian language), it is better this way.
292  if (!modf.isEmpty()) {
293  list += lang + QLatin1Char('@') + modf;
294  }
295  if (!ctry.isEmpty()) {
296  list += lang + QLatin1Char('_') + ctry;
297  }
298  list += lang;
299  }
300  }
301 }
302 
303 // init the country at construction only, will ensure we always have a country set
304 void KLocalePrivate::initCountry(const QString &country, const QString &configCountry)
305 {
306  // Cache the valid countries list and add the default C as it is valid to use
307  QStringList validCountries = allCountriesList();
308  validCountries.append(defaultCountry());
309 
310  // First check if the constructor passed in a value and if so if it is valid
311  QString putativeCountry = country;
312 
313  if (putativeCountry.isEmpty() || !validCountries.contains(putativeCountry, Qt::CaseInsensitive)) {
314 
315  // If the requested country is not valid, try the country as set in the config:
316  putativeCountry = configCountry;
317 
318  if (putativeCountry.isEmpty() || !validCountries.contains(putativeCountry, Qt::CaseInsensitive)) {
319 
320  // If the config country is not valid try the current host system country
321  putativeCountry = systemCountry();
322 
323  if (putativeCountry.isEmpty() || !validCountries.contains(putativeCountry, Qt::CaseInsensitive)) {
324  // Only if no other option, resort to the default C
325  putativeCountry = defaultCountry();
326  }
327  }
328  }
329 
330  // Always save as lowercase, unless it's C when we want it uppercase
331  if (putativeCountry.toLower() == defaultCountry().toLower()) {
332  m_country = defaultCountry();
333  } else {
334  m_country = putativeCountry.toLower();
335  }
336 }
337 
338 QString KLocalePrivate::systemCountry() const
339 {
340  // Use QLocale for now as it supposedly provides a sensible default most times,
341  // e.g. if locale is only "de" it is assumed to mean country of "DE"
342  QString systemCountry, s1, s2, s3;
343  splitLocale(QLocale::system().name(), s1, systemCountry, s2, s3);
344  return systemCountry.toLower();
345 }
346 
347 void KLocalePrivate::initLanguageList(const QString &language, const QString &configLanguages,
348  bool useEnvironmentVariables)
349 {
350  m_language = language;
351 
352  // Collect possible languages by decreasing priority.
353  // The priority is as follows:
354  // - the internally set language, if any
355  // - KDE_LANG environment variable (can be a list)
356  // - KDE configuration (can be a list)
357  // - environment variables considered by gettext(3)
358  // The environment variables are not considered if useEnvironmentVariables is false.
359  QStringList list;
360  if (!m_language.isEmpty()) {
361  list += m_language;
362  }
363 
364  // If the Locale object was created with a specific config file, then do not use the
365  // environmental variables. If the locale object was created with the global config, then
366  // do use the environmental variables.
367  if (useEnvironmentVariables) {
368  // KDE_LANG contains list of language codes, not locale string.
369  getLanguagesFromVariable(list, "KDE_LANG", true);
370  }
371 
372  if (!configLanguages.isEmpty()) {
373  list += configLanguages.split(QLatin1Char(':'));
374  }
375 
376  if (useEnvironmentVariables) {
377  // Collect languages by same order of priority as for gettext(3).
378  // LANGUAGE contains list of language codes, not locale string.
379  getLanguagesFromVariable(list, "LANGUAGE", true);
380  getLanguagesFromVariable(list, "LC_ALL");
381  getLanguagesFromVariable(list, "LC_MESSAGES");
382  getLanguagesFromVariable(list, "LANG");
383  }
384 
385  // fall back to the system language
386  list += systemLanguageList();
387 
388  // Send the list to filter for really present languages on the system.
389  setLanguage(list);
390 }
391 
392 QStringList KLocalePrivate::systemLanguageList() const
393 {
394  return QStringList();
395 }
396 
397 void KLocalePrivate::initFormat()
398 {
399  KConfigGroup cg(config(), "Locale");
400 
401  KConfig entryFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("kf5/locale/countries/") + QString::fromLatin1("%1/country.desktop").arg(m_country)));
402  entryFile.setLocale(m_language);
403  KConfigGroup entry(&entryFile, "KCM Locale");
404 
405  //One-time conversion in 4.4 from FracDigits to DecimalPlaces and MonetaryDecimalPlaces
406  //If user has personal setting for FracDigits then use it for both Decimal Places
407  //TODO: Possible to do with kconf_update
408  if (cg.hasKey("FracDigits")) {
409  QString fracDigits = cg.readEntry("FracDigits", "");
410  if (!fracDigits.isEmpty()) {
411  cg.writeEntry("DecimalPlaces", fracDigits);
412  cg.writeEntry("MonetaryDecimalPlaces", fracDigits);
413  }
414  cg.deleteEntry("FracDigits");
415  cg.config()->sync();
416  }
417 
418  // Numeric
419 #define readConfigEntry(key, default, save) \
420  save = entry.readEntry(key, default); \
421  save = cg.readEntry(key, save);
422 
423 #define readConfigNumEntry(key, default, save, type) \
424  save = (type)entry.readEntry(key, int(default)); \
425  save = (type)cg.readEntry(key, int(save));
426 
427  // Country settings
428  readConfigEntry("CountryDivisionCode", QString(), m_countryDivisionCode);
429 
430  // Numeric formats
431  readConfigNumEntry("DecimalPlaces", 2, m_decimalPlaces, int);
432 
433  readConfigEntry("DecimalSymbol", ".", m_decimalSymbol);
434  readConfigEntry("ThousandsSeparator", ",", m_thousandsSeparator);
435  m_thousandsSeparator.remove(QString::fromLatin1("$0"));
436  QString digitGroupFormat;
437  readConfigEntry("DigitGroupFormat", "3", digitGroupFormat);
438  m_numericDigitGrouping = digitGroupFormatToList(digitGroupFormat);
439 
440  readConfigEntry("PositiveSign", "", m_positiveSign);
441  readConfigEntry("NegativeSign", "-", m_negativeSign);
442 
443  readConfigNumEntry("DigitSet", KLocale::ArabicDigits, m_digitSet, KLocale::DigitSet);
444  // FIXME: Temporary until full language-sensitivity implemented.
445  readConfigEntry("LanguageSensitiveDigits", true, m_languageSensitiveDigits);
446 
447  // Currency
448  readConfigEntry("CurrencyCode", "USD", m_currencyCode);
449  initCurrency();
450  readConfigEntry("CurrencySymbol", m_currency->defaultSymbol(), m_currencySymbol);
451  readConfigEntry("CurrencyCodesInUse", QStringList(m_currencyCode), m_currencyCodeList);
452 
453  // Monetary formats
454  readConfigNumEntry("MonetaryDecimalPlaces", m_currency->decimalPlaces(), m_monetaryDecimalPlaces, int);
455 
456  readConfigEntry("MonetaryDecimalSymbol", ".", m_monetaryDecimalSymbol);
457  readConfigEntry("MonetaryThousandsSeparator", ",", m_monetaryThousandsSeparator);
458  m_monetaryThousandsSeparator.remove(QString::fromLatin1("$0"));
459  readConfigEntry("MonetaryDigitGroupFormat", "3", digitGroupFormat);
460  m_monetaryDigitGrouping = digitGroupFormatToList(digitGroupFormat);
461 
462  readConfigEntry("PositivePrefixCurrencySymbol", true, m_positivePrefixCurrencySymbol);
463  readConfigEntry("NegativePrefixCurrencySymbol", true, m_negativePrefixCurrencySymbol);
464  readConfigNumEntry("PositiveMonetarySignPosition", KLocale::BeforeQuantityMoney,
465  m_positiveMonetarySignPosition, KLocale::SignPosition);
466  readConfigNumEntry("NegativeMonetarySignPosition", KLocale::ParensAround,
467  m_negativeMonetarySignPosition, KLocale::SignPosition);
468 
469  readConfigNumEntry("MonetaryDigitSet", KLocale::ArabicDigits,
470  m_monetaryDigitSet, KLocale::DigitSet);
471  readConfigNumEntry("BinaryUnitDialect", KLocale::IECBinaryDialect,
472  m_binaryUnitDialect, KLocale::BinaryUnitDialect);
473 
474  // Date and time
475  readConfigEntry("TimeFormat", "%H:%M:%S", m_timeFormat);
476  readConfigEntry("DateFormat", "%A %d %B %Y", m_dateFormat);
477  readConfigEntry("DateFormatShort", "%Y-%m-%d", m_dateFormatShort);
478  readConfigNumEntry("WeekStartDay", 1, m_weekStartDay, int); //default to Monday
479  readConfigNumEntry("WorkingWeekStartDay", 1, m_workingWeekStartDay, int); //default to Monday
480  readConfigNumEntry("WorkingWeekEndDay", 5, m_workingWeekEndDay, int); //default to Friday
481  readConfigNumEntry("WeekDayOfPray", 7, m_weekDayOfPray, int); //default to Sunday
482  readConfigNumEntry("DateTimeDigitSet", KLocale::ArabicDigits,
483  m_dateTimeDigitSet, KLocale::DigitSet);
484  readConfigNumEntry("WeekNumberSystem", KLocale::IsoWeekNumber,
485  m_weekNumberSystem, KLocale::WeekNumberSystem);
486 
487  // other
488 #ifndef QT_NO_PRINTER
489  readConfigNumEntry("PageSize", QPrinter::A4, m_pageSize, QPrinter::PageSize);
490 #endif
491  readConfigNumEntry("MeasureSystem", KLocale::Metric, m_measureSystem, KLocale::MeasureSystem);
492  QString calendarType;
493  readConfigEntry("CalendarSystem", "gregorian", calendarType);
494  setCalendar(calendarType);
495 
496  //Grammatical
497  //Precedence here is l10n / i18n / config file
498  KConfig langCfg(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("locale/") + QString::fromLatin1("%1/kf5_entry.desktop").arg(m_language)));
499  KConfigGroup lang(&langCfg, "KCM Locale");
500 #define read3ConfigBoolEntry(key, default, save) \
501  save = entry.readEntry(key, default); \
502  save = lang.readEntry(key, save); \
503  save = cg.readEntry(key, save);
504 
505  read3ConfigBoolEntry("NounDeclension", false, m_nounDeclension);
506  read3ConfigBoolEntry("DateMonthNamePossessive", false, m_dateMonthNamePossessive);
507 
508  initDayPeriods(cg);
509 }
510 
511 void KLocalePrivate::initDayPeriods(const KConfigGroup &cg)
512 {
513  // Prefer any l10n file value for country/language,
514  // otherwise default to language only value which will be filled in later when i18n available
515 
516  //Day Period are stored in config as one QStringList entry per Day Period
517  //PeriodCode,LongName,ShortName,NarrowName,StartTime,EndTime,Offset,OffsetIfZero
518  //where start and end time are in the format HH:MM:SS.MMM
519 
520  m_dayPeriods.clear();
521  QString periodKey = QString::fromLatin1("DayPeriod1");
522  int i = 1;
523  while (cg.hasKey(periodKey)) {
524  QStringList period = cg.readEntry(periodKey, QStringList());
525  if (period.count() == 8) {
526  m_dayPeriods.append(KDayPeriod(period[0], period[1], period[2], period[3],
527  QTime::fromString(period[4], QString::fromLatin1("HH:mm:ss.zzz")),
528  QTime::fromString(period[5], QString::fromLatin1("HH:mm:ss.zzz")),
529  period[6].toInt(), period[7].toInt()));
530  }
531  i = i + 1;
532  periodKey = QString::fromLatin1("DayPeriod%1").arg(i);
533  }
534 }
535 
536 bool KLocalePrivate::setCountry(const QString &country, KConfig *newConfig)
537 {
538  // Cache the valid countries list and add the default C as it is valid to use
539  QStringList validCountries = allCountriesList();
540  validCountries.append(defaultCountry());
541 
542  QString putativeCountry = country;
543 
544  if (putativeCountry.isEmpty()) {
545  // An empty string means to use the system country
546  putativeCountry = systemCountry();
547  if (putativeCountry.isEmpty() || !validCountries.contains(putativeCountry, Qt::CaseInsensitive)) {
548  // If the system country is not valid, use the default
549  putativeCountry = defaultCountry();
550  }
551  } else if (!validCountries.contains(putativeCountry, Qt::CaseInsensitive)) {
552  return false;
553  }
554 
555  // Always save as lowercase, unless it's C when we want it uppercase
556  if (putativeCountry.toLower() == defaultCountry().toLower()) {
557  m_country = defaultCountry();
558  } else {
559  m_country = putativeCountry.toLower();
560  }
561 
562  // Get rid of the old config, start again with the new
563  m_config = KSharedConfig::Ptr();
564  initConfig(newConfig);
565 
566  // Init all the settings
567  initFormat();
568 
569  return true;
570 }
571 
572 bool KLocalePrivate::setCountryDivisionCode(const QString &countryDivisionCode)
573 {
574  m_countryDivisionCode = countryDivisionCode;
575  return true;
576 }
577 
578 bool KLocalePrivate::setLanguage(const QString &language, KConfig *config)
579 {
580  QMutexLocker lock(s_kLocaleMutex());
581  m_languageList.removeAll(language);
582  m_languageList.prepend(language); // let us consider this language to be the most important one
583 
584  m_language = language; // remember main language for shortcut evaluation
585 
586  // Get rid of the old config, start again with the new
587  m_config = KSharedConfig::Ptr();
588  initConfig(config);
589 
590  // Init the new format settings
591  initFormat();
592 
593  // Maybe the mo-files for this language are empty, but in principle we can speak all languages
594  return true;
595 }
596 
597 // KDE5 Unlike the other setLanguage call this does not reparse the config so the localized config
598 // settings for the new primary language will _not_ be loaded. In KDE5 always keep the original
599 // config so this can be reparsed when required.
600 bool KLocalePrivate::setLanguage(const QStringList &languages)
601 {
602  QMutexLocker lock(s_kLocaleMutex());
603  // This list might contain
604  // 1) some empty strings that we have to eliminate
605  // 2) duplicate entries like in de:fr:de, where we have to keep the first occurrence of a
606  // language in order to preserve the order of precenence of the user
607  // 3) languages into which the application is not translated. For those languages we should not
608  // even load kdelibs.mo or kio.po. these languages have to be dropped. Otherwise we get
609  // strange side effects, e.g. with Hebrew: the right/left switch for languages that write
610  // from right to left (like Hebrew or Arabic) is set in kdelibs.mo. If you only have
611  // kdelibs.mo but nothing from appname.mo, you get a mostly English app with layout from
612  // right to left. That was considered to be a bug by the Hebrew translators.
613  QStringList list;
614  foreach (const QString &language, languages) {
615  if (!language.isEmpty() && !list.contains(language) && KLocalizedString::isApplicationTranslatedInto(language)) {
616  list.append(language);
617  }
618  }
619 
620  if (!list.contains(KLocale::defaultLanguage())) {
621  // English should always be added as final possibility; this is important
622  // for proper initialization of message text post-processors which are
623  // needed for English too, like semantic to visual formatting, etc.
625  }
626 
627  m_language = list.first(); // keep this for shortcut evaluations
628 
629  m_languageList = list; // keep this new list of languages to use
630 
631  return true; // we found something. Maybe it's only English, but we found something
632 }
633 
634 void KLocalePrivate::initCurrency()
635 {
636  if (m_currencyCode.isEmpty() || !KCurrencyCode::isValid(m_currencyCode)) {
637  m_currencyCode = KLocale::defaultCurrencyCode();
638  }
639 
640  if (!m_currency || m_currencyCode != m_currency->isoCurrencyCode() || !m_currency->isValid()) {
641  delete m_currency;
642  m_currency = new KCurrencyCode(m_currencyCode, m_language);
643  }
644 }
645 
646 void KLocalePrivate::setCurrencyCode(const QString &newCurrencyCode)
647 {
648  if (!newCurrencyCode.isEmpty() && newCurrencyCode != m_currency->isoCurrencyCode() &&
649  KCurrencyCode::isValid(newCurrencyCode)) {
650  m_currencyCode = newCurrencyCode;
651  initCurrency();
652  }
653 }
654 
655 void KLocalePrivate::splitLocale(const QString &aLocale, QString &language, QString &country,
656  QString &modifier, QString &charset)
657 {
658  QString locale = aLocale;
659 
660  language.clear();
661  country.clear();
662  modifier.clear();
663  charset.clear();
664 
665  // In case there are several concatenated locale specifications,
666  // truncate all but first.
667  int f = locale.indexOf(QLatin1Char(':'));
668  if (f >= 0) {
669  locale.truncate(f);
670  }
671 
672  f = locale.indexOf(QLatin1Char('.'));
673  if (f >= 0) {
674  charset = locale.mid(f + 1);
675  locale.truncate(f);
676  }
677 
678  f = locale.indexOf(QLatin1Char('@'));
679  if (f >= 0) {
680  modifier = locale.mid(f + 1);
681  locale.truncate(f);
682  }
683 
684  f = locale.indexOf(QLatin1Char('_'));
685  if (f >= 0) {
686  country = locale.mid(f + 1);
687  locale.truncate(f);
688  }
689 
690  language = locale;
691 }
692 
693 QString KLocalePrivate::language() const
694 {
695  return m_language;
696 }
697 
698 QString KLocalePrivate::country() const
699 {
700  return m_country;
701 }
702 
703 QString KLocalePrivate::countryDivisionCode() const
704 {
705  if (m_countryDivisionCode.isEmpty()) {
706  return country().toUpper();
707  } else {
708  return m_countryDivisionCode;
709  }
710 }
711 
712 KCurrencyCode *KLocalePrivate::currency()
713 {
714  if (!m_currency) {
715  initCurrency();
716  }
717  return m_currency;
718 }
719 
720 QString KLocalePrivate::currencyCode() const
721 {
722  return m_currencyCode;
723 }
724 
725 QList<KLocale::DigitSet> KLocalePrivate::allDigitSetsList() const
726 {
727  QList<KLocale::DigitSet> digitSets;
728  digitSets.append(KLocale::ArabicDigits);
730  digitSets.append(KLocale::BengaliDigits);
733  digitSets.append(KLocale::GujaratiDigits);
734  digitSets.append(KLocale::GurmukhiDigits);
735  digitSets.append(KLocale::KannadaDigits);
736  digitSets.append(KLocale::KhmerDigits);
737  digitSets.append(KLocale::MalayalamDigits);
738  digitSets.append(KLocale::OriyaDigits);
739  digitSets.append(KLocale::TamilDigits);
740  digitSets.append(KLocale::TeluguDigits);
741  digitSets.append(KLocale::ThaiDigits);
742  std::sort(digitSets.begin(), digitSets.end());
743  return digitSets;
744 }
745 
746 QString KLocalePrivate::digitSetString(KLocale::DigitSet digitSet)
747 {
748  switch (digitSet) {
750  return QString::fromUtf8("٠١٢٣٤٥٦٧٨٩");
752  return QString::fromUtf8("০১২৩৪৫৬৭৮৯");
754  return QString::fromUtf8("०१२३४५६७८९");
756  return QString::fromUtf8("۰۱۲۳۴۵۶۷۸۹");
758  return QString::fromUtf8("૦૧૨૩૪૫૬૭૮૯");
760  return QString::fromUtf8("੦੧੨੩੪੫੬੭੮੯");
762  return QString::fromUtf8("೦೧೨೩೪೫೬೭೮೯");
764  return QString::fromUtf8("០១២៣៤៥៦៧៨៩");
766  return QString::fromUtf8("൦൧൨൩൪൫൬൭൮൯");
768  return QString::fromUtf8("୦୧୨୩୪୫୬୭୮୯");
770  return QString::fromUtf8("௦௧௨௩௪௫௬௭௮");
772  return QString::fromUtf8("౦౧౨౩౪౫౬౭౯");
773  case KLocale::ThaiDigits:
774  return QString::fromUtf8("๐๑๒๓๔๕๖๗๘๙");
775  default:
776  return QString::fromUtf8("0123456789");
777  }
778 }
779 
780 QString KLocalePrivate::digitSetToName(KLocale::DigitSet digitSet, bool withDigits) const
781 {
782  QString name;
783  switch (digitSet) {
785  name = i18nc("digit set", "Arabic-Indic");
786  break;
788  name = i18nc("digit set", "Bengali");
789  break;
791  name = i18nc("digit set", "Devanagari");
792  break;
794  name = i18nc("digit set", "Eastern Arabic-Indic");
795  break;
797  name = i18nc("digit set", "Gujarati");
798  break;
800  name = i18nc("digit set", "Gurmukhi");
801  break;
803  name = i18nc("digit set", "Kannada");
804  break;
806  name = i18nc("digit set", "Khmer");
807  break;
809  name = i18nc("digit set", "Malayalam");
810  break;
812  name = i18nc("digit set", "Oriya");
813  break;
815  name = i18nc("digit set", "Tamil");
816  break;
818  name = i18nc("digit set", "Telugu");
819  break;
820  case KLocale::ThaiDigits:
821  name = i18nc("digit set", "Thai");
822  break;
823  default:
824  name = i18nc("digit set", "Arabic");
825  }
826  if (withDigits) {
827  QString digits = digitSetString(digitSet);
828  QString nameWithDigits = i18nc("name of digit set with digit string, "
829  "e.g. 'Arabic (0123456789)'", "%1 (%2)", name, digits);
830  return nameWithDigits;
831  } else {
832  return name;
833  }
834 }
835 
836 QString KLocalePrivate::convertDigits(const QString &str, KLocale::DigitSet digitSet, bool ignoreContext) const
837 {
838  if (!ignoreContext) {
839  // Fall back to Western Arabic digits if requested digit set
840  // is not appropriate for current application language.
841  // FIXME: Temporary until full language-sensitivity implemented.
842  KLocaleStaticData *s = staticData();
843  if (m_languageSensitiveDigits && !s->languagesUsingDigitSet[digitSet].contains(m_language)) {
844  digitSet = KLocale::ArabicDigits;
845  }
846  }
847 
848  QString nstr;
849  QString digitDraw = digitSetString(digitSet);
850  foreach (const QChar &c, str) {
851  if (c.isDigit()) {
852  nstr += digitDraw[c.digitValue()];
853  } else {
854  nstr += c;
855  }
856  }
857  return nstr;
858 }
859 
860 QString KLocalePrivate::toArabicDigits(const QString &str)
861 {
862  QString nstr;
863  foreach (const QChar &c, str) {
864  if (c.isDigit()) {
865  nstr += QChar('0' + c.digitValue());
866  } else {
867  nstr += c;
868  }
869  }
870  return nstr;
871 }
872 
873 bool KLocalePrivate::nounDeclension() const
874 {
875  return m_nounDeclension;
876 }
877 
878 bool KLocalePrivate::dateMonthNamePossessive() const
879 {
880  return m_dateMonthNamePossessive;
881 }
882 
883 int KLocalePrivate::weekStartDay() const
884 {
885  return m_weekStartDay;
886 }
887 
888 int KLocalePrivate::workingWeekStartDay() const
889 {
890  return m_workingWeekStartDay;
891 }
892 
893 int KLocalePrivate::workingWeekEndDay() const
894 {
895  return m_workingWeekEndDay;
896 }
897 
898 int KLocalePrivate::weekDayOfPray() const
899 {
900  return m_weekDayOfPray;
901 }
902 
903 int KLocalePrivate::decimalPlaces() const
904 {
905  return m_decimalPlaces;
906 }
907 
908 QString KLocalePrivate::decimalSymbol() const
909 {
910  return m_decimalSymbol;
911 }
912 
913 QString KLocalePrivate::thousandsSeparator() const
914 {
915  return m_thousandsSeparator;
916 }
917 
918 QList<int> KLocalePrivate::numericDigitGrouping() const
919 {
920  return m_numericDigitGrouping;
921 }
922 
923 QString KLocalePrivate::currencySymbol() const
924 {
925  return m_currencySymbol;
926 }
927 
928 QString KLocalePrivate::monetaryDecimalSymbol() const
929 {
930  return m_monetaryDecimalSymbol;
931 }
932 
933 QString KLocalePrivate::monetaryThousandsSeparator() const
934 {
935  return m_monetaryThousandsSeparator;
936 }
937 
938 QList<int> KLocalePrivate::monetaryDigitGrouping() const
939 {
940  return m_monetaryDigitGrouping;
941 }
942 
943 QString KLocalePrivate::positiveSign() const
944 {
945  return m_positiveSign;
946 }
947 
948 QString KLocalePrivate::negativeSign() const
949 {
950  return m_negativeSign;
951 }
952 
953 /* Just copy to keep the diff looking clean, delete later
954 int KLocale::fracDigits() const
955 {
956  return monetaryDecimalPlaces();
957 }
958 */
959 
960 int KLocalePrivate::monetaryDecimalPlaces() const
961 {
962  return m_monetaryDecimalPlaces;
963 }
964 
965 bool KLocalePrivate::positivePrefixCurrencySymbol() const
966 {
967  return m_positivePrefixCurrencySymbol;
968 }
969 
970 bool KLocalePrivate::negativePrefixCurrencySymbol() const
971 {
972  return m_negativePrefixCurrencySymbol;
973 }
974 
975 KLocale::SignPosition KLocalePrivate::positiveMonetarySignPosition() const
976 {
977  return m_positiveMonetarySignPosition;
978 }
979 
980 KLocale::SignPosition KLocalePrivate::negativeMonetarySignPosition() const
981 {
982  return m_negativeMonetarySignPosition;
983 }
984 
985 static inline void put_it_in(QChar *buffer, int &index, const QString &s)
986 {
987  for (int l = 0; l < s.length(); l++) {
988  buffer[index++] = s.at(l);
989  }
990 }
991 
992 static inline void put_it_in(QChar *buffer, int &index, int number)
993 {
994  buffer[index++] = number / 10 + '0';
995  buffer[index++] = number % 10 + '0';
996 }
997 
998 // Convert POSIX Digit Group Format string into a Qlist<int>, e.g. "3;2" converts to (3,2)
999 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;
1118  res.prepend(sign);
1119  break;
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) {
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 
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 
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 
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,
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 {
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 {
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,
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()) {
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;
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;
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")) {
2763  } else if (calendarType == QLatin1String("gregorian")) {
2764  return KLocale::QDateCalendar;
2765  } else if (calendarType == QLatin1String("gregorian-proleptic")) {
2767  } else if (calendarType == QLatin1String("hebrew")) {
2768  return KLocale::HebrewCalendar;
2769  } else if (calendarType == QLatin1String("hijri")) {
2771  } else if (calendarType == QLatin1String("indian-national")) {
2773  } else if (calendarType == QLatin1String("jalali")) {
2774  return KLocale::JalaliCalendar;
2775  } else if (calendarType == QLatin1String("japanese")) {
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) {
2792  return QLatin1String("gregorian");
2794  return QLatin1String("coptic");
2796  return QLatin1String("ethiopian");
2798  return QLatin1String("hebrew");
2800  return QLatin1String("hijri");
2802  return QLatin1String("indian-national");
2804  return QLatin1String("jalali");
2806  return QLatin1String("japanese");
2808  return QLatin1String("julian");
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 }
Include a time zone string.
Definition: klocale.h:826
Suffix the currency symbol with the sign, e.g.
Definition: klocale.h:168
qint64 daysTo(const QDate &d) const const
int minute() const const
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
Read/format time string as duration.
Definition: klocale.h:886
Ethiopian Calendar, aka Ethiopic Calendar.
Definition: klocale.h:652
QString & append(QChar ch)
const QChar * constData() const const
QHash::iterator insert(const Key &key, const T &value)
QString toUpper() const const
void truncate(int position)
void markAsClean() override
QString name(const QVariant &location)
Put parantheses around the quantity, e.g.
Definition: klocale.h:152
ushort lowSurrogate(uint ucs4)
QString escape(const QString &plain)
Date/times with associated time zone.
QStringList locateAll(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options)
Japanese Calendar, Gregorian calculation using Japanese Era (Nengô)
Definition: klocale.h:664
Only accept a time string with seconds. Default (no flag set)
Definition: klocale.h:1156
static QString defaultCurrencyCode()
Definition: klocale.cpp:551
Process time in a strict manner, ie.
Definition: klocale.h:1184
Spec timeSpec() const
Returns the time specification of the date/time, i.e.
Definition: kdatetime.cpp:914
QString & prepend(QChar ch)
MeasureSystem
The Metric system will give you information in mm, while the Imperial system will give you informatio...
Definition: klocale.h:1636
௦௧௨௩௪௫௬௭௮ (Tamil)
Definition: klocale.h:189
bool isDigit() const const
const T & at(int i) const const
Locale Short date format, e.g.
Definition: klocale.h:784
int size() const const
bool contains(const QString &str, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString simplified() const const
bool isValid() const const
DateFormat
Format for date string.
Definition: klocale.h:783
QString formatLocaleTime(const QTime &pTime, TimeFormatOptions options=KLocale::TimeDefault) const
Definition: klocale.cpp:361
੦੧੨੩੪੫੬੭੮੯ (Punjabi)
Definition: klocale.h:184
virtual QString weekDayName(int weekDay, WeekDayNameFormat format=LongDayName) const =0
Gets specific calendar type week day name.
int digitValue() const const
QString toString(const QString &format) const
Returns the date/time as a string.
Definition: kdatetime.cpp:1482
QTime time() const const
void writeEntry(const QString &key, const QVariant &value, WriteConfigFlags pFlags=Normal)
Prefix the currency symbol with the sign, e.g.
Definition: klocale.h:164
Suffix the quanitity with the sign, e.g.
Definition: klocale.h:160
Coptic Calendar as used Coptic Church and some parts of Egypt.
Definition: klocale.h:651
QTime fromString(const QString &string, Qt::DateFormat format)
bool isDateOnly() const
Returns whether the instance represents a date/time or a date-only value.
Definition: kdatetime.cpp:877
KDE Default, KiB, MiB, etc. 2^(10*n)
Definition: klocale.h:525
๐๑๒๓๔๕๖๗๘๙ (Thai)
Definition: klocale.h:191
bool exists() const const
QString & remove(int position, int n)
Hebrew Calendar, aka Jewish Calendar.
Definition: klocale.h:654
BinarySizeUnits
These binary units are used in KDE by the formatByteSize() functions.
Definition: klocale.h:488
double toDouble(bool *ok) const const
൦൧൨൩൪൫൬൭൮൯ (Malayalam)
Definition: klocale.h:187
QTextCodec * codecForLocale()
int second() const const
Read/format time string as duration.
Definition: klocale.h:881
KCalendarSystem abstract base class, provides support for local Calendar Systems in KDE...
bool isNull() const const
bool isHighSurrogate() const const
QLocale system()
void clear()
void resize(int size)
void deleteEntry(const QString &pKey, WriteConfigFlags pFlags=Normal)
Indian National Calendar, not the Lunar Calendar.
Definition: klocale.h:659
KDE 3.5 default, KB, MB, etc. 2^(10*n)
Definition: klocale.h:526
KIOCORE_EXPORT CopyJob * copy(const QUrl &src, const QUrl &dest, JobFlags flags=DefaultFlags)
const KCalendarSystem * calendar() const
Returns a pointer to the calendar system object.
Definition: klocale.cpp:626
QString number(int n, int base)
int count(const T &value) const const
bool sync() override
Auto-choose a unit such that the result is in the range [0, 1000 or 1024)
Definition: klocale.h:490
void append(const T &value)
uint surrogateToUcs4(ushort high, ushort low)
QString fromUtf8(const char *str, int size)
bool isSpace() const const
ReadTimeFlags
Flags for the old version of readTime()
Definition: klocale.h:1155
QString & insert(int position, QChar ch)
CalendarSystem
Definition: klocale.h:646
Minguo Calendar, aka ROC, Republic of China or Taiwanese.
Definition: klocale.h:667
bool setLocale(const QString &aLocale)
ISO Week Number.
Definition: klocale.h:683
qint64 secsTo(const KDateTime &other) const
Returns the number of seconds from this date/time to the other date/time.
Definition: kdatetime.cpp:1218
bool isLowSurrogate() const const
QString i18nc(const char *context, const char *text, const TYPE &arg...)
Jalali Calendar, aka Persian or Iranian, also used in Afghanistan.
Definition: klocale.h:661
CaseInsensitive
int toInt(bool *ok, int base) const const
bool isEmpty() const const
bool isEmpty() const const
QString trimmed() const const
const char * constData() const const
QDate date() const
Returns the date part of the date/time.
Definition: kdatetime.cpp:901
YiB/YB/YB 2^80/10^24 bytes.
Definition: klocale.h:501
QDateTime dateTime() const
Returns the date/time component of the instance, ignoring the time zone.
Definition: kdatetime.cpp:909
WeekNumberSystem
Definition: klocale.h:681
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
SI Units, kB, MB, etc. 10^(3*n)
Definition: klocale.h:527
Read/format time string without am/pm suffix but.
Definition: klocale.h:877
୦୧୨୩୪୫୬୭୮୯ (Oriya)
Definition: klocale.h:188
a clock time which ignores time zones and simply uses whatever the local system clock says the time i...
Definition: kdatetime.h:179
T & first()
ReadDateFlags
Flags for readDate()
Definition: klocale.h:1108
bool isPunct() const const
KConfig * copyTo(const QString &file, KConfig *config=nullptr) const
౦౧౨౩౪౫౬౭౯ (Telugu)
Definition: klocale.h:190
KDE Default, proleptic Gregorian Calendar as used by QDate.
Definition: klocale.h:647
QString right(int n) const const
০১২৩৪৫৬৭৮৯ (Bengali and Assamese)
Definition: klocale.h:182
ushort unicode() const const
int hour() const const
static KDateTime currentDateTime(const Spec &spec)
Returns the current date and time, as reported by the system clock, expressed in a given time specifi...
Definition: kdatetime.cpp:1312
QList::iterator end()
QString toLower() const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
A class representing a date and time with an associated time zone.
Definition: kdatetime.h:148
virtual int mibEnum() const const =0
Used if no specific preference.
Definition: klocale.h:524
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
QString i18ncp(const char *context, const char *singular, const char *plural, const TYPE &arg...)
bool hasKey(const QString &key) const
Short text format, e.g.
Definition: klocale.h:776
Default formatting using seconds and the format.
Definition: klocale.h:874
QString i18n(const char *text, const TYPE &arg...)
KConfigGroup group(const QString &group)
QString & replace(int position, int n, QChar after)
static KCalendarSystem * create(KLocale::CalendarSystem calendarSystem, const KLocale *locale)
const QList< QKeySequence > & end()
a local time which has a fixed offset from UTC.
Definition: kdatetime.h:161
KConfig * config()
Same as ShortDate for dates a week or more ago.
Definition: klocale.h:786
QDateTime currentDateTime()
QByteArray toLatin1() const const
QString mid(int position, int n) const const
QDate date() const const
a UTC time.
Definition: kdatetime.h:160
KLocale provides support for language and country specific stuff.
Definition: klocale.h:75
bool isValid() const
Return if the currency object loaded/initialised correctly.
SignPosition
Various positions for where to place the positive or negative sign when they are related to a monetar...
Definition: klocale.h:148
qint64 secsTo(const QDateTime &other) const const
QStringList entryList(QDir::Filters filters, QDir::SortFlags sort) const const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
const QChar at(int position) const const
۰۱۲۳۴۵۶۷۸۹ (Persian and Urdu)
Definition: klocale.h:180
QTextCodec * codecForName(const QByteArray &name)
QTextCodec * codecForMib(int mib)
TimeFormatOption
Definition: klocale.h:873
#define I18N_NOOP2_NOSTRIP(context, text)
int length() const const
void reserve(int size)
char * data()
೦೧೨೩೪೫೬೭೮೯ (Kannada)
Definition: klocale.h:185
QString left(int n) const const
QString fromLatin1(const char *str, int size)
void sort(Qt::CaseSensitivity cs)
Prefix the quantity with the sign, e.g.
Definition: klocale.h:156
QString formatDate(const QDate &date, DateFormat format=LongDate) const
Returns a string formatted to the current locale&#39;s conventions regarding dates.
Definition: klocale.cpp:291
ushort highSurrogate(uint ucs4)
Include the seconds value.
Definition: klocale.h:827
Locale Long date format, e.g.
Definition: klocale.h:785
BinaryUnitDialect
This enum chooses what dialect is used for binary units.
Definition: klocale.h:523
DigitSet
Definition: klocale.h:176
Same as LongDate for dates a week or more ago.
Definition: klocale.h:789
static void splitLocale(const QString &locale, QString &language, QString &country, QString &modifier, QString &charset)
Parses locale string into distinct parts.
Definition: klocale.cpp:94
0123456789 (European and some Asian languages and western Arabic dialects)
Definition: klocale.h:177
Metric system (used e.g. in Europe)
Definition: klocale.h:1637
០១២៣៤៥៦៧៨៩ (Khmer)
Definition: klocale.h:186
QChar * data()
LocaleWrapper locale()
Returns the global locale object.
Definition: kglobal.cpp:117
static bool isApplicationTranslatedInto(const QString &language)
Thai Calendar, aka Buddhist or Thai Buddhist.
Definition: klocale.h:668
T readEntry(const QString &key, const T &aDefault) const
૦૧૨૩૪૫૬૭૮૯ (Gujarati)
Definition: klocale.h:183
٠١٢٣٤٥٦٧٨٩ (eastern Arabic dialects)
Definition: klocale.h:179
Julian Calendar, as used in Orthodox Churches.
Definition: klocale.h:666
०१२३४५६७८९ (Hindi)
Definition: klocale.h:181
SpecType timeType() const
Returns the time specification type of the date/time, i.e.
Definition: kdatetime.cpp:918
QList::iterator begin()
void squeeze()
static QString defaultLanguage()
Returns the name of the internal language.
Definition: klocale.cpp:541
QString decodeName(const QByteArray &localFileName)
Islamic Civil Calendar, aka Hijri, not the Lunar Calendar.
Definition: klocale.h:657
a time in a specified time zone.
Definition: kdatetime.h:162
QString locate(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options)
Exclude the seconds part of the time from display.
Definition: klocale.h:876
QByteArray toUtf8() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Sat Oct 24 2020 23:03:51 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.