kdecore Library API Documentation

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