klocale.cpp

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