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