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