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