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(), NULL); 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, NULL); 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 QByteArray catname; 00239 if (catalogName != NULL) { 00240 catname = catalogName->toUtf8(); 00241 } 00242 if (locale != NULL) { 00243 if (!ctxt.isEmpty() && !plural.isEmpty()) { 00244 locale->translateRawFrom(catname, ctxt, msg, plural, number, 00245 &lang, &rawtrans); 00246 } else if (!plural.isEmpty()) { 00247 locale->translateRawFrom(catname, msg, plural, number, 00248 &lang, &rawtrans); 00249 } else if (!ctxt.isEmpty()) { 00250 locale->translateRawFrom(catname, ctxt, msg, 00251 &lang, &rawtrans); 00252 } else { 00253 locale->translateRawFrom(catname, msg, 00254 &lang, &rawtrans); 00255 } 00256 ctry = locale->country(); 00257 } else { 00258 lang = KLocale::defaultLanguage(); 00259 ctry = QLatin1Char('C'); 00260 rawtrans = selectForEnglish(); 00261 } 00262 00263 // Set ordinary translation and possibly scripted translation. 00264 QString trans, strans; 00265 int cdpos = rawtrans.indexOf(s->theFence); 00266 if (cdpos > 0) 00267 { 00268 // Script fence has been found, strip the scripted from the 00269 // ordinary translation. 00270 trans = rawtrans.left(cdpos); 00271 00272 // Scripted translation. 00273 strans = rawtrans.mid(cdpos + s->theFence.length()); 00274 00275 // Try to initialize Transcript if not initialized, and script not empty. 00276 if ( !s->loadTranscriptCalled && !strans.isEmpty() 00277 && locale && locale->useTranscript()) 00278 { 00279 if (KGlobal::hasMainComponent()) 00280 loadTranscript(); 00281 else 00282 kDebug(173) << QString::fromLatin1("Scripted message {%1} before transcript engine can be loaded.") 00283 .arg(shortenMessage(trans)); 00284 } 00285 } 00286 else if (cdpos < 0) 00287 { 00288 // No script fence, use translation as is. 00289 trans = rawtrans; 00290 } 00291 else // cdpos == 0 00292 { 00293 // The msgstr starts with the script fence, no ordinary translation. 00294 // This is not allowed, consider message not translated. 00295 kDebug(173) << QString::fromLatin1("Scripted message {%1} without ordinary translation, discarded.") 00296 .arg(shortenMessage(trans)) ; 00297 trans = selectForEnglish(); 00298 } 00299 00300 // Substitute placeholders in ordinary translation. 00301 QString final = substituteSimple(trans); 00302 // Post-format ordinary translation. 00303 final = postFormat(final, lang, QString::fromLatin1(ctxt)); 00304 00305 // If there is also a scripted translation. 00306 if (!strans.isEmpty()) { 00307 // Evaluate scripted translation. 00308 bool fallback; 00309 QString sfinal = substituteTranscript(strans, lang, ctry, final, fallback); 00310 00311 // If any translation produced and no fallback requested. 00312 if (!sfinal.isEmpty() && !fallback) { 00313 final = postFormat(sfinal, lang, QString::fromLatin1(ctxt)); 00314 } 00315 } 00316 00317 // Execute any scripted post calls; they cannot modify the final result, 00318 // but are used to set states. 00319 if (s->ktrs != NULL) 00320 { 00321 QStringList pcalls = s->ktrs->postCalls(lang); 00322 foreach(const QString &pcall, pcalls) 00323 postTranscript(pcall, lang, ctry, final); 00324 } 00325 00326 return final; 00327 } 00328 00329 QString KLocalizedStringPrivate::selectForEnglish () const 00330 { 00331 QString trans; 00332 00333 if (!plural.isEmpty()) { 00334 if (number == 1) { 00335 trans = QString::fromUtf8(msg); 00336 } 00337 else { 00338 trans = QString::fromUtf8(plural); 00339 } 00340 } 00341 else { 00342 trans = QString::fromUtf8(msg); 00343 } 00344 00345 return trans; 00346 } 00347 00348 QString KLocalizedStringPrivate::substituteSimple (const QString &trans, 00349 const QChar &plchar, 00350 bool partial) const 00351 { 00352 #ifdef NDEBUG 00353 Q_UNUSED(partial); 00354 #endif 00355 00356 QStringList tsegs; // text segments per placeholder occurrence 00357 QList<int> plords; // ordinal numbers per placeholder occurrence 00358 #ifndef NDEBUG 00359 QVector<int> ords; // indicates which placeholders are present 00360 #endif 00361 int slen = trans.length(); 00362 int spos = 0; 00363 int tpos = trans.indexOf(plchar); 00364 while (tpos >= 0) 00365 { 00366 int ctpos = tpos; 00367 00368 tpos++; 00369 if (tpos == slen) 00370 break; 00371 00372 if (trans[tpos].digitValue() > 0) // %0 not considered a placeholder 00373 { 00374 // Get the placeholder ordinal. 00375 int plord = 0; 00376 while (tpos < slen && trans[tpos].digitValue() >= 0) 00377 { 00378 plord = 10 * plord + trans[tpos].digitValue(); 00379 tpos++; 00380 } 00381 plord--; // ordinals are zero based 00382 00383 #ifndef NDEBUG 00384 // Perhaps enlarge storage for indicators. 00385 // Note that QVector<int> will initialize new elements to 0, 00386 // as they are supposed to be. 00387 if (plord >= ords.size()) 00388 ords.resize(plord + 1); 00389 00390 // Indicate that placeholder with computed ordinal is present. 00391 ords[plord] = 1; 00392 #endif 00393 00394 // Store text segment prior to placeholder and placeholder number. 00395 tsegs.append(trans.mid(spos, ctpos - spos)); 00396 plords.append(plord); 00397 00398 // Position of next text segment. 00399 spos = tpos; 00400 } 00401 00402 tpos = trans.indexOf(plchar, tpos); 00403 } 00404 // Store last text segment. 00405 tsegs.append(trans.mid(spos)); 00406 00407 #ifndef NDEBUG 00408 // Perhaps enlarge storage for plural-number ordinal. 00409 if (!plural.isEmpty() && numberOrd >= ords.size()) 00410 ords.resize(numberOrd + 1); 00411 00412 // Message might have plural but without plural placeholder, which is an 00413 // allowed state. To ease further logic, indicate that plural placeholder 00414 // is present anyway if message has plural. 00415 if (!plural.isEmpty()) 00416 ords[numberOrd] = 1; 00417 #endif 00418 00419 // Assemble the final string from text segments and arguments. 00420 QString final; 00421 for (int i = 0; i < plords.size(); i++) 00422 { 00423 final.append(tsegs.at(i)); 00424 if (plords.at(i) >= args.size()) 00425 // too little arguments 00426 { 00427 // put back the placeholder 00428 final.append(QLatin1Char('%') + QString::number(plords.at(i) + 1)); 00429 #ifndef NDEBUG 00430 if (!partial) 00431 // spoof the message 00432 final.append(QLatin1String("(I18N_ARGUMENT_MISSING)")); 00433 #endif 00434 } 00435 else 00436 // just fine 00437 final.append(args.at(plords.at(i))); 00438 } 00439 final.append(tsegs.last()); 00440 00441 #ifndef NDEBUG 00442 if (!partial) 00443 { 00444 // Check that there are no gaps in numbering sequence of placeholders. 00445 bool gaps = false; 00446 for (int i = 0; i < ords.size(); i++) 00447 if (!ords.at(i)) 00448 { 00449 gaps = true; 00450 kDebug(173) << QString::fromLatin1("Placeholder %%1 skipped in message {%2}.") 00451 .arg(QString::number(i + 1), shortenMessage(trans)); 00452 } 00453 // If no gaps, check for mismatch between number of unique placeholders and 00454 // actually supplied arguments. 00455 if (!gaps && ords.size() != args.size()) 00456 kDebug(173) << QString::fromLatin1("%1 instead of %2 arguments to message {%3} supplied before conversion.") 00457 .arg(args.size()).arg(ords.size()).arg(shortenMessage(trans)); 00458 00459 // Some spoofs. 00460 if (gaps) 00461 final.append(QLatin1String("(I18N_GAPS_IN_PLACEHOLDER_SEQUENCE)")); 00462 if (ords.size() < args.size()) 00463 final.append(QLatin1String("(I18N_EXCESS_ARGUMENTS_SUPPLIED)")); 00464 if (!plural.isEmpty() && !numberSet) 00465 final.append(QLatin1String("(I18N_PLURAL_ARGUMENT_MISSING)")); 00466 } 00467 #endif 00468 00469 return final; 00470 } 00471 00472 QString KLocalizedStringPrivate::postFormat (const QString &text, 00473 const QString &lang, 00474 const QString &ctxt) const 00475 { 00476 const KLocalizedStringPrivateStatics *s = staticsKLSP; 00477 QMutexLocker lock(kLocaleMutex()); 00478 00479 QString final = text; 00480 00481 // Transform any semantic markup into visual formatting. 00482 if (s->formatters.contains(lang)) { 00483 final = s->formatters[lang]->format(final, ctxt); 00484 } 00485 00486 return final; 00487 } 00488 00489 QString KLocalizedStringPrivate::substituteTranscript (const QString &strans, 00490 const QString &lang, 00491 const QString &ctry, 00492 const QString &final, 00493 bool &fallback) const 00494 { 00495 const KLocalizedStringPrivateStatics *s = staticsKLSP; 00496 QMutexLocker lock(kLocaleMutex()); 00497 00498 if (s->ktrs == NULL) 00499 // Scripting engine not available. 00500 return QString(); 00501 00502 // Iterate by interpolations. 00503 QString sfinal; 00504 fallback = false; 00505 int ppos = 0; 00506 int tpos = strans.indexOf(s->startInterp); 00507 while (tpos >= 0) 00508 { 00509 // Resolve substitutions in preceding text. 00510 QString ptext = substituteSimple(strans.mid(ppos, tpos - ppos), 00511 s->scriptPlchar, true); 00512 sfinal.append(ptext); 00513 00514 // Resolve interpolation. 00515 QString result; 00516 bool fallbackLocal; 00517 tpos = resolveInterpolation(strans, tpos, lang, ctry, final, 00518 result, fallbackLocal); 00519 00520 // If there was a problem in parsing the interpolation, cannot proceed 00521 // (debug info already reported while parsing). 00522 if (tpos < 0) { 00523 return QString(); 00524 } 00525 // If fallback has been explicitly requested, indicate global fallback 00526 // but proceed with evaluations (other interpolations may set states). 00527 if (fallbackLocal) { 00528 fallback = true; 00529 } 00530 00531 // Add evaluated interpolation to the text. 00532 sfinal.append(result); 00533 00534 // On to next interpolation. 00535 ppos = tpos; 00536 tpos = strans.indexOf(s->startInterp, tpos); 00537 } 00538 // Last text segment. 00539 sfinal.append(substituteSimple(strans.mid(ppos), s->scriptPlchar, true)); 00540 00541 // Return empty string if fallback was requested. 00542 return fallback ? QString() : sfinal; 00543 } 00544 00545 int KLocalizedStringPrivate::resolveInterpolation (const QString &strans, 00546 int pos, 00547 const QString &lang, 00548 const QString &ctry, 00549 const QString &final, 00550 QString &result, 00551 bool &fallback) const 00552 { 00553 // pos is the position of opening character sequence. 00554 // Returns the position of first character after closing sequence, 00555 // or -1 in case of parsing error. 00556 // result is set to result of Transcript evaluation. 00557 // fallback is set to true if Transcript evaluation requested so. 00558 00559 KLocalizedStringPrivateStatics *s = staticsKLSP; 00560 QMutexLocker lock(kLocaleMutex()); 00561 00562 result.clear(); 00563 fallback = false; 00564 00565 // Split interpolation into arguments. 00566 QList<QVariant> iargs; 00567 int slen = strans.length(); 00568 int islen = s->startInterp.length(); 00569 int ielen = s->endInterp.length(); 00570 int tpos = pos + s->startInterp.length(); 00571 while (1) 00572 { 00573 // Skip whitespace. 00574 while (tpos < slen && strans[tpos].isSpace()) { 00575 ++tpos; 00576 } 00577 if (tpos == slen) { 00578 kDebug(173) << QString::fromLatin1("Unclosed interpolation {%1} in message {%2}.") 00579 .arg(strans.mid(pos, tpos - pos), shortenMessage(strans)); 00580 return -1; 00581 } 00582 if (strans.mid(tpos, ielen) == s->endInterp) { 00583 break; // no more arguments 00584 } 00585 00586 // Parse argument: may be concatenated from free and quoted text, 00587 // and sub-interpolations. 00588 // Free and quoted segments may contain placeholders, substitute them; 00589 // recurse into sub-interpolations. 00590 // Free segments may be value references, parse and record for 00591 // consideration at the end. 00592 // Mind backslash escapes throughout. 00593 QStringList segs; 00594 QVariant vref; 00595 while ( !strans[tpos].isSpace() 00596 && strans.mid(tpos, ielen) != s->endInterp) 00597 { 00598 if (strans[tpos] == QLatin1Char('\'')) { // quoted segment 00599 QString seg; 00600 ++tpos; // skip opening quote 00601 // Find closing quote. 00602 while (tpos < slen && strans[tpos] != QLatin1Char('\'')) { 00603 if (strans[tpos] == QLatin1Char('\\')) 00604 ++tpos; // escape next character 00605 seg.append(strans[tpos]); 00606 ++tpos; 00607 } 00608 if (tpos == slen) { 00609 kDebug(173) << QString::fromLatin1("Unclosed quote in interpolation {%1} in message {%2}.") 00610 .arg(strans.mid(pos, tpos - pos), shortenMessage(strans)); 00611 return -1; 00612 } 00613 00614 // Append to list of segments, resolving placeholders. 00615 segs.append(substituteSimple(seg, s->scriptPlchar, true)); 00616 00617 ++tpos; // skip closing quote 00618 } 00619 else if (strans.mid(tpos, islen) == s->startInterp) { // sub-interpolation 00620 QString resultLocal; 00621 bool fallbackLocal; 00622 tpos = resolveInterpolation(strans, tpos, lang, ctry, final, 00623 resultLocal, fallbackLocal); 00624 if (tpos < 0) { // unrecoverable problem in sub-interpolation 00625 // Error reported in the subcall. 00626 return tpos; 00627 } 00628 if (fallbackLocal) { // sub-interpolation requested fallback 00629 fallback = true; 00630 } 00631 segs.append(resultLocal); 00632 } 00633 else { // free segment 00634 QString seg; 00635 // Find whitespace, quote, opening or closing sequence. 00636 while ( tpos < slen 00637 && !strans[tpos].isSpace() && strans[tpos] != QLatin1Char('\'') 00638 && strans.mid(tpos, islen) != s->startInterp 00639 && strans.mid(tpos, ielen) != s->endInterp) 00640 { 00641 if (strans[tpos] == QLatin1Char('\\')) 00642 ++tpos; // escape next character 00643 seg.append(strans[tpos]); 00644 ++tpos; 00645 } 00646 if (tpos == slen) { 00647 kDebug(173) << QString::fromLatin1("Non-terminated interpolation {%1} in message {%2}.") 00648 .arg(strans.mid(pos, tpos - pos), shortenMessage(strans)); 00649 return -1; 00650 } 00651 00652 // The free segment may look like a value reference; 00653 // in that case, record which value it would reference, 00654 // and add verbatim to the segment list. 00655 // Otherwise, do a normal substitution on the segment. 00656 vref = segmentToValue(seg); 00657 if (vref.isValid()) { 00658 segs.append(seg); 00659 } 00660 else { 00661 segs.append(substituteSimple(seg, s->scriptPlchar, true)); 00662 } 00663 } 00664 } 00665 00666 // Append this argument to rest of the arguments. 00667 // If the there was a single text segment and it was a proper value 00668 // reference, add it instead of the joined segments. 00669 // Otherwise, add the joined segments. 00670 if (segs.size() == 1 && vref.isValid()) { 00671 iargs.append(vref); 00672 } 00673 else { 00674 iargs.append(segs.join(QString())); 00675 } 00676 } 00677 tpos += ielen; // skip to first character after closing sequence 00678 00679 // NOTE: Why not substitute placeholders (via substituteSimple) in one 00680 // global pass, then handle interpolations in second pass? Because then 00681 // there is the danger of substituted text or sub-interpolations producing 00682 // quotes and escapes themselves, which would mess up the parsing. 00683 00684 // Evaluate interpolation. 00685 QString msgctxt = QString::fromUtf8(ctxt); 00686 QString msgid = QString::fromUtf8(msg); 00687 QString scriptError; 00688 bool fallbackLocal; 00689 result = s->ktrs->eval(iargs, lang, ctry, 00690 msgctxt, dynctxt, msgid, 00691 args, vals, final, s->scriptModulesToLoad, 00692 scriptError, fallbackLocal); 00693 // s->scriptModulesToLoad will be cleared during the call. 00694 00695 if (fallbackLocal) { // evaluation requested fallback 00696 fallback = true; 00697 } 00698 if (!scriptError.isEmpty()) { // problem with evaluation 00699 fallback = true; // also signal fallback 00700 if (!scriptError.isEmpty()) { 00701 kDebug(173) << QString::fromLatin1("Interpolation {%1} in {%2} failed: %3") 00702 .arg(strans.mid(pos, tpos - pos), shortenMessage(strans), scriptError); 00703 } 00704 } 00705 00706 return tpos; 00707 } 00708 00709 QVariant KLocalizedStringPrivate::segmentToValue (const QString &seg) const 00710 { 00711 const KLocalizedStringPrivateStatics *s = staticsKLSP; 00712 QMutexLocker lock(kLocaleMutex()); 00713 00714 // Return invalid variant if segment is either not a proper 00715 // value reference, or the reference is out of bounds. 00716 00717 // Value reference must start with a special character. 00718 if (seg.left(1) != s->scriptVachar) { 00719 return QVariant(); 00720 } 00721 00722 // Reference number must start with 1-9. 00723 // (If numstr is empty, toInt() will return 0.) 00724 QString numstr = seg.mid(1); 00725 if (numstr.left(1).toInt() < 1) { 00726 return QVariant(); 00727 } 00728 00729 // Number must be valid and in bounds. 00730 bool ok; 00731 int index = numstr.toInt(&ok) - 1; 00732 if (!ok || index >= vals.size()) { 00733 return QVariant(); 00734 } 00735 00736 // Passed all hoops. 00737 return vals.at(index); 00738 } 00739 00740 QString KLocalizedStringPrivate::postTranscript (const QString &pcall, 00741 const QString &lang, 00742 const QString &ctry, 00743 const QString &final) const 00744 { 00745 KLocalizedStringPrivateStatics *s = staticsKLSP; 00746 QMutexLocker lock(kLocaleMutex()); 00747 00748 if (s->ktrs == NULL) 00749 // Scripting engine not available. 00750 // (Though this cannot happen, we wouldn't be here then.) 00751 return QString(); 00752 00753 // Resolve the post call. 00754 QList<QVariant> iargs; 00755 iargs.append(pcall); 00756 QString msgctxt = QString::fromUtf8(ctxt); 00757 QString msgid = QString::fromUtf8(msg); 00758 QString scriptError; 00759 bool fallback; 00760 QString dummy = s->ktrs->eval(iargs, lang, ctry, 00761 msgctxt, dynctxt, msgid, 00762 args, vals, final, s->scriptModulesToLoad, 00763 scriptError, fallback); 00764 // s->scriptModulesToLoad will be cleared during the call. 00765 00766 // If the evaluation went wrong. 00767 if (!scriptError.isEmpty()) 00768 { 00769 kDebug(173) << QString::fromLatin1("Post call {%1} for message {%2} failed: %3") 00770 .arg(pcall, shortenMessage(msgid), scriptError); 00771 return QString(); 00772 } 00773 00774 return final; 00775 } 00776 00777 static QString wrapNum (const QString &tag, const QString &numstr, 00778 int fieldWidth, const QChar &fillChar) 00779 { 00780 QString optag; 00781 if (fieldWidth != 0) { 00782 QString fillString = KuitSemantics::escape(fillChar); 00783 optag = QString::fromLatin1("<%1 width='%2' fill='%3'>") 00784 .arg(tag, QString::number(fieldWidth), fillString); 00785 } else { 00786 optag = QString::fromLatin1("<%1>").arg(tag); 00787 } 00788 QString cltag = QString::fromLatin1("</%1>").arg(tag); 00789 return optag + numstr + cltag; 00790 } 00791 00792 KLocalizedString KLocalizedString::subs (int a, int fieldWidth, int base, 00793 const QChar &fillChar) const 00794 { 00795 KLocalizedString kls(*this); 00796 if (!kls.d->plural.isEmpty() && !kls.d->numberSet) { 00797 kls.d->number = static_cast<pluraln>(abs(a)); 00798 kls.d->numberSet = true; 00799 kls.d->numberOrd = d->args.size(); 00800 } 00801 kls.d->args.append(wrapNum(QString::fromLatin1(KUIT_NUMINTG), QString::number(a, base), 00802 fieldWidth, fillChar)); 00803 kls.d->vals.append(static_cast<intn>(a)); 00804 return kls; 00805 } 00806 00807 KLocalizedString KLocalizedString::subs (uint a, int fieldWidth, int base, 00808 const QChar &fillChar) const 00809 { 00810 KLocalizedString kls(*this); 00811 if (!kls.d->plural.isEmpty() && !kls.d->numberSet) { 00812 kls.d->number = static_cast<pluraln>(a); 00813 kls.d->numberSet = true; 00814 kls.d->numberOrd = d->args.size(); 00815 } 00816 kls.d->args.append(wrapNum(QString::fromLatin1(KUIT_NUMINTG), QString::number(a, base), 00817 fieldWidth, fillChar)); 00818 kls.d->vals.append(static_cast<uintn>(a)); 00819 return kls; 00820 } 00821 00822 KLocalizedString KLocalizedString::subs (long a, int fieldWidth, int base, 00823 const QChar &fillChar) const 00824 { 00825 KLocalizedString kls(*this); 00826 if (!kls.d->plural.isEmpty() && !kls.d->numberSet) { 00827 kls.d->number = static_cast<pluraln>(abs(a)); 00828 kls.d->numberSet = true; 00829 kls.d->numberOrd = d->args.size(); 00830 } 00831 kls.d->args.append(wrapNum(QString::fromLatin1(KUIT_NUMINTG), QString::number(a, base), 00832 fieldWidth, fillChar)); 00833 kls.d->vals.append(static_cast<intn>(a)); 00834 return kls; 00835 } 00836 00837 KLocalizedString KLocalizedString::subs (ulong a, int fieldWidth, int base, 00838 const QChar &fillChar) const 00839 { 00840 KLocalizedString kls(*this); 00841 if (!kls.d->plural.isEmpty() && !kls.d->numberSet) { 00842 kls.d->number = static_cast<pluraln>(a); 00843 kls.d->numberSet = true; 00844 kls.d->numberOrd = d->args.size(); 00845 } 00846 kls.d->args.append(wrapNum(QString::fromLatin1(KUIT_NUMINTG), QString::number(a, base), 00847 fieldWidth, fillChar)); 00848 kls.d->vals.append(static_cast<uintn>(a)); 00849 return kls; 00850 } 00851 00852 KLocalizedString KLocalizedString::subs (qlonglong a, int fieldWidth, int base, 00853 const QChar &fillChar) const 00854 { 00855 KLocalizedString kls(*this); 00856 if (!kls.d->plural.isEmpty() && !kls.d->numberSet) { 00857 kls.d->number = static_cast<pluraln>(qAbs(a)); 00858 kls.d->numberSet = true; 00859 kls.d->numberOrd = d->args.size(); 00860 } 00861 kls.d->args.append(wrapNum(QString::fromLatin1(KUIT_NUMINTG), QString::number(a, base), 00862 fieldWidth, fillChar)); 00863 kls.d->vals.append(static_cast<intn>(a)); 00864 return kls; 00865 } 00866 00867 KLocalizedString KLocalizedString::subs (qulonglong a, int fieldWidth, int base, 00868 const QChar &fillChar) const 00869 { 00870 KLocalizedString kls(*this); 00871 if (!kls.d->plural.isEmpty() && !kls.d->numberSet) { 00872 kls.d->number = static_cast<pluraln>(a); 00873 kls.d->numberSet = true; 00874 kls.d->numberOrd = d->args.size(); 00875 } 00876 kls.d->args.append(wrapNum(QString::fromLatin1(KUIT_NUMINTG), QString::number(a, base), 00877 fieldWidth, fillChar)); 00878 kls.d->vals.append(static_cast<uintn>(a)); 00879 return kls; 00880 } 00881 00882 KLocalizedString KLocalizedString::subs (double a, int fieldWidth, 00883 char format, int precision, 00884 const QChar &fillChar) const 00885 { 00886 KLocalizedString kls(*this); 00887 kls.d->args.append(wrapNum(QString::fromLatin1(KUIT_NUMREAL), 00888 QString::number(a, format, precision), 00889 fieldWidth, fillChar)); 00890 kls.d->vals.append(static_cast<realn>(a)); 00891 return kls; 00892 } 00893 00894 KLocalizedString KLocalizedString::subs (QChar a, int fieldWidth, 00895 const QChar &fillChar) const 00896 { 00897 KLocalizedString kls(*this); 00898 kls.d->args.append(QString::fromLatin1("%1").arg(a, fieldWidth, fillChar)); 00899 kls.d->vals.append(QString(a)); 00900 return kls; 00901 } 00902 00903 KLocalizedString KLocalizedString::subs (const QString &a, int fieldWidth, 00904 const QChar &fillChar) const 00905 { 00906 KLocalizedString kls(*this); 00907 // if (!KuitSemantics::mightBeRichText(a)) { ... 00908 // Do not try to auto-escape non-rich-text alike arguments; 00909 // breaks compatibility with 4.0. Perhaps for KDE 5? 00910 // Perhaps bad idea alltogether (too much surprise)? 00911 kls.d->args.append(QString::fromLatin1("%1").arg(a, fieldWidth, fillChar)); 00912 kls.d->vals.append(a); 00913 return kls; 00914 } 00915 00916 KLocalizedString KLocalizedString::inContext (const QString &key, 00917 const QString &text) const 00918 { 00919 KLocalizedString kls(*this); 00920 kls.d->dynctxt[key] = text; 00921 return kls; 00922 } 00923 00924 KLocalizedString ki18n (const char* msg) 00925 { 00926 return KLocalizedString(NULL, msg, NULL); 00927 } 00928 00929 KLocalizedString ki18nc (const char* ctxt, const char *msg) 00930 { 00931 return KLocalizedString(ctxt, msg, NULL); 00932 } 00933 00934 KLocalizedString ki18np (const char* singular, const char* plural) 00935 { 00936 return KLocalizedString(NULL, singular, plural); 00937 } 00938 00939 KLocalizedString ki18ncp (const char* ctxt, 00940 const char* singular, const char* plural) 00941 { 00942 return KLocalizedString(ctxt, singular, plural); 00943 } 00944 00945 extern "C" 00946 { 00947 typedef KTranscript *(*InitFunc)(); 00948 } 00949 00950 void KLocalizedStringPrivate::loadTranscript () 00951 { 00952 KLocalizedStringPrivateStatics *s = staticsKLSP; 00953 QMutexLocker lock(kLocaleMutex()); 00954 00955 s->loadTranscriptCalled = true; 00956 s->ktrs = NULL; // null indicates that Transcript is not available 00957 00958 KLibrary lib(QLatin1String("ktranscript")); 00959 if (!lib.load()) { 00960 kDebug(173) << "Cannot load transcript plugin:" << lib.errorString(); 00961 return; 00962 } 00963 00964 InitFunc initf = (InitFunc) lib.resolveFunction("load_transcript"); 00965 if (!initf) { 00966 lib.unload(); 00967 kDebug(173) << "Cannot find function load_transcript in transcript plugin."; 00968 return; 00969 } 00970 00971 s->ktrs = initf(); 00972 } 00973 00974 void KLocalizedString::notifyCatalogsUpdated (const QStringList &languages, 00975 const QList<KCatalogName> &catalogs) 00976 { 00977 KLocalizedStringPrivate::notifyCatalogsUpdated(languages, catalogs); 00978 } 00979 00980 void KLocalizedStringPrivate::notifyCatalogsUpdated (const QStringList &languages, 00981 const QList<KCatalogName> &catalogs) 00982 { 00983 if (staticsKLSP.isDestroyed()) { 00984 return; 00985 } 00986 KLocalizedStringPrivateStatics *s = staticsKLSP; 00987 // Very important: do not the mutex here. 00988 //QMutexLocker lock(kLocaleMutex()); 00989 00990 // Find script modules for all included language/catalogs that have them, 00991 // and remember their paths. 00992 // A more specific module may reference the calls from a less specific, 00993 // and the catalog list is ordered from more to less specific. Therefore, 00994 // work on reversed list of catalogs. 00995 foreach (const QString &lang, languages) { 00996 for (int i = catalogs.size() - 1; i >= 0; --i) { 00997 const KCatalogName &cat(catalogs[i]); 00998 00999 // Assemble module's relative path. 01000 QString modrpath = lang + QLatin1Char('/') + s->scriptDir + QLatin1Char('/') 01001 + cat.name + QLatin1Char('/') + cat.name + QLatin1String(".js"); 01002 01003 // Try to find this module. 01004 QString modapath = KStandardDirs::locate("locale", modrpath); 01005 01006 // If the module exists and hasn't been already included. 01007 if ( !modapath.isEmpty() 01008 && !s->scriptModules[lang].contains(cat.name)) 01009 { 01010 // Indicate that the module has been considered. 01011 s->scriptModules[lang].append(cat.name); 01012 01013 // Store the absolute path and language of the module, 01014 // to load on next script evaluation. 01015 QStringList mod; 01016 mod.append(modapath); 01017 mod.append(lang); 01018 s->scriptModulesToLoad.append(mod); 01019 } 01020 } 01021 } 01022 01023 // Create visual formatters for each new language. 01024 foreach (const QString &lang, languages) { 01025 if (!s->formatters.contains(lang)) { 01026 s->formatters.insert(lang, new KuitSemantics(lang)); 01027 } 01028 } 01029 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2019 The KDE developers.
Generated on Mon Jan 21 2019 12:28:11 by doxygen 1.7.5.1 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2019 The KDE developers.
Generated on Mon Jan 21 2019 12:28:11 by doxygen 1.7.5.1 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.