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