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