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

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
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
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