34 #include <QMutexLocker>
35 #include <QStringList>
45 const int maxlen = 20;
46 if (str.length() <= maxlen)
49 return str.left(maxlen).append(QLatin1String(
"..."));
57 class KLocalizedStringPrivateStatics;
59 class KLocalizedStringPrivate
74 QString selectForEnglish ()
const;
76 const QChar &plchar = QLatin1Char(
'%'),
77 bool partial =
false)
const;
85 bool &fallback)
const;
86 int resolveInterpolation (
const QString &trans,
int pos,
91 bool &fallback)
const;
100 static void loadTranscript ();
103 class KLocalizedStringPrivateStatics
110 const QChar scriptPlchar;
111 const QChar scriptVachar;
117 bool loadTranscriptCalled;
122 KLocalizedStringPrivateStatics () :
123 theFence(QLatin1String(
"|/|")),
124 startInterp(QLatin1String(
"$[")),
125 endInterp(QLatin1String(
"]")),
126 scriptPlchar(QLatin1Char(
'%')),
127 scriptVachar(QLatin1Char(
'^')),
129 scriptDir(QLatin1String(
"LC_SCRIPTS")),
131 scriptModulesToLoad(),
133 loadTranscriptCalled(false),
139 ~KLocalizedStringPrivateStatics ()
143 qDeleteAll(formatters);
149 : d(new KLocalizedStringPrivate)
151 d->numberSet =
false;
157 const char *msg,
const char *plural)
158 : d(new KLocalizedStringPrivate)
163 d->numberSet =
false;
169 : d(new KLocalizedStringPrivate(*rhs.d))
189 return d->msg.isEmpty();
204 return d->toString(locale, NULL);
208 const QString &catalogName)
const
210 return d->toString(locale, &catalogName);
214 const QString *catalogName)
const
216 const KLocalizedStringPrivateStatics *s = staticsKLSP;
223 kDebug(173) <<
"Trying to convert empty KLocalizedString to QString.";
225 return QString::fromLatin1(
"(I18N_EMPTY_MESSAGE)");
232 if (!plural.isEmpty() && !numberSet)
233 kDebug(173) << QString::fromLatin1(
"Plural argument to message {%1} not supplied before conversion.")
239 if (catalogName != NULL) {
240 catname = catalogName->toUtf8();
242 if (locale != NULL) {
243 if (!ctxt.isEmpty() && !plural.isEmpty()) {
246 }
else if (!plural.isEmpty()) {
249 }
else if (!ctxt.isEmpty()) {
259 ctry = QLatin1Char(
'C');
260 rawtrans = selectForEnglish();
265 int cdpos = rawtrans.indexOf(s->theFence);
270 trans = rawtrans.left(cdpos);
273 strans = rawtrans.mid(cdpos + s->theFence.length());
276 if ( !s->loadTranscriptCalled && !strans.isEmpty()
282 kDebug(173) << QString::fromLatin1(
"Scripted message {%1} before transcript engine can be loaded.")
295 kDebug(173) << QString::fromLatin1(
"Scripted message {%1} without ordinary translation, discarded.")
297 trans = selectForEnglish();
301 QString final = substituteSimple(trans);
303 final = postFormat(
final, lang, QString::fromLatin1(ctxt));
306 if (!strans.isEmpty()) {
309 QString sfinal = substituteTranscript(strans, lang, ctry,
final, fallback);
312 if (!sfinal.isEmpty() && !fallback) {
313 final = postFormat(sfinal, lang, QString::fromLatin1(ctxt));
322 foreach(
const QString &pcall, pcalls)
323 postTranscript(pcall, lang, ctry,
final);
329 QString KLocalizedStringPrivate::selectForEnglish ()
const
333 if (!plural.isEmpty()) {
335 trans = QString::fromUtf8(msg);
338 trans = QString::fromUtf8(plural);
342 trans = QString::fromUtf8(msg);
348 QString KLocalizedStringPrivate::substituteSimple (
const QString &trans,
361 int slen = trans.length();
363 int tpos = trans.indexOf(plchar);
372 if (trans[tpos].digitValue() > 0)
376 while (tpos < slen && trans[tpos].digitValue() >= 0)
378 plord = 10 * plord + trans[tpos].digitValue();
387 if (plord >= ords.size())
388 ords.resize(plord + 1);
395 tsegs.append(trans.mid(spos, ctpos - spos));
396 plords.append(plord);
402 tpos = trans.indexOf(plchar, tpos);
405 tsegs.append(trans.mid(spos));
409 if (!plural.isEmpty() && numberOrd >= ords.size())
410 ords.resize(numberOrd + 1);
415 if (!plural.isEmpty())
421 for (
int i = 0; i < plords.size(); i++)
423 final.append(tsegs.at(i));
424 if (plords.at(i) >= args.size())
428 final.append(QLatin1Char(
'%') + QString::number(plords.at(i) + 1));
432 final.append(QLatin1String(
"(I18N_ARGUMENT_MISSING)"));
437 final.append(args.at(plords.at(i)));
439 final.append(tsegs.last());
446 for (
int i = 0; i < ords.size(); i++)
450 kDebug(173) << QString::fromLatin1(
"Placeholder %%1 skipped in message {%2}.")
455 if (!gaps && ords.size() != args.size())
456 kDebug(173) << QString::fromLatin1(
"%1 instead of %2 arguments to message {%3} supplied before conversion.")
461 final.append(QLatin1String(
"(I18N_GAPS_IN_PLACEHOLDER_SEQUENCE)"));
462 if (ords.size() < args.size())
463 final.append(QLatin1String(
"(I18N_EXCESS_ARGUMENTS_SUPPLIED)"));
464 if (!plural.isEmpty() && !numberSet)
465 final.append(QLatin1String(
"(I18N_PLURAL_ARGUMENT_MISSING)"));
476 const KLocalizedStringPrivateStatics *s = staticsKLSP;
482 if (s->formatters.contains(lang)) {
483 final = s->formatters[lang]->format(
final, ctxt);
489 QString KLocalizedStringPrivate::substituteTranscript (
const QString &strans,
493 bool &fallback)
const
495 const KLocalizedStringPrivateStatics *s = staticsKLSP;
506 int tpos = strans.indexOf(s->startInterp);
510 QString ptext = substituteSimple(strans.mid(ppos, tpos - ppos),
511 s->scriptPlchar,
true);
512 sfinal.append(ptext);
517 tpos = resolveInterpolation(strans, tpos, lang, ctry,
final,
518 result, fallbackLocal);
532 sfinal.append(result);
536 tpos = strans.indexOf(s->startInterp, tpos);
539 sfinal.append(substituteSimple(strans.mid(ppos), s->scriptPlchar,
true));
542 return fallback ?
QString() : sfinal;
545 int KLocalizedStringPrivate::resolveInterpolation (
const QString &strans,
551 bool &fallback)
const
559 KLocalizedStringPrivateStatics *s = staticsKLSP;
567 int slen = strans.length();
568 int islen = s->startInterp.length();
569 int ielen = s->endInterp.length();
570 int tpos = pos + s->startInterp.length();
574 while (tpos < slen && strans[tpos].isSpace()) {
578 kDebug(173) << QString::fromLatin1(
"Unclosed interpolation {%1} in message {%2}.")
582 if (strans.mid(tpos, ielen) == s->endInterp) {
595 while ( !strans[tpos].isSpace()
596 && strans.mid(tpos, ielen) != s->endInterp)
598 if (strans[tpos] == QLatin1Char(
'\'')) {
602 while (tpos < slen && strans[tpos] != QLatin1Char(
'\'')) {
603 if (strans[tpos] == QLatin1Char(
'\\'))
605 seg.append(strans[tpos]);
609 kDebug(173) << QString::fromLatin1(
"Unclosed quote in interpolation {%1} in message {%2}.")
615 segs.append(substituteSimple(seg, s->scriptPlchar,
true));
619 else if (strans.mid(tpos, islen) == s->startInterp) {
622 tpos = resolveInterpolation(strans, tpos, lang, ctry,
final,
623 resultLocal, fallbackLocal);
631 segs.append(resultLocal);
637 && !strans[tpos].isSpace() && strans[tpos] != QLatin1Char(
'\'')
638 && strans.mid(tpos, islen) != s->startInterp
639 && strans.mid(tpos, ielen) != s->endInterp)
641 if (strans[tpos] == QLatin1Char(
'\\'))
643 seg.append(strans[tpos]);
647 kDebug(173) << QString::fromLatin1(
"Non-terminated interpolation {%1} in message {%2}.")
656 vref = segmentToValue(seg);
657 if (vref.isValid()) {
661 segs.append(substituteSimple(seg, s->scriptPlchar,
true));
670 if (segs.size() == 1 && vref.isValid()) {
674 iargs.append(segs.join(
QString()));
685 QString msgctxt = QString::fromUtf8(ctxt);
686 QString msgid = QString::fromUtf8(msg);
689 result = s->ktrs->eval(iargs, lang, ctry,
690 msgctxt, dynctxt, msgid,
691 args, vals,
final, s->scriptModulesToLoad,
692 scriptError, fallbackLocal);
698 if (!scriptError.isEmpty()) {
700 if (!scriptError.isEmpty()) {
701 kDebug(173) << QString::fromLatin1(
"Interpolation {%1} in {%2} failed: %3")
702 .arg(strans.mid(pos, tpos - pos),
shortenMessage(strans), scriptError);
709 QVariant KLocalizedStringPrivate::segmentToValue (
const QString &seg)
const
711 const KLocalizedStringPrivateStatics *s = staticsKLSP;
718 if (seg.left(1) != s->scriptVachar) {
725 if (numstr.left(1).toInt() < 1) {
731 int index = numstr.toInt(&ok) - 1;
732 if (!ok || index >= vals.size()) {
737 return vals.at(index);
740 QString KLocalizedStringPrivate::postTranscript (
const QString &pcall,
745 KLocalizedStringPrivateStatics *s = staticsKLSP;
756 QString msgctxt = QString::fromUtf8(ctxt);
757 QString msgid = QString::fromUtf8(msg);
760 QString dummy = s->ktrs->eval(iargs, lang, ctry,
761 msgctxt, dynctxt, msgid,
762 args, vals,
final, s->scriptModulesToLoad,
763 scriptError, fallback);
767 if (!scriptError.isEmpty())
769 kDebug(173) << QString::fromLatin1(
"Post call {%1} for message {%2} failed: %3")
778 int fieldWidth,
const QChar &fillChar)
781 if (fieldWidth != 0) {
783 optag = QString::fromLatin1(
"<%1 width='%2' fill='%3'>")
784 .arg(tag, QString::number(fieldWidth), fillString);
786 optag = QString::fromLatin1(
"<%1>").arg(tag);
788 QString cltag = QString::fromLatin1(
"</%1>").arg(tag);
789 return optag + numstr + cltag;
793 const QChar &fillChar)
const
796 if (!kls.d->plural.isEmpty() && !kls.d->numberSet) {
797 kls.d->number =
static_cast<pluraln>(abs(a));
798 kls.d->numberSet =
true;
799 kls.d->numberOrd = d->args.size();
802 fieldWidth, fillChar));
803 kls.d->vals.append(static_cast<intn>(a));
808 const QChar &fillChar)
const
811 if (!kls.d->plural.isEmpty() && !kls.d->numberSet) {
812 kls.d->number =
static_cast<pluraln>(a);
813 kls.d->numberSet =
true;
814 kls.d->numberOrd = d->args.size();
817 fieldWidth, fillChar));
818 kls.d->vals.append(static_cast<uintn>(a));
823 const QChar &fillChar)
const
826 if (!kls.d->plural.isEmpty() && !kls.d->numberSet) {
827 kls.d->number =
static_cast<pluraln>(abs(a));
828 kls.d->numberSet =
true;
829 kls.d->numberOrd = d->args.size();
832 fieldWidth, fillChar));
833 kls.d->vals.append(static_cast<intn>(a));
838 const QChar &fillChar)
const
841 if (!kls.d->plural.isEmpty() && !kls.d->numberSet) {
842 kls.d->number =
static_cast<pluraln>(a);
843 kls.d->numberSet =
true;
844 kls.d->numberOrd = d->args.size();
847 fieldWidth, fillChar));
848 kls.d->vals.append(static_cast<uintn>(a));
853 const QChar &fillChar)
const
856 if (!kls.d->plural.isEmpty() && !kls.d->numberSet) {
857 kls.d->number =
static_cast<pluraln>(qAbs(a));
858 kls.d->numberSet =
true;
859 kls.d->numberOrd = d->args.size();
862 fieldWidth, fillChar));
863 kls.d->vals.append(static_cast<intn>(a));
868 const QChar &fillChar)
const
871 if (!kls.d->plural.isEmpty() && !kls.d->numberSet) {
872 kls.d->number =
static_cast<pluraln>(a);
873 kls.d->numberSet =
true;
874 kls.d->numberOrd = d->args.size();
877 fieldWidth, fillChar));
878 kls.d->vals.append(static_cast<uintn>(a));
883 char format,
int precision,
884 const QChar &fillChar)
const
888 QString::number(a, format, precision),
889 fieldWidth, fillChar));
890 kls.d->vals.append(static_cast<realn>(a));
895 const QChar &fillChar)
const
898 kls.d->args.append(QString::fromLatin1(
"%1").arg(a, fieldWidth, fillChar));
899 kls.d->vals.append(
QString(a));
904 const QChar &fillChar)
const
911 kls.d->args.append(QString::fromLatin1(
"%1").arg(a, fieldWidth, fillChar));
912 kls.d->vals.append(a);
920 kls.d->dynctxt[key] = text;
940 const char* singular,
const char* plural)
950 void KLocalizedStringPrivate::loadTranscript ()
952 KLocalizedStringPrivateStatics *s = staticsKLSP;
955 s->loadTranscriptCalled =
true;
958 KLibrary lib(QLatin1String(
"ktranscript"));
960 kDebug(173) <<
"Cannot load transcript plugin:" << lib.errorString();
967 kDebug(173) <<
"Cannot find function load_transcript in transcript plugin.";
977 KLocalizedStringPrivate::notifyCatalogsUpdated(languages, catalogs);
980 void KLocalizedStringPrivate::notifyCatalogsUpdated (
const QStringList &languages,
983 if (staticsKLSP.isDestroyed()) {
986 KLocalizedStringPrivateStatics *s = staticsKLSP;
995 foreach (
const QString &lang, languages) {
996 for (
int i = catalogs.size() - 1; i >= 0; --i) {
1000 QString modrpath = lang + QLatin1Char(
'/') + s->scriptDir + QLatin1Char(
'/')
1001 + cat.name + QLatin1Char(
'/') + cat.name + QLatin1String(
".js");
1007 if ( !modapath.isEmpty()
1008 && !s->scriptModules[lang].contains(cat.name))
1011 s->scriptModules[lang].append(cat.name);
1016 mod.append(modapath);
1018 s->scriptModulesToLoad.append(mod);
1024 foreach (
const QString &lang, languages) {
1025 if (!s->formatters.contains(lang)) {