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 | |
67 | class KLocaleStaticData |
68 | { |
69 | public: |
70 | |
71 | KLocaleStaticData(); |
72 | |
73 | QString maincatalog; |
74 | |
75 | // FIXME: Temporary until full language-sensitivity implemented. |
76 | QHash<KLocale::DigitSet, QStringList> languagesUsingDigitSet; |
77 | }; |
78 | |
79 | KLocaleStaticData::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 | |
98 | K_GLOBAL_STATIC(KLocaleStaticData, staticData) |
99 | |
100 | |
101 | QDebug operator<<(QDebug debug, const KCatalogName &cn) |
102 | { |
103 | return debug << cn.name << cn.loadCount; |
104 | } |
105 | |
106 | KLocalePrivate::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 | |
119 | KLocalePrivate::KLocalePrivate(const KLocalePrivate &rhs) |
120 | { |
121 | copy(rhs); |
122 | } |
123 | |
124 | KLocalePrivate &KLocalePrivate::operator=(const KLocalePrivate &rhs) |
125 | { |
126 | copy(rhs); |
127 | return *this; |
128 | } |
129 | |
130 | KConfig *KLocalePrivate::config() |
131 | { |
132 | if (m_config != KSharedConfig::Ptr()) { |
133 | return m_config.data(); |
134 | } else { |
135 | return KGlobal::config().data(); |
136 | } |
137 | } |
138 | |
139 | void 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 | |
220 | KLocalePrivate::~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 |
229 | void 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 |
271 | void 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 | |
296 | void 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 | |
329 | void 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 |
359 | void 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 | |
393 | QString 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 | |
402 | void 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 | |
447 | QStringList KLocalePrivate::systemLanguageList() const |
448 | { |
449 | return QStringList(); |
450 | } |
451 | |
452 | void 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 | |
568 | void 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 | |
593 | bool 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 | |
629 | bool KLocalePrivate::setCountryDivisionCode(const QString &countryDivisionCode) |
630 | { |
631 | m_countryDivisionCode = countryDivisionCode; |
632 | return true; |
633 | } |
634 | |
635 | bool 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. |
661 | bool 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 | |
699 | void 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 | |
711 | void 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 | |
720 | bool 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 | |
742 | void 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 | |
780 | QString KLocalePrivate::language() const |
781 | { |
782 | return m_language; |
783 | } |
784 | |
785 | QString KLocalePrivate::country() const |
786 | { |
787 | return m_country; |
788 | } |
789 | |
790 | QString KLocalePrivate::countryDivisionCode() const |
791 | { |
792 | if (m_countryDivisionCode.isEmpty()) { |
793 | return country().toUpper(); |
794 | } else { |
795 | return m_countryDivisionCode; |
796 | } |
797 | } |
798 | |
799 | KCurrencyCode *KLocalePrivate::currency() |
800 | { |
801 | if (!m_currency) { |
802 | initCurrency(); |
803 | } |
804 | return m_currency; |
805 | } |
806 | |
807 | QString KLocalePrivate::currencyCode() const |
808 | { |
809 | return m_currencyCode; |
810 | } |
811 | |
812 | void 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 | |
827 | void 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 | |
861 | void 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 | |
878 | void 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 | |
890 | void 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 | |
973 | QString KLocalePrivate::translateQt(const char *context, const char *sourceText, const char *) 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 | |
1034 | QList<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 | |
1055 | QString 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 | |
1089 | QString 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 | |
1145 | QString 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 | |
1169 | QString 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 | |
1182 | bool KLocalePrivate::nounDeclension() const |
1183 | { |
1184 | return m_nounDeclension; |
1185 | } |
1186 | |
1187 | bool KLocalePrivate::dateMonthNamePossessive() const |
1188 | { |
1189 | return m_dateMonthNamePossessive; |
1190 | } |
1191 | |
1192 | int KLocalePrivate::weekStartDay() const |
1193 | { |
1194 | return m_weekStartDay; |
1195 | } |
1196 | |
1197 | int KLocalePrivate::workingWeekStartDay() const |
1198 | { |
1199 | return m_workingWeekStartDay; |
1200 | } |
1201 | |
1202 | int KLocalePrivate::workingWeekEndDay() const |
1203 | { |
1204 | return m_workingWeekEndDay; |
1205 | } |
1206 | |
1207 | int KLocalePrivate::weekDayOfPray() const |
1208 | { |
1209 | return m_weekDayOfPray; |
1210 | } |
1211 | |
1212 | int KLocalePrivate::decimalPlaces() const |
1213 | { |
1214 | return m_decimalPlaces; |
1215 | } |
1216 | |
1217 | QString KLocalePrivate::decimalSymbol() const |
1218 | { |
1219 | return m_decimalSymbol; |
1220 | } |
1221 | |
1222 | QString KLocalePrivate::thousandsSeparator() const |
1223 | { |
1224 | return m_thousandsSeparator; |
1225 | } |
1226 | |
1227 | QList<int> KLocalePrivate::numericDigitGrouping() const |
1228 | { |
1229 | return m_numericDigitGrouping; |
1230 | } |
1231 | |
1232 | QString KLocalePrivate::currencySymbol() const |
1233 | { |
1234 | return m_currencySymbol; |
1235 | } |
1236 | |
1237 | QString KLocalePrivate::monetaryDecimalSymbol() const |
1238 | { |
1239 | return m_monetaryDecimalSymbol; |
1240 | } |
1241 | |
1242 | QString KLocalePrivate::monetaryThousandsSeparator() const |
1243 | { |
1244 | return m_monetaryThousandsSeparator; |
1245 | } |
1246 | |
1247 | QList<int> KLocalePrivate::monetaryDigitGrouping() const |
1248 | { |
1249 | return m_monetaryDigitGrouping; |
1250 | } |
1251 | |
1252 | QString KLocalePrivate::positiveSign() const |
1253 | { |
1254 | return m_positiveSign; |
1255 | } |
1256 | |
1257 | QString KLocalePrivate::negativeSign() const |
1258 | { |
1259 | return m_negativeSign; |
1260 | } |
1261 | |
1262 | /* Just copy to keep the diff looking clean, delete later |
1263 | int KLocale::fracDigits() const |
1264 | { |
1265 | return monetaryDecimalPlaces(); |
1266 | } |
1267 | */ |
1268 | |
1269 | int KLocalePrivate::monetaryDecimalPlaces() const |
1270 | { |
1271 | return m_monetaryDecimalPlaces; |
1272 | } |
1273 | |
1274 | bool KLocalePrivate::positivePrefixCurrencySymbol() const |
1275 | { |
1276 | return m_positivePrefixCurrencySymbol; |
1277 | } |
1278 | |
1279 | bool KLocalePrivate::negativePrefixCurrencySymbol() const |
1280 | { |
1281 | return m_negativePrefixCurrencySymbol; |
1282 | } |
1283 | |
1284 | KLocale::SignPosition KLocalePrivate::positiveMonetarySignPosition() const |
1285 | { |
1286 | return m_positiveMonetarySignPosition; |
1287 | } |
1288 | |
1289 | KLocale::SignPosition KLocalePrivate::negativeMonetarySignPosition() const |
1290 | { |
1291 | return m_negativeMonetarySignPosition; |
1292 | } |
1293 | |
1294 | static 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 | |
1301 | static 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) |
1308 | QList<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. |
1319 | QString 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 |
1348 | QString 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 | |
1392 | QString 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 | |
1456 | QString 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 | |
1465 | QString KLocalePrivate::formatLong(long num) const |
1466 | { |
1467 | return formatNumber((double)num, 0); |
1468 | } |
1469 | |
1470 | // increase the digit at 'position' by one |
1471 | static 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' |
1515 | static 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 | |
1563 | QString 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 |
1620 | QList<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 | |
1700 | QString 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 | |
1758 | QString KLocalePrivate::formatByteSize(double size) |
1759 | { |
1760 | return formatByteSize(size, 1); |
1761 | } |
1762 | |
1763 | KLocale::BinaryUnitDialect KLocalePrivate::binaryUnitDialect() const |
1764 | { |
1765 | return m_binaryUnitDialect; |
1766 | } |
1767 | |
1768 | void 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 | |
1777 | QString 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 | |
1795 | QString 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 | |
1810 | QString 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 | |
1852 | QString KLocalePrivate::formatDate(const QDate &date, KLocale::DateFormat format) |
1853 | { |
1854 | return calendar()->formatDate(date, format); |
1855 | } |
1856 | |
1857 | void KLocalePrivate::setMainCatalog(const char *catalog) |
1858 | { |
1859 | KLocaleStaticData *s = staticData; |
1860 | s->maincatalog = QString::fromUtf8(catalog); |
1861 | } |
1862 | |
1863 | double 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 | |
1950 | double 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 | */ |
2079 | static 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 | |
2093 | QDate KLocalePrivate::readDate(const QString &intstr, bool *ok) |
2094 | { |
2095 | return calendar()->readDate(intstr, ok); |
2096 | } |
2097 | |
2098 | QDate KLocalePrivate::readDate(const QString &intstr, KLocale::ReadDateFlags flags, bool *ok) |
2099 | { |
2100 | return calendar()->readDate(intstr, flags, ok); |
2101 | } |
2102 | |
2103 | QDate KLocalePrivate::readDate(const QString &intstr, const QString &fmt, bool *ok) |
2104 | { |
2105 | return calendar()->readDate(intstr, fmt, ok); |
2106 | } |
2107 | |
2108 | QTime 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 | |
2117 | QTime 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. |
2126 | static 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. |
2153 | static 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. |
2169 | static 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 | |
2192 | QTime 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 | |
2329 | QString 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 | |
2341 | QString 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 | |
2439 | bool 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 | |
2449 | void 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 | |
2461 | QList<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 | |
2482 | KDayPeriod 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 | |
2494 | QStringList KLocalePrivate::languageList() const |
2495 | { |
2496 | return m_languageList; |
2497 | } |
2498 | |
2499 | QStringList KLocalePrivate::currencyCodeList() const |
2500 | { |
2501 | return m_currencyCodeList; |
2502 | } |
2503 | |
2504 | QString 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 | |
2547 | QString 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 | |
2555 | QString 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 | |
2589 | QString 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 | |
2618 | bool KLocalePrivate::useDefaultLanguage() const |
2619 | { |
2620 | return language() == KLocale::defaultLanguage(); |
2621 | } |
2622 | |
2623 | void 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 | |
2649 | QByteArray 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 | |
2668 | void 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 | |
2707 | static inline bool isUnicodeNonCharacter(uint ucs4) |
2708 | { |
2709 | return (ucs4 & 0xfffe) == 0xfffe || (ucs4 - 0xfdd0U) < 16; |
2710 | } |
2711 | |
2712 | QByteArray 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 | |
2789 | QString 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 | |
2851 | error: |
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 | |
2868 | void KLocalePrivate::setDateFormat(const QString &format) |
2869 | { |
2870 | m_dateFormat = format.trimmed(); |
2871 | } |
2872 | |
2873 | void KLocalePrivate::setDateFormatShort(const QString &format) |
2874 | { |
2875 | m_dateFormatShort = format.trimmed(); |
2876 | } |
2877 | |
2878 | void KLocalePrivate::setDateMonthNamePossessive(bool possessive) |
2879 | { |
2880 | m_dateMonthNamePossessive = possessive; |
2881 | } |
2882 | |
2883 | void KLocalePrivate::setTimeFormat(const QString &format) |
2884 | { |
2885 | m_timeFormat = format.trimmed(); |
2886 | } |
2887 | |
2888 | void KLocalePrivate::setWeekStartDay(int day) |
2889 | { |
2890 | if (day >= 1 && day <= calendar()->daysInWeek(QDate())) { |
2891 | m_weekStartDay = day; |
2892 | } |
2893 | } |
2894 | |
2895 | void KLocalePrivate::setWorkingWeekStartDay(int day) |
2896 | { |
2897 | if (day >= 1 && day <= calendar()->daysInWeek(QDate())) { |
2898 | m_workingWeekStartDay = day; |
2899 | } |
2900 | } |
2901 | |
2902 | void KLocalePrivate::setWorkingWeekEndDay(int day) |
2903 | { |
2904 | if (day >= 1 && day <= calendar()->daysInWeek(QDate())) { |
2905 | m_workingWeekEndDay = day; |
2906 | } |
2907 | } |
2908 | |
2909 | void KLocalePrivate::setWeekDayOfPray(int day) |
2910 | { |
2911 | if (day >= 0 && day <= calendar()->daysInWeek(QDate())) { // 0 = None |
2912 | m_weekDayOfPray = day; |
2913 | } |
2914 | } |
2915 | |
2916 | QString KLocalePrivate::dateFormat() const |
2917 | { |
2918 | return m_dateFormat; |
2919 | } |
2920 | |
2921 | QString KLocalePrivate::dateFormatShort() const |
2922 | { |
2923 | return m_dateFormatShort; |
2924 | } |
2925 | |
2926 | QString KLocalePrivate::timeFormat() const |
2927 | { |
2928 | return m_timeFormat; |
2929 | } |
2930 | |
2931 | void KLocalePrivate::setDecimalPlaces(int digits) |
2932 | { |
2933 | m_decimalPlaces = digits; |
2934 | } |
2935 | |
2936 | void KLocalePrivate::setDecimalSymbol(const QString &symbol) |
2937 | { |
2938 | m_decimalSymbol = symbol.trimmed(); |
2939 | } |
2940 | |
2941 | void KLocalePrivate::setThousandsSeparator(const QString &separator) |
2942 | { |
2943 | // allow spaces here |
2944 | m_thousandsSeparator = separator; |
2945 | } |
2946 | |
2947 | void KLocalePrivate::setNumericDigitGrouping(QList<int> groupList) |
2948 | { |
2949 | m_numericDigitGrouping = groupList; |
2950 | } |
2951 | |
2952 | void KLocalePrivate::setPositiveSign(const QString &sign) |
2953 | { |
2954 | m_positiveSign = sign.trimmed(); |
2955 | } |
2956 | |
2957 | void KLocalePrivate::setNegativeSign(const QString &sign) |
2958 | { |
2959 | m_negativeSign = sign.trimmed(); |
2960 | } |
2961 | |
2962 | void KLocalePrivate::setPositiveMonetarySignPosition(KLocale::SignPosition signpos) |
2963 | { |
2964 | m_positiveMonetarySignPosition = signpos; |
2965 | } |
2966 | |
2967 | void KLocalePrivate::setNegativeMonetarySignPosition(KLocale::SignPosition signpos) |
2968 | { |
2969 | m_negativeMonetarySignPosition = signpos; |
2970 | } |
2971 | |
2972 | void KLocalePrivate::setPositivePrefixCurrencySymbol(bool prefix) |
2973 | { |
2974 | m_positivePrefixCurrencySymbol = prefix; |
2975 | } |
2976 | |
2977 | void KLocalePrivate::setNegativePrefixCurrencySymbol(bool prefix) |
2978 | { |
2979 | m_negativePrefixCurrencySymbol = prefix; |
2980 | } |
2981 | |
2982 | void KLocalePrivate::setMonetaryDecimalPlaces(int digits) |
2983 | { |
2984 | m_monetaryDecimalPlaces = digits; |
2985 | } |
2986 | |
2987 | void KLocalePrivate::setMonetaryThousandsSeparator(const QString &separator) |
2988 | { |
2989 | // allow spaces here |
2990 | m_monetaryThousandsSeparator = separator; |
2991 | } |
2992 | |
2993 | void KLocalePrivate::setMonetaryDigitGrouping(QList<int> groupList) |
2994 | { |
2995 | m_monetaryDigitGrouping = groupList; |
2996 | } |
2997 | |
2998 | void KLocalePrivate::setMonetaryDecimalSymbol(const QString &symbol) |
2999 | { |
3000 | m_monetaryDecimalSymbol = symbol.trimmed(); |
3001 | } |
3002 | |
3003 | void KLocalePrivate::setCurrencySymbol(const QString & symbol) |
3004 | { |
3005 | m_currencySymbol = symbol.trimmed(); |
3006 | } |
3007 | |
3008 | int KLocalePrivate::pageSize() const |
3009 | { |
3010 | return m_pageSize; |
3011 | } |
3012 | |
3013 | void KLocalePrivate::setPageSize(int size) |
3014 | { |
3015 | // #### check if it's in range?? |
3016 | m_pageSize = size; |
3017 | } |
3018 | |
3019 | KLocale::MeasureSystem KLocalePrivate::measureSystem() const |
3020 | { |
3021 | return m_measureSystem; |
3022 | } |
3023 | |
3024 | void KLocalePrivate::setMeasureSystem(KLocale::MeasureSystem value) |
3025 | { |
3026 | m_measureSystem = value; |
3027 | } |
3028 | |
3029 | QString KLocalePrivate::defaultLanguage() |
3030 | { |
3031 | static const QString en_US = QString::fromLatin1("en_US" ); |
3032 | return en_US; |
3033 | } |
3034 | |
3035 | QString KLocalePrivate::defaultCountry() |
3036 | { |
3037 | return QString::fromLatin1("C" ); |
3038 | } |
3039 | |
3040 | QString KLocalePrivate::defaultCurrencyCode() |
3041 | { |
3042 | return QString::fromLatin1("USD" ); |
3043 | } |
3044 | |
3045 | bool KLocalePrivate::useTranscript() const |
3046 | { |
3047 | return m_useTranscript; |
3048 | } |
3049 | |
3050 | const QByteArray KLocalePrivate::encoding() |
3051 | { |
3052 | return codecForEncoding()->name(); |
3053 | } |
3054 | |
3055 | int KLocalePrivate::encodingMib() const |
3056 | { |
3057 | return codecForEncoding()->mibEnum(); |
3058 | } |
3059 | |
3060 | int KLocalePrivate::fileEncodingMib() const |
3061 | { |
3062 | if (m_utf8FileEncoding) { |
3063 | return 106; |
3064 | } |
3065 | return codecForEncoding()->mibEnum(); |
3066 | } |
3067 | |
3068 | QTextCodec *KLocalePrivate::codecForEncoding() const |
3069 | { |
3070 | return m_codecForEncoding; |
3071 | } |
3072 | |
3073 | bool 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 | |
3083 | QStringList 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 | |
3091 | QStringList 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 | |
3103 | QString 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 | |
3113 | QStringList 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 | |
3126 | QString 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 | |
3138 | KLocale::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 | |
3169 | QString 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 | |
3201 | void KLocalePrivate::setCalendar(const QString &calendarType) |
3202 | { |
3203 | setCalendarSystem(calendarTypeToCalendarSystem(calendarType)); |
3204 | } |
3205 | |
3206 | void KLocalePrivate::setCalendarSystem(KLocale::CalendarSystem calendarSystem) |
3207 | { |
3208 | m_calendarSystem = calendarSystem; |
3209 | delete m_calendar; |
3210 | m_calendar = 0; |
3211 | } |
3212 | |
3213 | QString KLocalePrivate::calendarType() const |
3214 | { |
3215 | return calendarSystemToCalendarType(m_calendarSystem); |
3216 | } |
3217 | |
3218 | KLocale::CalendarSystem KLocalePrivate::calendarSystem() const |
3219 | { |
3220 | return m_calendarSystem; |
3221 | } |
3222 | |
3223 | const 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 | |
3232 | void KLocalePrivate::setWeekNumberSystem(KLocale::WeekNumberSystem weekNumberSystem) |
3233 | { |
3234 | m_weekNumberSystem = weekNumberSystem; |
3235 | } |
3236 | |
3237 | KLocale::WeekNumberSystem KLocalePrivate::weekNumberSystem() |
3238 | { |
3239 | return m_weekNumberSystem; |
3240 | } |
3241 | |
3242 | void KLocalePrivate::copyCatalogsTo(KLocale *locale) |
3243 | { |
3244 | QMutexLocker lock(kLocaleMutex()); |
3245 | locale->d->m_catalogNames = m_catalogNames; |
3246 | locale->d->updateCatalogs(); |
3247 | } |
3248 | |
3249 | QString 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 | |
3282 | QString KLocalePrivate::removeAcceleratorMarker(const QString &label) const |
3283 | { |
3284 | return ::removeAcceleratorMarker(label); |
3285 | } |
3286 | |
3287 | void KLocalePrivate::setDigitSet(KLocale::DigitSet digitSet) |
3288 | { |
3289 | m_digitSet = digitSet; |
3290 | } |
3291 | |
3292 | KLocale::DigitSet KLocalePrivate::digitSet() const |
3293 | { |
3294 | return m_digitSet; |
3295 | } |
3296 | |
3297 | void KLocalePrivate::setMonetaryDigitSet(KLocale::DigitSet digitSet) |
3298 | { |
3299 | m_monetaryDigitSet = digitSet; |
3300 | } |
3301 | |
3302 | KLocale::DigitSet KLocalePrivate::monetaryDigitSet() const |
3303 | { |
3304 | return m_monetaryDigitSet; |
3305 | } |
3306 | |
3307 | void KLocalePrivate::setDateTimeDigitSet(KLocale::DigitSet digitSet) |
3308 | { |
3309 | m_dateTimeDigitSet = digitSet; |
3310 | } |
3311 | |
3312 | KLocale::DigitSet KLocalePrivate::dateTimeDigitSet() const |
3313 | { |
3314 | return m_dateTimeDigitSet; |
3315 | } |
3316 | |
3317 | Q_GLOBAL_STATIC_WITH_ARGS(QMutex, s_kLocaleMutex, (QMutex::Recursive)) |
3318 | |
3319 | QMutex *kLocaleMutex() |
3320 | { |
3321 | return s_kLocaleMutex(); |
3322 | } |
3323 | |