kdecore Library API Documentation

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