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