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