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