• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

KDECore

klocalizedstring.cpp

Go to the documentation of this file.
00001 /*  This file is part of the KDE libraries
00002     Copyright (C) 2006 Chusslove Illich <caslav.ilic@gmx.net>
00003 
00004     This library is free software; you can redistribute it and/or
00005     modify it under the terms of the GNU Library General Public
00006     License as published by the Free Software Foundation; either
00007     version 2 of the License, or (at your option) any later version.
00008 
00009     This library is distributed in the hope that it will be useful,
00010     but WITHOUT ANY WARRANTY; without even the implied warranty of
00011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012     Library General Public License for more details.
00013 
00014     You should have received a copy of the GNU Library General Public License
00015     along with this library; see the file COPYING.LIB.  If not, write to
00016     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017     Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include <klocalizedstring.h>
00021 
00022 #include <config.h>
00023 
00024 #include <kglobal.h>
00025 #include <kdebug.h>
00026 #include <klocale.h>
00027 #include <klocale_p.h>
00028 #include <klibrary.h>
00029 #include <kstandarddirs.h>
00030 #include <ktranscript_p.h>
00031 #include <kuitsemantics_p.h>
00032 #include "kcatalogname_p.h"
00033 
00034 #include <QMutexLocker>
00035 #include <QStringList>
00036 #include <QByteArray>
00037 #include <QChar>
00038 #include <QHash>
00039 #include <QList>
00040 #include <QVector>
00041 
00042 // Truncates string, for output of long messages.
00043 static QString shortenMessage (const QString &str)
00044 {
00045     const int maxlen = 20;
00046     if (str.length() <= maxlen)
00047         return str;
00048     else
00049         return str.left(maxlen).append(QLatin1String("..."));
00050 }
00051 
00052 typedef qulonglong pluraln;
00053 typedef qlonglong intn;
00054 typedef qulonglong uintn;
00055 typedef double realn;
00056 
00057 class KLocalizedStringPrivateStatics;
00058 
00059 class KLocalizedStringPrivate
00060 {
00061     friend class KLocalizedString;
00062 
00063     QStringList args;
00064     QList<QVariant> vals;
00065     bool numberSet;
00066     pluraln number;
00067     int numberOrd;
00068     QByteArray ctxt;
00069     QHash<QString, QString> dynctxt;
00070     QByteArray msg;
00071     QByteArray plural;
00072 
00073     QString toString (const KLocale *locale, const QString &catalogName) const;
00074     QString selectForEnglish () const;
00075     QString substituteSimple (const QString &trans,
00076                               const QChar &plchar = QLatin1Char('%'),
00077                               bool partial = false) const;
00078     QString postFormat (const QString &text,
00079                         const QString &lang,
00080                         const QString &ctxt) const;
00081     QString substituteTranscript (const QString &trans,
00082                                   const QString &lang,
00083                                   const QString &ctry,
00084                                   const QString &final,
00085                                   bool &fallback) const;
00086     int resolveInterpolation (const QString &trans, int pos,
00087                               const QString &lang,
00088                               const QString &ctry,
00089                               const QString &final,
00090                               QString &result,
00091                               bool &fallback) const;
00092     QVariant segmentToValue (const QString &arg) const;
00093     QString postTranscript (const QString &pcall,
00094                             const QString &lang,
00095                             const QString &ctry,
00096                             const QString &final) const;
00097 
00098     static void notifyCatalogsUpdated (const QStringList &languages,
00099                                        const QList<KCatalogName> &catalogs);
00100     static void loadTranscript ();
00101 };
00102 
00103 class KLocalizedStringPrivateStatics
00104 {
00105     public:
00106 
00107     const QString theFence;
00108     const QString startInterp;
00109     const QString endInterp;
00110     const QChar scriptPlchar;
00111     const QChar scriptVachar;
00112 
00113     const QString scriptDir;
00114     QHash<QString, QStringList> scriptModules;
00115     QList<QStringList> scriptModulesToLoad;
00116 
00117     bool loadTranscriptCalled;
00118     KTranscript *ktrs;
00119 
00120     QHash<QString, KuitSemantics*> formatters;
00121 
00122     KLocalizedStringPrivateStatics () :
00123         theFence(QLatin1String("|/|")),
00124         startInterp(QLatin1String("$[")),
00125         endInterp(QLatin1String("]")),
00126         scriptPlchar(QLatin1Char('%')),
00127         scriptVachar(QLatin1Char('^')),
00128 
00129         scriptDir(QLatin1String("LC_SCRIPTS")),
00130         scriptModules(),
00131         scriptModulesToLoad(),
00132 
00133         loadTranscriptCalled(false),
00134         ktrs(NULL),
00135 
00136         formatters()
00137     {}
00138 
00139     ~KLocalizedStringPrivateStatics ()
00140     {
00141         // ktrs is handled by KLibLoader.
00142         //delete ktrs;
00143         qDeleteAll(formatters);
00144     }
00145 };
00146 K_GLOBAL_STATIC(KLocalizedStringPrivateStatics, staticsKLSP)
00147 
00148 KLocalizedString::KLocalizedString ()
00149 : d(new KLocalizedStringPrivate)
00150 {
00151     d->numberSet = false;
00152     d->number = 0;
00153     d->numberOrd = 0;
00154 }
00155 
00156 KLocalizedString::KLocalizedString (const char *ctxt,
00157                                     const char *msg, const char *plural)
00158 : d(new KLocalizedStringPrivate)
00159 {
00160     d->ctxt = ctxt;
00161     d->msg = msg;
00162     d->plural = plural;
00163     d->numberSet = false;
00164     d->number = 0;
00165     d->numberOrd = 0;
00166 }
00167 
00168 KLocalizedString::KLocalizedString(const KLocalizedString &rhs)
00169 : d(new KLocalizedStringPrivate(*rhs.d))
00170 {
00171 }
00172 
00173 KLocalizedString& KLocalizedString::operator= (const KLocalizedString &rhs)
00174 {
00175     if (&rhs != this)
00176     {
00177         *d = *rhs.d;
00178     }
00179     return *this;
00180 }
00181 
00182 KLocalizedString::~KLocalizedString ()
00183 {
00184     delete d;
00185 }
00186 
00187 bool KLocalizedString::isEmpty () const
00188 {
00189     return d->msg.isEmpty();
00190 }
00191 
00192 QString KLocalizedString::toString () const
00193 {
00194     return d->toString(KGlobal::locale(), QString());
00195 }
00196 
00197 QString KLocalizedString::toString (const QString &catalogName) const
00198 {
00199     return d->toString(KGlobal::locale(), catalogName);
00200 }
00201 
00202 QString KLocalizedString::toString (const KLocale *locale) const
00203 {
00204     return d->toString(locale, QString());
00205 }
00206 
00207 QString KLocalizedString::toString (const KLocale *locale,
00208                                     const QString &catalogName) const
00209 {
00210     return d->toString(locale, catalogName);
00211 }
00212 
00213 QString KLocalizedStringPrivate::toString (const KLocale *locale,
00214                                            const QString &catalogName) const
00215 {
00216     const KLocalizedStringPrivateStatics *s = staticsKLSP;
00217 
00218     QMutexLocker lock(kLocaleMutex());
00219 
00220     // Assure the message has been supplied.
00221     if (msg.isEmpty())
00222     {
00223         kDebug(173) << "Trying to convert empty KLocalizedString to QString.";
00224         #ifndef NDEBUG
00225         return QString::fromLatin1("(I18N_EMPTY_MESSAGE)");
00226         #else
00227         return QString();
00228         #endif
00229     }
00230 
00231     // Check whether plural argument has been supplied, if message has plural.
00232     if (!plural.isEmpty() && !numberSet)
00233         kDebug(173) << QString::fromLatin1("Plural argument to message {%1} not supplied before conversion.")
00234                               .arg(shortenMessage(QString::fromUtf8(msg)));
00235 
00236     // Get raw translation.
00237     QString rawtrans, lang, ctry;
00238     const char *catname = catalogName.toUtf8();
00239     if (locale != NULL) {
00240         if (!ctxt.isEmpty() && !plural.isEmpty()) {
00241             locale->translateRawFrom(catname, ctxt, msg, plural, number,
00242                                      &lang, &rawtrans);
00243         } else if (!plural.isEmpty()) {
00244             locale->translateRawFrom(catname, msg, plural, number,
00245                                      &lang, &rawtrans);
00246         } else if (!ctxt.isEmpty()) {
00247             locale->translateRawFrom(catname, ctxt, msg,
00248                                      &lang, &rawtrans);
00249         } else {
00250             locale->translateRawFrom(catname, msg,
00251                                      &lang, &rawtrans);
00252         }
00253         ctry = locale->country();
00254     } else {
00255         lang = KLocale::defaultLanguage();
00256         ctry = QLatin1Char('C');
00257         rawtrans = selectForEnglish();
00258     }
00259 
00260     // Set ordinary translation and possibly scripted translation.
00261     QString trans, strans;
00262     int cdpos = rawtrans.indexOf(s->theFence);
00263     if (cdpos > 0)
00264     {
00265         // Script fence has been found, strip the scripted from the
00266         // ordinary translation.
00267         trans = rawtrans.left(cdpos);
00268 
00269         // Scripted translation.
00270         strans = rawtrans.mid(cdpos + s->theFence.length());
00271 
00272         // Try to initialize Transcript if not initialized, and script not empty.
00273         if (   !s->loadTranscriptCalled && !strans.isEmpty()
00274             && locale && locale->useTranscript())
00275         {
00276             if (KGlobal::hasMainComponent())
00277                 loadTranscript();
00278             else
00279                 kDebug(173) << QString::fromLatin1("Scripted message {%1} before transcript engine can be loaded.")
00280                                       .arg(shortenMessage(trans));
00281         }
00282     }
00283     else if (cdpos < 0)
00284     {
00285         // No script fence, use translation as is.
00286         trans = rawtrans;
00287     }
00288     else // cdpos == 0
00289     {
00290         // The msgstr starts with the script fence, no ordinary translation.
00291         // This is not allowed, consider message not translated.
00292         kDebug(173) << QString::fromLatin1("Scripted message {%1} without ordinary translation, discarded.")
00293                                .arg(shortenMessage(trans)) ;
00294         trans = selectForEnglish();
00295     }
00296 
00297     // Substitute placeholders in ordinary translation.
00298     QString final = substituteSimple(trans);
00299     // Post-format ordinary translation.
00300     final = postFormat(final, lang, QString::fromLatin1(ctxt));
00301 
00302     // If there is also a scripted translation.
00303     if (!strans.isEmpty()) {
00304         // Evaluate scripted translation.
00305         bool fallback;
00306         QString sfinal = substituteTranscript(strans, lang, ctry, final, fallback);
00307 
00308         // If any translation produced and no fallback requested.
00309         if (!sfinal.isEmpty() && !fallback) {
00310             final = postFormat(sfinal, lang, QString::fromLatin1(ctxt));
00311         }
00312     }
00313 
00314     // Execute any scripted post calls; they cannot modify the final result,
00315     // but are used to set states.
00316     if (s->ktrs != NULL)
00317     {
00318         QStringList pcalls = s->ktrs->postCalls(lang);
00319         foreach(const QString &pcall, pcalls)
00320             postTranscript(pcall, lang, ctry, final);
00321     }
00322 
00323     return final;
00324 }
00325 
00326 QString KLocalizedStringPrivate::selectForEnglish () const
00327 {
00328     QString trans;
00329 
00330     if (!plural.isEmpty()) {
00331         if (number == 1) {
00332             trans = QString::fromUtf8(msg);
00333         }
00334         else {
00335             trans = QString::fromUtf8(plural);
00336         }
00337     }
00338     else {
00339         trans = QString::fromUtf8(msg);
00340     }
00341 
00342     return trans;
00343 }
00344 
00345 QString KLocalizedStringPrivate::substituteSimple (const QString &trans,
00346                                                    const QChar &plchar,
00347                                                    bool partial) const
00348 {
00349     #ifdef NDEBUG
00350     Q_UNUSED(partial);
00351     #endif
00352 
00353     QStringList tsegs; // text segments per placeholder occurrence
00354     QList<int> plords; // ordinal numbers per placeholder occurrence
00355     #ifndef NDEBUG
00356     QVector<int> ords; // indicates which placeholders are present
00357     #endif
00358     int slen = trans.length();
00359     int spos = 0;
00360     int tpos = trans.indexOf(plchar);
00361     while (tpos >= 0)
00362     {
00363         int ctpos = tpos;
00364 
00365         tpos++;
00366         if (tpos == slen)
00367             break;
00368 
00369         if (trans[tpos].digitValue() > 0) // %0 not considered a placeholder
00370         {
00371             // Get the placeholder ordinal.
00372             int plord = 0;
00373             while (tpos < slen && trans[tpos].digitValue() >= 0)
00374             {
00375                 plord = 10 * plord + trans[tpos].digitValue();
00376                 tpos++;
00377             }
00378             plord--; // ordinals are zero based
00379 
00380             #ifndef NDEBUG
00381             // Perhaps enlarge storage for indicators.
00382             // Note that QVector<int> will initialize new elements to 0,
00383             // as they are supposed to be.
00384             if (plord >= ords.size())
00385                 ords.resize(plord + 1);
00386 
00387             // Indicate that placeholder with computed ordinal is present.
00388             ords[plord] = 1;
00389             #endif
00390 
00391             // Store text segment prior to placeholder and placeholder number.
00392             tsegs.append(trans.mid(spos, ctpos - spos));
00393             plords.append(plord);
00394 
00395             // Position of next text segment.
00396             spos = tpos;
00397         }
00398 
00399         tpos = trans.indexOf(plchar, tpos);
00400     }
00401     // Store last text segment.
00402     tsegs.append(trans.mid(spos));
00403 
00404     #ifndef NDEBUG
00405     // Perhaps enlarge storage for plural-number ordinal.
00406     if (!plural.isEmpty() && numberOrd >= ords.size())
00407         ords.resize(numberOrd + 1);
00408 
00409     // Message might have plural but without plural placeholder, which is an
00410     // allowed state. To ease further logic, indicate that plural placeholder
00411     // is present anyway if message has plural.
00412     if (!plural.isEmpty())
00413         ords[numberOrd] = 1;
00414     #endif
00415 
00416     // Assemble the final string from text segments and arguments.
00417     QString final;
00418     for (int i = 0; i < plords.size(); i++)
00419     {
00420         final.append(tsegs.at(i));
00421         if (plords.at(i) >= args.size())
00422         // too little arguments
00423         {
00424             // put back the placeholder
00425             final.append(QLatin1Char('%') + QString::number(plords.at(i) + 1));
00426             #ifndef NDEBUG
00427             if (!partial)
00428                 // spoof the message
00429                 final.append(QLatin1String("(I18N_ARGUMENT_MISSING)"));
00430             #endif
00431         }
00432         else
00433         // just fine
00434             final.append(args.at(plords.at(i)));
00435     }
00436     final.append(tsegs.last());
00437 
00438     #ifndef NDEBUG
00439     if (!partial)
00440     {
00441         // Check that there are no gaps in numbering sequence of placeholders.
00442         bool gaps = false;
00443         for (int i = 0; i < ords.size(); i++)
00444             if (!ords.at(i))
00445             {
00446                 gaps = true;
00447                 kDebug(173) << QString::fromLatin1("Placeholder %%1 skipped in message {%2}.")
00448                                       .arg(QString::number(i + 1), shortenMessage(trans));
00449             }
00450         // If no gaps, check for mismatch between number of unique placeholders and
00451         // actually supplied arguments.
00452         if (!gaps && ords.size() != args.size())
00453             kDebug(173) << QString::fromLatin1("%1 instead of %2 arguments to message {%3} supplied before conversion.")
00454                                   .arg(args.size()).arg(ords.size()).arg(shortenMessage(trans));
00455 
00456         // Some spoofs.
00457         if (gaps)
00458             final.append(QLatin1String("(I18N_GAPS_IN_PLACEHOLDER_SEQUENCE)"));
00459         if (ords.size() < args.size())
00460             final.append(QLatin1String("(I18N_EXCESS_ARGUMENTS_SUPPLIED)"));
00461         if (!plural.isEmpty() && !numberSet)
00462             final.append(QLatin1String("(I18N_PLURAL_ARGUMENT_MISSING)"));
00463     }
00464     #endif
00465 
00466     return final;
00467 }
00468 
00469 QString KLocalizedStringPrivate::postFormat (const QString &text,
00470                                              const QString &lang,
00471                                              const QString &ctxt) const
00472 {
00473     const KLocalizedStringPrivateStatics *s = staticsKLSP;
00474     QMutexLocker lock(kLocaleMutex());
00475 
00476     QString final = text;
00477 
00478     // Transform any semantic markup into visual formatting.
00479     if (s->formatters.contains(lang)) {
00480         final = s->formatters[lang]->format(final, ctxt);
00481     }
00482 
00483     return final;
00484 }
00485 
00486 QString KLocalizedStringPrivate::substituteTranscript (const QString &strans,
00487                                                        const QString &lang,
00488                                                        const QString &ctry,
00489                                                        const QString &final,
00490                                                        bool &fallback) const
00491 {
00492     const KLocalizedStringPrivateStatics *s = staticsKLSP;
00493     QMutexLocker lock(kLocaleMutex());
00494 
00495     if (s->ktrs == NULL)
00496         // Scripting engine not available.
00497         return QString();
00498 
00499     // Iterate by interpolations.
00500     QString sfinal;
00501     fallback = false;
00502     int ppos = 0;
00503     int tpos = strans.indexOf(s->startInterp);
00504     while (tpos >= 0)
00505     {
00506         // Resolve substitutions in preceding text.
00507         QString ptext = substituteSimple(strans.mid(ppos, tpos - ppos),
00508                                          s->scriptPlchar, true);
00509         sfinal.append(ptext);
00510 
00511         // Resolve interpolation.
00512         QString result;
00513         bool fallbackLocal;
00514         tpos = resolveInterpolation(strans, tpos, lang, ctry, final,
00515                                     result, fallbackLocal);
00516 
00517         // If there was a problem in parsing the interpolation, cannot proceed
00518         // (debug info already reported while parsing).
00519         if (tpos < 0) {
00520             return QString();
00521         }
00522         // If fallback has been explicitly requested, indicate global fallback
00523         // but proceed with evaluations (other interpolations may set states).
00524         if (fallbackLocal) {
00525             fallback = true;
00526         }
00527 
00528         // Add evaluated interpolation to the text.
00529         sfinal.append(result);
00530 
00531         // On to next interpolation.
00532         ppos = tpos;
00533         tpos = strans.indexOf(s->startInterp, tpos);
00534     }
00535     // Last text segment.
00536     sfinal.append(substituteSimple(strans.mid(ppos), s->scriptPlchar, true));
00537 
00538     // Return empty string if fallback was requested.
00539     return fallback ? QString() : sfinal;
00540 }
00541 
00542 int KLocalizedStringPrivate::resolveInterpolation (const QString &strans,
00543                                                    int pos,
00544                                                    const QString &lang,
00545                                                    const QString &ctry,
00546                                                    const QString &final,
00547                                                    QString &result,
00548                                                    bool &fallback) const
00549 {
00550     // pos is the position of opening character sequence.
00551     // Returns the position of first character after closing sequence,
00552     // or -1 in case of parsing error.
00553     // result is set to result of Transcript evaluation.
00554     // fallback is set to true if Transcript evaluation requested so.
00555 
00556     KLocalizedStringPrivateStatics *s = staticsKLSP;
00557     QMutexLocker lock(kLocaleMutex());
00558 
00559     result.clear();
00560     fallback = false;
00561 
00562     // Split interpolation into arguments.
00563     QList<QVariant> iargs;
00564     int slen = strans.length();
00565     int islen = s->startInterp.length();
00566     int ielen = s->endInterp.length();
00567     int tpos = pos + s->startInterp.length();
00568     while (1)
00569     {
00570         // Skip whitespace.
00571         while (tpos < slen && strans[tpos].isSpace()) {
00572             ++tpos;
00573         }
00574         if (tpos == slen) {
00575             kDebug(173) << QString::fromLatin1("Unclosed interpolation {%1} in message {%2}.")
00576                                   .arg(strans.mid(pos, tpos - pos), shortenMessage(strans));
00577             return -1;
00578         }
00579         if (strans.mid(tpos, ielen) == s->endInterp) {
00580             break; // no more arguments
00581         }
00582 
00583         // Parse argument: may be concatenated from free and quoted text,
00584         // and sub-interpolations.
00585         // Free and quoted segments may contain placeholders, substitute them;
00586         // recurse into sub-interpolations.
00587         // Free segments may be value references, parse and record for
00588         // consideration at the end.
00589         // Mind backslash escapes throughout.
00590         QStringList segs;
00591         QVariant vref;
00592         while (   !strans[tpos].isSpace()
00593                && strans.mid(tpos, ielen) != s->endInterp)
00594         {
00595             if (strans[tpos] == QLatin1Char('\'')) { // quoted segment
00596                 QString seg;
00597                 ++tpos; // skip opening quote
00598                 // Find closing quote.
00599                 while (tpos < slen && strans[tpos] != QLatin1Char('\'')) {
00600                     if (strans[tpos] == QLatin1Char('\\'))
00601                         ++tpos; // escape next character
00602                     seg.append(strans[tpos]);
00603                     ++tpos;
00604                 }
00605                 if (tpos == slen) {
00606                     kDebug(173) << QString::fromLatin1("Unclosed quote in interpolation {%1} in message {%2}.")
00607                                         .arg(strans.mid(pos, tpos - pos), shortenMessage(strans));
00608                     return -1;
00609                 }
00610 
00611                 // Append to list of segments, resolving placeholders.
00612                 segs.append(substituteSimple(seg, s->scriptPlchar, true));
00613 
00614                 ++tpos; // skip closing quote
00615             }
00616             else if (strans.mid(tpos, islen) == s->startInterp) { // sub-interpolation
00617                 QString resultLocal;
00618                 bool fallbackLocal;
00619                 tpos = resolveInterpolation(strans, tpos, lang, ctry, final,
00620                                             resultLocal, fallbackLocal);
00621                 if (tpos < 0) { // unrecoverable problem in sub-interpolation
00622                     // Error reported in the subcall.
00623                     return tpos;
00624                 }
00625                 if (fallbackLocal) { // sub-interpolation requested fallback
00626                     fallback = true;
00627                 }
00628                 segs.append(resultLocal);
00629             }
00630             else { // free segment
00631                 QString seg;
00632                 // Find whitespace, quote, opening or closing sequence.
00633                 while (   tpos < slen
00634                        && !strans[tpos].isSpace() && strans[tpos] != QLatin1Char('\'')
00635                        && strans.mid(tpos, islen) != s->startInterp
00636                        && strans.mid(tpos, ielen) != s->endInterp)
00637                 {
00638                     if (strans[tpos] == QLatin1Char('\\'))
00639                         ++tpos; // escape next character
00640                     seg.append(strans[tpos]);
00641                     ++tpos;
00642                 }
00643                 if (tpos == slen) {
00644                     kDebug(173) << QString::fromLatin1("Non-terminated interpolation {%1} in message {%2}.")
00645                                         .arg(strans.mid(pos, tpos - pos), shortenMessage(strans));
00646                     return -1;
00647                 }
00648 
00649                 // The free segment may look like a value reference;
00650                 // in that case, record which value it would reference,
00651                 // and add verbatim to the segment list.
00652                 // Otherwise, do a normal substitution on the segment.
00653                 vref = segmentToValue(seg);
00654                 if (vref.isValid()) {
00655                     segs.append(seg);
00656                 }
00657                 else {
00658                     segs.append(substituteSimple(seg, s->scriptPlchar, true));
00659                 }
00660             }
00661         }
00662 
00663         // Append this argument to rest of the arguments.
00664         // If the there was a single text segment and it was a proper value
00665         // reference, add it instead of the joined segments.
00666         // Otherwise, add the joined segments.
00667         if (segs.size() == 1 && vref.isValid()) {
00668             iargs.append(vref);
00669         }
00670         else {
00671             iargs.append(segs.join(QString()));
00672         }
00673     }
00674     tpos += ielen; // skip to first character after closing sequence
00675 
00676     // NOTE: Why not substitute placeholders (via substituteSimple) in one
00677     // global pass, then handle interpolations in second pass? Because then
00678     // there is the danger of substituted text or sub-interpolations producing
00679     // quotes and escapes themselves, which would mess up the parsing.
00680 
00681     // Evaluate interpolation.
00682     QString msgctxt = QString::fromUtf8(ctxt);
00683     QString msgid = QString::fromUtf8(msg);
00684     QString scriptError;
00685     bool fallbackLocal;
00686     result = s->ktrs->eval(iargs, lang, ctry,
00687                            msgctxt, dynctxt, msgid,
00688                            args, vals, final, s->scriptModulesToLoad,
00689                            scriptError, fallbackLocal);
00690     // s->scriptModulesToLoad will be cleared during the call.
00691 
00692     if (fallbackLocal) { // evaluation requested fallback
00693         fallback = true;
00694     }
00695     if (!scriptError.isEmpty()) { // problem with evaluation
00696         fallback = true; // also signal fallback
00697         if (!scriptError.isEmpty()) {
00698             kDebug(173) << QString::fromLatin1("Interpolation {%1} in {%2} failed: %3")
00699                                   .arg(strans.mid(pos, tpos - pos), shortenMessage(strans), scriptError);
00700         }
00701     }
00702 
00703     return tpos;
00704 }
00705 
00706 QVariant KLocalizedStringPrivate::segmentToValue (const QString &seg) const
00707 {
00708     const KLocalizedStringPrivateStatics *s = staticsKLSP;
00709     QMutexLocker lock(kLocaleMutex());
00710 
00711     // Return invalid variant if segment is either not a proper
00712     // value reference, or the reference is out of bounds.
00713 
00714     // Value reference must start with a special character.
00715     if (seg.left(1) != s->scriptVachar) {
00716         return QVariant();
00717     }
00718 
00719     // Reference number must start with 1-9.
00720     // (If numstr is empty, toInt() will return 0.)
00721     QString numstr = seg.mid(1);
00722     if (numstr.left(1).toInt() < 1) {
00723         return QVariant();
00724     }
00725 
00726     // Number must be valid and in bounds.
00727     bool ok;
00728     int index = numstr.toInt(&ok) - 1;
00729     if (!ok || index >= vals.size()) {
00730         return QVariant();
00731     }
00732 
00733     // Passed all hoops.
00734     return vals.at(index);
00735 }
00736 
00737 QString KLocalizedStringPrivate::postTranscript (const QString &pcall,
00738                                                  const QString &lang,
00739                                                  const QString &ctry,
00740                                                  const QString &final) const
00741 {
00742     KLocalizedStringPrivateStatics *s = staticsKLSP;
00743     QMutexLocker lock(kLocaleMutex());
00744 
00745     if (s->ktrs == NULL)
00746         // Scripting engine not available.
00747         // (Though this cannot happen, we wouldn't be here then.)
00748         return QString();
00749 
00750     // Resolve the post call.
00751     QList<QVariant> iargs;
00752     iargs.append(pcall);
00753     QString msgctxt = QString::fromUtf8(ctxt);
00754     QString msgid = QString::fromUtf8(msg);
00755     QString scriptError;
00756     bool fallback;
00757     QString dummy = s->ktrs->eval(iargs, lang, ctry,
00758                                   msgctxt, dynctxt, msgid,
00759                                   args, vals, final, s->scriptModulesToLoad,
00760                                   scriptError, fallback);
00761     // s->scriptModulesToLoad will be cleared during the call.
00762 
00763     // If the evaluation went wrong.
00764     if (!scriptError.isEmpty())
00765     {
00766         kDebug(173) << QString::fromLatin1("Post call {%1} for message {%2} failed: %3")
00767                               .arg(pcall, shortenMessage(msgid), scriptError);
00768         return QString();
00769     }
00770 
00771     return final;
00772 }
00773 
00774 static QString wrapNum (const QString &tag, const QString &numstr,
00775                         int fieldWidth, const QChar &fillChar)
00776 {
00777     QString optag;
00778     if (fieldWidth != 0) {
00779         QString fillString = KuitSemantics::escape(fillChar);
00780         optag = QString::fromLatin1("<%1 width='%2' fill='%3'>")
00781                        .arg(tag, QString::number(fieldWidth), fillString);
00782     } else {
00783         optag = QString::fromLatin1("<%1>").arg(tag);
00784     }
00785     QString cltag = QString::fromLatin1("</%1>").arg(tag);
00786     return optag + numstr + cltag;
00787 }
00788 
00789 KLocalizedString KLocalizedString::subs (int a, int fieldWidth, int base,
00790                                          const QChar &fillChar) const
00791 {
00792     KLocalizedString kls(*this);
00793     if (!kls.d->plural.isEmpty() && !kls.d->numberSet) {
00794         kls.d->number = static_cast<pluraln>(abs(a));
00795         kls.d->numberSet = true;
00796         kls.d->numberOrd = d->args.size();
00797     }
00798     kls.d->args.append(wrapNum(QString::fromLatin1(KUIT_NUMINTG), QString::number(a, base),
00799                                fieldWidth, fillChar));
00800     kls.d->vals.append(static_cast<intn>(a));
00801     return kls;
00802 }
00803 
00804 KLocalizedString KLocalizedString::subs (uint a, int fieldWidth, int base,
00805                                          const QChar &fillChar) const
00806 {
00807     KLocalizedString kls(*this);
00808     if (!kls.d->plural.isEmpty() && !kls.d->numberSet) {
00809         kls.d->number = static_cast<pluraln>(a);
00810         kls.d->numberSet = true;
00811         kls.d->numberOrd = d->args.size();
00812     }
00813     kls.d->args.append(wrapNum(QString::fromLatin1(KUIT_NUMINTG), QString::number(a, base),
00814                                fieldWidth, fillChar));
00815     kls.d->vals.append(static_cast<uintn>(a));
00816     return kls;
00817 }
00818 
00819 KLocalizedString KLocalizedString::subs (long a, int fieldWidth, int base,
00820                                          const QChar &fillChar) const
00821 {
00822     KLocalizedString kls(*this);
00823     if (!kls.d->plural.isEmpty() && !kls.d->numberSet) {
00824         kls.d->number = static_cast<pluraln>(abs(a));
00825         kls.d->numberSet = true;
00826         kls.d->numberOrd = d->args.size();
00827     }
00828     kls.d->args.append(wrapNum(QString::fromLatin1(KUIT_NUMINTG), QString::number(a, base),
00829                                fieldWidth, fillChar));
00830     kls.d->vals.append(static_cast<intn>(a));
00831     return kls;
00832 }
00833 
00834 KLocalizedString KLocalizedString::subs (ulong a, int fieldWidth, int base,
00835                                          const QChar &fillChar) const
00836 {
00837     KLocalizedString kls(*this);
00838     if (!kls.d->plural.isEmpty() && !kls.d->numberSet) {
00839         kls.d->number = static_cast<pluraln>(a);
00840         kls.d->numberSet = true;
00841         kls.d->numberOrd = d->args.size();
00842     }
00843     kls.d->args.append(wrapNum(QString::fromLatin1(KUIT_NUMINTG), QString::number(a, base),
00844                                fieldWidth, fillChar));
00845     kls.d->vals.append(static_cast<uintn>(a));
00846     return kls;
00847 }
00848 
00849 KLocalizedString KLocalizedString::subs (qlonglong a, int fieldWidth, int base,
00850                                          const QChar &fillChar) const
00851 {
00852     KLocalizedString kls(*this);
00853     if (!kls.d->plural.isEmpty() && !kls.d->numberSet) {
00854         kls.d->number = static_cast<pluraln>(qAbs(a));
00855         kls.d->numberSet = true;
00856         kls.d->numberOrd = d->args.size();
00857     }
00858     kls.d->args.append(wrapNum(QString::fromLatin1(KUIT_NUMINTG), QString::number(a, base),
00859                                fieldWidth, fillChar));
00860     kls.d->vals.append(static_cast<intn>(a));
00861     return kls;
00862 }
00863 
00864 KLocalizedString KLocalizedString::subs (qulonglong a, int fieldWidth, int base,
00865                                          const QChar &fillChar) const
00866 {
00867     KLocalizedString kls(*this);
00868     if (!kls.d->plural.isEmpty() && !kls.d->numberSet) {
00869         kls.d->number = static_cast<pluraln>(a);
00870         kls.d->numberSet = true;
00871         kls.d->numberOrd = d->args.size();
00872     }
00873     kls.d->args.append(wrapNum(QString::fromLatin1(KUIT_NUMINTG), QString::number(a, base),
00874                                fieldWidth, fillChar));
00875     kls.d->vals.append(static_cast<uintn>(a));
00876     return kls;
00877 }
00878 
00879 KLocalizedString KLocalizedString::subs (double a, int fieldWidth,
00880                                          char format, int precision,
00881                                          const QChar &fillChar) const
00882 {
00883     KLocalizedString kls(*this);
00884     kls.d->args.append(wrapNum(QString::fromLatin1(KUIT_NUMREAL),
00885                                QString::number(a, format, precision),
00886                                fieldWidth, fillChar));
00887     kls.d->vals.append(static_cast<realn>(a));
00888     return kls;
00889 }
00890 
00891 KLocalizedString KLocalizedString::subs (QChar a, int fieldWidth,
00892                                          const QChar &fillChar) const
00893 {
00894     KLocalizedString kls(*this);
00895     kls.d->args.append(QString::fromLatin1("%1").arg(a, fieldWidth, fillChar));
00896     kls.d->vals.append(QString(a));
00897     return kls;
00898 }
00899 
00900 KLocalizedString KLocalizedString::subs (const QString &a, int fieldWidth,
00901                                          const QChar &fillChar) const
00902 {
00903     KLocalizedString kls(*this);
00904     // if (!KuitSemantics::mightBeRichText(a)) { ...
00905     // Do not try to auto-escape non-rich-text alike arguments;
00906     // breaks compatibility with 4.0. Perhaps for KDE 5?
00907     // Perhaps bad idea alltogether (too much surprise)?
00908     kls.d->args.append(QString::fromLatin1("%1").arg(a, fieldWidth, fillChar));
00909     kls.d->vals.append(a);
00910     return kls;
00911 }
00912 
00913 KLocalizedString KLocalizedString::inContext (const QString &key,
00914                                               const QString &text) const
00915 {
00916     KLocalizedString kls(*this);
00917     kls.d->dynctxt[key] = text;
00918     return kls;
00919 }
00920 
00921 KLocalizedString ki18n (const char* msg)
00922 {
00923     return KLocalizedString(NULL, msg, NULL);
00924 }
00925 
00926 KLocalizedString ki18nc (const char* ctxt, const char *msg)
00927 {
00928     return KLocalizedString(ctxt, msg, NULL);
00929 }
00930 
00931 KLocalizedString ki18np (const char* singular, const char* plural)
00932 {
00933     return KLocalizedString(NULL, singular, plural);
00934 }
00935 
00936 KLocalizedString ki18ncp (const char* ctxt,
00937                           const char* singular, const char* plural)
00938 {
00939     return KLocalizedString(ctxt, singular, plural);
00940 }
00941 
00942 extern "C"
00943 {
00944     typedef KTranscript *(*InitFunc)();
00945 }
00946 
00947 void KLocalizedStringPrivate::loadTranscript ()
00948 {
00949     KLocalizedStringPrivateStatics *s = staticsKLSP;
00950     QMutexLocker lock(kLocaleMutex());
00951 
00952     s->loadTranscriptCalled = true;
00953     s->ktrs = NULL; // null indicates that Transcript is not available
00954 
00955     KLibrary lib(QLatin1String("ktranscript"));
00956     if (!lib.load()) {
00957         kDebug(173) << "Cannot load transcript plugin:" << lib.errorString();
00958         return;
00959     }
00960 
00961     InitFunc initf = (InitFunc) lib.resolveFunction("load_transcript");
00962     if (!initf) {
00963         lib.unload();
00964         kDebug(173) << "Cannot find function load_transcript in transcript plugin.";
00965         return;
00966     }
00967 
00968     s->ktrs = initf();
00969 }
00970 
00971 void KLocalizedString::notifyCatalogsUpdated (const QStringList &languages,
00972                                               const QList<KCatalogName> &catalogs)
00973 {
00974     KLocalizedStringPrivate::notifyCatalogsUpdated(languages, catalogs);
00975 }
00976 
00977 void KLocalizedStringPrivate::notifyCatalogsUpdated (const QStringList &languages,
00978                                                      const QList<KCatalogName> &catalogs)
00979 {
00980     if (staticsKLSP.isDestroyed()) {
00981         return;
00982     }
00983     KLocalizedStringPrivateStatics *s = staticsKLSP;
00984     // Very important: do not the mutex here.
00985     //QMutexLocker lock(kLocaleMutex());
00986 
00987     // Find script modules for all included language/catalogs that have them,
00988     // and remember their paths.
00989     // A more specific module may reference the calls from a less specific,
00990     // and the catalog list is ordered from more to less specific. Therefore,
00991     // work on reversed list of catalogs.
00992     foreach (const QString &lang, languages) {
00993         for (int i = catalogs.size() - 1; i >= 0; --i) {
00994             const KCatalogName &cat(catalogs[i]);
00995 
00996             // Assemble module's relative path.
00997             QString modrpath =   lang + QLatin1Char('/') + s->scriptDir + QLatin1Char('/')
00998                             + cat.name + QLatin1Char('/') + cat.name + QLatin1String(".js");
00999 
01000             // Try to find this module.
01001             QString modapath = KStandardDirs::locate("locale", modrpath);
01002 
01003             // If the module exists and hasn't been already included.
01004             if (   !modapath.isEmpty()
01005                 && !s->scriptModules[lang].contains(cat.name))
01006             {
01007                 // Indicate that the module has been considered.
01008                 s->scriptModules[lang].append(cat.name);
01009 
01010                 // Store the absolute path and language of the module,
01011                 // to load on next script evaluation.
01012                 QStringList mod;
01013                 mod.append(modapath);
01014                 mod.append(lang);
01015                 s->scriptModulesToLoad.append(mod);
01016             }
01017         }
01018     }
01019 
01020     // Create visual formatters for each new language.
01021     foreach (const QString &lang, languages) {
01022         if (!s->formatters.contains(lang)) {
01023             s->formatters.insert(lang, new KuitSemantics(lang));
01024         }
01025     }
01026 }

KDECore

Skip menu "KDECore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal