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