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