klocale.cpp

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