klocale.cpp
00001 // -*- c-basic-offset: 2 -*- 00002 /* This file is part of the KDE libraries 00003 Copyright (c) 1997,2001 Stephan Kulow <coolo@kde.org> 00004 Copyright (c) 1999 Preston Brown <pbrown@kde.org> 00005 Copyright (c) 1999-2002 Hans Petter Bieker <bieker@kde.org> 00006 Copyright (c) 2002 Lukas Tinkl <lukas@kde.org> 00007 00008 This library is free software; you can redistribute it and/or 00009 modify it under the terms of the GNU Library General Public 00010 License as published by the Free Software Foundation; either 00011 version 2 of the License, or (at your option) any later version. 00012 00013 This library is distributed in the hope that it will be useful, 00014 but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 Library General Public License for more details. 00017 00018 You should have received a copy of the GNU Library General Public License 00019 along with this library; see the file COPYING.LIB. If not, write to 00020 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00021 Boston, MA 02110-1301, USA. 00022 */ 00023 00024 #include <config.h> 00025 00026 #include <stdlib.h> // getenv 00027 00028 #include <qtextcodec.h> 00029 #include <qfile.h> 00030 #include <qprinter.h> 00031 #include <qdatetime.h> 00032 #include <qfileinfo.h> 00033 #include <qregexp.h> 00034 00035 #include "kcatalogue.h" 00036 #include "kglobal.h" 00037 #include "kstandarddirs.h" 00038 #include "ksimpleconfig.h" 00039 #include "kinstance.h" 00040 #include "kconfig.h" 00041 #include "kdebug.h" 00042 #include "kcalendarsystem.h" 00043 #include "kcalendarsystemfactory.h" 00044 #include "klocale.h" 00045 00046 #ifdef Q_WS_WIN 00047 #include <windows.h> 00048 #endif 00049 00050 static const char * const SYSTEM_MESSAGES = "kdelibs"; 00051 00052 static const char *maincatalogue = 0; 00053 00054 class KLocalePrivate 00055 { 00056 public: 00057 int weekStartDay; 00058 bool nounDeclension; 00059 bool dateMonthNamePossessive; 00060 QStringList languageList; 00061 QStringList catalogNames; // list of all catalogs (regardless of language) 00062 QValueList<KCatalogue> catalogues; // list of all loaded catalogs, contains one instance per catalog name and language 00063 QString encoding; 00064 QTextCodec * codecForEncoding; 00065 KConfig * config; 00066 bool formatInited; 00067 int /*QPrinter::PageSize*/ pageSize; 00068 KLocale::MeasureSystem measureSystem; 00069 QStringList langTwoAlpha; 00070 KConfig *languages; 00071 00072 QString calendarType; 00073 KCalendarSystem * calendar; 00074 bool utf8FileEncoding; 00075 QString appName; 00076 #ifdef Q_WS_WIN 00077 char win32SystemEncoding[3+7]; //"cp " + lang ID 00078 #endif 00079 }; 00080 00081 static KLocale *this_klocale = 0; 00082 00083 KLocale::KLocale( const QString & catalog, KConfig * config ) 00084 { 00085 d = new KLocalePrivate; 00086 d->config = config; 00087 d->languages = 0; 00088 d->calendar = 0; 00089 d->formatInited = false; 00090 00091 initEncoding(0); 00092 initFileNameEncoding(0); 00093 00094 KConfig *cfg = d->config; 00095 this_klocale = this; 00096 if (!cfg) cfg = KGlobal::instance()->config(); 00097 this_klocale = 0; 00098 Q_ASSERT( cfg ); 00099 00100 d->appName = catalog; 00101 initLanguageList( cfg, config == 0); 00102 initMainCatalogues(catalog); 00103 } 00104 00105 QString KLocale::_initLanguage(KConfigBase *config) 00106 { 00107 if (this_klocale) 00108 { 00109 // ### HPB Why this cast?? 00110 this_klocale->initLanguageList((KConfig *) config, true); 00111 // todo: adapt current catalog list: remove unused languages, insert main catalogs, if not already found 00112 return this_klocale->language(); 00113 } 00114 return QString::null; 00115 } 00116 00117 void KLocale::initMainCatalogues(const QString & catalog) 00118 { 00119 // Use the first non-null string. 00120 QString mainCatalogue = catalog; 00121 if (maincatalogue) 00122 mainCatalogue = QString::fromLatin1(maincatalogue); 00123 00124 if (mainCatalogue.isEmpty()) { 00125 kdDebug(173) << "KLocale instance created called without valid " 00126 << "catalog! Give an argument or call setMainCatalogue " 00127 << "before init" << endl; 00128 } 00129 else { 00130 // do not use insertCatalogue here, that would already trigger updateCatalogs 00131 d->catalogNames.append( mainCatalogue ); // application catalog 00132 d->catalogNames.append( SYSTEM_MESSAGES ); // always include kdelibs.mo 00133 d->catalogNames.append( "kio" ); // always include kio.mo 00134 updateCatalogues(); // evaluate this for all languages 00135 } 00136 } 00137 00138 void KLocale::initLanguageList(KConfig * config, bool useEnv) 00139 { 00140 KConfigGroupSaver saver(config, "Locale"); 00141 00142 m_country = config->readEntry( "Country" ); 00143 if ( m_country.isEmpty() ) 00144 m_country = defaultCountry(); 00145 00146 // same order as setlocale use 00147 QStringList languageList; 00148 if ( useEnv ) 00149 { 00150 // HPB: Only run splitLocale on the environment variables.. 00151 QStringList langs; 00152 00153 langs << QFile::decodeName( ::getenv("LC_ALL") ); 00154 langs << QFile::decodeName( ::getenv("LC_MESSAGES") ); 00155 langs << QFile::decodeName( ::getenv("LANG") ); 00156 00157 for ( QStringList::Iterator it = langs.begin(); 00158 it != langs.end(); 00159 ++it ) 00160 { 00161 QString ln, ct, chrset; 00162 splitLocale(*it, ln, ct, chrset); 00163 00164 if (!ct.isEmpty()) { 00165 if (!chrset.isEmpty()) 00166 langs.insert(it, ln + '_' + ct + '.' + chrset); 00167 langs.insert(it, ln + '_' + ct); 00168 } 00169 00170 langs.insert(it, ln); 00171 } 00172 00173 languageList += langs; 00174 } 00175 00176 // Reset the list and add the new languages 00177 if ( useEnv ) 00178 languageList += QStringList::split 00179 (':', QFile::decodeName( ::getenv("KDE_LANG") )); 00180 00181 languageList += config->readListEntry("Language", ':'); 00182 00183 // now we have a language list -- let's use the first OK language 00184 setLanguage( languageList ); 00185 } 00186 00187 void KLocale::initPluralTypes() 00188 { 00189 for ( QValueList<KCatalogue>::Iterator it = d->catalogues.begin(); 00190 it != d->catalogues.end(); 00191 ++it ) 00192 { 00193 QString language = (*it).language(); 00194 int pt = pluralType( language ); 00195 (*it).setPluralType( pt ); 00196 } 00197 } 00198 00199 00200 int KLocale::pluralType( const QString & language ) 00201 { 00202 for ( QValueList<KCatalogue>::ConstIterator it = d->catalogues.begin(); 00203 it != d->catalogues.end(); 00204 ++it ) 00205 { 00206 if ( ((*it).name() == SYSTEM_MESSAGES ) && ((*it).language() == language )) { 00207 return pluralType( *it ); 00208 } 00209 } 00210 // kdelibs.mo does not seem to exist for this language 00211 return -1; 00212 } 00213 00214 int KLocale::pluralType( const KCatalogue& catalog ) 00215 { 00216 const char* pluralFormString = 00217 I18N_NOOP("_: Dear translator, please do not translate this string " 00218 "in any form, but pick the _right_ value out of " 00219 "NoPlural/TwoForms/French... If not sure what to do mail " 00220 "thd@kde.org and coolo@kde.org, they will tell you. " 00221 "Better leave that out if unsure, the programs will " 00222 "crash!!\nDefinition of PluralForm - to be set by the " 00223 "translator of kdelibs.po"); 00224 QString pf (catalog.translate( pluralFormString)); 00225 if ( pf.isEmpty() ) { 00226 return -1; 00227 } 00228 else if ( pf == "NoPlural" ) 00229 return 0; 00230 else if ( pf == "TwoForms" ) 00231 return 1; 00232 else if ( pf == "French" ) 00233 return 2; 00234 else if ( pf == "OneTwoRest" ) 00235 return 3; 00236 else if ( pf == "Russian" ) 00237 return 4; 00238 else if ( pf == "Polish" ) 00239 return 5; 00240 else if ( pf == "Slovenian" ) 00241 return 6; 00242 else if ( pf == "Lithuanian" ) 00243 return 7; 00244 else if ( pf == "Czech" ) 00245 return 8; 00246 else if ( pf == "Slovak" ) 00247 return 9; 00248 else if ( pf == "Maltese" ) 00249 return 10; 00250 else if ( pf == "Arabic" ) 00251 return 11; 00252 else if ( pf == "Balcan" ) 00253 return 12; 00254 else if ( pf == "Macedonian" ) 00255 return 13; 00256 else if ( pf == "Gaeilge" ) 00257 return 14; 00258 else { 00259 kdWarning(173) << "Definition of PluralForm is none of " 00260 << "NoPlural/" 00261 << "TwoForms/" 00262 << "French/" 00263 << "OneTwoRest/" 00264 << "Russian/" 00265 << "Polish/" 00266 << "Slovenian/" 00267 << "Lithuanian/" 00268 << "Czech/" 00269 << "Slovak/" 00270 << "Arabic/" 00271 << "Balcan/" 00272 << "Macedonian/" 00273 << "Gaeilge/" 00274 << "Maltese: " << pf << endl; 00275 exit(1); 00276 } 00277 } 00278 00279 void KLocale::doFormatInit() const 00280 { 00281 if ( d->formatInited ) return; 00282 00283 KLocale * that = const_cast<KLocale *>(this); 00284 that->initFormat(); 00285 00286 d->formatInited = true; 00287 } 00288 00289 void KLocale::initFormat() 00290 { 00291 KConfig *config = d->config; 00292 if (!config) config = KGlobal::instance()->config(); 00293 Q_ASSERT( config ); 00294 00295 kdDebug(173) << "KLocale::initFormat" << endl; 00296 00297 // make sure the config files are read using the correct locale 00298 // ### Why not add a KConfigBase::setLocale( const KLocale * )? 00299 // ### Then we could remove this hack 00300 KLocale *lsave = KGlobal::_locale; 00301 KGlobal::_locale = this; 00302 00303 KConfigGroupSaver saver(config, "Locale"); 00304 00305 KSimpleConfig entry(locate("locale", 00306 QString::fromLatin1("l10n/%1/entry.desktop") 00307 .arg(m_country)), true); 00308 entry.setGroup("KCM Locale"); 00309 00310 // Numeric 00311 #define readConfigEntry(key, default, save) \ 00312 save = entry.readEntry(key, QString::fromLatin1(default)); \ 00313 save = config->readEntry(key, save); 00314 00315 #define readConfigNumEntry(key, default, save, type) \ 00316 save = (type)entry.readNumEntry(key, default); \ 00317 save = (type)config->readNumEntry(key, save); 00318 00319 #define readConfigBoolEntry(key, default, save) \ 00320 save = entry.readBoolEntry(key, default); \ 00321 save = config->readBoolEntry(key, save); 00322 00323 readConfigEntry("DecimalSymbol", ".", m_decimalSymbol); 00324 readConfigEntry("ThousandsSeparator", ",", m_thousandsSeparator); 00325 m_thousandsSeparator.replace( QString::fromLatin1("$0"), QString::null ); 00326 //kdDebug(173) << "m_thousandsSeparator=" << m_thousandsSeparator << endl; 00327 00328 readConfigEntry("PositiveSign", "", m_positiveSign); 00329 readConfigEntry("NegativeSign", "-", m_negativeSign); 00330 00331 // Monetary 00332 readConfigEntry("CurrencySymbol", "$", m_currencySymbol); 00333 readConfigEntry("MonetaryDecimalSymbol", ".", m_monetaryDecimalSymbol); 00334 readConfigEntry("MonetaryThousandsSeparator", ",", 00335 m_monetaryThousandsSeparator); 00336 m_monetaryThousandsSeparator.replace(QString::fromLatin1("$0"), QString::null); 00337 00338 readConfigNumEntry("FracDigits", 2, m_fracDigits, int); 00339 readConfigBoolEntry("PositivePrefixCurrencySymbol", true, 00340 m_positivePrefixCurrencySymbol); 00341 readConfigBoolEntry("NegativePrefixCurrencySymbol", true, 00342 m_negativePrefixCurrencySymbol); 00343 readConfigNumEntry("PositiveMonetarySignPosition", (int)BeforeQuantityMoney, 00344 m_positiveMonetarySignPosition, SignPosition); 00345 readConfigNumEntry("NegativeMonetarySignPosition", (int)ParensAround, 00346 m_negativeMonetarySignPosition, SignPosition); 00347 00348 00349 // Date and time 00350 readConfigEntry("TimeFormat", "%H:%M:%S", m_timeFormat); 00351 readConfigEntry("DateFormat", "%A %d %B %Y", m_dateFormat); 00352 readConfigEntry("DateFormatShort", "%Y-%m-%d", m_dateFormatShort); 00353 readConfigNumEntry("WeekStartDay", 1, d->weekStartDay, int); 00354 00355 // other 00356 readConfigNumEntry("PageSize", (int)QPrinter::A4, d->pageSize, int); 00357 readConfigNumEntry("MeasureSystem", (int)Metric, d->measureSystem, 00358 MeasureSystem); 00359 readConfigEntry("CalendarSystem", "gregorian", d->calendarType); 00360 delete d->calendar; 00361 d->calendar = 0; // ### HPB Is this the correct place? 00362 00363 //Grammatical 00364 //Precedence here is l10n / i18n / config file 00365 KSimpleConfig language(locate("locale", 00366 QString::fromLatin1("%1/entry.desktop") 00367 .arg(m_language)), true); 00368 language.setGroup("KCM Locale"); 00369 #define read3ConfigBoolEntry(key, default, save) \ 00370 save = entry.readBoolEntry(key, default); \ 00371 save = language.readBoolEntry(key, save); \ 00372 save = config->readBoolEntry(key, save); 00373 00374 read3ConfigBoolEntry("NounDeclension", false, d->nounDeclension); 00375 read3ConfigBoolEntry("DateMonthNamePossessive", false, 00376 d->dateMonthNamePossessive); 00377 00378 // end of hack 00379 KGlobal::_locale = lsave; 00380 } 00381 00382 bool KLocale::setCountry(const QString & country) 00383 { 00384 // Check if the file exists too?? 00385 if ( country.isEmpty() ) 00386 return false; 00387 00388 m_country = country; 00389 00390 d->formatInited = false; 00391 00392 return true; 00393 } 00394 00395 QString KLocale::catalogueFileName(const QString & language, 00396 const KCatalogue & catalog) 00397 { 00398 QString path = QString::fromLatin1("%1/LC_MESSAGES/%2.mo") 00399 .arg( language ) 00400 .arg( catalog.name() ); 00401 00402 return locate( "locale", path ); 00403 } 00404 00405 bool KLocale::setLanguage(const QString & language) 00406 { 00407 if ( d->languageList.contains( language ) ) { 00408 d->languageList.remove( language ); 00409 } 00410 d->languageList.prepend( language ); // let us consider this language to be the most important one 00411 00412 m_language = language; // remember main language for shortcut evaluation 00413 00414 // important when called from the outside and harmless when called before populating the 00415 // catalog name list 00416 updateCatalogues(); 00417 00418 d->formatInited = false; 00419 00420 return true; // Maybe the mo-files for this language are empty, but in principle we can speak all languages 00421 } 00422 00423 bool KLocale::setLanguage(const QStringList & languages) 00424 { 00425 QStringList languageList( languages ); 00426 // This list might contain 00427 // 1) some empty strings that we have to eliminate 00428 // 2) duplicate entries like in de:fr:de, where we have to keep the first occurrance of a language in order 00429 // to preserve the order of precenence of the user => iterate backwards 00430 // 3) languages into which the application is not translated. For those languages we should not even load kdelibs.mo or kio.po. 00431 // these langugage have to be dropped. Otherwise we get strange side effects, e.g. with Hebrew: 00432 // the right/left switch for languages that write from 00433 // right to left (like Hebrew or Arabic) is set in kdelibs.mo. If you only have kdelibs.mo 00434 // but nothing from appname.mo, you get a mostly English app with layout from right to left. 00435 // That was considered to be a bug by the Hebrew translators. 00436 for( QStringList::Iterator it = languageList.fromLast(); 00437 it != languageList.begin(); --it ) 00438 { 00439 // kdDebug() << "checking " << (*it) << endl; 00440 bool bIsTranslated = isApplicationTranslatedInto( *it ); 00441 if ( languageList.contains(*it) > 1 || (*it).isEmpty() || (!bIsTranslated) ) { 00442 // kdDebug() << "removing " << (*it) << endl; 00443 it = languageList.remove( it ); 00444 } 00445 } 00446 // now this has left the first element of the list unchecked. 00447 // The question why this is the case is left as an exercise for the reader... 00448 // Besides the list might have been empty all the way, so check that too. 00449 if ( languageList.begin() != languageList.end() ) { 00450 QStringList::Iterator it = languageList.begin(); // now pointing to the first element 00451 // kdDebug() << "checking " << (*it) << endl; 00452 if( (*it).isEmpty() || !(isApplicationTranslatedInto( *it )) ) { 00453 // kdDebug() << "removing " << (*it) << endl; 00454 languageList.remove( it ); // that's what the iterator was for... 00455 } 00456 } 00457 00458 if ( languageList.isEmpty() ) { 00459 // user picked no language, so we assume he/she speaks English. 00460 languageList.append( defaultLanguage() ); 00461 } 00462 m_language = languageList.first(); // keep this for shortcut evaluations 00463 00464 d->languageList = languageList; // keep this new list of languages to use 00465 d->langTwoAlpha.clear(); // Flush cache 00466 00467 // important when called from the outside and harmless when called before populating the 00468 // catalog name list 00469 updateCatalogues(); 00470 00471 return true; // we found something. Maybe it's only English, but we found something 00472 } 00473 00474 bool KLocale::isApplicationTranslatedInto( const QString & language) 00475 { 00476 if ( language.isEmpty() ) { 00477 return false; 00478 } 00479 00480 if ( language == defaultLanguage() ) { 00481 // en_us is always "installed" 00482 return true; 00483 } 00484 00485 QString appName = d->appName; 00486 if (maincatalogue) { 00487 appName = QString::fromLatin1(maincatalogue); 00488 } 00489 // sorry, catalogueFileName requires catalog object,k which we do not have here 00490 // path finding was supposed to be moved completely to KCatalogue. The interface cannot 00491 // be changed that far during deep freeze. So in order to fix the bug now, we have 00492 // duplicated code for file path evaluation. Cleanup will follow later. We could have e.g. 00493 // a static method in KCataloge that can translate between these file names. 00494 // a stat 00495 QString sFileName = QString::fromLatin1("%1/LC_MESSAGES/%2.mo") 00496 .arg( language ) 00497 .arg( appName ); 00498 // kdDebug() << "isApplicationTranslatedInto: filename " << sFileName << endl; 00499 00500 QString sAbsFileName = locate( "locale", sFileName ); 00501 // kdDebug() << "isApplicationTranslatedInto: absname " << sAbsFileName << endl; 00502 return ! sAbsFileName.isEmpty(); 00503 } 00504 00505 void KLocale::splitLocale(const QString & aStr, 00506 QString & language, 00507 QString & country, 00508 QString & chrset) 00509 { 00510 QString str = aStr; 00511 00512 // just in case, there is another language appended 00513 int f = str.find(':'); 00514 if (f >= 0) 00515 str.truncate(f); 00516 00517 country = QString::null; 00518 chrset = QString::null; 00519 language = QString::null; 00520 00521 f = str.find('.'); 00522 if (f >= 0) 00523 { 00524 chrset = str.mid(f + 1); 00525 str.truncate(f); 00526 } 00527 00528 f = str.find('_'); 00529 if (f >= 0) 00530 { 00531 country = str.mid(f + 1); 00532 str.truncate(f); 00533 } 00534 00535 language = str; 00536 } 00537 00538 QString KLocale::language() const 00539 { 00540 return m_language; 00541 } 00542 00543 QString KLocale::country() const 00544 { 00545 return m_country; 00546 } 00547 00548 QString KLocale::monthName(int i, bool shortName) const 00549 { 00550 if ( shortName ) 00551 switch ( i ) 00552 { 00553 case 1: return translate("January", "Jan"); 00554 case 2: return translate("February", "Feb"); 00555 case 3: return translate("March", "Mar"); 00556 case 4: return translate("April", "Apr"); 00557 case 5: return translate("May short", "May"); 00558 case 6: return translate("June", "Jun"); 00559 case 7: return translate("July", "Jul"); 00560 case 8: return translate("August", "Aug"); 00561 case 9: return translate("September", "Sep"); 00562 case 10: return translate("October", "Oct"); 00563 case 11: return translate("November", "Nov"); 00564 case 12: return translate("December", "Dec"); 00565 } 00566 else 00567 switch (i) 00568 { 00569 case 1: return translate("January"); 00570 case 2: return translate("February"); 00571 case 3: return translate("March"); 00572 case 4: return translate("April"); 00573 case 5: return translate("May long", "May"); 00574 case 6: return translate("June"); 00575 case 7: return translate("July"); 00576 case 8: return translate("August"); 00577 case 9: return translate("September"); 00578 case 10: return translate("October"); 00579 case 11: return translate("November"); 00580 case 12: return translate("December"); 00581 } 00582 00583 return QString::null; 00584 } 00585 00586 QString KLocale::monthNamePossessive(int i, bool shortName) const 00587 { 00588 if ( shortName ) 00589 switch ( i ) 00590 { 00591 case 1: return translate("of January", "of Jan"); 00592 case 2: return translate("of February", "of Feb"); 00593 case 3: return translate("of March", "of Mar"); 00594 case 4: return translate("of April", "of Apr"); 00595 case 5: return translate("of May short", "of May"); 00596 case 6: return translate("of June", "of Jun"); 00597 case 7: return translate("of July", "of Jul"); 00598 case 8: return translate("of August", "of Aug"); 00599 case 9: return translate("of September", "of Sep"); 00600 case 10: return translate("of October", "of Oct"); 00601 case 11: return translate("of November", "of Nov"); 00602 case 12: return translate("of December", "of Dec"); 00603 } 00604 else 00605 switch (i) 00606 { 00607 case 1: return translate("of January"); 00608 case 2: return translate("of February"); 00609 case 3: return translate("of March"); 00610 case 4: return translate("of April"); 00611 case 5: return translate("of May long", "of May"); 00612 case 6: return translate("of June"); 00613 case 7: return translate("of July"); 00614 case 8: return translate("of August"); 00615 case 9: return translate("of September"); 00616 case 10: return translate("of October"); 00617 case 11: return translate("of November"); 00618 case 12: return translate("of December"); 00619 } 00620 00621 return QString::null; 00622 } 00623 00624 QString KLocale::weekDayName (int i, bool shortName) const 00625 { 00626 return calendar()->weekDayName(i, shortName); 00627 } 00628 00629 void KLocale::insertCatalogue( const QString & catalog ) 00630 { 00631 if ( !d->catalogNames.contains( catalog) ) { 00632 d->catalogNames.append( catalog ); 00633 } 00634 updateCatalogues( ); // evaluate the changed list and generate the neccessary KCatalog objects 00635 } 00636 00637 void KLocale::updateCatalogues( ) 00638 { 00639 // some changes have occured. Maybe we have learned or forgotten some languages. 00640 // Maybe the language precedence has changed. 00641 // Maybe we have learned or forgotten some catalog names. 00642 // Now examine the list of KCatalogue objects and change it according to the new circumstances. 00643 00644 // this could be optimized: try to reuse old KCatalog objects, but remember that the order of 00645 // catalogs might have changed: e.g. in this fashion 00646 // 1) move all catalogs into a temporary list 00647 // 2) iterate over all languages and catalog names 00648 // 3.1) pick the catalog from the saved list, if it already exists 00649 // 3.2) else create a new catalog. 00650 // but we will do this later. 00651 00652 for ( QValueList<KCatalogue>::Iterator it = d->catalogues.begin(); 00653 it != d->catalogues.end(); ) 00654 { 00655 it = d->catalogues.remove(it); 00656 } 00657 00658 // now iterate over all languages and all wanted catalog names and append or create them in the right order 00659 // the sequence must be e.g. nds/appname nds/kdelibs nds/kio de/appname de/kdelibs de/kio etc. 00660 // and not nds/appname de/appname nds/kdelibs de/kdelibs etc. Otherwise we would be in trouble with a language 00661 // sequende nds,en_US, de. In this case en_US must hide everything below in the language list. 00662 for ( QStringList::ConstIterator itLangs = d->languageList.begin(); 00663 itLangs != d->languageList.end(); ++itLangs) 00664 { 00665 for ( QStringList::ConstIterator itNames = d->catalogNames.begin(); 00666 itNames != d->catalogNames.end(); ++itNames) 00667 { 00668 KCatalogue cat( *itNames, *itLangs ); // create Catalog for this name and this language 00669 d->catalogues.append( cat ); 00670 } 00671 } 00672 initPluralTypes(); // evaluate the plural type for all languages and remember this in each KCatalogue 00673 } 00674 00675 00676 00677 00678 void KLocale::removeCatalogue(const QString &catalog) 00679 { 00680 if ( d->catalogNames.contains( catalog )) { 00681 d->catalogNames.remove( catalog ); 00682 if (KGlobal::_instance) 00683 updateCatalogues(); // walk through the KCatalogue instances and weed out everything we no longer need 00684 } 00685 } 00686 00687 void KLocale::setActiveCatalogue(const QString &catalog) 00688 { 00689 if ( d->catalogNames.contains( catalog ) ) { 00690 d->catalogNames.remove( catalog ); 00691 d->catalogNames.prepend( catalog ); 00692 updateCatalogues(); // walk through the KCatalogue instances and adapt to the new order 00693 } 00694 } 00695 00696 KLocale::~KLocale() 00697 { 00698 delete d->calendar; 00699 delete d->languages; 00700 delete d; 00701 d = 0L; 00702 } 00703 00704 QString KLocale::translate_priv(const char *msgid, 00705 const char *fallback, 00706 const char **translated, 00707 int* pluralType ) const 00708 { 00709 if ( pluralType) { 00710 *pluralType = -1; // unless we find something more precise 00711 } 00712 if (!msgid || !msgid[0]) 00713 { 00714 kdWarning() << "KLocale: trying to look up \"\" in catalog. " 00715 << "Fix the program" << endl; 00716 return QString::null; 00717 } 00718 00719 if ( useDefaultLanguage() ) { // shortcut evaluation if en_US is main language: do not consult the catalogs 00720 return QString::fromUtf8( fallback ); 00721 } 00722 00723 for ( QValueList<KCatalogue>::ConstIterator it = d->catalogues.begin(); 00724 it != d->catalogues.end(); 00725 ++it ) 00726 { 00727 // shortcut evaluation: once we have arrived at en_US (default language) we cannot consult 00728 // the catalog as it will not have an assiciated mo-file. For this default language we can 00729 // immediately pick the fallback string. 00730 if ( (*it).language() == defaultLanguage() ) { 00731 return QString::fromUtf8( fallback ); 00732 } 00733 00734 const char * text = (*it).translate( msgid ); 00735 00736 if ( text ) 00737 { 00738 // we found it 00739 if (translated) { 00740 *translated = text; 00741 } 00742 if ( pluralType) { 00743 *pluralType = (*it).pluralType(); // remember the plural type information from the catalog that was used 00744 } 00745 return QString::fromUtf8( text ); 00746 } 00747 } 00748 00749 // Always use UTF-8 if the string was not found 00750 return QString::fromUtf8( fallback ); 00751 } 00752 00753 QString KLocale::translate(const char* msgid) const 00754 { 00755 return translate_priv(msgid, msgid); 00756 } 00757 00758 QString KLocale::translate( const char *index, const char *fallback) const 00759 { 00760 if (!index || !index[0] || !fallback || !fallback[0]) 00761 { 00762 kdDebug(173) << "KLocale: trying to look up \"\" in catalog. " 00763 << "Fix the program" << endl; 00764 return QString::null; 00765 } 00766 00767 if ( useDefaultLanguage() ) 00768 return QString::fromUtf8( fallback ); 00769 00770 char *newstring = new char[strlen(index) + strlen(fallback) + 5]; 00771 sprintf(newstring, "_: %s\n%s", index, fallback); 00772 // as copying QString is very fast, it looks slower as it is ;/ 00773 QString r = translate_priv(newstring, fallback); 00774 delete [] newstring; 00775 00776 return r; 00777 } 00778 00779 static QString put_n_in(const QString &orig, unsigned long n) 00780 { 00781 QString ret = orig; 00782 int index = ret.find("%n"); 00783 if (index == -1) 00784 return ret; 00785 ret.replace(index, 2, QString::number(n)); 00786 return ret; 00787 } 00788 00789 #define EXPECT_LENGTH(x) \ 00790 if (forms.count() != x) { \ 00791 kdError() << "translation of \"" << singular << "\" doesn't contain " << x << " different plural forms as expected\n"; \ 00792 return QString( "BROKEN TRANSLATION %1" ).arg( singular ); } 00793 00794 QString KLocale::translate( const char *singular, const char *plural, 00795 unsigned long n ) const 00796 { 00797 if (!singular || !singular[0] || !plural || !plural[0]) 00798 { 00799 kdWarning() << "KLocale: trying to look up \"\" in catalog. " 00800 << "Fix the program" << endl; 00801 return QString::null; 00802 } 00803 00804 char *newstring = new char[strlen(singular) + strlen(plural) + 6]; 00805 sprintf(newstring, "_n: %s\n%s", singular, plural); 00806 // as copying QString is very fast, it looks slower as it is ;/ 00807 int pluralType = -1; 00808 QString r = translate_priv(newstring, 0, 0, &pluralType); 00809 delete [] newstring; 00810 00811 if ( r.isEmpty() || useDefaultLanguage() || pluralType == -1) { 00812 if ( n == 1 ) { 00813 return put_n_in( QString::fromUtf8( singular ), n ); 00814 } else { 00815 QString tmp = QString::fromUtf8( plural ); 00816 #ifndef NDEBUG 00817 if (tmp.find("%n") == -1) { 00818 kdDebug() << "the message for i18n should contain a '%n'! " << plural << endl; 00819 } 00820 #endif 00821 return put_n_in( tmp, n ); 00822 } 00823 } 00824 00825 QStringList forms = QStringList::split( "\n", r, false ); 00826 switch ( pluralType ) { 00827 case 0: // NoPlural 00828 EXPECT_LENGTH( 1 ); 00829 return put_n_in( forms[0], n); 00830 case 1: // TwoForms 00831 EXPECT_LENGTH( 2 ); 00832 if ( n == 1 ) 00833 return put_n_in( forms[0], n); 00834 else 00835 return put_n_in( forms[1], n); 00836 case 2: // French 00837 EXPECT_LENGTH( 2 ); 00838 if ( n == 1 || n == 0 ) 00839 return put_n_in( forms[0], n); 00840 else 00841 return put_n_in( forms[1], n); 00842 case 3: // OneTwoRest 00843 EXPECT_LENGTH( 3 ); 00844 if ( n == 1 ) 00845 return put_n_in( forms[0], n); 00846 else if ( n == 2 ) 00847 return put_n_in( forms[1], n); 00848 else 00849 return put_n_in( forms[2], n); 00850 case 4: // Russian, corrected by mok 00851 EXPECT_LENGTH( 3 ); 00852 if ( n%10 == 1 && n%100 != 11) 00853 return put_n_in( forms[0], n); // odin fail 00854 else if (( n%10 >= 2 && n%10 <=4 ) && (n%100<10 || n%100>20)) 00855 return put_n_in( forms[1], n); // dva faila 00856 else 00857 return put_n_in( forms[2], n); // desyat' failov 00858 case 5: // Polish 00859 EXPECT_LENGTH( 3 ); 00860 if ( n == 1 ) 00861 return put_n_in( forms[0], n); 00862 else if ( n%10 >= 2 && n%10 <=4 && (n%100<10 || n%100>=20) ) 00863 return put_n_in( forms[1], n); 00864 else 00865 return put_n_in( forms[2], n); 00866 case 6: // Slovenian 00867 EXPECT_LENGTH( 4 ); 00868 if ( n%100 == 1 ) 00869 return put_n_in( forms[1], n); // ena datoteka 00870 else if ( n%100 == 2 ) 00871 return put_n_in( forms[2], n); // dve datoteki 00872 else if ( n%100 == 3 || n%100 == 4 ) 00873 return put_n_in( forms[3], n); // tri datoteke 00874 else 00875 return put_n_in( forms[0], n); // sto datotek 00876 case 7: // Lithuanian 00877 EXPECT_LENGTH( 3 ); 00878 if ( n%10 == 0 || (n%100>=11 && n%100<=19) ) 00879 return put_n_in( forms[2], n); 00880 else if ( n%10 == 1 ) 00881 return put_n_in( forms[0], n); 00882 else 00883 return put_n_in( forms[1], n); 00884 case 8: // Czech - use modern form which is equivalent to Slovak 00885 case 9: // Slovak 00886 EXPECT_LENGTH( 3 ); 00887 if ( n == 1 ) 00888 return put_n_in( forms[0], n); 00889 else if (( n >= 2 ) && ( n <= 4 )) 00890 return put_n_in( forms[1], n); 00891 else 00892 return put_n_in( forms[2], n); 00893 case 10: // Maltese 00894 EXPECT_LENGTH( 4 ); 00895 if ( n == 1 ) 00896 return put_n_in( forms[0], n ); 00897 else if ( ( n == 0 ) || ( n%100 > 0 && n%100 <= 10 ) ) 00898 return put_n_in( forms[1], n ); 00899 else if ( n%100 > 10 && n%100 < 20 ) 00900 return put_n_in( forms[2], n ); 00901 else 00902 return put_n_in( forms[3], n ); 00903 case 11: // Arabic 00904 EXPECT_LENGTH( 4 ); 00905 if (n == 1) 00906 return put_n_in(forms[0], n); 00907 else if (n == 2) 00908 return put_n_in(forms[1], n); 00909 else if ( n < 11) 00910 return put_n_in(forms[2], n); 00911 else 00912 return put_n_in(forms[3], n); 00913 case 12: // Balcan 00914 EXPECT_LENGTH( 3 ); 00915 if (n != 11 && n % 10 == 1) 00916 return put_n_in(forms[0], n); 00917 else if (n / 10 != 1 && n % 10 >= 2 && n % 10 <= 4) 00918 return put_n_in(forms[1], n); 00919 else 00920 return put_n_in(forms[2], n); 00921 case 13: // Macedonian 00922 EXPECT_LENGTH(3); 00923 if (n % 10 == 1) 00924 return put_n_in(forms[0], n); 00925 else if (n % 10 == 2) 00926 return put_n_in(forms[1], n); 00927 else 00928 return put_n_in(forms[2], n); 00929 case 14: // Gaeilge 00930 EXPECT_LENGTH(5); 00931 if (n == 1) // "ceann amhain" 00932 return put_n_in(forms[0], n); 00933 else if (n == 2) // "dha cheann" 00934 return put_n_in(forms[1], n); 00935 else if (n < 7) // "%n cinn" 00936 return put_n_in(forms[2], n); 00937 else if (n < 11) // "%n gcinn" 00938 return put_n_in(forms[3], n); 00939 else // "%n ceann" 00940 return put_n_in(forms[4], n); 00941 } 00942 kdFatal() << "The function should have been returned in another way\n"; 00943 00944 return QString::null; 00945 } 00946 00947 QString KLocale::translateQt( const char *context, const char *source, 00948 const char *message) const 00949 { 00950 if (!source || !source[0]) { 00951 kdWarning() << "KLocale: trying to look up \"\" in catalog. " 00952 << "Fix the program" << endl; 00953 return QString::null; 00954 } 00955 00956 if ( useDefaultLanguage() ) { 00957 return QString::null; 00958 } 00959 00960 char *newstring = 0; 00961 const char *translation = 0; 00962 QString r; 00963 00964 if ( message && message[0]) { 00965 char *newstring = new char[strlen(source) + strlen(message) + 5]; 00966 sprintf(newstring, "_: %s\n%s", source, message); 00967 const char *translation = 0; 00968 // as copying QString is very fast, it looks slower as it is ;/ 00969 r = translate_priv(newstring, source, &translation); 00970 delete [] newstring; 00971 if (translation) 00972 return r; 00973 } 00974 00975 if ( context && context[0] && message && message[0]) { 00976 newstring = new char[strlen(context) + strlen(message) + 5]; 00977 sprintf(newstring, "_: %s\n%s", context, message); 00978 // as copying QString is very fast, it looks slower as it is ;/ 00979 r = translate_priv(newstring, source, &translation); 00980 delete [] newstring; 00981 if (translation) 00982 return r; 00983 } 00984 00985 r = translate_priv(source, source, &translation); 00986 if (translation) 00987 return r; 00988 return QString::null; 00989 } 00990 00991 bool KLocale::nounDeclension() const 00992 { 00993 doFormatInit(); 00994 return d->nounDeclension; 00995 } 00996 00997 bool KLocale::dateMonthNamePossessive() const 00998 { 00999 doFormatInit(); 01000 return d->dateMonthNamePossessive; 01001 } 01002 01003 int KLocale::weekStartDay() const 01004 { 01005 doFormatInit(); 01006 return d->weekStartDay; 01007 } 01008 01009 bool KLocale::weekStartsMonday() const //deprecated 01010 { 01011 doFormatInit(); 01012 return (d->weekStartDay==1); 01013 } 01014 01015 QString KLocale::decimalSymbol() const 01016 { 01017 doFormatInit(); 01018 return m_decimalSymbol; 01019 } 01020 01021 QString KLocale::thousandsSeparator() const 01022 { 01023 doFormatInit(); 01024 return m_thousandsSeparator; 01025 } 01026 01027 QString KLocale::currencySymbol() const 01028 { 01029 doFormatInit(); 01030 return m_currencySymbol; 01031 } 01032 01033 QString KLocale::monetaryDecimalSymbol() const 01034 { 01035 doFormatInit(); 01036 return m_monetaryDecimalSymbol; 01037 } 01038 01039 QString KLocale::monetaryThousandsSeparator() const 01040 { 01041 doFormatInit(); 01042 return m_monetaryThousandsSeparator; 01043 } 01044 01045 QString KLocale::positiveSign() const 01046 { 01047 doFormatInit(); 01048 return m_positiveSign; 01049 } 01050 01051 QString KLocale::negativeSign() const 01052 { 01053 doFormatInit(); 01054 return m_negativeSign; 01055 } 01056 01057 int KLocale::fracDigits() const 01058 { 01059 doFormatInit(); 01060 return m_fracDigits; 01061 } 01062 01063 bool KLocale::positivePrefixCurrencySymbol() const 01064 { 01065 doFormatInit(); 01066 return m_positivePrefixCurrencySymbol; 01067 } 01068 01069 bool KLocale::negativePrefixCurrencySymbol() const 01070 { 01071 doFormatInit(); 01072 return m_negativePrefixCurrencySymbol; 01073 } 01074 01075 KLocale::SignPosition KLocale::positiveMonetarySignPosition() const 01076 { 01077 doFormatInit(); 01078 return m_positiveMonetarySignPosition; 01079 } 01080 01081 KLocale::SignPosition KLocale::negativeMonetarySignPosition() const 01082 { 01083 doFormatInit(); 01084 return m_negativeMonetarySignPosition; 01085 } 01086 01087 static inline void put_it_in( QChar *buffer, uint& index, const QString &s ) 01088 { 01089 for ( uint l = 0; l < s.length(); l++ ) 01090 buffer[index++] = s.at( l ); 01091 } 01092 01093 static inline void put_it_in( QChar *buffer, uint& index, int number ) 01094 { 01095 buffer[index++] = number / 10 + '0'; 01096 buffer[index++] = number % 10 + '0'; 01097 } 01098 01099 // insert (thousands)-"separator"s into the non-fractional part of str 01100 static void _insertSeparator(QString &str, const QString &separator, 01101 const QString &decimalSymbol) 01102 { 01103 // leave fractional part untouched 01104 QString mainPart = str.section(decimalSymbol, 0, 0); 01105 QString fracPart = str.section(decimalSymbol, 1, 1, 01106 QString::SectionIncludeLeadingSep); 01107 01108 for (int pos = mainPart.length() - 3; pos > 0; pos -= 3) 01109 mainPart.insert(pos, separator); 01110 01111 str = mainPart + fracPart; 01112 } 01113 01114 QString KLocale::formatMoney(double num, 01115 const QString & symbol, 01116 int precision) const 01117 { 01118 // some defaults 01119 QString currency = symbol.isNull() 01120 ? currencySymbol() 01121 : symbol; 01122 if (precision < 0) precision = fracDigits(); 01123 01124 // the number itself 01125 bool neg = num < 0; 01126 QString res = QString::number(neg?-num:num, 'f', precision); 01127 01128 // Replace dot with locale decimal separator 01129 res.replace(QChar('.'), monetaryDecimalSymbol()); 01130 01131 // Insert the thousand separators 01132 _insertSeparator(res, monetaryThousandsSeparator(), monetaryDecimalSymbol()); 01133 01134 // set some variables we need later 01135 int signpos = neg 01136 ? negativeMonetarySignPosition() 01137 : positiveMonetarySignPosition(); 01138 QString sign = neg 01139 ? negativeSign() 01140 : positiveSign(); 01141 01142 switch (signpos) 01143 { 01144 case ParensAround: 01145 res.prepend('('); 01146 res.append (')'); 01147 break; 01148 case BeforeQuantityMoney: 01149 res.prepend(sign); 01150 break; 01151 case AfterQuantityMoney: 01152 res.append(sign); 01153 break; 01154 case BeforeMoney: 01155 currency.prepend(sign); 01156 break; 01157 case AfterMoney: 01158 currency.append(sign); 01159 break; 01160 } 01161 01162 if (neg?negativePrefixCurrencySymbol(): 01163 positivePrefixCurrencySymbol()) 01164 { 01165 res.prepend(' '); 01166 res.prepend(currency); 01167 } else { 01168 res.append (' '); 01169 res.append (currency); 01170 } 01171 01172 return res; 01173 } 01174 01175 QString KLocale::formatMoney(const QString &numStr) const 01176 { 01177 return formatMoney(numStr.toDouble()); 01178 } 01179 01180 QString KLocale::formatNumber(double num, int precision) const 01181 { 01182 if (precision == -1) precision = 2; 01183 // no need to round since QString::number does this for us 01184 return formatNumber(QString::number(num, 'f', precision), false, 0); 01185 } 01186 01187 QString KLocale::formatLong(long num) const 01188 { 01189 return formatNumber((double)num, 0); 01190 } 01191 01192 QString KLocale::formatNumber(const QString &numStr) const 01193 { 01194 return formatNumber(numStr, true, 2); 01195 } 01196 01197 // increase the digit at 'position' by one 01198 static void _inc_by_one(QString &str, int position) 01199 { 01200 for (int i = position; i >= 0; i--) 01201 { 01202 char last_char = str[i].latin1(); 01203 switch(last_char) 01204 { 01205 case '0': 01206 str[i] = '1'; 01207 break; 01208 case '1': 01209 str[i] = '2'; 01210 break; 01211 case '2': 01212 str[i] = '3'; 01213 break; 01214 case '3': 01215 str[i] = '4'; 01216 break; 01217 case '4': 01218 str[i] = '5'; 01219 break; 01220 case '5': 01221 str[i] = '6'; 01222 break; 01223 case '6': 01224 str[i] = '7'; 01225 break; 01226 case '7': 01227 str[i] = '8'; 01228 break; 01229 case '8': 01230 str[i] = '9'; 01231 break; 01232 case '9': 01233 str[i] = '0'; 01234 if (i == 0) str.prepend('1'); 01235 continue; 01236 case '.': 01237 continue; 01238 } 01239 break; 01240 } 01241 } 01242 01243 // Cut off if more digits in fractional part than 'precision' 01244 static void _round(QString &str, int precision) 01245 { 01246 int decimalSymbolPos = str.find('.'); 01247 01248 if (decimalSymbolPos == -1) 01249 if (precision == 0) return; 01250 else if (precision > 0) // add dot if missing (and needed) 01251 { 01252 str.append('.'); 01253 decimalSymbolPos = str.length() - 1; 01254 } 01255 01256 // fill up with more than enough zeroes (in case fractional part too short) 01257 str.append(QString().fill('0', precision)); 01258 01259 // Now decide whether to round up or down 01260 char last_char = str[decimalSymbolPos + precision + 1].latin1(); 01261 switch (last_char) 01262 { 01263 case '0': 01264 case '1': 01265 case '2': 01266 case '3': 01267 case '4': 01268 // nothing to do, rounding down 01269 break; 01270 case '5': 01271 case '6': 01272 case '7': 01273 case '8': 01274 case '9': 01275 _inc_by_one(str, decimalSymbolPos + precision); 01276 break; 01277 default: 01278 break; 01279 } 01280 01281 decimalSymbolPos = str.find('.'); 01282 str.truncate(decimalSymbolPos + precision + 1); 01283 01284 // if precision == 0 delete also '.' 01285 if (precision == 0) str = str.section('.', 0, 0); 01286 } 01287 01288 QString KLocale::formatNumber(const QString &numStr, bool round, 01289 int precision) const 01290 { 01291 QString tmpString = numStr; 01292 if ((round && precision < 0) || 01293 ! QRegExp("^[+-]?\\d+(\\.\\d+)*(e[+-]?\\d+)?$").exactMatch(tmpString)) 01294 return numStr; 01295 01296 01297 // Skip the sign (for now) 01298 bool neg = (tmpString[0] == '-'); 01299 if (neg || tmpString[0] == '+') tmpString.remove(0, 1); 01300 01301 // Split off exponential part (including 'e'-symbol) 01302 QString mantString = tmpString.section('e', 0, 0, 01303 QString::SectionCaseInsensitiveSeps); 01304 QString expString = tmpString.section('e', 1, 1, 01305 QString::SectionCaseInsensitiveSeps | 01306 QString::SectionIncludeLeadingSep); 01307 01308 if (round) _round(mantString, precision); 01309 01310 // Replace dot with locale decimal separator 01311 mantString.replace(QChar('.'), decimalSymbol()); 01312 01313 // Insert the thousand separators 01314 _insertSeparator(mantString, thousandsSeparator(), decimalSymbol()); 01315 01316 // How can we know where we should put the sign? 01317 mantString.prepend(neg?negativeSign():positiveSign()); 01318 01319 return mantString + expString; 01320 } 01321 01322 QString KLocale::formatDate(const QDate &pDate, bool shortFormat) const 01323 { 01324 const QString rst = shortFormat?dateFormatShort():dateFormat(); 01325 01326 QString buffer; 01327 01328 if ( ! pDate.isValid() ) return buffer; 01329 01330 bool escape = false; 01331 01332 int year = calendar()->year(pDate); 01333 int month = calendar()->month(pDate); 01334 01335 for ( uint format_index = 0; format_index < rst.length(); ++format_index ) 01336 { 01337 if ( !escape ) 01338 { 01339 if ( rst.at( format_index ).unicode() == '%' ) 01340 escape = true; 01341 else 01342 buffer.append(rst.at(format_index)); 01343 } 01344 else 01345 { 01346 switch ( rst.at( format_index ).unicode() ) 01347 { 01348 case '%': 01349 buffer.append('%'); 01350 break; 01351 case 'Y': 01352 buffer.append(calendar()->yearString(pDate, false)); 01353 break; 01354 case 'y': 01355 buffer.append(calendar()->yearString(pDate, true)); 01356 break; 01357 case 'n': 01358 buffer.append(calendar()->monthString(pDate, true)); 01359 break; 01360 case 'e': 01361 buffer.append(calendar()->dayString(pDate, true)); 01362 break; 01363 case 'm': 01364 buffer.append(calendar()->monthString(pDate, false)); 01365 break; 01366 case 'b': 01367 if (d->nounDeclension && d->dateMonthNamePossessive) 01368 buffer.append(calendar()->monthNamePossessive(month, year, true)); 01369 else 01370 buffer.append(calendar()->monthName(month, year, true)); 01371 break; 01372 case 'B': 01373 if (d->nounDeclension && d->dateMonthNamePossessive) 01374 buffer.append(calendar()->monthNamePossessive(month, year, false)); 01375 else 01376 buffer.append(calendar()->monthName(month, year, false)); 01377 break; 01378 case 'd': 01379 buffer.append(calendar()->dayString(pDate, false)); 01380 break; 01381 case 'a': 01382 buffer.append(calendar()->weekDayName(pDate, true)); 01383 break; 01384 case 'A': 01385 buffer.append(calendar()->weekDayName(pDate, false)); 01386 break; 01387 default: 01388 buffer.append(rst.at(format_index)); 01389 break; 01390 } 01391 escape = false; 01392 } 01393 } 01394 return buffer; 01395 } 01396 01397 void KLocale::setMainCatalogue(const char *catalog) 01398 { 01399 maincatalogue = catalog; 01400 } 01401 01402 double KLocale::readNumber(const QString &_str, bool * ok) const 01403 { 01404 QString str = _str.stripWhiteSpace(); 01405 bool neg = str.find(negativeSign()) == 0; 01406 if (neg) 01407 str.remove( 0, negativeSign().length() ); 01408 01409 /* will hold the scientific notation portion of the number. 01410 Example, with 2.34E+23, exponentialPart == "E+23" 01411 */ 01412 QString exponentialPart; 01413 int EPos; 01414 01415 EPos = str.find('E', 0, false); 01416 01417 if (EPos != -1) 01418 { 01419 exponentialPart = str.mid(EPos); 01420 str = str.left(EPos); 01421 } 01422 01423 int pos = str.find(decimalSymbol()); 01424 QString major; 01425 QString minor; 01426 if ( pos == -1 ) 01427 major = str; 01428 else 01429 { 01430 major = str.left(pos); 01431 minor = str.mid(pos + decimalSymbol().length()); 01432 } 01433 01434 // Remove thousand separators 01435 int thlen = thousandsSeparator().length(); 01436 int lastpos = 0; 01437 while ( ( pos = major.find( thousandsSeparator() ) ) > 0 ) 01438 { 01439 // e.g. 12,,345,,678,,922 Acceptable positions (from the end) are 5, 10, 15... i.e. (3+thlen)*N 01440 int fromEnd = major.length() - pos; 01441 if ( fromEnd % (3+thlen) != 0 // Needs to be a multiple, otherwise it's an error 01442 || pos - lastpos > 3 // More than 3 digits between two separators -> error 01443 || pos == 0 // Can't start with a separator 01444 || (lastpos>0 && pos-lastpos!=3)) // Must have exactly 3 digits between two separators 01445 { 01446 if (ok) *ok = false; 01447 return 0.0; 01448 } 01449 01450 lastpos = pos; 01451 major.remove( pos, thlen ); 01452 } 01453 if (lastpos>0 && major.length()-lastpos!=3) // Must have exactly 3 digits after the last separator 01454 { 01455 if (ok) *ok = false; 01456 return 0.0; 01457 } 01458 01459 QString tot; 01460 if (neg) tot = '-'; 01461 01462 tot += major + '.' + minor + exponentialPart; 01463 01464 return tot.toDouble(ok); 01465 } 01466 01467 double KLocale::readMoney(const QString &_str, bool * ok) const 01468 { 01469 QString str = _str.stripWhiteSpace(); 01470 bool neg = false; 01471 bool currencyFound = false; 01472 QString symbol = currencySymbol(); 01473 // First try removing currency symbol from either end 01474 int pos = str.find(symbol); 01475 if ( pos == 0 || pos == (int) str.length()-symbol.length() ) 01476 { 01477 str.remove(pos,symbol.length()); 01478 str = str.stripWhiteSpace(); 01479 currencyFound = true; 01480 } 01481 if (str.isEmpty()) 01482 { 01483 if (ok) *ok = false; 01484 return 0; 01485 } 01486 // Then try removing negative sign from either end 01487 // (with a special case for parenthesis) 01488 if (negativeMonetarySignPosition() == ParensAround) 01489 { 01490 if (str[0] == '(' && str[str.length()-1] == ')') 01491 { 01492 neg = true; 01493 str.remove(str.length()-1,1); 01494 str.remove(0,1); 01495 } 01496 } 01497 else 01498 { 01499 int i1 = str.find(negativeSign()); 01500 if ( i1 == 0 || i1 == (int) str.length()-1 ) 01501 { 01502 neg = true; 01503 str.remove(i1,negativeSign().length()); 01504 } 01505 } 01506 if (neg) str = str.stripWhiteSpace(); 01507 01508 // Finally try again for the currency symbol, if we didn't find 01509 // it already (because of the negative sign being in the way). 01510 if ( !currencyFound ) 01511 { 01512 pos = str.find(symbol); 01513 if ( pos == 0 || pos == (int) str.length()-symbol.length() ) 01514 { 01515 str.remove(pos,symbol.length()); 01516 str = str.stripWhiteSpace(); 01517 } 01518 } 01519 01520 // And parse the rest as a number 01521 pos = str.find(monetaryDecimalSymbol()); 01522 QString major; 01523 QString minior; 01524 if (pos == -1) 01525 major = str; 01526 else 01527 { 01528 major = str.left(pos); 01529 minior = str.mid(pos + monetaryDecimalSymbol().length()); 01530 } 01531 01532 // Remove thousand separators 01533 int thlen = monetaryThousandsSeparator().length(); 01534 int lastpos = 0; 01535 while ( ( pos = major.find( monetaryThousandsSeparator() ) ) > 0 ) 01536 { 01537 // e.g. 12,,345,,678,,922 Acceptable positions (from the end) are 5, 10, 15... i.e. (3+thlen)*N 01538 int fromEnd = major.length() - pos; 01539 if ( fromEnd % (3+thlen) != 0 // Needs to be a multiple, otherwise it's an error 01540 || pos - lastpos > 3 // More than 3 digits between two separators -> error 01541 || pos == 0 // Can't start with a separator 01542 || (lastpos>0 && pos-lastpos!=3)) // Must have exactly 3 digits between two separators 01543 { 01544 if (ok) *ok = false; 01545 return 0.0; 01546 } 01547 lastpos = pos; 01548 major.remove( pos, thlen ); 01549 } 01550 if (lastpos>0 && major.length()-lastpos!=3) // Must have exactly 3 digits after the last separator 01551 { 01552 if (ok) *ok = false; 01553 return 0.0; 01554 } 01555 01556 QString tot; 01557 if (neg) tot = '-'; 01558 tot += major + '.' + minior; 01559 return tot.toDouble(ok); 01560 } 01561 01568 static int readInt(const QString &str, uint &pos) 01569 { 01570 if (!str.at(pos).isDigit()) return -1; 01571 int result = 0; 01572 for (; str.length() > pos && str.at(pos).isDigit(); pos++) 01573 { 01574 result *= 10; 01575 result += str.at(pos).digitValue(); 01576 } 01577 01578 return result; 01579 } 01580 01581 QDate KLocale::readDate(const QString &intstr, bool* ok) const 01582 { 01583 QDate date; 01584 date = readDate(intstr, ShortFormat, ok); 01585 if (date.isValid()) return date; 01586 return readDate(intstr, NormalFormat, ok); 01587 } 01588 01589 QDate KLocale::readDate(const QString &intstr, ReadDateFlags flags, bool* ok) const 01590 { 01591 QString fmt = ((flags & ShortFormat) ? dateFormatShort() : dateFormat()).simplifyWhiteSpace(); 01592 return readDate( intstr, fmt, ok ); 01593 } 01594 01595 QDate KLocale::readDate(const QString &intstr, const QString &fmt, bool* ok) const 01596 { 01597 //kdDebug() << "KLocale::readDate intstr=" << intstr << " fmt=" << fmt << endl; 01598 QString str = intstr.simplifyWhiteSpace().lower(); 01599 int day = -1, month = -1; 01600 // allow the year to be omitted if not in the format 01601 int year = calendar()->year(QDate::currentDate()); 01602 uint strpos = 0; 01603 uint fmtpos = 0; 01604 01605 int iLength; // Temporary variable used when reading input 01606 01607 bool error = false; 01608 01609 while (fmt.length() > fmtpos && str.length() > strpos && !error) 01610 { 01611 01612 QChar c = fmt.at(fmtpos++); 01613 01614 if (c != '%') { 01615 if (c.isSpace() && str.at(strpos).isSpace()) 01616 strpos++; 01617 else if (c != str.at(strpos++)) 01618 error = true; 01619 } 01620 else 01621 { 01622 int j; 01623 // remove space at the beginning 01624 if (str.length() > strpos && str.at(strpos).isSpace()) 01625 strpos++; 01626 01627 c = fmt.at(fmtpos++); 01628 switch (c) 01629 { 01630 case 'a': 01631 case 'A': 01632 01633 error = true; 01634 j = 1; 01635 while (error && (j < 8)) { 01636 QString s = calendar()->weekDayName(j, c == 'a').lower(); 01637 int len = s.length(); 01638 if (str.mid(strpos, len) == s) 01639 { 01640 strpos += len; 01641 error = false; 01642 } 01643 j++; 01644 } 01645 break; 01646 case 'b': 01647 case 'B': 01648 01649 error = true; 01650 if (d->nounDeclension && d->dateMonthNamePossessive) { 01651 j = 1; 01652 while (error && (j < 13)) { 01653 QString s = calendar()->monthNamePossessive(j, year, c == 'b').lower(); 01654 int len = s.length(); 01655 if (str.mid(strpos, len) == s) { 01656 month = j; 01657 strpos += len; 01658 error = false; 01659 } 01660 j++; 01661 } 01662 } 01663 j = 1; 01664 while (error && (j < 13)) { 01665 QString s = calendar()->monthName(j, year, c == 'b').lower(); 01666 int len = s.length(); 01667 if (str.mid(strpos, len) == s) { 01668 month = j; 01669 strpos += len; 01670 error = false; 01671 } 01672 j++; 01673 } 01674 break; 01675 case 'd': 01676 case 'e': 01677 day = calendar()->dayStringToInteger(str.mid(strpos), iLength); 01678 strpos += iLength; 01679 01680 error = iLength <= 0; 01681 break; 01682 01683 case 'n': 01684 case 'm': 01685 month = calendar()->monthStringToInteger(str.mid(strpos), iLength); 01686 strpos += iLength; 01687 01688 error = iLength <= 0; 01689 break; 01690 01691 case 'Y': 01692 case 'y': 01693 year = calendar()->yearStringToInteger(str.mid(strpos), iLength); 01694 strpos += iLength; 01695 01696 error = iLength <= 0; 01697 break; 01698 } 01699 } 01700 } 01701 01702 /* for a match, we should reach the end of both strings, not just one of 01703 them */ 01704 if ( fmt.length() > fmtpos || str.length() > strpos ) 01705 { 01706 error = true; 01707 } 01708 01709 //kdDebug(173) << "KLocale::readDate day=" << day << " month=" << month << " year=" << year << endl; 01710 if ( year != -1 && month != -1 && day != -1 && !error) 01711 { 01712 if (ok) *ok = true; 01713 01714 QDate result; 01715 calendar()->setYMD(result, year, month, day); 01716 01717 return result; 01718 } 01719 else 01720 { 01721 if (ok) *ok = false; 01722 return QDate(); // invalid date 01723 } 01724 } 01725 01726 QTime KLocale::readTime(const QString &intstr, bool *ok) const 01727 { 01728 QTime _time; 01729 _time = readTime(intstr, WithSeconds, ok); 01730 if (_time.isValid()) return _time; 01731 return readTime(intstr, WithoutSeconds, ok); 01732 } 01733 01734 QTime KLocale::readTime(const QString &intstr, ReadTimeFlags flags, bool *ok) const 01735 { 01736 QString str = intstr.simplifyWhiteSpace().lower(); 01737 QString Format = timeFormat().simplifyWhiteSpace(); 01738 if (flags & WithoutSeconds) 01739 Format.remove(QRegExp(".%S")); 01740 01741 int hour = -1, minute = -1; 01742 int second = ( (flags & WithoutSeconds) == 0 ) ? -1 : 0; // don't require seconds 01743 bool g_12h = false; 01744 bool pm = false; 01745 uint strpos = 0; 01746 uint Formatpos = 0; 01747 01748 while (Format.length() > Formatpos || str.length() > strpos) 01749 { 01750 if ( !(Format.length() > Formatpos && str.length() > strpos) ) goto error; 01751 01752 QChar c = Format.at(Formatpos++); 01753 01754 if (c != '%') 01755 { 01756 if (c.isSpace()) 01757 strpos++; 01758 else if (c != str.at(strpos++)) 01759 goto error; 01760 continue; 01761 } 01762 01763 // remove space at the beginning 01764 if (str.length() > strpos && str.at(strpos).isSpace()) 01765 strpos++; 01766 01767 c = Format.at(Formatpos++); 01768 switch (c) 01769 { 01770 case 'p': 01771 { 01772 QString s; 01773 s = translate("pm").lower(); 01774 int len = s.length(); 01775 if (str.mid(strpos, len) == s) 01776 { 01777 pm = true; 01778 strpos += len; 01779 } 01780 else 01781 { 01782 s = translate("am").lower(); 01783 len = s.length(); 01784 if (str.mid(strpos, len) == s) { 01785 pm = false; 01786 strpos += len; 01787 } 01788 else 01789 goto error; 01790 } 01791 } 01792 break; 01793 01794 case 'k': 01795 case 'H': 01796 g_12h = false; 01797 hour = readInt(str, strpos); 01798 if (hour < 0 || hour > 23) 01799 goto error; 01800 01801 break; 01802 01803 case 'l': 01804 case 'I': 01805 g_12h = true; 01806 hour = readInt(str, strpos); 01807 if (hour < 1 || hour > 12) 01808 goto error; 01809 01810 break; 01811 01812 case 'M': 01813 minute = readInt(str, strpos); 01814 if (minute < 0 || minute > 59) 01815 goto error; 01816 01817 break; 01818 01819 case 'S': 01820 second = readInt(str, strpos); 01821 if (second < 0 || second > 59) 01822 goto error; 01823 01824 break; 01825 } 01826 } 01827 if (g_12h) { 01828 hour %= 12; 01829 if (pm) hour += 12; 01830 } 01831 01832 if (ok) *ok = true; 01833 return QTime(hour, minute, second); 01834 01835 error: 01836 if (ok) *ok = false; 01837 // ######## KDE4: remove this 01838 return QTime(-1, -1, -1); // return invalid date if it didn't work 01839 } 01840 01841 //BIC: merge with below 01842 QString KLocale::formatTime(const QTime &pTime, bool includeSecs) const 01843 { 01844 return formatTime( pTime, includeSecs, false ); 01845 } 01846 01847 QString KLocale::formatTime(const QTime &pTime, bool includeSecs, bool isDuration) const 01848 { 01849 const QString rst = timeFormat(); 01850 01851 // only "pm/am" here can grow, the rest shrinks, but 01852 // I'm rather safe than sorry 01853 QChar *buffer = new QChar[rst.length() * 3 / 2 + 30]; 01854 01855 uint index = 0; 01856 bool escape = false; 01857 int number = 0; 01858 01859 for ( uint format_index = 0; format_index < rst.length(); format_index++ ) 01860 { 01861 if ( !escape ) 01862 { 01863 if ( rst.at( format_index ).unicode() == '%' ) 01864 escape = true; 01865 else 01866 buffer[index++] = rst.at( format_index ); 01867 } 01868 else 01869 { 01870 switch ( rst.at( format_index ).unicode() ) 01871 { 01872 case '%': 01873 buffer[index++] = '%'; 01874 break; 01875 case 'H': 01876 put_it_in( buffer, index, pTime.hour() ); 01877 break; 01878 case 'I': 01879 if ( isDuration ) 01880 put_it_in( buffer, index, pTime.hour() ); 01881 else 01882 put_it_in( buffer, index, ( pTime.hour() + 11) % 12 + 1 ); 01883 break; 01884 case 'M': 01885 put_it_in( buffer, index, pTime.minute() ); 01886 break; 01887 case 'S': 01888 if (includeSecs) 01889 put_it_in( buffer, index, pTime.second() ); 01890 else if ( index > 0 ) 01891 { 01892 // we remove the separator sign before the seconds and 01893 // assume that works everywhere 01894 --index; 01895 break; 01896 } 01897 break; 01898 case 'k': 01899 number = pTime.hour(); 01900 case 'l': 01901 // to share the code 01902 if ( rst.at( format_index ).unicode() == 'l' ) 01903 number = isDuration ? pTime.hour() : (pTime.hour() + 11) % 12 + 1; 01904 if ( number / 10 ) 01905 buffer[index++] = number / 10 + '0'; 01906 buffer[index++] = number % 10 + '0'; 01907 break; 01908 case 'p': 01909 if ( !isDuration ) 01910 { 01911 QString s; 01912 if ( pTime.hour() >= 12 ) 01913 put_it_in( buffer, index, translate("pm") ); 01914 else 01915 put_it_in( buffer, index, translate("am") ); 01916 } 01917 break; 01918 default: 01919 buffer[index++] = rst.at( format_index ); 01920 break; 01921 } 01922 escape = false; 01923 } 01924 } 01925 QString ret( buffer, index ); 01926 delete [] buffer; 01927 if ( isDuration ) // eliminate trailing-space due to " %p" 01928 return ret.stripWhiteSpace(); 01929 else 01930 return ret; 01931 } 01932 01933 bool KLocale::use12Clock() const 01934 { 01935 if ((timeFormat().contains(QString::fromLatin1("%I")) > 0) || 01936 (timeFormat().contains(QString::fromLatin1("%l")) > 0)) 01937 return true; 01938 else 01939 return false; 01940 } 01941 01942 QString KLocale::languages() const 01943 { 01944 return d->languageList.join( QString::fromLatin1(":") ); 01945 } 01946 01947 QStringList KLocale::languageList() const 01948 { 01949 return d->languageList; 01950 } 01951 01952 QString KLocale::formatDateTime(const QDateTime &pDateTime, 01953 bool shortFormat, 01954 bool includeSeconds) const 01955 { 01956 return translate("concatenation of dates and time", "%1 %2") 01957 .arg( formatDate( pDateTime.date(), shortFormat ) ) 01958 .arg( formatTime( pDateTime.time(), includeSeconds ) ); 01959 } 01960 01961 QString i18n(const char* text) 01962 { 01963 register KLocale *instance = KGlobal::locale(); 01964 if (instance) 01965 return instance->translate(text); 01966 return QString::fromUtf8(text); 01967 } 01968 01969 QString i18n(const char* index, const char *text) 01970 { 01971 register KLocale *instance = KGlobal::locale(); 01972 if (instance) 01973 return instance->translate(index, text); 01974 return QString::fromUtf8(text); 01975 } 01976 01977 QString i18n(const char* singular, const char* plural, unsigned long n) 01978 { 01979 register KLocale *instance = KGlobal::locale(); 01980 if (instance) 01981 return instance->translate(singular, plural, n); 01982 if (n == 1) 01983 return put_n_in(QString::fromUtf8(singular), n); 01984 else 01985 return put_n_in(QString::fromUtf8(plural), n); 01986 } 01987 01988 void KLocale::initInstance() 01989 { 01990 if (KGlobal::_locale) 01991 return; 01992 01993 KInstance *app = KGlobal::instance(); 01994 if (app) { 01995 KGlobal::_locale = new KLocale(QString::fromLatin1(app->instanceName())); 01996 01997 // only do this for the global instance 01998 QTextCodec::setCodecForLocale(KGlobal::_locale->codecForEncoding()); 01999 } 02000 else 02001 kdDebug(173) << "no app name available using KLocale - nothing to do\n"; 02002 } 02003 02004 QString KLocale::langLookup(const QString &fname, const char *rtype) 02005 { 02006 QStringList search; 02007 02008 // assemble the local search paths 02009 const QStringList localDoc = KGlobal::dirs()->resourceDirs(rtype); 02010 02011 // look up the different languages 02012 for (int id=localDoc.count()-1; id >= 0; --id) 02013 { 02014 QStringList langs = KGlobal::locale()->languageList(); 02015 langs.append( "en" ); 02016 langs.remove( defaultLanguage() ); 02017 QStringList::ConstIterator lang; 02018 for (lang = langs.begin(); lang != langs.end(); ++lang) 02019 search.append(QString("%1%2/%3").arg(localDoc[id]).arg(*lang).arg(fname)); 02020 } 02021 02022 // try to locate the file 02023 QStringList::Iterator it; 02024 for (it = search.begin(); it != search.end(); ++it) 02025 { 02026 kdDebug(173) << "Looking for help in: " << *it << endl; 02027 02028 QFileInfo info(*it); 02029 if (info.exists() && info.isFile() && info.isReadable()) 02030 return *it; 02031 } 02032 02033 return QString::null; 02034 } 02035 02036 bool KLocale::useDefaultLanguage() const 02037 { 02038 return language() == defaultLanguage(); 02039 } 02040 02041 void KLocale::initEncoding(KConfig *) 02042 { 02043 const int mibDefault = 4; // ISO 8859-1 02044 02045 // This all made more sense when we still had the EncodingEnum config key. 02046 setEncoding( QTextCodec::codecForLocale()->mibEnum() ); 02047 02048 if ( !d->codecForEncoding ) 02049 { 02050 kdWarning(173) << " Defaulting to ISO 8859-1 encoding." << endl; 02051 setEncoding(mibDefault); 02052 } 02053 02054 Q_ASSERT( d->codecForEncoding ); 02055 } 02056 02057 void KLocale::initFileNameEncoding(KConfig *) 02058 { 02059 // If the following environment variable is set, assume all filenames 02060 // are in UTF-8 regardless of the current C locale. 02061 d->utf8FileEncoding = getenv("KDE_UTF8_FILENAMES") != 0; 02062 if (d->utf8FileEncoding) 02063 { 02064 QFile::setEncodingFunction(KLocale::encodeFileNameUTF8); 02065 QFile::setDecodingFunction(KLocale::decodeFileNameUTF8); 02066 } 02067 // Otherwise, stay with QFile's default filename encoding functions 02068 // which, on Unix platforms, use the locale's codec. 02069 } 02070 02071 QCString KLocale::encodeFileNameUTF8( const QString & fileName ) 02072 { 02073 return fileName.utf8(); 02074 } 02075 02076 QString KLocale::decodeFileNameUTF8( const QCString & localFileName ) 02077 { 02078 return QString::fromUtf8(localFileName); 02079 } 02080 02081 void KLocale::setDateFormat(const QString & format) 02082 { 02083 doFormatInit(); 02084 m_dateFormat = format.stripWhiteSpace(); 02085 } 02086 02087 void KLocale::setDateFormatShort(const QString & format) 02088 { 02089 doFormatInit(); 02090 m_dateFormatShort = format.stripWhiteSpace(); 02091 } 02092 02093 void KLocale::setDateMonthNamePossessive(bool possessive) 02094 { 02095 doFormatInit(); 02096 d->dateMonthNamePossessive = possessive; 02097 } 02098 02099 void KLocale::setTimeFormat(const QString & format) 02100 { 02101 doFormatInit(); 02102 m_timeFormat = format.stripWhiteSpace(); 02103 } 02104 02105 void KLocale::setWeekStartsMonday(bool start) //deprecated 02106 { 02107 doFormatInit(); 02108 if (start) 02109 d->weekStartDay = 1; 02110 else 02111 d->weekStartDay = 7; 02112 } 02113 02114 void KLocale::setWeekStartDay(int day) 02115 { 02116 doFormatInit(); 02117 if (day>7 || day<1) 02118 d->weekStartDay = 1; //Monday is default 02119 else 02120 d->weekStartDay = day; 02121 } 02122 02123 QString KLocale::dateFormat() const 02124 { 02125 doFormatInit(); 02126 return m_dateFormat; 02127 } 02128 02129 QString KLocale::dateFormatShort() const 02130 { 02131 doFormatInit(); 02132 return m_dateFormatShort; 02133 } 02134 02135 QString KLocale::timeFormat() const 02136 { 02137 doFormatInit(); 02138 return m_timeFormat; 02139 } 02140 02141 void KLocale::setDecimalSymbol(const QString & symbol) 02142 { 02143 doFormatInit(); 02144 m_decimalSymbol = symbol.stripWhiteSpace(); 02145 } 02146 02147 void KLocale::setThousandsSeparator(const QString & separator) 02148 { 02149 doFormatInit(); 02150 // allow spaces here 02151 m_thousandsSeparator = separator; 02152 } 02153 02154 void KLocale::setPositiveSign(const QString & sign) 02155 { 02156 doFormatInit(); 02157 m_positiveSign = sign.stripWhiteSpace(); 02158 } 02159 02160 void KLocale::setNegativeSign(const QString & sign) 02161 { 02162 doFormatInit(); 02163 m_negativeSign = sign.stripWhiteSpace(); 02164 } 02165 02166 void KLocale::setPositiveMonetarySignPosition(SignPosition signpos) 02167 { 02168 doFormatInit(); 02169 m_positiveMonetarySignPosition = signpos; 02170 } 02171 02172 void KLocale::setNegativeMonetarySignPosition(SignPosition signpos) 02173 { 02174 doFormatInit(); 02175 m_negativeMonetarySignPosition = signpos; 02176 } 02177 02178 void KLocale::setPositivePrefixCurrencySymbol(bool prefix) 02179 { 02180 doFormatInit(); 02181 m_positivePrefixCurrencySymbol = prefix; 02182 } 02183 02184 void KLocale::setNegativePrefixCurrencySymbol(bool prefix) 02185 { 02186 doFormatInit(); 02187 m_negativePrefixCurrencySymbol = prefix; 02188 } 02189 02190 void KLocale::setFracDigits(int digits) 02191 { 02192 doFormatInit(); 02193 m_fracDigits = digits; 02194 } 02195 02196 void KLocale::setMonetaryThousandsSeparator(const QString & separator) 02197 { 02198 doFormatInit(); 02199 // allow spaces here 02200 m_monetaryThousandsSeparator = separator; 02201 } 02202 02203 void KLocale::setMonetaryDecimalSymbol(const QString & symbol) 02204 { 02205 doFormatInit(); 02206 m_monetaryDecimalSymbol = symbol.stripWhiteSpace(); 02207 } 02208 02209 void KLocale::setCurrencySymbol(const QString & symbol) 02210 { 02211 doFormatInit(); 02212 m_currencySymbol = symbol.stripWhiteSpace(); 02213 } 02214 02215 int KLocale::pageSize() const 02216 { 02217 doFormatInit(); 02218 return d->pageSize; 02219 } 02220 02221 void KLocale::setPageSize(int pageSize) 02222 { 02223 // #### check if it's in range?? 02224 doFormatInit(); 02225 d->pageSize = pageSize; 02226 } 02227 02228 KLocale::MeasureSystem KLocale::measureSystem() const 02229 { 02230 doFormatInit(); 02231 return d->measureSystem; 02232 } 02233 02234 void KLocale::setMeasureSystem(MeasureSystem value) 02235 { 02236 doFormatInit(); 02237 d->measureSystem = value; 02238 } 02239 02240 QString KLocale::defaultLanguage() 02241 { 02242 return QString::fromLatin1("en_US"); 02243 } 02244 02245 QString KLocale::defaultCountry() 02246 { 02247 return QString::fromLatin1("C"); 02248 } 02249 02250 const char * KLocale::encoding() const 02251 { 02252 #ifdef Q_WS_WIN 02253 if (0==qstrcmp("System", codecForEncoding()->name())) 02254 { 02255 //win32 returns "System" codec name here but KDE apps expect a real name: 02256 strcpy(d->win32SystemEncoding, "cp "); 02257 if (GetLocaleInfoA( MAKELCID(MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), SORT_DEFAULT), 02258 LOCALE_IDEFAULTANSICODEPAGE, d->win32SystemEncoding+3, sizeof(d->win32SystemEncoding)-3-1 )) 02259 { 02260 return d->win32SystemEncoding; 02261 } 02262 } 02263 #endif 02264 return codecForEncoding()->name(); 02265 } 02266 02267 int KLocale::encodingMib() const 02268 { 02269 return codecForEncoding()->mibEnum(); 02270 } 02271 02272 int KLocale::fileEncodingMib() const 02273 { 02274 if (d->utf8FileEncoding) 02275 return 106; 02276 return codecForEncoding()->mibEnum(); 02277 } 02278 02279 QTextCodec * KLocale::codecForEncoding() const 02280 { 02281 return d->codecForEncoding; 02282 } 02283 02284 bool KLocale::setEncoding(int mibEnum) 02285 { 02286 QTextCodec * codec = QTextCodec::codecForMib(mibEnum); 02287 if (codec) 02288 d->codecForEncoding = codec; 02289 02290 return codec != 0; 02291 } 02292 02293 QStringList KLocale::languagesTwoAlpha() const 02294 { 02295 if (d->langTwoAlpha.count()) 02296 return d->langTwoAlpha; 02297 02298 const QStringList &origList = languageList(); 02299 02300 QStringList result; 02301 02302 KConfig config(QString::fromLatin1("language.codes"), true, false); 02303 config.setGroup("TwoLetterCodes"); 02304 02305 for ( QStringList::ConstIterator it = origList.begin(); 02306 it != origList.end(); 02307 ++it ) 02308 { 02309 QString lang = *it; 02310 QStringList langLst; 02311 if (config.hasKey( lang )) 02312 langLst = config.readListEntry( lang ); 02313 else 02314 { 02315 int i = lang.find('_'); 02316 if (i >= 0) 02317 lang.truncate(i); 02318 langLst << lang; 02319 } 02320 02321 for ( QStringList::ConstIterator langIt = langLst.begin(); 02322 langIt != langLst.end(); 02323 ++langIt ) 02324 { 02325 if ( !(*langIt).isEmpty() && !result.contains( *langIt ) ) 02326 result += *langIt; 02327 } 02328 } 02329 d->langTwoAlpha = result; 02330 return result; 02331 } 02332 02333 QStringList KLocale::allLanguagesTwoAlpha() const 02334 { 02335 if (!d->languages) 02336 d->languages = new KConfig("all_languages", true, false, "locale"); 02337 02338 return d->languages->groupList(); 02339 } 02340 02341 QString KLocale::twoAlphaToLanguageName(const QString &code) const 02342 { 02343 if (!d->languages) 02344 d->languages = new KConfig("all_languages", true, false, "locale"); 02345 02346 QString groupName = code; 02347 const int i = groupName.find('_'); 02348 groupName.replace(0, i, groupName.left(i).lower()); 02349 02350 d->languages->setGroup(groupName); 02351 return d->languages->readEntry("Name"); 02352 } 02353 02354 QStringList KLocale::allCountriesTwoAlpha() const 02355 { 02356 QStringList countries; 02357 QStringList paths = KGlobal::dirs()->findAllResources("locale", "l10n/*/entry.desktop"); 02358 for(QStringList::ConstIterator it = paths.begin(); 02359 it != paths.end(); ++it) 02360 { 02361 QString code = (*it).mid((*it).length()-16, 2); 02362 if (code != "/C") 02363 countries.append(code); 02364 } 02365 return countries; 02366 } 02367 02368 QString KLocale::twoAlphaToCountryName(const QString &code) const 02369 { 02370 KConfig cfg("l10n/"+code.lower()+"/entry.desktop", true, false, "locale"); 02371 cfg.setGroup("KCM Locale"); 02372 return cfg.readEntry("Name"); 02373 } 02374 02375 void KLocale::setCalendar(const QString & calType) 02376 { 02377 doFormatInit(); 02378 02379 d->calendarType = calType; 02380 02381 delete d->calendar; 02382 d->calendar = 0; 02383 } 02384 02385 QString KLocale::calendarType() const 02386 { 02387 doFormatInit(); 02388 02389 return d->calendarType; 02390 } 02391 02392 const KCalendarSystem * KLocale::calendar() const 02393 { 02394 doFormatInit(); 02395 02396 // Check if it's the correct calendar?!? 02397 if ( !d->calendar ) 02398 d->calendar = KCalendarSystemFactory::create( d->calendarType, this ); 02399 02400 return d->calendar; 02401 } 02402 02403 KLocale::KLocale(const KLocale & rhs) 02404 { 02405 d = new KLocalePrivate; 02406 02407 *this = rhs; 02408 } 02409 02410 KLocale & KLocale::operator=(const KLocale & rhs) 02411 { 02412 // Numbers and money 02413 m_decimalSymbol = rhs.m_decimalSymbol; 02414 m_thousandsSeparator = rhs.m_thousandsSeparator; 02415 m_currencySymbol = rhs.m_currencySymbol; 02416 m_monetaryDecimalSymbol = rhs.m_monetaryDecimalSymbol; 02417 m_monetaryThousandsSeparator = rhs.m_monetaryThousandsSeparator; 02418 m_positiveSign = rhs.m_positiveSign; 02419 m_negativeSign = rhs.m_negativeSign; 02420 m_fracDigits = rhs.m_fracDigits; 02421 m_positivePrefixCurrencySymbol = rhs.m_positivePrefixCurrencySymbol; 02422 m_negativePrefixCurrencySymbol = rhs.m_negativePrefixCurrencySymbol; 02423 m_positiveMonetarySignPosition = rhs.m_positiveMonetarySignPosition; 02424 m_negativeMonetarySignPosition = rhs.m_negativeMonetarySignPosition; 02425 02426 // Date and time 02427 m_timeFormat = rhs.m_timeFormat; 02428 m_dateFormat = rhs.m_dateFormat; 02429 m_dateFormatShort = rhs.m_dateFormatShort; 02430 02431 m_language = rhs.m_language; 02432 m_country = rhs.m_country; 02433 02434 // the assignment operator works here 02435 *d = *rhs.d; 02436 d->languages = 0; // Don't copy languages 02437 d->calendar = 0; // Don't copy the calendar 02438 02439 return *this; 02440 } 02441 02442 bool KLocale::setCharset(const QString & ) { return true; } 02443 QString KLocale::charset() const { return QString::fromLatin1("UTF-8"); } 02444 02445 // KDE4: remove 02446 #if 0 02447 void nothing() { i18n("&Next"); } 02448 #endif