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 }
KDE 4.6 API Reference