1/* This file is part of the KDE libraries
2 Copyright (c) 1997,2001 Stephan Kulow <coolo@kde.org>
3 Copyright (c) 1999 Preston Brown <pbrown@kde.org>
4 Copyright (c) 1999-2002 Hans Petter Bieker <bieker@kde.org>
5 Copyright (c) 2002 Lukas Tinkl <lukas@kde.org>
6 Copyright (C) 2007 Bernhard Loos <nhuh.put@web.de>
7 Copyright (C) 2009, 2010 John Layt <john@layt.net>
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#ifdef HAVE_SYS_TIME_H
33#include <sys/time.h>
34#endif
35#ifdef HAVE_TIME_H
36#include <time.h>
37#endif
38#if HAVE_LANGINFO_H
39#include <langinfo.h>
40#endif
41
42#include <QtCore/QTextCodec>
43#include <QtCore/QFile>
44#include <QtGui/QPrinter>
45#include <QtCore/QFileInfo>
46#include <QtCore/QRegExp>
47#include <QtCore/QLocale>
48#include <QtCore/QHash>
49#include <QtCore/QMutexLocker>
50#include <QtCore/QStringList>
51
52#include "kcatalog_p.h"
53#include "kglobal.h"
54#include "kstandarddirs.h"
55#include "kconfig.h"
56#include "kcomponentdata.h"
57#include "kdebug.h"
58#include "kdatetime.h"
59#include "kcalendarsystem.h"
60#include "kcurrencycode.h"
61#include "klocalizedstring.h"
62#include "kconfiggroup.h"
63#include "kcatalogname_p.h"
64#include "common_helpers_p.h"
65#include "kdayperiod_p.h"
66
67class KLocaleStaticData
68{
69public:
70
71 KLocaleStaticData();
72
73 QString maincatalog;
74
75 // FIXME: Temporary until full language-sensitivity implemented.
76 QHash<KLocale::DigitSet, QStringList> languagesUsingDigitSet;
77};
78
79KLocaleStaticData::KLocaleStaticData()
80{
81 // Languages using non-Western Arabic digit sets.
82 // FIXME: Temporary until full language-sensitivity implemented.
83 languagesUsingDigitSet.insert(KLocale::ArabicIndicDigits, QStringList() << QString::fromLatin1("ar") << QString::fromLatin1("ps"));
84 languagesUsingDigitSet.insert(KLocale::BengaliDigits, QStringList() << QString::fromLatin1("bn") << QString::fromLatin1("as") );
85 languagesUsingDigitSet.insert(KLocale::DevenagariDigits, QStringList() << QString::fromLatin1("hi") << QString::fromLatin1("ne"));
86 languagesUsingDigitSet.insert(KLocale::EasternArabicIndicDigits, QStringList() << QString::fromLatin1("fa") << QString::fromLatin1("ur"));
87 languagesUsingDigitSet.insert(KLocale::GujaratiDigits, QStringList() << QString::fromLatin1("gu") );
88 languagesUsingDigitSet.insert(KLocale::GurmukhiDigits, QStringList() << QString::fromLatin1("pa") );
89 languagesUsingDigitSet.insert(KLocale::KannadaDigits, QStringList() << QString::fromLatin1("kn") );
90 languagesUsingDigitSet.insert(KLocale::KhmerDigits, QStringList() << QString::fromLatin1("km") );
91 languagesUsingDigitSet.insert(KLocale::MalayalamDigits, QStringList() << QString::fromLatin1("ml") );
92 languagesUsingDigitSet.insert(KLocale::OriyaDigits, QStringList() << QString::fromLatin1("or") );
93 languagesUsingDigitSet.insert(KLocale::TamilDigits, QStringList() << QString::fromLatin1("ta") );
94 languagesUsingDigitSet.insert(KLocale::TeluguDigits, QStringList() << QString::fromLatin1("te") );
95 languagesUsingDigitSet.insert(KLocale::ThaiDigits, QStringList() << QString::fromLatin1("th"));
96}
97
98K_GLOBAL_STATIC(KLocaleStaticData, staticData)
99
100
101QDebug operator<<(QDebug debug, const KCatalogName &cn)
102{
103 return debug << cn.name << cn.loadCount;
104}
105
106KLocalePrivate::KLocalePrivate(KLocale *q_ptr)
107 : q(q_ptr),
108 m_config(KSharedConfig::Ptr()),
109 m_country(QString()),
110 m_language(QString()),
111 m_languages(0),
112 m_catalogName(QString()),
113 m_calendar(0),
114 m_currency(0),
115 m_codecForEncoding(0)
116{
117}
118
119KLocalePrivate::KLocalePrivate(const KLocalePrivate &rhs)
120{
121 copy(rhs);
122}
123
124KLocalePrivate &KLocalePrivate::operator=(const KLocalePrivate &rhs)
125{
126 copy(rhs);
127 return *this;
128}
129
130KConfig *KLocalePrivate::config()
131{
132 if (m_config != KSharedConfig::Ptr()) {
133 return m_config.data();
134 } else {
135 return KGlobal::config().data();
136 }
137}
138
139void KLocalePrivate::copy(const KLocalePrivate &rhs)
140{
141 // Parent KLocale
142 q = 0;
143
144 // Config
145 m_config = rhs.m_config;
146
147 // Country settings
148 m_country = rhs.m_country;
149 m_countryDivisionCode = rhs.m_countryDivisionCode;
150
151 // Language settings
152 m_language = rhs.m_language;
153 m_languages = 0;
154 m_languageList = rhs.m_languageList;
155 m_languageSensitiveDigits = rhs.m_languageSensitiveDigits;
156 m_nounDeclension = rhs.m_nounDeclension;
157
158 // Catalog settings
159 m_catalogName = rhs.m_catalogName;
160 m_catalogNames = rhs.m_catalogNames;
161 m_catalogs = rhs.m_catalogs;
162 m_numberOfSysCatalogs = rhs.m_numberOfSysCatalogs;
163 m_useTranscript = rhs.m_useTranscript;
164
165 // Calendar settings
166 m_calendarSystem = rhs.m_calendarSystem;
167 m_calendar = 0;
168 m_weekStartDay = rhs.m_weekStartDay;
169 m_workingWeekStartDay = rhs.m_workingWeekStartDay;
170 m_workingWeekEndDay = rhs.m_workingWeekEndDay;
171 m_weekDayOfPray = rhs.m_weekDayOfPray;
172
173 // Date/Time settings
174 m_dateFormat = rhs.m_dateFormat;
175 m_dateFormatShort = rhs.m_dateFormatShort;
176 m_timeFormat = rhs.m_timeFormat;
177 m_dateTimeDigitSet = rhs.m_dateTimeDigitSet;
178 m_dateMonthNamePossessive = rhs.m_dateMonthNamePossessive;
179 m_dayPeriods = rhs.m_dayPeriods;
180 m_weekNumberSystem = rhs.m_weekNumberSystem;
181
182 // Number settings
183 m_decimalPlaces = rhs.m_decimalPlaces;
184 m_decimalSymbol = rhs.m_decimalSymbol;
185 m_thousandsSeparator = rhs.m_thousandsSeparator;
186 m_numericDigitGrouping = rhs.m_numericDigitGrouping;
187 m_positiveSign = rhs.m_positiveSign;
188 m_negativeSign = rhs.m_negativeSign;
189 m_digitSet = rhs.m_digitSet;
190
191 // Currency settings
192 m_currencyCode = rhs.m_currencyCode;
193 m_currency = 0;
194 m_currencyCodeList = rhs.m_currencyCodeList;
195
196 // Money settings
197 m_currencySymbol = rhs.m_currencySymbol;
198 m_monetaryDecimalSymbol = rhs.m_monetaryDecimalSymbol;
199 m_monetaryThousandsSeparator = rhs.m_monetaryThousandsSeparator;
200 m_monetaryDigitGrouping = rhs.m_monetaryDigitGrouping;
201 m_monetaryDecimalPlaces = rhs.m_monetaryDecimalPlaces;
202 m_positiveMonetarySignPosition = rhs.m_positiveMonetarySignPosition;
203 m_negativeMonetarySignPosition = rhs.m_negativeMonetarySignPosition;
204 m_positivePrefixCurrencySymbol = rhs.m_positivePrefixCurrencySymbol;
205 m_negativePrefixCurrencySymbol = rhs.m_negativePrefixCurrencySymbol;
206 m_monetaryDigitSet = rhs.m_monetaryDigitSet;
207
208 // Units settings
209 m_binaryUnitDialect = rhs.m_binaryUnitDialect;
210 m_byteSizeFmt = rhs.m_byteSizeFmt;
211 m_pageSize = rhs.m_pageSize;
212 m_measureSystem = rhs.m_measureSystem;
213
214 // Encoding settings
215 m_encoding = rhs.m_encoding;
216 m_codecForEncoding = rhs.m_codecForEncoding;
217 m_utf8FileEncoding = rhs.m_utf8FileEncoding;
218}
219
220KLocalePrivate::~KLocalePrivate()
221{
222 delete m_currency;
223 delete m_calendar;
224 delete m_languages;
225}
226
227// init only called from platform specific constructor, so set everything up
228// Will be given a persistantConfig or a tempConfig or neither, but never both
229void KLocalePrivate::init(const QString& catalogName, const QString &language, const QString &country,
230 KSharedConfig::Ptr persistantConfig, KConfig *tempConfig)
231{
232 m_catalogName = catalogName;
233
234 // Only keep the persistant config if it is not the global
235 if (persistantConfig != KSharedConfig::Ptr() && persistantConfig != KGlobal::config()) {
236 m_config = persistantConfig;
237 }
238
239 KConfigGroup cg;
240 bool useEnvironmentVariables;
241
242 // We can't read the formats from the config until we know what locale to read in, but we need
243 // to read the config to find out the locale. The Country and Language settings should never
244 // be localized in the config, so we can read a temp copy of them to get us started.
245
246 // If no config given, use the global config and include envvars, otherwise use only the config.
247 if (m_config != KSharedConfig::Ptr()) {
248 cg = m_config->group(QLatin1String("Locale"));
249 useEnvironmentVariables = false;
250 } else if (tempConfig == 0 || tempConfig == KGlobal::config().data()) {
251 cg = KGlobal::config()->group(QLatin1String("Locale"));
252 useEnvironmentVariables = true;
253 } else {
254 cg = tempConfig->group(QLatin1String("Locale"));
255 useEnvironmentVariables = false;
256 }
257
258 initEncoding();
259 initFileNameEncoding();
260 initCountry(country, cg.readEntry(QLatin1String("Country")));
261 initLanguageList(language, cg.readEntry(QLatin1String("Language")), useEnvironmentVariables);
262 // Now that we have a language, we can set up the config which uses it to setLocale()
263 initConfig(tempConfig);
264 initMainCatalogs();
265 initFormat();
266}
267
268// Init the config, this is called during construction and by later setCountry/setLanguage calls.
269// You _must_ have the m_language set to a valid language or en_US before calling this so a
270// setLocale can be applied to the config
271void KLocalePrivate::initConfig(KConfig *config)
272{
273 // * If we were constructed with a KSharedConfig it means the user gave it to us
274 // to use for the life of the KLocale, so just keep using it after a setLocale
275 // * If passed in KConfig is null or the global config then use the global, but
276 // do the setLocale first.
277 // * If we have a KConfig we need to use that, but due to keeping old behaviour
278 // of not requiring access to it for life we can't keep a reference so instead
279 // take a copy and use that, but do setLocale first.
280
281 if (m_config != KSharedConfig::Ptr()) {
282 m_config->setLocale(m_language);
283 } else {
284 // If no config given then use the global
285 if (config == 0 || config == KGlobal::config().data()) {
286 KGlobal::config()->setLocale(m_language);
287 } else {
288 config->setLocale(m_language);
289 m_config = KSharedConfig::openConfig();
290 config->copyTo(QString(), m_config.data());
291 m_config->markAsClean();
292 }
293 }
294}
295
296void KLocalePrivate::initMainCatalogs()
297{
298 KLocaleStaticData *s = staticData;
299 QMutexLocker lock(kLocaleMutex());
300
301 if (!s->maincatalog.isEmpty()) {
302 // If setMainCatalog was called, then we use that
303 // (e.g. korgac calls setMainCatalog("korganizer") to use korganizer.po)
304 m_catalogName = s->maincatalog;
305 }
306
307 if (m_catalogName.isEmpty()) {
308 kDebug(173) << "KLocale instance created called without valid "
309 << "catalog! Give an argument or call setMainCatalog "
310 << "before init" << endl;
311 } else {
312 // do not use insertCatalog here, that would already trigger updateCatalogs
313 m_catalogNames.append(KCatalogName(m_catalogName)); // application catalog
314
315 // catalogs from which each application can draw translations
316 const int numberOfCatalogs = m_catalogNames.size();
317 m_catalogNames.append(KCatalogName(QString::fromLatin1("libphonon")));
318 m_catalogNames.append(KCatalogName(QString::fromLatin1("kio4")));
319 m_catalogNames.append(KCatalogName(QString::fromLatin1("kdelibs4")));
320 m_catalogNames.append(KCatalogName(QString::fromLatin1("kdeqt")));
321 m_catalogNames.append(KCatalogName(QString::fromLatin1("solid_qt")));
322 m_catalogNames.append(KCatalogName(QString::fromLatin1("kdecalendarsystems")));
323 m_numberOfSysCatalogs = m_catalogNames.size() - numberOfCatalogs;
324
325 updateCatalogs(); // evaluate this for all languages
326 }
327}
328
329void KLocalePrivate::getLanguagesFromVariable(QStringList &list, const char *variable, bool isLanguageList)
330{
331 QByteArray var(qgetenv(variable));
332 if (!var.isEmpty()) {
333 QString value = QFile::decodeName(var);
334 if (isLanguageList) {
335 list += value.split(QLatin1Char(':'));
336 } else {
337 // Process the value to create possible combinations.
338 QString lang, ctry, modf, cset;
339 KLocale::splitLocale(value, lang, ctry, modf, cset);
340
341 if (!ctry.isEmpty() && !modf.isEmpty()) {
342 list += lang + QLatin1Char('_') + ctry + QLatin1Char('@') + modf;
343 }
344 // NOTE: The priority is tricky in case both ctry and modf are present.
345 // Should really lang@modf be of higher priority than lang_ctry?
346 // For at least one case (Serbian language), it is better this way.
347 if (!modf.isEmpty()) {
348 list += lang + QLatin1Char('@') + modf;
349 }
350 if (!ctry.isEmpty()) {
351 list += lang + QLatin1Char('_') + ctry;
352 }
353 list += lang;
354 }
355 }
356}
357
358// init the country at construction only, will ensure we always have a country set
359void KLocalePrivate::initCountry(const QString &country, const QString &configCountry)
360{
361 // Cache the valid countries list and add the default C as it is valid to use
362 QStringList validCountries = allCountriesList();
363 validCountries.append( defaultCountry() );
364
365 // First check if the constructor passed in a value and if so if it is valid
366 QString putativeCountry = country;
367
368 if ( putativeCountry.isEmpty() || !validCountries.contains( putativeCountry, Qt::CaseInsensitive ) ) {
369
370 // If the requested country is not valid, try the country as set in the config:
371 putativeCountry = configCountry;
372
373 if ( putativeCountry.isEmpty() || !validCountries.contains( putativeCountry, Qt::CaseInsensitive ) ) {
374
375 // If the config country is not valid try the current host system country
376 putativeCountry = systemCountry();
377
378 if ( putativeCountry.isEmpty() || !validCountries.contains( putativeCountry, Qt::CaseInsensitive ) ) {
379 // Only if no other option, resort to the default C
380 putativeCountry = defaultCountry();
381 }
382 }
383 }
384
385 // Always save as lowercase, unless it's C when we want it uppercase
386 if ( putativeCountry.toLower() == defaultCountry().toLower() ) {
387 m_country = defaultCountry();
388 } else {
389 m_country = putativeCountry.toLower();
390 }
391}
392
393QString KLocalePrivate::systemCountry() const
394{
395 // Use QLocale for now as it supposedly provides a sensible default most times,
396 // e.g. if locale is only "de" it is assumed to mean country of "DE"
397 QString systemCountry, s1, s2, s3;
398 splitLocale( QLocale::system().name(), s1, systemCountry, s2, s3 );
399 return systemCountry.toLower();
400}
401
402void KLocalePrivate::initLanguageList(const QString &language, const QString &configLanguages,
403 bool useEnvironmentVariables)
404{
405 m_language = language;
406
407 // Collect possible languages by decreasing priority.
408 // The priority is as follows:
409 // - the internally set language, if any
410 // - KDE_LANG environment variable (can be a list)
411 // - KDE configuration (can be a list)
412 // - environment variables considered by gettext(3)
413 // The environment variables are not considered if useEnvironmentVariables is false.
414 QStringList list;
415 if (!m_language.isEmpty()) {
416 list += m_language;
417 }
418
419 // If the Locale object was created with a specific config file, then do not use the
420 // environmental variables. If the locale object was created with the global config, then
421 // do use the environmental variables.
422 if (useEnvironmentVariables) {
423 // KDE_LANG contains list of language codes, not locale string.
424 getLanguagesFromVariable(list, "KDE_LANG", true);
425 }
426
427 if (!configLanguages.isEmpty()) {
428 list += configLanguages.split(QLatin1Char(':'));
429 }
430
431 if (useEnvironmentVariables) {
432 // Collect languages by same order of priority as for gettext(3).
433 // LANGUAGE contains list of language codes, not locale string.
434 getLanguagesFromVariable(list, "LANGUAGE", true);
435 getLanguagesFromVariable(list, "LC_ALL");
436 getLanguagesFromVariable(list, "LC_MESSAGES");
437 getLanguagesFromVariable(list, "LANG");
438 }
439
440 // fall back to the system language
441 list += systemLanguageList();
442
443 // Send the list to filter for really present languages on the system.
444 setLanguage(list);
445}
446
447QStringList KLocalePrivate::systemLanguageList() const
448{
449 return QStringList();
450}
451
452void KLocalePrivate::initFormat()
453{
454 KConfigGroup cg(config(), "Locale");
455
456 KConfig entryFile(KStandardDirs::locate("locale", QString::fromLatin1("l10n/%1/entry.desktop").arg(m_country)));
457 entryFile.setLocale(m_language);
458 KConfigGroup entry(&entryFile, "KCM Locale");
459
460 //One-time conversion in 4.4 from FracDigits to DecimalPlaces and MonetaryDecimalPlaces
461 //If user has personal setting for FracDigits then use it for both Decimal Places
462 //TODO: Possible to do with kconf_update
463 if (cg.hasKey("FracDigits")) {
464 QString fracDigits = cg.readEntry("FracDigits", "");
465 if (!fracDigits.isEmpty()) {
466 cg.writeEntry("DecimalPlaces", fracDigits);
467 cg.writeEntry("MonetaryDecimalPlaces", fracDigits);
468 }
469 cg.deleteEntry("FracDigits");
470 cg.config()->sync();
471 }
472
473 // Numeric
474#define readConfigEntry(key, default, save) \
475 save = entry.readEntry(key, default); \
476 save = cg.readEntry(key, save);
477
478#define readConfigNumEntry(key, default, save, type) \
479 save = (type)entry.readEntry(key, int(default)); \
480 save = (type)cg.readEntry(key, int(save));
481
482 // Country settings
483 readConfigEntry("CountryDivisionCode", QString(), m_countryDivisionCode);
484
485 // Numeric formats
486 readConfigNumEntry("DecimalPlaces", 2, m_decimalPlaces, int);
487
488 readConfigEntry("DecimalSymbol", ".", m_decimalSymbol);
489 readConfigEntry("ThousandsSeparator", ",", m_thousandsSeparator);
490 m_thousandsSeparator.remove(QString::fromLatin1("$0"));
491 QString digitGroupFormat;
492 readConfigEntry("DigitGroupFormat", "3", digitGroupFormat);
493 m_numericDigitGrouping = digitGroupFormatToList(digitGroupFormat);
494
495 readConfigEntry("PositiveSign", "", m_positiveSign);
496 readConfigEntry("NegativeSign", "-", m_negativeSign);
497
498 readConfigNumEntry("DigitSet", KLocale::ArabicDigits, m_digitSet, KLocale::DigitSet);
499 // FIXME: Temporary until full language-sensitivity implemented.
500 readConfigEntry("LanguageSensitiveDigits", true, m_languageSensitiveDigits);
501
502 // Currency
503 readConfigEntry("CurrencyCode", "USD", m_currencyCode);
504 initCurrency();
505 readConfigEntry("CurrencySymbol", m_currency->defaultSymbol(), m_currencySymbol);
506 readConfigEntry("CurrencyCodesInUse", QStringList(m_currencyCode), m_currencyCodeList);
507
508 // Monetary formats
509 readConfigNumEntry("MonetaryDecimalPlaces", m_currency->decimalPlaces(), m_monetaryDecimalPlaces, int);
510
511 readConfigEntry("MonetaryDecimalSymbol", ".", m_monetaryDecimalSymbol);
512 readConfigEntry("MonetaryThousandsSeparator", ",", m_monetaryThousandsSeparator);
513 m_monetaryThousandsSeparator.remove(QString::fromLatin1("$0"));
514 readConfigEntry("MonetaryDigitGroupFormat", "3", digitGroupFormat);
515 m_monetaryDigitGrouping = digitGroupFormatToList(digitGroupFormat);
516
517 readConfigEntry("PositivePrefixCurrencySymbol", true, m_positivePrefixCurrencySymbol);
518 readConfigEntry("NegativePrefixCurrencySymbol", true, m_negativePrefixCurrencySymbol);
519 readConfigNumEntry("PositiveMonetarySignPosition", KLocale::BeforeQuantityMoney,
520 m_positiveMonetarySignPosition, KLocale::SignPosition);
521 readConfigNumEntry("NegativeMonetarySignPosition", KLocale::ParensAround,
522 m_negativeMonetarySignPosition, KLocale::SignPosition);
523
524 readConfigNumEntry("MonetaryDigitSet", KLocale::ArabicDigits,
525 m_monetaryDigitSet, KLocale::DigitSet);
526 readConfigNumEntry("BinaryUnitDialect", KLocale::IECBinaryDialect,
527 m_binaryUnitDialect, KLocale::BinaryUnitDialect);
528
529 // Date and time
530 readConfigEntry("TimeFormat", "%H:%M:%S", m_timeFormat);
531 readConfigEntry("DateFormat", "%A %d %B %Y", m_dateFormat);
532 readConfigEntry("DateFormatShort", "%Y-%m-%d", m_dateFormatShort);
533 readConfigNumEntry("WeekStartDay", 1, m_weekStartDay, int); //default to Monday
534 readConfigNumEntry("WorkingWeekStartDay", 1, m_workingWeekStartDay, int); //default to Monday
535 readConfigNumEntry("WorkingWeekEndDay", 5, m_workingWeekEndDay, int); //default to Friday
536 readConfigNumEntry("WeekDayOfPray", 7, m_weekDayOfPray, int); //default to Sunday
537 readConfigNumEntry("DateTimeDigitSet", KLocale::ArabicDigits,
538 m_dateTimeDigitSet, KLocale::DigitSet);
539 readConfigNumEntry("WeekNumberSystem", KLocale::IsoWeekNumber,
540 m_weekNumberSystem, KLocale::WeekNumberSystem);
541
542 // other
543#ifndef QT_NO_PRINTER
544 readConfigNumEntry("PageSize", QPrinter::A4, m_pageSize, QPrinter::PageSize);
545#endif
546 readConfigNumEntry("MeasureSystem", KLocale::Metric, m_measureSystem, KLocale::MeasureSystem);
547 QString calendarType;
548 readConfigEntry("CalendarSystem", "gregorian", calendarType);
549 setCalendar(calendarType);
550
551 readConfigEntry("Transcript", true, m_useTranscript);
552
553 //Grammatical
554 //Precedence here is l10n / i18n / config file
555 KConfig langCfg(KStandardDirs::locate("locale", QString::fromLatin1("%1/entry.desktop").arg(m_language)));
556 KConfigGroup lang(&langCfg, "KCM Locale");
557#define read3ConfigBoolEntry(key, default, save) \
558 save = entry.readEntry(key, default); \
559 save = lang.readEntry(key, save); \
560 save = cg.readEntry(key, save);
561
562 read3ConfigBoolEntry("NounDeclension", false, m_nounDeclension);
563 read3ConfigBoolEntry("DateMonthNamePossessive", false, m_dateMonthNamePossessive);
564
565 initDayPeriods(cg);
566}
567
568void KLocalePrivate::initDayPeriods(const KConfigGroup &cg)
569{
570 // Prefer any l10n file value for country/language,
571 // otherwise default to language only value which will be filled in later when i18n available
572
573 //Day Period are stored in config as one QStringList entry per Day Period
574 //PeriodCode,LongName,ShortName,NarrowName,StartTime,EndTime,Offset,OffsetIfZero
575 //where start and end time are in the format HH:MM:SS.MMM
576
577 m_dayPeriods.clear();
578 QString periodKey = QString::fromLatin1("DayPeriod1");
579 int i = 1;
580 while (cg.hasKey(periodKey)) {
581 QStringList period = cg.readEntry(periodKey, QStringList());
582 if (period.count() == 8) {
583 m_dayPeriods.append(KDayPeriod(period[0], period[1], period[2], period[3],
584 QTime::fromString(period[4], QString::fromLatin1("HH:mm:ss.zzz")),
585 QTime::fromString(period[5], QString::fromLatin1("HH:mm:ss.zzz")),
586 period[6].toInt(), period[7].toInt()));
587 }
588 i = i + 1;
589 periodKey = QString::fromLatin1("DayPeriod%1").arg(i);
590 }
591}
592
593bool KLocalePrivate::setCountry(const QString &country, KConfig *newConfig)
594{
595 // Cache the valid countries list and add the default C as it is valid to use
596 QStringList validCountries = allCountriesList();
597 validCountries.append(defaultCountry());
598
599 QString putativeCountry = country;
600
601 if (putativeCountry.isEmpty()) {
602 // An empty string means to use the system country
603 putativeCountry = systemCountry();
604 if (putativeCountry.isEmpty() || !validCountries.contains(putativeCountry, Qt::CaseInsensitive)) {
605 // If the system country is not valid, use the default
606 putativeCountry = defaultCountry();
607 }
608 } else if (!validCountries.contains(putativeCountry, Qt::CaseInsensitive)) {
609 return false;
610 }
611
612 // Always save as lowercase, unless it's C when we want it uppercase
613 if (putativeCountry.toLower() == defaultCountry().toLower()) {
614 m_country = defaultCountry();
615 } else {
616 m_country = putativeCountry.toLower();
617 }
618
619 // Get rid of the old config, start again with the new
620 m_config = KSharedConfig::Ptr();
621 initConfig(newConfig);
622
623 // Init all the settings
624 initFormat();
625
626 return true;
627}
628
629bool KLocalePrivate::setCountryDivisionCode(const QString &countryDivisionCode)
630{
631 m_countryDivisionCode = countryDivisionCode;
632 return true;
633}
634
635bool KLocalePrivate::setLanguage(const QString &language, KConfig *config)
636{
637 QMutexLocker lock(kLocaleMutex());
638 m_languageList.removeAll(language);
639 m_languageList.prepend(language); // let us consider this language to be the most important one
640
641 m_language = language; // remember main language for shortcut evaluation
642
643 // important when called from the outside and harmless when called before
644 // populating the catalog name list
645 updateCatalogs();
646
647 // Get rid of the old config, start again with the new
648 m_config = KSharedConfig::Ptr();
649 initConfig(config);
650
651 // Init the new format settings
652 initFormat();
653
654 // Maybe the mo-files for this language are empty, but in principle we can speak all languages
655 return true;
656}
657
658// KDE5 Unlike the other setLanguage call this does not reparse the config so the localized config
659// settings for the new primary language will _not_ be loaded. In KDE5 always keep the original
660// config so this can be reparsed when required.
661bool KLocalePrivate::setLanguage(const QStringList &languages)
662{
663 QMutexLocker lock(kLocaleMutex());
664 // This list might contain
665 // 1) some empty strings that we have to eliminate
666 // 2) duplicate entries like in de:fr:de, where we have to keep the first occurrence of a
667 // language in order to preserve the order of precenence of the user
668 // 3) languages into which the application is not translated. For those languages we should not
669 // even load kdelibs.mo or kio.po. these languages have to be dropped. Otherwise we get
670 // strange side effects, e.g. with Hebrew: the right/left switch for languages that write
671 // from right to left (like Hebrew or Arabic) is set in kdelibs.mo. If you only have
672 // kdelibs.mo but nothing from appname.mo, you get a mostly English app with layout from
673 // right to left. That was considered to be a bug by the Hebrew translators.
674 QStringList list;
675 foreach(const QString &language, languages) {
676 if (!language.isEmpty() && !list.contains(language) && isApplicationTranslatedInto(language)) {
677 list.append(language);
678 }
679 }
680
681 if (!list.contains(KLocale::defaultLanguage())) {
682 // English should always be added as final possibility; this is important
683 // for proper initialization of message text post-processors which are
684 // needed for English too, like semantic to visual formatting, etc.
685 list.append(KLocale::defaultLanguage());
686 }
687
688 m_language = list.first(); // keep this for shortcut evaluations
689
690 m_languageList = list; // keep this new list of languages to use
691
692 // important when called from the outside and harmless when called before populating the
693 // catalog name list
694 updateCatalogs();
695
696 return true; // we found something. Maybe it's only English, but we found something
697}
698
699void KLocalePrivate::initCurrency()
700{
701 if (m_currencyCode.isEmpty() || !KCurrencyCode::isValid(m_currencyCode)) {
702 m_currencyCode = KLocale::defaultCurrencyCode();
703 }
704
705 if (!m_currency || m_currencyCode != m_currency->isoCurrencyCode() || !m_currency->isValid()) {
706 delete m_currency;
707 m_currency = new KCurrencyCode(m_currencyCode, m_language);
708 }
709}
710
711void KLocalePrivate::setCurrencyCode(const QString &newCurrencyCode)
712{
713 if (!newCurrencyCode.isEmpty() && newCurrencyCode != m_currency->isoCurrencyCode() &&
714 KCurrencyCode::isValid(newCurrencyCode)) {
715 m_currencyCode = newCurrencyCode;
716 initCurrency();
717 }
718}
719
720bool KLocalePrivate::isApplicationTranslatedInto(const QString &lang)
721{
722 if (lang.isEmpty()) {
723 return false;
724 }
725
726 if (lang == KLocale::defaultLanguage()) {
727 // default language is always "installed"
728 return true;
729 }
730
731 if (m_catalogName.isEmpty()) {
732 kDebug() << "no appName!";
733 return false;
734 }
735
736 if (!KCatalog::catalogLocaleDir(m_catalogName, lang).isEmpty()) {
737 return true;
738 }
739 return false;
740}
741
742void KLocalePrivate::splitLocale(const QString &aLocale, QString &language, QString &country,
743 QString &modifier, QString &charset)
744{
745 QString locale = aLocale;
746
747 language.clear();
748 country.clear();
749 modifier.clear();
750 charset.clear();
751
752 // In case there are several concatenated locale specifications,
753 // truncate all but first.
754 int f = locale.indexOf(QLatin1Char(':'));
755 if (f >= 0) {
756 locale.truncate(f);
757 }
758
759 f = locale.indexOf(QLatin1Char('.'));
760 if (f >= 0) {
761 charset = locale.mid(f + 1);
762 locale.truncate(f);
763 }
764
765 f = locale.indexOf(QLatin1Char('@'));
766 if (f >= 0) {
767 modifier = locale.mid(f + 1);
768 locale.truncate(f);
769 }
770
771 f = locale.indexOf(QLatin1Char('_'));
772 if (f >= 0) {
773 country = locale.mid(f + 1);
774 locale.truncate(f);
775 }
776
777 language = locale;
778}
779
780QString KLocalePrivate::language() const
781{
782 return m_language;
783}
784
785QString KLocalePrivate::country() const
786{
787 return m_country;
788}
789
790QString KLocalePrivate::countryDivisionCode() const
791{
792 if (m_countryDivisionCode.isEmpty()) {
793 return country().toUpper();
794 } else {
795 return m_countryDivisionCode;
796 }
797}
798
799KCurrencyCode *KLocalePrivate::currency()
800{
801 if (!m_currency) {
802 initCurrency();
803 }
804 return m_currency;
805}
806
807QString KLocalePrivate::currencyCode() const
808{
809 return m_currencyCode;
810}
811
812void KLocalePrivate::insertCatalog(const QString &catalog)
813{
814 QMutexLocker lock(kLocaleMutex());
815 int pos = m_catalogNames.indexOf(KCatalogName(catalog));
816 if (pos != -1) {
817 ++m_catalogNames[pos].loadCount;
818 return;
819 }
820
821 // Insert new catalog just before system catalogs, to preserve the
822 // lowest priority of system catalogs.
823 m_catalogNames.insert(m_catalogNames.size() - m_numberOfSysCatalogs, KCatalogName(catalog));
824 updateCatalogs(); // evaluate the changed list and generate the necessary KCatalog objects
825}
826
827void KLocalePrivate::updateCatalogs()
828{
829 // some changes have occurred. Maybe we have learned or forgotten some languages.
830 // Maybe the language precedence has changed.
831 // Maybe we have learned or forgotten some catalog names.
832
833 QList<KCatalog> newCatalogs;
834
835 // now iterate over all languages and all wanted catalog names and append or create them in the
836 // right order the sequence must be e.g. nds/appname nds/kdelibs nds/kio de/appname de/kdelibs
837 // de/kio etc. and not nds/appname de/appname nds/kdelibs de/kdelibs etc. Otherwise we would be
838 // in trouble with a language sequende nds,<default>,de. In this case <default> must hide
839 // everything after itself in the language list.
840 foreach(const QString &lang, m_languageList) {
841 if (lang == KLocale::defaultLanguage()) {
842 // Default language has no catalogs (messages from the code),
843 // so loading catalogs for languages below the default
844 // would later confuse the fallback resolution.
845 break;
846 }
847 foreach(const KCatalogName &name, m_catalogNames) {
848 // create and add catalog for this name and language if it exists
849 if (! KCatalog::catalogLocaleDir(name.name, lang).isEmpty()) {
850 newCatalogs.append(KCatalog(name.name, lang));
851 //kDebug(173) << "Catalog: " << name << ":" << lang;
852 }
853 }
854 }
855
856 // notify KLocalizedString of catalog update.
857 m_catalogs = newCatalogs;
858 KLocalizedString::notifyCatalogsUpdated(m_languageList, m_catalogNames);
859}
860
861void KLocalePrivate::removeCatalog(const QString &catalog)
862{
863 QMutexLocker lock(kLocaleMutex());
864 int pos = m_catalogNames.indexOf(KCatalogName(catalog));
865 if (pos == -1) {
866 return;
867 }
868 if (--m_catalogNames[pos].loadCount > 0) {
869 return;
870 }
871 m_catalogNames.removeAt(pos);
872 if (KGlobal::hasMainComponent()) {
873 // walk through the KCatalog instances and weed out everything we no longer need
874 updateCatalogs();
875 }
876}
877
878void KLocalePrivate::setActiveCatalog(const QString &catalog)
879{
880 QMutexLocker lock(kLocaleMutex());
881 int pos = m_catalogNames.indexOf(KCatalogName(catalog));
882 if (pos == -1) {
883 return;
884 }
885 m_catalogNames.move(pos, 0);
886 // walk through the KCatalog instances and adapt to the new order
887 updateCatalogs();
888}
889
890void KLocalePrivate::translateRawFrom(const char *catname, const char *msgctxt, const char *msgid, const char *msgid_plural,
891 unsigned long n, QString *language, QString *translation) const
892{
893 if (!msgid || !msgid[0]) {
894 kDebug(173) << "KLocale: trying to look up \"\" in catalog. "
895 << "Fix the program" << endl;
896 language->clear();
897 translation->clear();
898 return;
899 }
900 if (msgctxt && !msgctxt[0]) {
901 kDebug(173) << "KLocale: trying to use \"\" as context to message. "
902 << "Fix the program" << endl;
903 }
904 if (msgid_plural && !msgid_plural[0]) {
905 kDebug(173) << "KLocale: trying to use \"\" as plural message. "
906 << "Fix the program" << endl;
907 }
908
909 QMutexLocker locker(kLocaleMutex());
910 // determine the fallback string
911 QString fallback;
912 if (msgid_plural == NULL) {
913 fallback = QString::fromUtf8(msgid);
914 } else {
915 if (n == 1) {
916 fallback = QString::fromUtf8(msgid);
917 } else {
918 fallback = QString::fromUtf8(msgid_plural);
919 }
920 }
921 if (language) {
922 *language = KLocale::defaultLanguage();
923 }
924 if (translation) {
925 *translation = fallback;
926 }
927
928 // shortcut evaluation if default language is main language: do not consult the catalogs
929 if (useDefaultLanguage()) {
930 return;
931 }
932
933 const QList<KCatalog> catalogList = m_catalogs;
934 QString catNameDecoded;
935 if (catname != NULL) {
936 catNameDecoded = QString::fromUtf8(catname);
937 }
938 for (QList<KCatalog>::ConstIterator it = catalogList.constBegin(); it != catalogList.constEnd();
939 ++it) {
940 // shortcut evaluation: once we have arrived at default language, we cannot consult
941 // the catalog as it will not have an assiciated mo-file. For this default language we can
942 // immediately pick the fallback string.
943 if ((*it).language() == KLocale::defaultLanguage()) {
944 return;
945 }
946
947 if (catNameDecoded.isEmpty() || catNameDecoded == (*it).name()) {
948 QString text;
949 if (msgctxt != NULL && msgid_plural != NULL) {
950 text = (*it).translateStrict(msgctxt, msgid, msgid_plural, n);
951 } else if (msgid_plural != NULL) {
952 text = (*it).translateStrict(msgid, msgid_plural, n);
953 } else if (msgctxt != NULL) {
954 text = (*it).translateStrict(msgctxt, msgid);
955 } else {
956 text = (*it).translateStrict(msgid);
957 }
958
959 if (!text.isEmpty()) {
960 // we found it
961 if (language) {
962 *language = (*it).language();
963 }
964 if (translation) {
965 *translation = text;
966 }
967 return;
968 }
969 }
970 }
971}
972
973QString KLocalePrivate::translateQt(const char *context, const char *sourceText, const char *comment) const
974{
975 // Qt's context is normally the name of the class of the method which makes
976 // the tr(sourceText) call. However, it can also be manually supplied via
977 // translate(context, sourceText) call.
978 //
979 // Qt's sourceText is the actual message displayed to the user.
980 //
981 // Qt's comment is an optional argument of tr() and translate(), like
982 // tr(sourceText, comment) and translate(context, sourceText, comment).
983 //
984 // We handle this in the following way:
985 //
986 // If the comment is given, then it is considered gettext's msgctxt, so a
987 // context call is made.
988 //
989 // If the comment is not given, but context is given, then we treat it as
990 // msgctxt only if it was manually supplied (the one in translate()) -- but
991 // we don't know this, so we first try a context call, and if translation
992 // is not found, we fallback to ordinary call.
993 //
994 // If neither comment nor context are given, it's just an ordinary call
995 // on sourceText.
996
997 if (!sourceText || !sourceText[0]) {
998 kDebug(173) << "KLocale: trying to look up \"\" in catalog. "
999 << "Fix the program" << endl;
1000 return QString();
1001 }
1002
1003 if (useDefaultLanguage()) {
1004 return QString();
1005 }
1006
1007 QString translation;
1008 QString language;
1009
1010 // NOTE: Condition (language != defaultLanguage()) means that translation
1011 // was found, otherwise we got the original string back as translation.
1012
1013 if (comment && comment[0]) {
1014 // Comment given, go for context call.
1015 translateRawFrom(0, comment, sourceText, 0, 0, &language, &translation);
1016 } else {
1017 // Comment not given, go for try-fallback with context.
1018 if (context && context[0]) {
1019 translateRawFrom(0, context, sourceText, 0, 0, &language, &translation);
1020 }
1021 if (language.isEmpty() || language == defaultLanguage()) {
1022 translateRawFrom(0, 0, sourceText, 0, 0, &language, &translation);
1023 }
1024 }
1025
1026 if (language != defaultLanguage()) {
1027 return translation;
1028 }
1029
1030 // No proper translation found, return empty according to Qt's expectation.
1031 return QString();
1032}
1033
1034QList<KLocale::DigitSet> KLocalePrivate::allDigitSetsList() const
1035{
1036 QList<KLocale::DigitSet> digitSets;
1037 digitSets.append(KLocale::ArabicDigits);
1038 digitSets.append(KLocale::ArabicIndicDigits);
1039 digitSets.append(KLocale::BengaliDigits);
1040 digitSets.append(KLocale::DevenagariDigits);
1041 digitSets.append(KLocale::EasternArabicIndicDigits);
1042 digitSets.append(KLocale::GujaratiDigits);
1043 digitSets.append(KLocale::GurmukhiDigits);
1044 digitSets.append(KLocale::KannadaDigits);
1045 digitSets.append(KLocale::KhmerDigits);
1046 digitSets.append(KLocale::MalayalamDigits);
1047 digitSets.append(KLocale::OriyaDigits);
1048 digitSets.append(KLocale::TamilDigits);
1049 digitSets.append(KLocale::TeluguDigits);
1050 digitSets.append(KLocale::ThaiDigits);
1051 qSort(digitSets);
1052 return digitSets;
1053}
1054
1055QString KLocalePrivate::digitSetString(KLocale::DigitSet digitSet)
1056{
1057 switch (digitSet) {
1058 case KLocale::ArabicIndicDigits:
1059 return QString::fromUtf8("٠١٢٣٤٥٦٧٨٩");
1060 case KLocale::BengaliDigits:
1061 return QString::fromUtf8("০১২৩৪৫৬৭৮৯");
1062 case KLocale::DevenagariDigits:
1063 return QString::fromUtf8("०१२३४५६७८९");
1064 case KLocale::EasternArabicIndicDigits:
1065 return QString::fromUtf8("۰۱۲۳۴۵۶۷۸۹");
1066 case KLocale::GujaratiDigits:
1067 return QString::fromUtf8("૦૧૨૩૪૫૬૭૮૯");
1068 case KLocale::GurmukhiDigits:
1069 return QString::fromUtf8("੦੧੨੩੪੫੬੭੮੯");
1070 case KLocale::KannadaDigits:
1071 return QString::fromUtf8("೦೧೨೩೪೫೬೭೮೯");
1072 case KLocale::KhmerDigits:
1073 return QString::fromUtf8("០១២៣៤៥៦៧៨៩");
1074 case KLocale::MalayalamDigits:
1075 return QString::fromUtf8("൦൧൨൩൪൫൬൭൮൯");
1076 case KLocale::OriyaDigits:
1077 return QString::fromUtf8("୦୧୨୩୪୫୬୭୮୯");
1078 case KLocale::TamilDigits:
1079 return QString::fromUtf8("௦௧௨௩௪௫௬௭௮");
1080 case KLocale::TeluguDigits:
1081 return QString::fromUtf8("౦౧౨౩౪౫౬౭౯");
1082 case KLocale::ThaiDigits:
1083 return QString::fromUtf8("๐๑๒๓๔๕๖๗๘๙");
1084 default:
1085 return QString::fromUtf8("0123456789");
1086 }
1087}
1088
1089QString KLocalePrivate::digitSetToName(KLocale::DigitSet digitSet, bool withDigits) const
1090{
1091 QString name;
1092 switch (digitSet) {
1093 case KLocale::ArabicIndicDigits:
1094 name = i18nc("digit set", "Arabic-Indic");
1095 break;
1096 case KLocale::BengaliDigits:
1097 name = i18nc("digit set", "Bengali");
1098 break;
1099 case KLocale::DevenagariDigits:
1100 name = i18nc("digit set", "Devanagari");
1101 break;
1102 case KLocale::EasternArabicIndicDigits:
1103 name = i18nc("digit set", "Eastern Arabic-Indic");
1104 break;
1105 case KLocale::GujaratiDigits:
1106 name = i18nc("digit set", "Gujarati");
1107 break;
1108 case KLocale::GurmukhiDigits:
1109 name = i18nc("digit set", "Gurmukhi");
1110 break;
1111 case KLocale::KannadaDigits:
1112 name = i18nc("digit set", "Kannada");
1113 break;
1114 case KLocale::KhmerDigits:
1115 name = i18nc("digit set", "Khmer");
1116 break;
1117 case KLocale::MalayalamDigits:
1118 name = i18nc("digit set", "Malayalam");
1119 break;
1120 case KLocale::OriyaDigits:
1121 name = i18nc("digit set", "Oriya");
1122 break;
1123 case KLocale::TamilDigits:
1124 name = i18nc("digit set", "Tamil");
1125 break;
1126 case KLocale::TeluguDigits:
1127 name = i18nc("digit set", "Telugu");
1128 break;
1129 case KLocale::ThaiDigits:
1130 name = i18nc("digit set", "Thai");
1131 break;
1132 default:
1133 name = i18nc("digit set", "Arabic");
1134 }
1135 if (withDigits) {
1136 QString digits = digitSetString(digitSet);
1137 QString nameWithDigits = i18nc("name of digit set with digit string, "
1138 "e.g. 'Arabic (0123456789)'", "%1 (%2)", name, digits);
1139 return nameWithDigits;
1140 } else {
1141 return name;
1142 }
1143}
1144
1145QString KLocalePrivate::convertDigits(const QString &str, KLocale::DigitSet digitSet, bool ignoreContext) const
1146{
1147 if (!ignoreContext) {
1148 // Fall back to Western Arabic digits if requested digit set
1149 // is not appropriate for current application language.
1150 // FIXME: Temporary until full language-sensitivity implemented.
1151 KLocaleStaticData *s = staticData;
1152 if (m_languageSensitiveDigits && !s->languagesUsingDigitSet[digitSet].contains(m_language)) {
1153 digitSet = KLocale::ArabicDigits;
1154 }
1155 }
1156
1157 QString nstr;
1158 QString digitDraw = digitSetString(digitSet);
1159 foreach(const QChar &c, str) {
1160 if (c.isDigit()) {
1161 nstr += digitDraw[c.digitValue()];
1162 } else {
1163 nstr += c;
1164 }
1165 }
1166 return nstr;
1167}
1168
1169QString KLocalePrivate::toArabicDigits(const QString &str)
1170{
1171 QString nstr;
1172 foreach(const QChar &c, str) {
1173 if (c.isDigit()) {
1174 nstr += QChar('0' + c.digitValue());
1175 } else {
1176 nstr += c;
1177 }
1178 }
1179 return nstr;
1180}
1181
1182bool KLocalePrivate::nounDeclension() const
1183{
1184 return m_nounDeclension;
1185}
1186
1187bool KLocalePrivate::dateMonthNamePossessive() const
1188{
1189 return m_dateMonthNamePossessive;
1190}
1191
1192int KLocalePrivate::weekStartDay() const
1193{
1194 return m_weekStartDay;
1195}
1196
1197int KLocalePrivate::workingWeekStartDay() const
1198{
1199 return m_workingWeekStartDay;
1200}
1201
1202int KLocalePrivate::workingWeekEndDay() const
1203{
1204 return m_workingWeekEndDay;
1205}
1206
1207int KLocalePrivate::weekDayOfPray() const
1208{
1209 return m_weekDayOfPray;
1210}
1211
1212int KLocalePrivate::decimalPlaces() const
1213{
1214 return m_decimalPlaces;
1215}
1216
1217QString KLocalePrivate::decimalSymbol() const
1218{
1219 return m_decimalSymbol;
1220}
1221
1222QString KLocalePrivate::thousandsSeparator() const
1223{
1224 return m_thousandsSeparator;
1225}
1226
1227QList<int> KLocalePrivate::numericDigitGrouping() const
1228{
1229 return m_numericDigitGrouping;
1230}
1231
1232QString KLocalePrivate::currencySymbol() const
1233{
1234 return m_currencySymbol;
1235}
1236
1237QString KLocalePrivate::monetaryDecimalSymbol() const
1238{
1239 return m_monetaryDecimalSymbol;
1240}
1241
1242QString KLocalePrivate::monetaryThousandsSeparator() const
1243{
1244 return m_monetaryThousandsSeparator;
1245}
1246
1247QList<int> KLocalePrivate::monetaryDigitGrouping() const
1248{
1249 return m_monetaryDigitGrouping;
1250}
1251
1252QString KLocalePrivate::positiveSign() const
1253{
1254 return m_positiveSign;
1255}
1256
1257QString KLocalePrivate::negativeSign() const
1258{
1259 return m_negativeSign;
1260}
1261
1262/* Just copy to keep the diff looking clean, delete later
1263int KLocale::fracDigits() const
1264{
1265 return monetaryDecimalPlaces();
1266}
1267*/
1268
1269int KLocalePrivate::monetaryDecimalPlaces() const
1270{
1271 return m_monetaryDecimalPlaces;
1272}
1273
1274bool KLocalePrivate::positivePrefixCurrencySymbol() const
1275{
1276 return m_positivePrefixCurrencySymbol;
1277}
1278
1279bool KLocalePrivate::negativePrefixCurrencySymbol() const
1280{
1281 return m_negativePrefixCurrencySymbol;
1282}
1283
1284KLocale::SignPosition KLocalePrivate::positiveMonetarySignPosition() const
1285{
1286 return m_positiveMonetarySignPosition;
1287}
1288
1289KLocale::SignPosition KLocalePrivate::negativeMonetarySignPosition() const
1290{
1291 return m_negativeMonetarySignPosition;
1292}
1293
1294static inline void put_it_in(QChar *buffer, int &index, const QString &s)
1295{
1296 for (int l = 0; l < s.length(); l++) {
1297 buffer[index++] = s.at(l);
1298 }
1299}
1300
1301static inline void put_it_in(QChar *buffer, int &index, int number)
1302{
1303 buffer[index++] = number / 10 + '0';
1304 buffer[index++] = number % 10 + '0';
1305}
1306
1307// Convert POSIX Digit Group Format string into a Qlist<int>, e.g. "3;2" converts to (3,2)
1308QList<int> KLocalePrivate::digitGroupFormatToList(const QString &digitGroupFormat) const
1309{
1310 QList<int> groupList;
1311 QStringList stringList = digitGroupFormat.split(QLatin1Char(';'));
1312 foreach(const QString &size, stringList) {
1313 groupList.append(size.toInt());
1314 }
1315 return groupList;
1316}
1317
1318// Inserts all required occurrences of the group separator into a number string.
1319QString KLocalePrivate::formatDigitGroup(const QString &number, const QString &groupSeparator, const QString &decimalSeperator, QList<int> groupList) const
1320{
1321 if (groupList.isEmpty() || groupSeparator.isEmpty()) {
1322 return number;
1323 }
1324
1325 QString num = number;
1326 int groupCount = groupList.count();
1327 int groupAt = 0;
1328 int groupSize = groupList.at(groupAt);
1329 int pos = num.indexOf(decimalSeperator);
1330 if (pos == -1) {
1331 pos = num.length();
1332 }
1333 pos = pos - groupSize;
1334
1335 while (pos > 0 && groupSize > 0) {
1336 num.insert(pos, groupSeparator);
1337 if (groupAt + 1 < groupCount) {
1338 ++groupAt;
1339 groupSize = groupList.at(groupAt);
1340 }
1341 pos = pos - groupSize;
1342 }
1343
1344 return num;
1345}
1346
1347// Strips all occurrences of the group separator from a number, returns ok if the separators were all in the valid positions
1348QString KLocalePrivate::parseDigitGroup(const QString &number, const QString &groupSeparator, const QString &decimalSeparator, QList<int> groupList, bool *ok) const
1349{
1350 QString num = number;
1351 bool valid = true;
1352
1353 if (!groupSeparator.isEmpty()) {
1354 if (!groupList.isEmpty()) {
1355 int separatorSize = groupSeparator.length();
1356 int groupCount = groupList.count();
1357 int groupAt = 0;
1358 int groupSize = groupList.at(groupAt);
1359 int pos = number.indexOf(decimalSeparator);
1360 if (pos == -1) {
1361 pos = number.length();
1362 }
1363 pos = pos - groupSize - separatorSize;
1364
1365 while (pos > 0 && valid && groupSize > 0) {
1366 if (num.mid(pos, separatorSize) == groupSeparator) {
1367 num.remove(pos, separatorSize);
1368 if (groupAt + 1 < groupCount) {
1369 ++groupAt;
1370 groupSize = groupList.at(groupAt);
1371 }
1372 pos = pos - groupSize - separatorSize;
1373 } else {
1374 valid = false;
1375 }
1376 }
1377 }
1378
1379 if (num.contains(groupSeparator)) {
1380 valid = false;
1381 num = num.remove(groupSeparator);
1382 }
1383 }
1384
1385 if (ok) {
1386 *ok = valid;
1387 }
1388
1389 return num;
1390}
1391
1392QString KLocalePrivate::formatMoney(double num, const QString &symbol, int precision) const
1393{
1394 // some defaults
1395 QString currencyString = symbol;
1396 if (symbol.isNull()) {
1397 currencyString = currencySymbol();
1398 }
1399 if (precision < 0) {
1400 precision = monetaryDecimalPlaces();
1401 }
1402
1403 // the number itself
1404 bool neg = num < 0;
1405 QString res = QString::number(neg ? -num : num, 'f', precision);
1406
1407 // Replace dot with locale decimal separator
1408 res.replace(QLatin1Char('.'), monetaryDecimalSymbol());
1409
1410 // Insert the thousand separators
1411 res = formatDigitGroup(res, monetaryThousandsSeparator(), monetaryDecimalSymbol(), monetaryDigitGrouping());
1412
1413 // set some variables we need later
1414 int signpos = neg
1415 ? negativeMonetarySignPosition()
1416 : positiveMonetarySignPosition();
1417 QString sign = neg
1418 ? negativeSign()
1419 : positiveSign();
1420
1421 switch (signpos) {
1422 case KLocale::ParensAround:
1423 res.prepend(QLatin1Char('('));
1424 res.append(QLatin1Char(')'));
1425 break;
1426 case KLocale::BeforeQuantityMoney:
1427 res.prepend(sign);
1428 break;
1429 case KLocale::AfterQuantityMoney:
1430 res.append(sign);
1431 break;
1432 case KLocale::BeforeMoney:
1433 currencyString.prepend(sign);
1434 break;
1435 case KLocale::AfterMoney:
1436 currencyString.append(sign);
1437 break;
1438 }
1439
1440 if (neg ? negativePrefixCurrencySymbol() :
1441 positivePrefixCurrencySymbol()) {
1442 res.prepend(QLatin1Char(' '));
1443 res.prepend(currencyString);
1444 } else {
1445 res.append(QLatin1Char(' '));
1446 res.append(currencyString);
1447 }
1448
1449 // Convert to target digit set.
1450 res = convertDigits(res, m_monetaryDigitSet);
1451
1452 return res;
1453}
1454
1455
1456QString KLocalePrivate::formatNumber(double num, int precision) const
1457{
1458 if (precision < 0) {
1459 precision = decimalPlaces();
1460 }
1461 // no need to round since QString::number does this for us
1462 return formatNumber(QString::number(num, 'f', precision), false, 0);
1463}
1464
1465QString KLocalePrivate::formatLong(long num) const
1466{
1467 return formatNumber((double)num, 0);
1468}
1469
1470// increase the digit at 'position' by one
1471static void _inc_by_one(QString &str, int position)
1472{
1473 for (int i = position; i >= 0; i--) {
1474 char last_char = str[i].toLatin1();
1475 switch (last_char) {
1476 case '0':
1477 str[i] = '1';
1478 break;
1479 case '1':
1480 str[i] = '2';
1481 break;
1482 case '2':
1483 str[i] = '3';
1484 break;
1485 case '3':
1486 str[i] = '4';
1487 break;
1488 case '4':
1489 str[i] = '5';
1490 break;
1491 case '5':
1492 str[i] = '6';
1493 break;
1494 case '6':
1495 str[i] = '7';
1496 break;
1497 case '7':
1498 str[i] = '8';
1499 break;
1500 case '8':
1501 str[i] = '9';
1502 break;
1503 case '9':
1504 str[i] = '0';
1505 if (i == 0) str.prepend(QLatin1Char('1'));
1506 continue;
1507 case '.':
1508 continue;
1509 }
1510 break;
1511 }
1512}
1513
1514// Cut off if more digits in fractional part than 'precision'
1515static void _round(QString &str, int precision)
1516{
1517 int decimalSymbolPos = str.indexOf(QLatin1Char('.'));
1518
1519 if (decimalSymbolPos == -1) {
1520 if (precision == 0) return;
1521 else if (precision > 0) { // add dot if missing (and needed)
1522 str.append(QLatin1Char('.'));
1523 decimalSymbolPos = str.length() - 1;
1524 }
1525 }
1526 // fill up with more than enough zeroes (in case fractional part too short)
1527 str.reserve(str.length() + precision);
1528 for (int i = 0; i < precision; ++i)
1529 str.append(QLatin1Char('0'));
1530
1531 // Now decide whether to round up or down
1532 char last_char = str[decimalSymbolPos + precision + 1].toLatin1();
1533 switch (last_char) {
1534 case '0':
1535 case '1':
1536 case '2':
1537 case '3':
1538 case '4':
1539 // nothing to do, rounding down
1540 break;
1541 case '5':
1542 case '6':
1543 case '7':
1544 case '8':
1545 case '9':
1546 _inc_by_one(str, decimalSymbolPos + precision);
1547 break;
1548 default:
1549 break;
1550 }
1551
1552 decimalSymbolPos = str.indexOf(QLatin1Char('.'));
1553 str.truncate(decimalSymbolPos + precision + 1);
1554
1555 // if precision == 0 delete also '.'
1556 if (precision == 0) {
1557 str = str.left(decimalSymbolPos);
1558 }
1559
1560 str.squeeze();
1561}
1562
1563QString KLocalePrivate::formatNumber(const QString &numStr, bool round, int precision) const
1564{
1565 QString tmpString = numStr;
1566
1567 if (precision < 0) {
1568 precision = decimalPlaces();
1569 }
1570
1571 // Skip the sign (for now)
1572 const bool neg = (tmpString[0] == QLatin1Char('-'));
1573 if (neg || tmpString[0] == QLatin1Char('+')) {
1574 tmpString.remove(0, 1);
1575 }
1576
1577 //kDebug(173)<<"tmpString:"<<tmpString;
1578
1579 // Split off exponential part (including 'e'-symbol)
1580 const int expPos = tmpString.indexOf(QLatin1Char('e')); // -1 if not found
1581 QString mantString = tmpString.left(expPos); // entire string if no 'e' found
1582 QString expString;
1583 if (expPos > -1) {
1584 expString = tmpString.mid(expPos); // includes the 'e', or empty if no 'e'
1585 if (expString.length() == 1) {
1586 expString.clear();
1587 }
1588 }
1589
1590 //kDebug(173)<<"mantString:"<<mantString;
1591 //kDebug(173)<<"expString:"<<expString;
1592 if (mantString.isEmpty() || !mantString[0].isDigit()) {// invalid number
1593 mantString = QLatin1Char('0');
1594 }
1595
1596 if (round) {
1597 _round(mantString, precision);
1598 }
1599
1600 // Replace dot with locale decimal separator
1601 mantString.replace(QLatin1Char('.'), decimalSymbol());
1602
1603 // Insert the thousand separators
1604 mantString = formatDigitGroup(mantString, thousandsSeparator(), decimalSymbol(), numericDigitGrouping());
1605
1606 // How can we know where we should put the sign?
1607 mantString.prepend(neg ? negativeSign() : positiveSign());
1608
1609 // Convert to target digit set.
1610 if (digitSet() != KLocale::ArabicDigits) {
1611 mantString = convertDigits(mantString, digitSet());
1612 expString = convertDigits(expString, digitSet());
1613 }
1614
1615 return mantString + expString;
1616}
1617
1618// Returns a list of already translated units to use later in formatByteSize
1619// and friends. Account for every unit in KLocale::BinarySizeUnits
1620QList<QString> KLocalePrivate::dialectUnitsList(KLocale::BinaryUnitDialect dialect)
1621{
1622 QList<QString> binaryUnits;
1623 QString s; // Used in CACHE_BYTE_FMT macro defined shortly
1624
1625 // Adds a given translation to the binaryUnits list.
1626#define CACHE_BYTE_FMT(ctxt_text) \
1627 translateRawFrom(0, ctxt_text, 0, 0, 0, &s); \
1628 binaryUnits.append(s);
1629
1630 // Do not remove i18n: comments below, they are used by the
1631 // translators.
1632
1633 // This prefix is shared by all current dialects.
1634 // i18n: Dumb message, avoid any markup or scripting.
1635 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in bytes", "%1 B"));
1636
1637 switch (dialect) {
1638 case KLocale::MetricBinaryDialect:
1639 // i18n: Dumb message, avoid any markup or scripting.
1640 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 1000 bytes", "%1 kB"));
1641 // i18n: Dumb message, avoid any markup or scripting.
1642 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 10^6 bytes", "%1 MB"));
1643 // i18n: Dumb message, avoid any markup or scripting.
1644 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 10^9 bytes", "%1 GB"));
1645 // i18n: Dumb message, avoid any markup or scripting.
1646 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 10^12 bytes", "%1 TB"));
1647 // i18n: Dumb message, avoid any markup or scripting.
1648 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 10^15 bytes", "%1 PB"));
1649 // i18n: Dumb message, avoid any markup or scripting.
1650 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 10^18 bytes", "%1 EB"));
1651 // i18n: Dumb message, avoid any markup or scripting.
1652 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 10^21 bytes", "%1 ZB"));
1653 // i18n: Dumb message, avoid any markup or scripting.
1654 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 10^24 bytes", "%1 YB"));
1655 break;
1656
1657 case KLocale::JEDECBinaryDialect:
1658 // i18n: Dumb message, avoid any markup or scripting.
1659 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 1024 bytes", "%1 KB"));
1660 // i18n: Dumb message, avoid any markup or scripting.
1661 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 2^20 bytes", "%1 MB"));
1662 // i18n: Dumb message, avoid any markup or scripting.
1663 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 2^30 bytes", "%1 GB"));
1664 // i18n: Dumb message, avoid any markup or scripting.
1665 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 2^40 bytes", "%1 TB"));
1666 // i18n: Dumb message, avoid any markup or scripting.
1667 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 2^50 bytes", "%1 PB"));
1668 // i18n: Dumb message, avoid any markup or scripting.
1669 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 2^60 bytes", "%1 EB"));
1670 // i18n: Dumb message, avoid any markup or scripting.
1671 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 2^70 bytes", "%1 ZB"));
1672 // i18n: Dumb message, avoid any markup or scripting.
1673 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 2^80 bytes", "%1 YB"));
1674 break;
1675
1676 case KLocale::IECBinaryDialect:
1677 default:
1678 // i18n: Dumb message, avoid any markup or scripting.
1679 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 1024 bytes", "%1 KiB"));
1680 // i18n: Dumb message, avoid any markup or scripting.
1681 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 2^20 bytes", "%1 MiB"));
1682 // i18n: Dumb message, avoid any markup or scripting.
1683 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 2^30 bytes", "%1 GiB"));
1684 // i18n: Dumb message, avoid any markup or scripting.
1685 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 2^40 bytes", "%1 TiB"));
1686 // i18n: Dumb message, avoid any markup or scripting.
1687 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 2^50 bytes", "%1 PiB"));
1688 // i18n: Dumb message, avoid any markup or scripting.
1689 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 2^60 bytes", "%1 EiB"));
1690 // i18n: Dumb message, avoid any markup or scripting.
1691 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 2^70 bytes", "%1 ZiB"));
1692 // i18n: Dumb message, avoid any markup or scripting.
1693 CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 2^80 bytes", "%1 YiB"));
1694 break;
1695 }
1696
1697 return binaryUnits;
1698}
1699
1700QString KLocalePrivate::formatByteSize(double size, int precision, KLocale::BinaryUnitDialect dialect,
1701 KLocale::BinarySizeUnits specificUnit)
1702{
1703 // Error checking
1704 if (dialect <= KLocale::DefaultBinaryDialect || dialect > KLocale::LastBinaryDialect) {
1705 dialect = m_binaryUnitDialect;
1706 }
1707
1708 if (specificUnit < KLocale::DefaultBinaryUnits || specificUnit > KLocale::UnitLastUnit) {
1709 specificUnit = KLocale::DefaultBinaryUnits;
1710 }
1711
1712 // Choose appropriate units.
1713 QList<QString> dialectUnits;
1714 if (dialect == m_binaryUnitDialect) {
1715 // Cache default units for speed
1716 if (m_byteSizeFmt.size() == 0) {
1717 QMutexLocker lock(kLocaleMutex());
1718
1719 // We only cache the user's default dialect.
1720 m_byteSizeFmt = dialectUnitsList(m_binaryUnitDialect);
1721 }
1722
1723 dialectUnits = m_byteSizeFmt;
1724 } else {
1725 dialectUnits = dialectUnitsList(dialect);
1726 }
1727
1728 int unit = 0; // Selects what unit to use from cached list
1729 double multiplier = 1024.0;
1730
1731 if (dialect == KLocale::MetricBinaryDialect) {
1732 multiplier = 1000.0;
1733 }
1734
1735 // If a specific unit conversion is given, use it directly. Otherwise
1736 // search until the result is in [0, multiplier) (or out of our range).
1737 if (specificUnit == KLocale::DefaultBinaryUnits) {
1738 while (qAbs(size) >= multiplier && unit < (int) KLocale::UnitYottaByte) {
1739 size /= multiplier;
1740 unit++;
1741 }
1742 } else {
1743 // A specific unit is in use
1744 unit = static_cast<int>(specificUnit);
1745 if (unit > 0) {
1746 size /= pow(multiplier, unit);
1747 }
1748 }
1749
1750 if (unit == 0) {
1751 // Bytes, no rounding
1752 return dialectUnits[unit].arg(formatNumber(size, 0));
1753 }
1754
1755 return dialectUnits[unit].arg(formatNumber(size, precision));
1756}
1757
1758QString KLocalePrivate::formatByteSize(double size)
1759{
1760 return formatByteSize(size, 1);
1761}
1762
1763KLocale::BinaryUnitDialect KLocalePrivate::binaryUnitDialect() const
1764{
1765 return m_binaryUnitDialect;
1766}
1767
1768void KLocalePrivate::setBinaryUnitDialect(KLocale::BinaryUnitDialect newDialect)
1769{
1770 if (newDialect > KLocale::DefaultBinaryDialect && newDialect <= KLocale::LastBinaryDialect) {
1771 QMutexLocker lock(kLocaleMutex());
1772 m_binaryUnitDialect = newDialect;
1773 m_byteSizeFmt.clear(); // Reset cached translations.
1774 }
1775}
1776
1777QString KLocalePrivate::formatDuration(unsigned long mSec) const
1778{
1779 if (mSec >= 24*3600000) {
1780 return i18nc("@item:intext %1 is a real number, e.g. 1.23 days", "%1 days",
1781 formatNumber(mSec / (24 * 3600000.0), 2));
1782 } else if (mSec >= 3600000) {
1783 return i18nc("@item:intext %1 is a real number, e.g. 1.23 hours", "%1 hours",
1784 formatNumber(mSec / 3600000.0, 2));
1785 } else if (mSec >= 60000) {
1786 return i18nc("@item:intext %1 is a real number, e.g. 1.23 minutes", "%1 minutes",
1787 formatNumber(mSec / 60000.0, 2));
1788 } else if (mSec >= 1000) {
1789 return i18nc("@item:intext %1 is a real number, e.g. 1.23 seconds", "%1 seconds",
1790 formatNumber(mSec / 1000.0, 2));
1791 }
1792 return i18ncp("@item:intext", "%1 millisecond", "%1 milliseconds", mSec);
1793}
1794
1795QString KLocalePrivate::formatSingleDuration(KLocalePrivate::DurationType durationType, int n)
1796{
1797 switch (durationType) {
1798 case KLocalePrivate::DaysDurationType:
1799 return i18ncp("@item:intext", "1 day", "%1 days", n);
1800 case KLocalePrivate::HoursDurationType:
1801 return i18ncp("@item:intext", "1 hour", "%1 hours", n);
1802 case KLocalePrivate::MinutesDurationType:
1803 return i18ncp("@item:intext", "1 minute", "%1 minutes", n);
1804 case KLocalePrivate::SecondsDurationType:
1805 return i18ncp("@item:intext", "1 second", "%1 seconds", n);
1806 }
1807 return QString();
1808}
1809
1810QString KLocalePrivate::prettyFormatDuration(unsigned long mSec) const
1811{
1812 unsigned long ms = mSec;
1813 int days = ms / (24 * 3600000);
1814 ms = ms % (24 * 3600000);
1815 int hours = ms / 3600000;
1816 ms = ms % 3600000;
1817 int minutes = ms / 60000;
1818 ms = ms % 60000;
1819 int seconds = qRound(ms / 1000.0);
1820
1821 // Handle correctly problematic case #1 (look at KLocaleTest::prettyFormatDuration()
1822 // at klocaletest.cpp)
1823 if (seconds == 60) {
1824 return prettyFormatDuration(mSec - ms + 60000);
1825 }
1826
1827 if (days && hours) {
1828 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",
1829 "%1 and %2", formatSingleDuration(KLocalePrivate::DaysDurationType, days),
1830 formatSingleDuration(KLocalePrivate::HoursDurationType, hours));
1831 } else if (days) {
1832 return formatSingleDuration(KLocalePrivate::DaysDurationType, days);
1833 } else if (hours && minutes) {
1834 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",
1835 "%1 and %2",
1836 formatSingleDuration(KLocalePrivate::HoursDurationType, hours),
1837 formatSingleDuration(KLocalePrivate::MinutesDurationType, minutes));
1838 } else if (hours) {
1839 return formatSingleDuration(KLocalePrivate::HoursDurationType, hours);
1840 } else if (minutes && seconds) {
1841 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",
1842 "%1 and %2",
1843 formatSingleDuration(KLocalePrivate::MinutesDurationType, minutes),
1844 formatSingleDuration(KLocalePrivate::SecondsDurationType, seconds));
1845 } else if (minutes) {
1846 return formatSingleDuration(KLocalePrivate::MinutesDurationType, minutes);
1847 } else {
1848 return formatSingleDuration(KLocalePrivate::SecondsDurationType, seconds);
1849 }
1850}
1851
1852QString KLocalePrivate::formatDate(const QDate &date, KLocale::DateFormat format)
1853{
1854 return calendar()->formatDate(date, format);
1855}
1856
1857void KLocalePrivate::setMainCatalog(const char *catalog)
1858{
1859 KLocaleStaticData *s = staticData;
1860 s->maincatalog = QString::fromUtf8(catalog);
1861}
1862
1863double KLocalePrivate::readNumber(const QString &_str, bool * ok) const
1864{
1865 QString str = _str.trimmed();
1866 bool neg = false;
1867
1868 // Check negative or positive signs
1869 // Assumes blank sign is positive even if pos sign set, unless already taken by negative
1870 if (!negativeSign().isEmpty() && str.indexOf(negativeSign()) == 0) {
1871 neg = true;
1872 str.remove(0, negativeSign().length());
1873 str = str.trimmed();
1874 } else if (!positiveSign().isEmpty() && str.indexOf(positiveSign()) == 0) {
1875 neg = false;
1876 str.remove(0, positiveSign().length());
1877 str = str.trimmed();
1878 } else if (negativeSign().isEmpty() && str[0].isDigit()) {
1879 neg = true;
1880 }
1881
1882 /* will hold the scientific notation portion of the number.
1883 Example, with 2.34E+23, exponentialPart == "E+23"
1884 */
1885 QString exponentialPart;
1886 int EPos;
1887
1888 EPos = str.indexOf(QLatin1Char('E'), 0, Qt::CaseInsensitive);
1889
1890 if (EPos != -1) {
1891 exponentialPart = str.mid(EPos);
1892 str = str.left(EPos);
1893 str = str.trimmed();
1894 }
1895
1896 // Remove group separators
1897 bool groupOk = true;
1898 if(str.contains(thousandsSeparator())) {
1899 str = parseDigitGroup(str, thousandsSeparator(), decimalSymbol(),
1900 numericDigitGrouping(), &groupOk);
1901 }
1902
1903 if (!groupOk) {
1904 if (ok) {
1905 *ok = false;
1906 }
1907 return 0.0;
1908 }
1909
1910 int pos = str.indexOf(decimalSymbol());
1911 QString major;
1912 QString minor;
1913 if (pos == -1) {
1914 major = str;
1915 } else {
1916 major = str.left(pos);
1917 minor = str.mid(pos + decimalSymbol().length());
1918 }
1919
1920 // Check the major and minor parts are only digits
1921 bool digitTest = true;
1922 foreach (const QChar &ch, major) {
1923 if (!ch.isDigit()) {
1924 digitTest = false;
1925 break;
1926 }
1927 }
1928 foreach (const QChar &ch, minor) {
1929 if (!ch.isDigit()) {
1930 digitTest = false;
1931 break;
1932 }
1933 }
1934 if (!digitTest) {
1935 if (ok) {
1936 *ok = false;
1937 }
1938 return 0.0;
1939 }
1940
1941 QString tot;
1942 if (neg) {
1943 tot = QLatin1Char('-');
1944 }
1945 tot += major + QLatin1Char('.') + minor + exponentialPart;
1946 tot = toArabicDigits(tot);
1947 return tot.toDouble(ok);
1948}
1949
1950double KLocalePrivate::readMoney(const QString &_str, bool *ok) const
1951{
1952 QString str = _str.trimmed();
1953 bool neg = false;
1954 bool currencyFound = false;
1955 QString symbol = currencySymbol();
1956
1957 // First try removing currency symbol from either end
1958 int pos = str.indexOf(symbol);
1959 if (pos == 0 || pos == (int) str.length() - symbol.length()) {
1960 str.remove(pos, symbol.length());
1961 str = str.trimmed();
1962 currencyFound = true;
1963 }
1964 if (str.isEmpty()) {
1965 if (ok) {
1966 *ok = false;
1967 }
1968 return 0;
1969 }
1970
1971 // Then try removing sign from either end (with a special case for parenthesis)
1972 if (str[0] == QLatin1Char('(') && str[str.length()-1] == QLatin1Char(')')) {
1973 if (positiveMonetarySignPosition() != KLocale::ParensAround) {
1974 neg = true;
1975 }
1976 str.remove(str.length() - 1, 1);
1977 str.remove(0, 1);
1978 str = str.trimmed();
1979 } else {
1980 int len = 0;
1981 QString sign;
1982 int negLen = negativeSign().length();
1983 QString negSign = negativeSign();
1984 if (!negSign.isEmpty() && (str.left(negLen) == negSign || str.right(negSign.length()) == negSign)) {
1985 neg = true;
1986 len = negLen;
1987 sign = negSign;
1988 } else {
1989 int posLen = positiveSign().length();
1990 QString posSign = positiveSign();
1991 if (!posSign.isEmpty() && (str.left(posLen) == posSign || str.right(posSign.length()) == posSign)) {
1992 len = posLen;
1993 sign = posSign;
1994 } else if (negSign.isEmpty() && str[0].isDigit() && str[str.length() - 1].isDigit()){
1995 neg = true;
1996 }
1997 }
1998 if (!sign.isEmpty()) {
1999 if (str.left(len) == sign) {
2000 str.remove(0, len);
2001 } else {
2002 str.remove(str.length() - len, len);
2003 }
2004 str = str.trimmed();
2005 }
2006 }
2007
2008 // Finally try again for the currency symbol, if we didn't find
2009 // it already (because of the negative sign being in the way).
2010 if (!currencyFound) {
2011 pos = str.indexOf(symbol);
2012 if (pos == 0 || pos == (int) str.length() - symbol.length()) {
2013 str.remove(pos, symbol.length());
2014 str = str.trimmed();
2015 }
2016 }
2017
2018 // Remove group separators
2019 bool groupOk = true;
2020 if(str.contains(monetaryThousandsSeparator())) {
2021 str = parseDigitGroup(str, monetaryThousandsSeparator(), monetaryDecimalSymbol(),
2022 monetaryDigitGrouping(), &groupOk);
2023 }
2024
2025 if (!groupOk) {
2026 if (ok) {
2027 *ok = false;
2028 }
2029 return 0.0;
2030 }
2031
2032 // And parse the rest as a number
2033 pos = str.indexOf(monetaryDecimalSymbol());
2034 QString major;
2035 QString minor;
2036 if (pos == -1) {
2037 major = str;
2038 } else {
2039 major = str.left(pos);
2040 minor = str.mid(pos + monetaryDecimalSymbol().length());
2041 }
2042
2043 // Check the major and minor parts are only digits
2044 bool digitTest = true;
2045 foreach (const QChar &ch, major) {
2046 if (!ch.isDigit()) {
2047 digitTest = false;
2048 break;
2049 }
2050 }
2051 foreach (const QChar &ch, minor) {
2052 if (!ch.isDigit()) {
2053 digitTest = false;
2054 break;
2055 }
2056 }
2057 if (!digitTest) {
2058 if (ok) {
2059 *ok = false;
2060 }
2061 return 0.0;
2062 }
2063
2064 QString tot;
2065 if (neg) {
2066 tot = QLatin1Char('-');
2067 }
2068 tot += major + QLatin1Char('.') + minor;
2069 tot = toArabicDigits(tot);
2070 return tot.toDouble(ok);
2071}
2072
2073/**
2074 * helper function to read integers
2075 * @param str
2076 * @param pos the position to start at. It will be updated when we parse it.
2077 * @return the integer read in the string, or -1 if no string
2078 */
2079static int readInt(const QString &str, int &pos)
2080{
2081 if (!str.at(pos).isDigit()) {
2082 return -1;
2083 }
2084 int result = 0;
2085 for (; str.length() > pos && str.at(pos).isDigit(); ++pos) {
2086 result *= 10;
2087 result += str.at(pos).digitValue();
2088 }
2089
2090 return result;
2091}
2092
2093QDate KLocalePrivate::readDate(const QString &intstr, bool *ok)
2094{
2095 return calendar()->readDate(intstr, ok);
2096}
2097
2098QDate KLocalePrivate::readDate(const QString &intstr, KLocale::ReadDateFlags flags, bool *ok)
2099{
2100 return calendar()->readDate(intstr, flags, ok);
2101}
2102
2103QDate KLocalePrivate::readDate(const QString &intstr, const QString &fmt, bool *ok)
2104{
2105 return calendar()->readDate(intstr, fmt, ok);
2106}
2107
2108QTime KLocalePrivate::readTime(const QString &intstr, bool *ok) const
2109{
2110 QTime time = readLocaleTime(intstr, ok, KLocale::TimeDefault, KLocale::ProcessStrict);
2111 if (time.isValid()) {
2112 return time;
2113 }
2114 return readLocaleTime(intstr, ok, KLocale::TimeWithoutSeconds, KLocale::ProcessStrict);
2115}
2116
2117QTime KLocalePrivate::readTime(const QString &intstr, KLocale::ReadTimeFlags flags, bool *ok) const
2118{
2119 return readLocaleTime(intstr, ok, (flags == KLocale::WithSeconds) ? KLocale::TimeDefault : KLocale::TimeWithoutSeconds,
2120 KLocale::ProcessStrict);
2121}
2122
2123// remove the first occurrence of the 2-character string
2124// strip2char from inout and if found, also remove one preceding
2125// punctuation character and arbitrary number of spaces.
2126static void stripStringAndPreceedingSeparator(QString &inout, const QLatin1String &strip2char)
2127{
2128 int remPos = inout.indexOf(strip2char);
2129 if (remPos == -1) {
2130 return;
2131 }
2132 int endPos = remPos + 2;
2133 int curPos = remPos - 1;
2134 while (curPos >= 0 && inout.at(curPos).isSpace()) {
2135 curPos--;
2136 }
2137 // remove the separator sign before the seconds
2138 // and assume that works everywhere
2139 if (curPos >= 0 && inout.at(curPos).isPunct() && inout.at(curPos) != QLatin1Char('%')) {
2140 curPos--;
2141 }
2142 while (curPos >= 0 && inout.at(curPos).isSpace()) {
2143 curPos--;
2144 }
2145
2146 remPos = qMax(curPos + 1, 0);
2147 inout.remove(remPos, endPos - remPos);
2148}
2149
2150// remove the first occurrence of the 2-character string
2151// strip2char from inout and if found, also remove one
2152// succeeding punctuation character and arbitrary number of spaces.
2153static void stripStringAndSucceedingSeparator(QString &inout, const QLatin1String &strip2char)
2154{
2155 int remPos = inout.indexOf(strip2char);
2156 if (remPos == -1) {
2157 return;
2158 }
2159 int curPos = remPos + 2;
2160 while (curPos < inout.size() &&
2161 (inout.at(curPos).isSpace() ||
2162 (inout.at(curPos).isPunct() && inout.at(curPos) != QLatin1Char('%')))) {
2163 curPos++;
2164 }
2165 inout.remove(remPos, curPos - remPos);
2166}
2167
2168// remove the first occurrence of "%p" from the inout.
2169static void stripAmPmFormat(QString &inout)
2170{
2171 // NOTE: this function assumes that %p - if it's present -
2172 // is either the first or the last element of the format
2173 // string. Either a succeeding or a preceding
2174 // punctuation symbol is stripped.
2175 int length = inout.size();
2176 int ppos = inout.indexOf(QLatin1String("%p"));
2177 if (ppos == -1) {
2178 return;
2179 } else if (ppos == 0) {
2180 // first element, eat succeeding punctuation and spaces
2181 ppos = 2;
2182 while (ppos < length && (inout.at(ppos).isSpace() || inout.at(ppos).isPunct()) &&
2183 inout.at(ppos) != QLatin1Char('%')) {
2184 ppos++;
2185 }
2186 inout = inout.mid(ppos);
2187 } else {
2188 stripStringAndPreceedingSeparator(inout, QLatin1String("%p"));
2189 }
2190}
2191
2192QTime KLocalePrivate::readLocaleTime(const QString &intstr, bool *ok, KLocale::TimeFormatOptions options,
2193 KLocale::TimeProcessingOptions processing) const
2194{
2195 QString str(intstr.simplified().toLower());
2196 QString format(timeFormat().simplified());
2197
2198 int hour = -1;
2199 int minute = -1;
2200 int second = -1;
2201 bool useDayPeriod = false;
2202 KDayPeriod dayPeriod = dayPeriodForTime(QTime(0,0,0));
2203 int strpos = 0;
2204 int formatpos = 0;
2205 bool error = false;
2206
2207 bool excludeSecs = ((options & KLocale::TimeWithoutSeconds) == KLocale::TimeWithoutSeconds);
2208 bool isDuration = ((options & KLocale::TimeDuration) == KLocale::TimeDuration);
2209 bool noAmPm = ((options & KLocale::TimeWithoutAmPm) == KLocale::TimeWithoutAmPm);
2210 bool foldHours = ((options & KLocale::TimeFoldHours) == KLocale::TimeFoldHours);
2211 bool strict = ((processing & KLocale::ProcessStrict) == KLocale::ProcessStrict);
2212
2213 // if seconds aren't needed, strip them from the timeFormat
2214 if (excludeSecs) {
2215 stripStringAndPreceedingSeparator(format, QLatin1String("%S"));
2216 second = 0; // seconds are always 0
2217 }
2218
2219 // if hours are folded, strip them from the timeFormat
2220 if (foldHours) {
2221 stripStringAndSucceedingSeparator(format, QLatin1String("%H"));
2222 stripStringAndSucceedingSeparator(format, QLatin1String("%k"));
2223 stripStringAndSucceedingSeparator(format, QLatin1String("%I"));
2224 stripStringAndSucceedingSeparator(format, QLatin1String("%l"));
2225 }
2226
2227 // if am/pm isn't needed, strip it from the timeFormat
2228 if (noAmPm) {
2229 stripAmPmFormat(format);
2230 }
2231
2232 while (!error && (format.length() > formatpos || str.length() > strpos)) {
2233 if (!(format.length() > formatpos && str.length() > strpos)) {
2234 error = true;
2235 break;
2236 }
2237
2238 QChar c = format.at(formatpos++);
2239 if (c.isSpace()) {
2240 if (strict) { // strict processing: space is needed
2241 if (!str.at(strpos).isSpace()) {
2242 error = true;
2243 break;
2244 }
2245 strpos++;
2246 } else { // lax processing: space in str not needed
2247 // 1 space maximum as str is simplified
2248 if (str.at(strpos).isSpace()) {
2249 strpos++;
2250 }
2251 }
2252 continue;
2253 }
2254
2255 if (c != QLatin1Char('%')) {
2256 if (c != str.at(strpos++)) {
2257 error = true;
2258 break;
2259 }
2260 continue;
2261 }
2262
2263 c = format.at(formatpos++);
2264 switch (c.unicode()) {
2265
2266 case 'p': // Day Period, normally AM/PM
2267 case 'P': // Lowercase Day Period, normally am/pm
2268 {
2269 error = true;
2270 foreach (const KDayPeriod &testDayPeriod, dayPeriods()) {
2271 QString dayPeriodText = testDayPeriod.periodName(KLocale::ShortName);
2272 int len = dayPeriodText.length();
2273 if (str.mid(strpos, len) == dayPeriodText.toLower()) {
2274 dayPeriod = testDayPeriod;
2275 strpos += len;
2276 error = false;
2277 useDayPeriod = true;
2278 break;
2279 }
2280 }
2281 break;
2282 }
2283
2284 case 'k': // 24h Hours Short Number
2285 case 'H': // 24h Hours Long Number
2286 useDayPeriod = false;
2287 hour = readInt(str, strpos);
2288 break;
2289
2290 case 'l': // 12h Hours Short Number
2291 case 'I': // 12h Hours Long Number
2292 useDayPeriod = !isDuration;
2293 hour = readInt(str, strpos);
2294 break;
2295
2296 case 'M':
2297 minute = readInt(str, strpos);
2298 // minutes can be bigger than 59 if hours are folded
2299 if (foldHours) {
2300 // if hours are folded, make sure minutes doesn't get bigger than 59.
2301 hour = minute / 60;
2302 minute = minute % 60;
2303 }
2304 break;
2305
2306 case 'S':
2307 second = readInt(str, strpos);
2308 break;
2309 }
2310
2311 // NOTE: if anything is performed inside this loop, be sure to
2312 // check for error!
2313 }
2314
2315 QTime returnTime;
2316 if (!error) {
2317 if (useDayPeriod) {
2318 returnTime = dayPeriod.time(hour, minute, second);
2319 } else {
2320 returnTime = QTime(hour, minute, second);
2321 }
2322 }
2323 if (ok) {
2324 *ok = returnTime.isValid();
2325 }
2326 return returnTime;
2327}
2328
2329QString KLocalePrivate::formatTime(const QTime &time, bool includeSecs, bool isDuration) const
2330{
2331 KLocale::TimeFormatOptions options = KLocale::TimeDefault;
2332 if (!includeSecs) {
2333 options |= KLocale::TimeWithoutSeconds;
2334 }
2335 if (isDuration) {
2336 options |= KLocale::TimeDuration;
2337 }
2338 return formatLocaleTime(time, options);
2339}
2340
2341QString KLocalePrivate::formatLocaleTime(const QTime &time, KLocale::TimeFormatOptions options) const
2342{
2343 QString rst(timeFormat());
2344
2345 bool excludeSecs = ((options & KLocale::TimeWithoutSeconds) == KLocale::TimeWithoutSeconds);
2346 bool isDuration = ((options & KLocale::TimeDuration) == KLocale::TimeDuration);
2347 bool noAmPm = ((options & KLocale::TimeWithoutAmPm) == KLocale::TimeWithoutAmPm);
2348 bool foldHours = ((options & KLocale::TimeFoldHours) == KLocale::TimeFoldHours);
2349
2350 // if seconds aren't needed, strip them from the timeFormat
2351 if (excludeSecs) {
2352 stripStringAndPreceedingSeparator(rst, QLatin1String("%S"));
2353 }
2354
2355 // if hours should be folded, strip all hour symbols from the timeFormat
2356 if (foldHours) {
2357 stripStringAndSucceedingSeparator(rst, QLatin1String("%H"));
2358 stripStringAndSucceedingSeparator(rst, QLatin1String("%k"));
2359 stripStringAndSucceedingSeparator(rst, QLatin1String("%I"));
2360 stripStringAndSucceedingSeparator(rst, QLatin1String("%l"));
2361 }
2362
2363 // if am/pm isn't needed, strip it from the timeFormat
2364 if (noAmPm) {
2365 stripAmPmFormat(rst);
2366 }
2367
2368 // only "pm/am" and %M here can grow, the rest shrinks, but
2369 // I'm rather safe than sorry
2370 QChar *buffer = new QChar[rst.length() * 3 / 2 + 32];
2371
2372 int index = 0;
2373 bool escape = false;
2374 int number = 0;
2375
2376 for (int format_index = 0; format_index < rst.length(); format_index++) {
2377 if (!escape) {
2378 if (rst.at(format_index).unicode() == '%') {
2379 escape = true;
2380 } else {
2381 buffer[index++] = rst.at(format_index);
2382 }
2383 } else {
2384 switch (rst.at(format_index).unicode()) {
2385 case '%':
2386 buffer[index++] = QLatin1Char('%');
2387 break;
2388 case 'H':
2389 put_it_in(buffer, index, time.hour());
2390 break;
2391 case 'I':
2392 if (isDuration) {
2393 put_it_in(buffer, index, time.hour());
2394 } else {
2395 put_it_in(buffer, index, dayPeriodForTime(time).hourInPeriod(time));
2396 }
2397 break;
2398 case 'M':
2399 if (foldHours) {
2400 put_it_in(buffer, index, QString::number(time.hour() * 60 + time.minute()));
2401 } else {
2402 put_it_in(buffer, index, time.minute());
2403 }
2404 break;
2405 case 'S':
2406 put_it_in(buffer, index, time.second());
2407 break;
2408 case 'k':
2409 case 'l':
2410 // to share the code
2411 if (!isDuration && rst.at(format_index).unicode() == 'l') {
2412 number = dayPeriodForTime(time).hourInPeriod(time);
2413 } else {
2414 number = time.hour();
2415 }
2416 if (number / 10) {
2417 buffer[index++] = number / 10 + '0';
2418 }
2419 buffer[index++] = number % 10 + '0';
2420 break;
2421 case 'p':
2422 {
2423 put_it_in(buffer, index, dayPeriodForTime(time).periodName(KLocale::ShortName));
2424 break;
2425 }
2426 default:
2427 buffer[index++] = rst.at(format_index);
2428 break;
2429 }
2430 escape = false;
2431 }
2432 }
2433 QString ret(buffer, index);
2434 delete [] buffer;
2435 ret = convertDigits(ret, dateTimeDigitSet());
2436 return ret.trimmed();
2437}
2438
2439bool KLocalePrivate::use12Clock() const
2440{
2441 if ((timeFormat().contains(QString::fromLatin1("%I")) > 0) ||
2442 (timeFormat().contains(QString::fromLatin1("%l")) > 0)) {
2443 return true;
2444 } else {
2445 return false;
2446 }
2447}
2448
2449void KLocalePrivate::setDayPeriods(const QList<KDayPeriod> &dayPeriods)
2450{
2451 if (dayPeriods.count() > 0) {
2452 foreach (const KDayPeriod &dayPeriod, dayPeriods) {
2453 if (!dayPeriod.isValid()) {
2454 return;
2455 }
2456 }
2457 m_dayPeriods = dayPeriods;
2458 }
2459}
2460
2461QList<KDayPeriod> KLocalePrivate::dayPeriods() const
2462{
2463 // If no Day Periods currently loaded then it means there were no country specific ones defined
2464 // in the country l10n file, so default to standard AM/PM translations for the users language.
2465 // Note we couldn't do this in initDayPeriods() as i18n isn't available until we have a
2466 // valid loacle constructed.
2467 if (m_dayPeriods.isEmpty()) {
2468 m_dayPeriods.append(KDayPeriod(QString::fromLatin1("am"),
2469 i18nc( "Before Noon KLocale::LongName", "Ante Meridiem" ),
2470 i18nc( "Before Noon KLocale::ShortName", "AM" ),
2471 i18nc( "Before Noon KLocale::NarrowName", "A" ),
2472 QTime( 0, 0, 0 ), QTime( 11, 59, 59, 999 ), 0, 12 ));
2473 m_dayPeriods.append(KDayPeriod(QString::fromLatin1("pm"),
2474 i18nc( "After Noon KLocale::LongName", "Post Meridiem" ),
2475 i18nc( "After Noon KLocale::ShortName", "PM" ),
2476 i18nc( "After Noon KLocale::NarrowName", "P" ),
2477 QTime( 12, 0, 0 ), QTime( 23, 59, 59, 999 ), 0, 12 ));
2478 }
2479 return m_dayPeriods;
2480}
2481
2482KDayPeriod KLocalePrivate::dayPeriodForTime(const QTime &time) const
2483{
2484 if (time.isValid()) {
2485 foreach (const KDayPeriod &dayPeriod, dayPeriods()) {
2486 if (dayPeriod.isValid(time)) {
2487 return dayPeriod;
2488 }
2489 }
2490 }
2491 return KDayPeriod();
2492}
2493
2494QStringList KLocalePrivate::languageList() const
2495{
2496 return m_languageList;
2497}
2498
2499QStringList KLocalePrivate::currencyCodeList() const
2500{
2501 return m_currencyCodeList;
2502}
2503
2504QString KLocalePrivate::formatDateTime(const KLocale *locale, const QDateTime &dateTime, KLocale::DateFormat format,
2505 bool includeSeconds, int daysTo, int secsTo)
2506{
2507 // Have to do Fancy Date formatting here rather than using normal KCalendarSystem::formatDate()
2508 // as daysTo is relative to the time spec which formatDate doesn't know about. Needs to be
2509 // kept in sync with Fancy Date code in KCalendarSystem::formatDate(). Fix in KDE5.
2510
2511 // Only do Fancy if less than an hour into the future or less than a week in the past
2512 if ((daysTo == 0 && secsTo > 3600) || daysTo < 0 || daysTo > 6) {
2513 if (format == KLocale::FancyShortDate) {
2514 format = KLocale::ShortDate;
2515 } else if (format == KLocale::FancyLongDate) {
2516 format = KLocale::LongDate;
2517 }
2518 }
2519
2520 QString dateStr;
2521 if (format == KLocale::FancyShortDate || format == KLocale::FancyLongDate) {
2522 switch (daysTo) {
2523 case 0:
2524 dateStr = i18n("Today");
2525 break;
2526 case 1:
2527 dateStr = i18n("Yesterday");
2528 break;
2529 default:
2530 dateStr = locale->calendar()->weekDayName(dateTime.date());
2531 }
2532 } else {
2533 dateStr = locale->formatDate(dateTime.date(), format);
2534 }
2535
2536 KLocale::TimeFormatOption timeFormat;
2537 if (includeSeconds) {
2538 timeFormat = KLocale::TimeDefault;
2539 } else {
2540 timeFormat = KLocale::TimeWithoutSeconds;
2541 }
2542
2543 return i18nc("concatenation of dates and time", "%1 %2", dateStr,
2544 locale->formatLocaleTime(dateTime.time(), timeFormat));
2545}
2546
2547QString KLocalePrivate::formatDateTime(const QDateTime &dateTime, KLocale::DateFormat format, bool includeSeconds) const
2548{
2549 QDateTime now = QDateTime::currentDateTime();
2550 int daysTo = dateTime.date().daysTo(now.date());
2551 int secsTo = now.secsTo(dateTime);
2552 return KLocalePrivate::formatDateTime(q, dateTime, format, includeSeconds, daysTo, secsTo);
2553}
2554
2555QString KLocalePrivate::formatDateTime(const KDateTime &dateTime, KLocale::DateFormat format,
2556 KLocale::DateTimeFormatOptions options)
2557{
2558 QString dt;
2559
2560 if (dateTime.isDateOnly()) {
2561 dt = formatDate(dateTime.date(), format);
2562 } else {
2563 KDateTime now = KDateTime::currentDateTime(dateTime.timeSpec());
2564 int daysTo = dateTime.date().daysTo(now.date());
2565 int secsTo = now.secsTo(dateTime);
2566 dt = KLocalePrivate::formatDateTime(q, dateTime.dateTime(), format, (options & KLocale::Seconds), daysTo, secsTo);
2567 }
2568
2569 if (options & KLocale::TimeZone) {
2570 QString tz;
2571 switch (dateTime.timeType()) {
2572 case KDateTime::OffsetFromUTC:
2573 tz = i18n(dateTime.toString(QString::fromLatin1("%z")).toUtf8());
2574 break;
2575 case KDateTime::UTC:
2576 case KDateTime::TimeZone:
2577 tz = i18n(dateTime.toString(QString::fromLatin1((format == KLocale::ShortDate) ? "%Z" : "%:Z")).toUtf8());
2578 break;
2579 case KDateTime::ClockTime:
2580 default:
2581 break;
2582 }
2583 return i18nc("concatenation of date/time and time zone", "%1 %2", dt, tz);
2584 }
2585
2586 return dt;
2587}
2588
2589QString KLocalePrivate::langLookup(const QString &fname, const char *rtype)
2590{
2591 QStringList search;
2592
2593 // assemble the local search paths
2594 const QStringList localDoc = KGlobal::dirs()->resourceDirs(rtype);
2595
2596 // look up the different languages
2597 for (int id = localDoc.count() - 1; id >= 0; --id) {
2598 QStringList langs = KGlobal::locale()->languageList();
2599 // FIXME: KDE 4.5, change such that English is not assumed.
2600 langs.replaceInStrings(QLatin1String("en_US"), QLatin1String("en"));
2601 langs.append(QLatin1String("en"));
2602 Q_FOREACH(const QString &lang, langs)
2603 search.append(QString::fromLatin1("%1%2/%3").arg(localDoc[id]).arg(lang).arg(fname));
2604 }
2605
2606 // try to locate the file
2607 Q_FOREACH(const QString &file, search) {
2608 kDebug(173) << "Looking for help in: " << file;
2609
2610 QFileInfo info(file);
2611 if (info.exists() && info.isFile() && info.isReadable())
2612 return file;
2613 }
2614
2615 return QString();
2616}
2617
2618bool KLocalePrivate::useDefaultLanguage() const
2619{
2620 return language() == KLocale::defaultLanguage();
2621}
2622
2623void KLocalePrivate::initEncoding()
2624{
2625 m_codecForEncoding = 0;
2626
2627 // This all made more sense when we still had the EncodingEnum config key.
2628
2629 QByteArray codeset = systemCodeset();
2630
2631 if (!codeset.isEmpty()) {
2632 QTextCodec* codec = QTextCodec::codecForName(codeset);
2633 if (codec) {
2634 setEncoding(codec->mibEnum());
2635 }
2636 } else {
2637 setEncoding(QTextCodec::codecForLocale()->mibEnum());
2638 }
2639
2640 if (!m_codecForEncoding) {
2641 kWarning() << "Cannot resolve system encoding, defaulting to ISO 8859-1.";
2642 const int mibDefault = 4; // ISO 8859-1
2643 setEncoding(mibDefault);
2644 }
2645
2646 Q_ASSERT(m_codecForEncoding);
2647}
2648
2649QByteArray KLocalePrivate::systemCodeset() const
2650{
2651 QByteArray codeset;
2652#if HAVE_LANGINFO_H
2653 // Qt since 4.2 always returns 'System' as codecForLocale and KDE (for example
2654 // KEncodingFileDialog) expects real encoding name. So on systems that have langinfo.h use
2655 // nl_langinfo instead, just like Qt compiled without iconv does. Windows already has its own
2656 // workaround
2657
2658 codeset = nl_langinfo(CODESET);
2659
2660 if ((codeset == "ANSI_X3.4-1968") || (codeset == "US-ASCII")) {
2661 // means ascii, "C"; QTextCodec doesn't know, so avoid warning
2662 codeset = "ISO-8859-1";
2663 }
2664#endif
2665 return codeset;
2666}
2667
2668void KLocalePrivate::initFileNameEncoding()
2669{
2670 // If the following environment variable is set, assume all filenames
2671 // are in UTF-8 regardless of the current C locale.
2672 m_utf8FileEncoding = !qgetenv("KDE_UTF8_FILENAMES").isEmpty();
2673 if (m_utf8FileEncoding) {
2674 QFile::setEncodingFunction(KLocalePrivate::encodeFileNameUTF8);
2675 QFile::setDecodingFunction(KLocalePrivate::decodeFileNameUTF8);
2676 } else {
2677 const QByteArray ctype = setlocale(LC_CTYPE, 0);
2678 int indexOfDot = ctype.indexOf('.');
2679 if (indexOfDot != -1) {
2680 if (!qstrnicmp(ctype.data() + indexOfDot + 1, "UTF-8", 5)) {
2681 QFile::setEncodingFunction(KLocalePrivate::encodeFileNameUTF8);
2682 QFile::setDecodingFunction(KLocalePrivate::decodeFileNameUTF8);
2683 m_utf8FileEncoding = true;
2684 }
2685 return;
2686 }
2687 QByteArray lang = qgetenv("LC_ALL");
2688 if (lang.isEmpty() || lang == "C") {
2689 lang = qgetenv("LC_CTYPE");
2690 }
2691 if (lang.isEmpty() || lang == "C") {
2692 lang = qgetenv("LANG");
2693 }
2694 indexOfDot = lang.indexOf('.');
2695 if (indexOfDot != -1) {
2696 if (!qstrnicmp(lang.data() + indexOfDot + 1, "UTF-8", 5)) {
2697 QFile::setEncodingFunction(KLocalePrivate::encodeFileNameUTF8);
2698 QFile::setDecodingFunction(KLocalePrivate::decodeFileNameUTF8);
2699 m_utf8FileEncoding = true;
2700 }
2701 }
2702 }
2703 // Otherwise, stay with QFile's default filename encoding functions
2704 // which, on Unix platforms, use the locale's codec.
2705}
2706
2707static inline bool isUnicodeNonCharacter(uint ucs4)
2708{
2709 return (ucs4 & 0xfffe) == 0xfffe || (ucs4 - 0xfdd0U) < 16;
2710}
2711
2712QByteArray KLocalePrivate::encodeFileNameUTF8(const QString & fileName)
2713{
2714 if (fileName.isNull()) return QByteArray();
2715 int len = fileName.length();
2716 const QChar *uc = fileName.constData();
2717
2718 uchar replacement = '?';
2719 int rlen = 3*len;
2720 int surrogate_high = -1;
2721
2722 QByteArray rstr;
2723 rstr.resize(rlen);
2724 uchar* cursor = (uchar*)rstr.data();
2725 const QChar *ch = uc;
2726 int invalid = 0;
2727
2728 const QChar *end = ch + len;
2729 while (ch < end) {
2730 uint u = ch->unicode();
2731 if (surrogate_high >= 0) {
2732 if (ch->isLowSurrogate()) {
2733 u = QChar::surrogateToUcs4(surrogate_high, u);
2734 surrogate_high = -1;
2735 } else {
2736 // high surrogate without low
2737 *cursor = replacement;
2738 ++ch;
2739 ++invalid;
2740 surrogate_high = -1;
2741 continue;
2742 }
2743 } else if (ch->isLowSurrogate()) {
2744 // low surrogate without high
2745 *cursor = replacement;
2746 ++ch;
2747 ++invalid;
2748 continue;
2749 } else if (ch->isHighSurrogate()) {
2750 surrogate_high = u;
2751 ++ch;
2752 continue;
2753 }
2754
2755 if (u >= 0x10FE00 && u <= 0x10FE7F) {
2756 *cursor++ = (uchar)(u - 0x10FE00 + 128) ;
2757 }
2758 else if (u < 0x80) {
2759 *cursor++ = (uchar)u;
2760 } else {
2761 if (u < 0x0800) {
2762 *cursor++ = 0xc0 | ((uchar) (u >> 6));
2763 } else {
2764 // is it one of the Unicode non-characters?
2765 if (isUnicodeNonCharacter(u)) {
2766 *cursor++ = replacement;
2767 ++ch;
2768 ++invalid;
2769 continue;
2770 }
2771
2772 if (u > 0xffff) {
2773 *cursor++ = 0xf0 | ((uchar) (u >> 18));
2774 *cursor++ = 0x80 | (((uchar) (u >> 12)) & 0x3f);
2775 } else {
2776 *cursor++ = 0xe0 | (((uchar) (u >> 12)) & 0x3f);
2777 }
2778 *cursor++ = 0x80 | (((uchar) (u >> 6)) & 0x3f);
2779 }
2780 *cursor++ = 0x80 | ((uchar) (u&0x3f));
2781 }
2782 ++ch;
2783 }
2784
2785 rstr.resize(cursor - (const uchar*)rstr.constData());
2786 return rstr;
2787}
2788
2789QString KLocalePrivate::decodeFileNameUTF8(const QByteArray &localFileName)
2790{
2791 const char *chars = localFileName;
2792 int len = qstrlen(chars);
2793 int need = 0;
2794 uint uc = 0;
2795 uint min_uc = 0;
2796
2797 QString result(2 * (len + 1), Qt::Uninitialized); // worst case
2798 ushort *qch = (ushort *)result.unicode();
2799 uchar ch;
2800
2801 for (int i = 0; i < len; ++i) {
2802 ch = chars[i];
2803 if (need) {
2804 if ((ch&0xc0) == 0x80) {
2805 uc = (uc << 6) | (ch & 0x3f);
2806 --need;
2807 if (!need) {
2808 bool nonCharacter;
2809 if (!(nonCharacter = isUnicodeNonCharacter(uc)) && uc > 0xffff && uc < 0x110000) {
2810 // surrogate pair
2811 Q_ASSERT((qch - (ushort*)result.unicode()) + 2 < result.length());
2812 *qch++ = QChar::highSurrogate(uc);
2813 *qch++ = QChar::lowSurrogate(uc);
2814 } else if ((uc < min_uc) || (uc >= 0xd800 && uc <= 0xdfff) || nonCharacter || uc >= 0x110000) {
2815 // error: overlong sequence, UTF16 surrogate or non-character
2816 goto error;
2817 } else {
2818 *qch++ = uc;
2819 }
2820 }
2821 } else {
2822 goto error;
2823 }
2824 } else {
2825 if (ch < 128) {
2826 *qch++ = ushort(ch);
2827 } else if ((ch & 0xe0) == 0xc0) {
2828 uc = ch & 0x1f;
2829 need = 1;
2830 min_uc = 0x80;
2831 } else if ((ch & 0xf0) == 0xe0) {
2832 uc = ch & 0x0f;
2833 need = 2;
2834 min_uc = 0x800;
2835 } else if ((ch&0xf8) == 0xf0) {
2836 uc = ch & 0x07;
2837 need = 3;
2838 min_uc = 0x10000;
2839 } else {
2840 goto error;
2841 }
2842 }
2843 }
2844 if (need > 0) {
2845 // unterminated UTF sequence
2846 goto error;
2847 }
2848 result.truncate(qch - (ushort *)result.unicode());
2849 return result;
2850
2851error:
2852
2853 qch = (ushort *)result.unicode();
2854 for (int i = 0; i < len; ++i) {
2855 ch = chars[i];
2856 if (ch < 128) {
2857 *qch++ = ushort(ch);
2858 } else {
2859 uint uc = ch - 128 + 0x10FE00; //U+10FE00-U+10FE7F
2860 *qch++ = QChar::highSurrogate(uc);
2861 *qch++ = QChar::lowSurrogate(uc);
2862 }
2863 }
2864 result.truncate(qch - (ushort *)result.unicode());
2865 return result;
2866}
2867
2868void KLocalePrivate::setDateFormat(const QString &format)
2869{
2870 m_dateFormat = format.trimmed();
2871}
2872
2873void KLocalePrivate::setDateFormatShort(const QString &format)
2874{
2875 m_dateFormatShort = format.trimmed();
2876}
2877
2878void KLocalePrivate::setDateMonthNamePossessive(bool possessive)
2879{
2880 m_dateMonthNamePossessive = possessive;
2881}
2882
2883void KLocalePrivate::setTimeFormat(const QString &format)
2884{
2885 m_timeFormat = format.trimmed();
2886}
2887
2888void KLocalePrivate::setWeekStartDay(int day)
2889{
2890 if (day >= 1 && day <= calendar()->daysInWeek(QDate())) {
2891 m_weekStartDay = day;
2892 }
2893}
2894
2895void KLocalePrivate::setWorkingWeekStartDay(int day)
2896{
2897 if (day >= 1 && day <= calendar()->daysInWeek(QDate())) {
2898 m_workingWeekStartDay = day;
2899 }
2900}
2901
2902void KLocalePrivate::setWorkingWeekEndDay(int day)
2903{
2904 if (day >= 1 && day <= calendar()->daysInWeek(QDate())) {
2905 m_workingWeekEndDay = day;
2906 }
2907}
2908
2909void KLocalePrivate::setWeekDayOfPray(int day)
2910{
2911 if (day >= 0 && day <= calendar()->daysInWeek(QDate())) { // 0 = None
2912 m_weekDayOfPray = day;
2913 }
2914}
2915
2916QString KLocalePrivate::dateFormat() const
2917{
2918 return m_dateFormat;
2919}
2920
2921QString KLocalePrivate::dateFormatShort() const
2922{
2923 return m_dateFormatShort;
2924}
2925
2926QString KLocalePrivate::timeFormat() const
2927{
2928 return m_timeFormat;
2929}
2930
2931void KLocalePrivate::setDecimalPlaces(int digits)
2932{
2933 m_decimalPlaces = digits;
2934}
2935
2936void KLocalePrivate::setDecimalSymbol(const QString &symbol)
2937{
2938 m_decimalSymbol = symbol.trimmed();
2939}
2940
2941void KLocalePrivate::setThousandsSeparator(const QString &separator)
2942{
2943 // allow spaces here
2944 m_thousandsSeparator = separator;
2945}
2946
2947void KLocalePrivate::setNumericDigitGrouping(QList<int> groupList)
2948{
2949 m_numericDigitGrouping = groupList;
2950}
2951
2952void KLocalePrivate::setPositiveSign(const QString &sign)
2953{
2954 m_positiveSign = sign.trimmed();
2955}
2956
2957void KLocalePrivate::setNegativeSign(const QString &sign)
2958{
2959 m_negativeSign = sign.trimmed();
2960}
2961
2962void KLocalePrivate::setPositiveMonetarySignPosition(KLocale::SignPosition signpos)
2963{
2964 m_positiveMonetarySignPosition = signpos;
2965}
2966
2967void KLocalePrivate::setNegativeMonetarySignPosition(KLocale::SignPosition signpos)
2968{
2969 m_negativeMonetarySignPosition = signpos;
2970}
2971
2972void KLocalePrivate::setPositivePrefixCurrencySymbol(bool prefix)
2973{
2974 m_positivePrefixCurrencySymbol = prefix;
2975}
2976
2977void KLocalePrivate::setNegativePrefixCurrencySymbol(bool prefix)
2978{
2979 m_negativePrefixCurrencySymbol = prefix;
2980}
2981
2982void KLocalePrivate::setMonetaryDecimalPlaces(int digits)
2983{
2984 m_monetaryDecimalPlaces = digits;
2985}
2986
2987void KLocalePrivate::setMonetaryThousandsSeparator(const QString &separator)
2988{
2989 // allow spaces here
2990 m_monetaryThousandsSeparator = separator;
2991}
2992
2993void KLocalePrivate::setMonetaryDigitGrouping(QList<int> groupList)
2994{
2995 m_monetaryDigitGrouping = groupList;
2996}
2997
2998void KLocalePrivate::setMonetaryDecimalSymbol(const QString &symbol)
2999{
3000 m_monetaryDecimalSymbol = symbol.trimmed();
3001}
3002
3003void KLocalePrivate::setCurrencySymbol(const QString & symbol)
3004{
3005 m_currencySymbol = symbol.trimmed();
3006}
3007
3008int KLocalePrivate::pageSize() const
3009{
3010 return m_pageSize;
3011}
3012
3013void KLocalePrivate::setPageSize(int size)
3014{
3015 // #### check if it's in range??
3016 m_pageSize = size;
3017}
3018
3019KLocale::MeasureSystem KLocalePrivate::measureSystem() const
3020{
3021 return m_measureSystem;
3022}
3023
3024void KLocalePrivate::setMeasureSystem(KLocale::MeasureSystem value)
3025{
3026 m_measureSystem = value;
3027}
3028
3029QString KLocalePrivate::defaultLanguage()
3030{
3031 static const QString en_US = QString::fromLatin1("en_US");
3032 return en_US;
3033}
3034
3035QString KLocalePrivate::defaultCountry()
3036{
3037 return QString::fromLatin1("C");
3038}
3039
3040QString KLocalePrivate::defaultCurrencyCode()
3041{
3042 return QString::fromLatin1("USD");
3043}
3044
3045bool KLocalePrivate::useTranscript() const
3046{
3047 return m_useTranscript;
3048}
3049
3050const QByteArray KLocalePrivate::encoding()
3051{
3052 return codecForEncoding()->name();
3053}
3054
3055int KLocalePrivate::encodingMib() const
3056{
3057 return codecForEncoding()->mibEnum();
3058}
3059
3060int KLocalePrivate::fileEncodingMib() const
3061{
3062 if (m_utf8FileEncoding) {
3063 return 106;
3064 }
3065 return codecForEncoding()->mibEnum();
3066}
3067
3068QTextCodec *KLocalePrivate::codecForEncoding() const
3069{
3070 return m_codecForEncoding;
3071}
3072
3073bool KLocalePrivate::setEncoding(int mibEnum)
3074{
3075 QTextCodec * codec = QTextCodec::codecForMib(mibEnum);
3076 if (codec) {
3077 m_codecForEncoding = codec;
3078 }
3079
3080 return codec != 0;
3081}
3082
3083QStringList KLocalePrivate::allLanguagesList()
3084{
3085 if (!m_languages) {
3086 m_languages = new KConfig(QLatin1String("all_languages"), KConfig::NoGlobals, "locale");
3087 }
3088 return m_languages->groupList();
3089}
3090
3091QStringList KLocalePrivate::installedLanguages()
3092{
3093 QStringList languages;
3094 QStringList paths = KGlobal::dirs()->findAllResources("locale", QLatin1String("*/entry.desktop"));
3095 foreach (const QString &path, paths) {
3096 QString part = path.left(path.length() - 14);
3097 languages.append(part.mid(part.lastIndexOf(QLatin1Char('/')) + 1));
3098 }
3099 languages.sort();
3100 return languages;
3101}
3102
3103QString KLocalePrivate::languageCodeToName(const QString &language)
3104{
3105 if (!m_languages) {
3106 m_languages = new KConfig(QLatin1String("all_languages"), KConfig::NoGlobals, "locale");
3107 }
3108
3109 KConfigGroup cg(m_languages, language);
3110 return cg.readEntry("Name");
3111}
3112
3113QStringList KLocalePrivate::allCountriesList() const
3114{
3115 QStringList countries;
3116 const QStringList paths = KGlobal::dirs()->findAllResources("locale", QLatin1String("l10n/*/entry.desktop"));
3117 for (QStringList::ConstIterator it = paths.begin(); it != paths.end(); ++it) {
3118 QString code = (*it).mid((*it).length() - 16, 2);
3119 if (code != QLatin1String("/C")) {
3120 countries.append(code);
3121 }
3122 }
3123 return countries;
3124}
3125
3126QString KLocalePrivate::countryCodeToName(const QString &country) const
3127{
3128 QString countryName;
3129 QString entryFile = KStandardDirs::locate("locale", QString::fromLatin1("l10n/") + country.toLower() + QLatin1String("/entry.desktop"));
3130 if (!entryFile.isEmpty()) {
3131 KConfig cfg(entryFile);
3132 KConfigGroup cg(&cfg, "KCM Locale");
3133 countryName = cg.readEntry("Name");
3134 }
3135 return countryName;
3136}
3137
3138KLocale::CalendarSystem KLocalePrivate::calendarTypeToCalendarSystem(const QString &calendarType) const
3139{
3140 if (calendarType == QLatin1String("coptic")) {
3141 return KLocale::CopticCalendar;
3142 } else if (calendarType == QLatin1String("ethiopian")) {
3143 return KLocale::EthiopianCalendar;
3144 } else if (calendarType == QLatin1String("gregorian")) {
3145 return KLocale::QDateCalendar;
3146 } else if (calendarType == QLatin1String("gregorian-proleptic")) {
3147 return KLocale::GregorianCalendar;
3148 } else if (calendarType == QLatin1String("hebrew")) {
3149 return KLocale::HebrewCalendar;
3150 } else if (calendarType == QLatin1String("hijri")) {
3151 return KLocale::IslamicCivilCalendar;
3152 } else if (calendarType == QLatin1String("indian-national")) {
3153 return KLocale::IndianNationalCalendar;
3154 } else if (calendarType == QLatin1String("jalali")) {
3155 return KLocale::JalaliCalendar;
3156 } else if (calendarType == QLatin1String("japanese")) {
3157 return KLocale::JapaneseCalendar;
3158 } else if (calendarType == QLatin1String("julian")) {
3159 return KLocale::JulianCalendar;
3160 } else if (calendarType == QLatin1String("minguo")) {
3161 return KLocale::MinguoCalendar;
3162 } else if (calendarType == QLatin1String("thai")) {
3163 return KLocale::ThaiCalendar;
3164 } else {
3165 return KLocale::QDateCalendar;
3166 }
3167}
3168
3169QString KLocalePrivate::calendarSystemToCalendarType(KLocale::CalendarSystem calendarSystem) const
3170{
3171 switch (calendarSystem) {
3172 case KLocale::QDateCalendar:
3173 return QLatin1String("gregorian");
3174 case KLocale::CopticCalendar:
3175 return QLatin1String("coptic");
3176 case KLocale::EthiopianCalendar:
3177 return QLatin1String("ethiopian");
3178 case KLocale::GregorianCalendar:
3179 return QLatin1String("gregorian-proleptic");
3180 case KLocale::HebrewCalendar:
3181 return QLatin1String("hebrew");
3182 case KLocale::IslamicCivilCalendar:
3183 return QLatin1String("hijri");
3184 case KLocale::IndianNationalCalendar:
3185 return QLatin1String("indian-national");
3186 case KLocale::JalaliCalendar:
3187 return QLatin1String("jalali");
3188 case KLocale::JapaneseCalendar:
3189 return QLatin1String("japanese");
3190 case KLocale::JulianCalendar:
3191 return QLatin1String("julian");
3192 case KLocale::MinguoCalendar:
3193 return QLatin1String("minguo");
3194 case KLocale::ThaiCalendar:
3195 return QLatin1String("thai");
3196 default:
3197 return QLatin1String("gregorian");
3198 }
3199}
3200
3201void KLocalePrivate::setCalendar(const QString &calendarType)
3202{
3203 setCalendarSystem(calendarTypeToCalendarSystem(calendarType));
3204}
3205
3206void KLocalePrivate::setCalendarSystem(KLocale::CalendarSystem calendarSystem)
3207{
3208 m_calendarSystem = calendarSystem;
3209 delete m_calendar;
3210 m_calendar = 0;
3211}
3212
3213QString KLocalePrivate::calendarType() const
3214{
3215 return calendarSystemToCalendarType(m_calendarSystem);
3216}
3217
3218KLocale::CalendarSystem KLocalePrivate::calendarSystem() const
3219{
3220 return m_calendarSystem;
3221}
3222
3223const KCalendarSystem * KLocalePrivate::calendar()
3224{
3225 if (!m_calendar) {
3226 m_calendar = KCalendarSystem::create(m_calendarSystem, m_config, q);
3227 }
3228
3229 return m_calendar;
3230}
3231
3232void KLocalePrivate::setWeekNumberSystem(KLocale::WeekNumberSystem weekNumberSystem)
3233{
3234 m_weekNumberSystem = weekNumberSystem;
3235}
3236
3237KLocale::WeekNumberSystem KLocalePrivate::weekNumberSystem()
3238{
3239 return m_weekNumberSystem;
3240}
3241
3242void KLocalePrivate::copyCatalogsTo(KLocale *locale)
3243{
3244 QMutexLocker lock(kLocaleMutex());
3245 locale->d->m_catalogNames = m_catalogNames;
3246 locale->d->updateCatalogs();
3247}
3248
3249QString KLocalePrivate::localizedFilePath(const QString &filePath) const
3250{
3251 // Stop here if the default language is primary.
3252 if (useDefaultLanguage()) {
3253 return filePath;
3254 }
3255
3256 // Check if l10n sudir is present, stop if not.
3257 QFileInfo fileInfo(filePath);
3258 QString locDirPath = fileInfo.path() + QLatin1String("/l10n");
3259 QFileInfo locDirInfo(locDirPath);
3260 if (!locDirInfo.isDir()) {
3261 return filePath;
3262 }
3263
3264 // Go through possible localized paths by priority of languages,
3265 // return first that exists.
3266 QString fileName = fileInfo.fileName();
3267 foreach(const QString &lang, languageList()) {
3268 // Stop when the default language is reached.
3269 if (lang == KLocale::defaultLanguage()) {
3270 return filePath;
3271 }
3272 QString locFilePath = locDirPath + QLatin1Char('/') + lang + QLatin1Char('/') + fileName;
3273 QFileInfo locFileInfo(locFilePath);
3274 if (locFileInfo.isFile() && locFileInfo.isReadable()) {
3275 return locFilePath;
3276 }
3277 }
3278
3279 return filePath;
3280}
3281
3282QString KLocalePrivate::removeAcceleratorMarker(const QString &label) const
3283{
3284 return ::removeAcceleratorMarker(label);
3285}
3286
3287void KLocalePrivate::setDigitSet(KLocale::DigitSet digitSet)
3288{
3289 m_digitSet = digitSet;
3290}
3291
3292KLocale::DigitSet KLocalePrivate::digitSet() const
3293{
3294 return m_digitSet;
3295}
3296
3297void KLocalePrivate::setMonetaryDigitSet(KLocale::DigitSet digitSet)
3298{
3299 m_monetaryDigitSet = digitSet;
3300}
3301
3302KLocale::DigitSet KLocalePrivate::monetaryDigitSet() const
3303{
3304 return m_monetaryDigitSet;
3305}
3306
3307void KLocalePrivate::setDateTimeDigitSet(KLocale::DigitSet digitSet)
3308{
3309 m_dateTimeDigitSet = digitSet;
3310}
3311
3312KLocale::DigitSet KLocalePrivate::dateTimeDigitSet() const
3313{
3314 return m_dateTimeDigitSet;
3315}
3316
3317Q_GLOBAL_STATIC_WITH_ARGS(QMutex, s_kLocaleMutex, (QMutex::Recursive))
3318
3319QMutex *kLocaleMutex()
3320{
3321 return s_kLocaleMutex();
3322}
3323