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
69 QHash<QString, QString> dynctxt;
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;
99 const QList<KCatalogName> &catalogs);
100 static void loadTranscript ();
103 class KLocalizedStringPrivateStatics
110 const QChar scriptPlchar;
111 const QChar scriptVachar;
114 QHash<QString, QStringList> scriptModules;
115 QList<QStringList> scriptModulesToLoad;
117 bool loadTranscriptCalled;
120 QHash<QString, KuitSemantics*> formatters;
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,
QString());
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.")
238 const char *catname = catalogName.toUtf8();
239 if (locale != NULL) {
240 if (!ctxt.isEmpty() && !plural.isEmpty()) {
243 }
else if (!plural.isEmpty()) {
246 }
else if (!ctxt.isEmpty()) {
256 ctry = QLatin1Char(
'C');
257 rawtrans = selectForEnglish();
262 int cdpos = rawtrans.indexOf(s->theFence);
267 trans = rawtrans.left(cdpos);
270 strans = rawtrans.mid(cdpos + s->theFence.length());
273 if ( !s->loadTranscriptCalled && !strans.isEmpty()
279 kDebug(173) << QString::fromLatin1(
"Scripted message {%1} before transcript engine can be loaded.")
292 kDebug(173) << QString::fromLatin1(
"Scripted message {%1} without ordinary translation, discarded.")
294 trans = selectForEnglish();
298 QString final = substituteSimple(trans);
300 final = postFormat(
final, lang, QString::fromLatin1(ctxt));
303 if (!strans.isEmpty()) {
306 QString sfinal = substituteTranscript(strans, lang, ctry,
final, fallback);
309 if (!sfinal.isEmpty() && !fallback) {
310 final = postFormat(sfinal, lang, QString::fromLatin1(ctxt));
319 foreach(
const QString &pcall, pcalls)
320 postTranscript(pcall, lang, ctry,
final);
326 QString KLocalizedStringPrivate::selectForEnglish ()
const
330 if (!plural.isEmpty()) {
332 trans = QString::fromUtf8(msg);
335 trans = QString::fromUtf8(plural);
339 trans = QString::fromUtf8(msg);
345 QString KLocalizedStringPrivate::substituteSimple (
const QString &trans,
358 int slen = trans.length();
360 int tpos = trans.indexOf(plchar);
369 if (trans[tpos].digitValue() > 0)
373 while (tpos < slen && trans[tpos].digitValue() >= 0)
375 plord = 10 * plord + trans[tpos].digitValue();
384 if (plord >= ords.size())
385 ords.resize(plord + 1);
392 tsegs.append(trans.mid(spos, ctpos - spos));
393 plords.append(plord);
399 tpos = trans.indexOf(plchar, tpos);
402 tsegs.append(trans.mid(spos));
406 if (!plural.isEmpty() && numberOrd >= ords.size())
407 ords.resize(numberOrd + 1);
412 if (!plural.isEmpty())
418 for (
int i = 0; i < plords.size(); i++)
420 final.append(tsegs.at(i));
421 if (plords.at(i) >= args.size())
425 final.append(QLatin1Char(
'%') + QString::number(plords.at(i) + 1));
429 final.append(QLatin1String(
"(I18N_ARGUMENT_MISSING)"));
434 final.append(args.at(plords.at(i)));
436 final.append(tsegs.last());
443 for (
int i = 0; i < ords.size(); i++)
447 kDebug(173) << QString::fromLatin1(
"Placeholder %%1 skipped in message {%2}.")
452 if (!gaps && ords.size() != args.size())
453 kDebug(173) << QString::fromLatin1(
"%1 instead of %2 arguments to message {%3} supplied before conversion.")
458 final.append(QLatin1String(
"(I18N_GAPS_IN_PLACEHOLDER_SEQUENCE)"));
459 if (ords.size() < args.size())
460 final.append(QLatin1String(
"(I18N_EXCESS_ARGUMENTS_SUPPLIED)"));
461 if (!plural.isEmpty() && !numberSet)
462 final.append(QLatin1String(
"(I18N_PLURAL_ARGUMENT_MISSING)"));
473 const KLocalizedStringPrivateStatics *s = staticsKLSP;
479 if (s->formatters.contains(lang)) {
480 final = s->formatters[lang]->format(
final, ctxt);
486 QString KLocalizedStringPrivate::substituteTranscript (
const QString &strans,
490 bool &fallback)
const
492 const KLocalizedStringPrivateStatics *s = staticsKLSP;
503 int tpos = strans.indexOf(s->startInterp);
507 QString ptext = substituteSimple(strans.mid(ppos, tpos - ppos),
508 s->scriptPlchar,
true);
509 sfinal.append(ptext);
514 tpos = resolveInterpolation(strans, tpos, lang, ctry,
final,
515 result, fallbackLocal);
529 sfinal.append(result);
533 tpos = strans.indexOf(s->startInterp, tpos);
536 sfinal.append(substituteSimple(strans.mid(ppos), s->scriptPlchar,
true));
539 return fallback ?
QString() : sfinal;
542 int KLocalizedStringPrivate::resolveInterpolation (
const QString &strans,
548 bool &fallback)
const
556 KLocalizedStringPrivateStatics *s = staticsKLSP;
563 QList<QVariant> iargs;
564 int slen = strans.length();
565 int islen = s->startInterp.length();
566 int ielen = s->endInterp.length();
567 int tpos = pos + s->startInterp.length();
571 while (tpos < slen && strans[tpos].isSpace()) {
575 kDebug(173) << QString::fromLatin1(
"Unclosed interpolation {%1} in message {%2}.")
579 if (strans.mid(tpos, ielen) == s->endInterp) {
592 while ( !strans[tpos].isSpace()
593 && strans.mid(tpos, ielen) != s->endInterp)
595 if (strans[tpos] == QLatin1Char(
'\'')) {
599 while (tpos < slen && strans[tpos] != QLatin1Char(
'\'')) {
600 if (strans[tpos] == QLatin1Char(
'\\'))
602 seg.append(strans[tpos]);
606 kDebug(173) << QString::fromLatin1(
"Unclosed quote in interpolation {%1} in message {%2}.")
612 segs.append(substituteSimple(seg, s->scriptPlchar,
true));
616 else if (strans.mid(tpos, islen) == s->startInterp) {
619 tpos = resolveInterpolation(strans, tpos, lang, ctry,
final,
620 resultLocal, fallbackLocal);
628 segs.append(resultLocal);
634 && !strans[tpos].isSpace() && strans[tpos] != QLatin1Char(
'\'')
635 && strans.mid(tpos, islen) != s->startInterp
636 && strans.mid(tpos, ielen) != s->endInterp)
638 if (strans[tpos] == QLatin1Char(
'\\'))
640 seg.append(strans[tpos]);
644 kDebug(173) << QString::fromLatin1(
"Non-terminated interpolation {%1} in message {%2}.")
653 vref = segmentToValue(seg);
654 if (vref.isValid()) {
658 segs.append(substituteSimple(seg, s->scriptPlchar,
true));
667 if (segs.size() == 1 && vref.isValid()) {
671 iargs.append(segs.join(
QString()));
682 QString msgctxt = QString::fromUtf8(ctxt);
683 QString msgid = QString::fromUtf8(msg);
686 result = s->ktrs->eval(iargs, lang, ctry,
687 msgctxt, dynctxt, msgid,
688 args, vals,
final, s->scriptModulesToLoad,
689 scriptError, fallbackLocal);
695 if (!scriptError.isEmpty()) {
697 if (!scriptError.isEmpty()) {
698 kDebug(173) << QString::fromLatin1(
"Interpolation {%1} in {%2} failed: %3")
699 .arg(strans.mid(pos, tpos - pos),
shortenMessage(strans), scriptError);
706 QVariant KLocalizedStringPrivate::segmentToValue (
const QString &seg)
const
708 const KLocalizedStringPrivateStatics *s = staticsKLSP;
715 if (seg.left(1) != s->scriptVachar) {
722 if (numstr.left(1).toInt() < 1) {
728 int index = numstr.toInt(&ok) - 1;
729 if (!ok || index >= vals.size()) {
734 return vals.at(index);
737 QString KLocalizedStringPrivate::postTranscript (
const QString &pcall,
742 KLocalizedStringPrivateStatics *s = staticsKLSP;
751 QList<QVariant> iargs;
753 QString msgctxt = QString::fromUtf8(ctxt);
754 QString msgid = QString::fromUtf8(msg);
757 QString dummy = s->ktrs->eval(iargs, lang, ctry,
758 msgctxt, dynctxt, msgid,
759 args, vals,
final, s->scriptModulesToLoad,
760 scriptError, fallback);
764 if (!scriptError.isEmpty())
766 kDebug(173) << QString::fromLatin1(
"Post call {%1} for message {%2} failed: %3")
775 int fieldWidth,
const QChar &fillChar)
778 if (fieldWidth != 0) {
780 optag = QString::fromLatin1(
"<%1 width='%2' fill='%3'>")
781 .arg(tag, QString::number(fieldWidth), fillString);
783 optag = QString::fromLatin1(
"<%1>").arg(tag);
785 QString cltag = QString::fromLatin1(
"</%1>").arg(tag);
786 return optag + numstr + cltag;
790 const QChar &fillChar)
const
793 if (!kls.d->plural.isEmpty() && !kls.d->numberSet) {
794 kls.d->number =
static_cast<pluraln>(abs(a));
795 kls.d->numberSet =
true;
796 kls.d->numberOrd = d->args.size();
799 fieldWidth, fillChar));
800 kls.d->vals.append(static_cast<intn>(a));
805 const QChar &fillChar)
const
808 if (!kls.d->plural.isEmpty() && !kls.d->numberSet) {
809 kls.d->number =
static_cast<pluraln>(a);
810 kls.d->numberSet =
true;
811 kls.d->numberOrd = d->args.size();
814 fieldWidth, fillChar));
815 kls.d->vals.append(static_cast<uintn>(a));
820 const QChar &fillChar)
const
823 if (!kls.d->plural.isEmpty() && !kls.d->numberSet) {
824 kls.d->number =
static_cast<pluraln>(abs(a));
825 kls.d->numberSet =
true;
826 kls.d->numberOrd = d->args.size();
829 fieldWidth, fillChar));
830 kls.d->vals.append(static_cast<intn>(a));
835 const QChar &fillChar)
const
838 if (!kls.d->plural.isEmpty() && !kls.d->numberSet) {
839 kls.d->number =
static_cast<pluraln>(a);
840 kls.d->numberSet =
true;
841 kls.d->numberOrd = d->args.size();
844 fieldWidth, fillChar));
845 kls.d->vals.append(static_cast<uintn>(a));
850 const QChar &fillChar)
const
853 if (!kls.d->plural.isEmpty() && !kls.d->numberSet) {
854 kls.d->number =
static_cast<pluraln>(qAbs(a));
855 kls.d->numberSet =
true;
856 kls.d->numberOrd = d->args.size();
859 fieldWidth, fillChar));
860 kls.d->vals.append(static_cast<intn>(a));
865 const QChar &fillChar)
const
868 if (!kls.d->plural.isEmpty() && !kls.d->numberSet) {
869 kls.d->number =
static_cast<pluraln>(a);
870 kls.d->numberSet =
true;
871 kls.d->numberOrd = d->args.size();
874 fieldWidth, fillChar));
875 kls.d->vals.append(static_cast<uintn>(a));
880 char format,
int precision,
881 const QChar &fillChar)
const
885 QString::number(a, format, precision),
886 fieldWidth, fillChar));
887 kls.d->vals.append(static_cast<realn>(a));
892 const QChar &fillChar)
const
895 kls.d->args.append(QString::fromLatin1(
"%1").arg(a, fieldWidth, fillChar));
896 kls.d->vals.append(
QString(a));
901 const QChar &fillChar)
const
908 kls.d->args.append(QString::fromLatin1(
"%1").arg(a, fieldWidth, fillChar));
909 kls.d->vals.append(a);
917 kls.d->dynctxt[key] = text;
937 const char* singular,
const char* plural)
947 void KLocalizedStringPrivate::loadTranscript ()
949 KLocalizedStringPrivateStatics *s = staticsKLSP;
952 s->loadTranscriptCalled =
true;
955 KLibrary lib(QLatin1String(
"ktranscript"));
957 kDebug(173) <<
"Cannot load transcript plugin:" << lib.errorString();
964 kDebug(173) <<
"Cannot find function load_transcript in transcript plugin.";
972 const QList<KCatalogName> &catalogs)
977 void KLocalizedStringPrivate::notifyCatalogsUpdated (
const QStringList &languages,
978 const QList<KCatalogName> &catalogs)
980 if (staticsKLSP.isDestroyed()) {
983 KLocalizedStringPrivateStatics *s = staticsKLSP;
992 foreach (
const QString &lang, languages) {
993 for (
int i = catalogs.size() - 1; i >= 0; --i) {
997 QString modrpath = lang + QLatin1Char(
'/') + s->scriptDir + QLatin1Char(
'/')
998 + cat.name + QLatin1Char(
'/') + cat.name + QLatin1String(
".js");
1004 if ( !modapath.isEmpty()
1005 && !s->scriptModules[lang].contains(cat.name))
1008 s->scriptModules[lang].append(cat.name);
1013 mod.append(modapath);
1015 s->scriptModulesToLoad.append(mod);
1021 foreach (
const QString &lang, languages) {
1022 if (!s->formatters.contains(lang)) {