mirror of
https://bitbucket.org/smil3y/kdelibs.git
synced 2025-02-24 19:02:48 +00:00
850 lines
28 KiB
C++
850 lines
28 KiB
C++
/* This file is part of the KDE libraries
|
|
Copyright (C) 2006 Chusslove Illich <caslav.ilic@gmx.net>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include <klocalizedstring.h>
|
|
|
|
#include <config.h>
|
|
|
|
#include <kglobal.h>
|
|
#include <kdebug.h>
|
|
#include <klocale.h>
|
|
#include <klocale_p.h>
|
|
#include <kstandarddirs.h>
|
|
#include <kuitsemantics_p.h>
|
|
#include "kcatalogname_p.h"
|
|
|
|
#include <QtCore/qmutex.h>
|
|
#include <QStringList>
|
|
#include <QByteArray>
|
|
#include <QChar>
|
|
#include <QHash>
|
|
#include <QList>
|
|
#include <QVector>
|
|
|
|
// Truncates string, for output of long messages.
|
|
static QString shortenMessage (const QString &str)
|
|
{
|
|
const int maxlen = 20;
|
|
if (str.length() <= maxlen)
|
|
return str;
|
|
else
|
|
return str.left(maxlen).append(QLatin1String("..."));
|
|
}
|
|
|
|
typedef qulonglong pluraln;
|
|
typedef qlonglong intn;
|
|
typedef qulonglong uintn;
|
|
typedef double realn;
|
|
|
|
class KLocalizedStringPrivateStatics;
|
|
|
|
class KLocalizedStringPrivate
|
|
{
|
|
friend class KLocalizedString;
|
|
|
|
QStringList args;
|
|
QList<QVariant> vals;
|
|
bool numberSet;
|
|
pluraln number;
|
|
int numberOrd;
|
|
QByteArray ctxt;
|
|
QHash<QString, QString> dynctxt;
|
|
QByteArray msg;
|
|
QByteArray plural;
|
|
|
|
QString toString (const KLocale *locale, const QString *catalogName) const;
|
|
QString selectForEnglish () const;
|
|
QString substituteSimple (const QString &trans,
|
|
const QChar &plchar = QLatin1Char('%'),
|
|
bool partial = false) const;
|
|
QString postFormat (const QString &text,
|
|
const QString &lang,
|
|
const QString &ctxt) const;
|
|
int resolveInterpolation (const QString &trans, int pos,
|
|
const QString &lang,
|
|
const QString &ctry,
|
|
const QString &final,
|
|
QString &result,
|
|
bool &fallback) const;
|
|
QVariant segmentToValue (const QString &arg) const;
|
|
|
|
static void notifyCatalogsUpdated (const QStringList &languages,
|
|
const QList<KCatalogName> &catalogs);
|
|
};
|
|
|
|
class KLocalizedStringPrivateStatics
|
|
{
|
|
public:
|
|
|
|
const QString theFence;
|
|
const QString startInterp;
|
|
const QString endInterp;
|
|
const QChar scriptPlchar;
|
|
const QChar scriptVachar;
|
|
|
|
const QString scriptDir;
|
|
QHash<QString, QStringList> scriptModules;
|
|
QList<QStringList> scriptModulesToLoad;
|
|
|
|
QHash<QString, KuitSemantics*> formatters;
|
|
|
|
KLocalizedStringPrivateStatics () :
|
|
theFence(QLatin1String("|/|")),
|
|
startInterp(QLatin1String("$[")),
|
|
endInterp(QLatin1String("]")),
|
|
scriptPlchar(QLatin1Char('%')),
|
|
scriptVachar(QLatin1Char('^')),
|
|
|
|
scriptDir(QLatin1String("LC_SCRIPTS")),
|
|
scriptModules(),
|
|
scriptModulesToLoad(),
|
|
|
|
formatters()
|
|
{}
|
|
|
|
~KLocalizedStringPrivateStatics ()
|
|
{
|
|
// ktrs is handled by KLibLoader.
|
|
//delete ktrs;
|
|
qDeleteAll(formatters);
|
|
}
|
|
};
|
|
K_GLOBAL_STATIC(KLocalizedStringPrivateStatics, staticsKLSP)
|
|
|
|
KLocalizedString::KLocalizedString ()
|
|
: d(new KLocalizedStringPrivate)
|
|
{
|
|
d->numberSet = false;
|
|
d->number = 0;
|
|
d->numberOrd = 0;
|
|
}
|
|
|
|
KLocalizedString::KLocalizedString (const char *ctxt,
|
|
const char *msg, const char *plural)
|
|
: d(new KLocalizedStringPrivate)
|
|
{
|
|
d->ctxt = ctxt;
|
|
d->msg = msg;
|
|
d->plural = plural;
|
|
d->numberSet = false;
|
|
d->number = 0;
|
|
d->numberOrd = 0;
|
|
}
|
|
|
|
KLocalizedString::KLocalizedString(const KLocalizedString &rhs)
|
|
: d(new KLocalizedStringPrivate(*rhs.d))
|
|
{
|
|
}
|
|
|
|
KLocalizedString& KLocalizedString::operator= (const KLocalizedString &rhs)
|
|
{
|
|
if (&rhs != this)
|
|
{
|
|
*d = *rhs.d;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
KLocalizedString::~KLocalizedString ()
|
|
{
|
|
delete d;
|
|
}
|
|
|
|
bool KLocalizedString::isEmpty () const
|
|
{
|
|
return d->msg.isEmpty();
|
|
}
|
|
|
|
QString KLocalizedString::toString () const
|
|
{
|
|
return d->toString(KGlobal::locale(), NULL);
|
|
}
|
|
|
|
QString KLocalizedString::toString (const QString &catalogName) const
|
|
{
|
|
return d->toString(KGlobal::locale(), &catalogName);
|
|
}
|
|
|
|
QString KLocalizedString::toString (const KLocale *locale) const
|
|
{
|
|
return d->toString(locale, NULL);
|
|
}
|
|
|
|
QString KLocalizedString::toString (const KLocale *locale,
|
|
const QString &catalogName) const
|
|
{
|
|
return d->toString(locale, &catalogName);
|
|
}
|
|
|
|
QString KLocalizedStringPrivate::toString (const KLocale *locale,
|
|
const QString *catalogName) const
|
|
{
|
|
const KLocalizedStringPrivateStatics *s = staticsKLSP;
|
|
|
|
QMutexLocker lock(kLocaleMutex());
|
|
|
|
// Assure the message has been supplied.
|
|
if (msg.isEmpty())
|
|
{
|
|
kDebug(173) << "Trying to convert empty KLocalizedString to QString.";
|
|
#ifndef NDEBUG
|
|
return QString::fromLatin1("(I18N_EMPTY_MESSAGE)");
|
|
#else
|
|
return QString();
|
|
#endif
|
|
}
|
|
|
|
// Check whether plural argument has been supplied, if message has plural.
|
|
if (!plural.isEmpty() && !numberSet)
|
|
kDebug(173) << QString::fromLatin1("Plural argument to message {%1} not supplied before conversion.")
|
|
.arg(shortenMessage(QString::fromUtf8(msg)));
|
|
|
|
// Get raw translation.
|
|
QString rawtrans, lang, ctry;
|
|
QByteArray catname;
|
|
if (catalogName != NULL) {
|
|
catname = catalogName->toUtf8();
|
|
}
|
|
if (locale != NULL) {
|
|
if (!ctxt.isEmpty() && !plural.isEmpty()) {
|
|
locale->translateRawFrom(catname, ctxt, msg, plural, number,
|
|
&lang, &rawtrans);
|
|
} else if (!plural.isEmpty()) {
|
|
locale->translateRawFrom(catname, msg, plural, number,
|
|
&lang, &rawtrans);
|
|
} else if (!ctxt.isEmpty()) {
|
|
locale->translateRawFrom(catname, ctxt, msg,
|
|
&lang, &rawtrans);
|
|
} else {
|
|
locale->translateRawFrom(catname, msg,
|
|
&lang, &rawtrans);
|
|
}
|
|
ctry = locale->country();
|
|
} else {
|
|
lang = KLocale::defaultLanguage();
|
|
ctry = QLatin1Char('C');
|
|
rawtrans = selectForEnglish();
|
|
}
|
|
|
|
// Set ordinary translation and possibly scripted translation.
|
|
QString trans, strans;
|
|
int cdpos = rawtrans.indexOf(s->theFence);
|
|
if (cdpos > 0)
|
|
{
|
|
// Script fence has been found, strip the scripted from the
|
|
// ordinary translation.
|
|
trans = rawtrans.left(cdpos);
|
|
|
|
// Scripted translation.
|
|
strans = rawtrans.mid(cdpos + s->theFence.length());
|
|
}
|
|
else if (cdpos < 0)
|
|
{
|
|
// No script fence, use translation as is.
|
|
trans = rawtrans;
|
|
}
|
|
else // cdpos == 0
|
|
{
|
|
// The msgstr starts with the script fence, no ordinary translation.
|
|
// This is not allowed, consider message not translated.
|
|
kDebug(173) << QString::fromLatin1("Scripted message {%1} without ordinary translation, discarded.")
|
|
.arg(shortenMessage(trans)) ;
|
|
trans = selectForEnglish();
|
|
}
|
|
|
|
// Substitute placeholders in ordinary translation.
|
|
QString final = substituteSimple(trans);
|
|
// Post-format ordinary translation.
|
|
final = postFormat(final, lang, QString::fromLatin1(ctxt));
|
|
|
|
return final;
|
|
}
|
|
|
|
QString KLocalizedStringPrivate::selectForEnglish () const
|
|
{
|
|
QString trans;
|
|
|
|
if (!plural.isEmpty()) {
|
|
if (number == 1) {
|
|
trans = QString::fromUtf8(msg);
|
|
}
|
|
else {
|
|
trans = QString::fromUtf8(plural);
|
|
}
|
|
}
|
|
else {
|
|
trans = QString::fromUtf8(msg);
|
|
}
|
|
|
|
return trans;
|
|
}
|
|
|
|
QString KLocalizedStringPrivate::substituteSimple (const QString &trans,
|
|
const QChar &plchar,
|
|
bool partial) const
|
|
{
|
|
#ifdef NDEBUG
|
|
Q_UNUSED(partial);
|
|
#endif
|
|
|
|
QStringList tsegs; // text segments per placeholder occurrence
|
|
QList<int> plords; // ordinal numbers per placeholder occurrence
|
|
#ifndef NDEBUG
|
|
QVector<int> ords; // indicates which placeholders are present
|
|
#endif
|
|
int slen = trans.length();
|
|
int spos = 0;
|
|
int tpos = trans.indexOf(plchar);
|
|
while (tpos >= 0)
|
|
{
|
|
int ctpos = tpos;
|
|
|
|
tpos++;
|
|
if (tpos == slen)
|
|
break;
|
|
|
|
if (trans[tpos].digitValue() > 0) // %0 not considered a placeholder
|
|
{
|
|
// Get the placeholder ordinal.
|
|
int plord = 0;
|
|
while (tpos < slen && trans[tpos].digitValue() >= 0)
|
|
{
|
|
plord = 10 * plord + trans[tpos].digitValue();
|
|
tpos++;
|
|
}
|
|
plord--; // ordinals are zero based
|
|
|
|
#ifndef NDEBUG
|
|
// Perhaps enlarge storage for indicators.
|
|
// Note that QVector<int> will initialize new elements to 0,
|
|
// as they are supposed to be.
|
|
if (plord >= ords.size())
|
|
ords.resize(plord + 1);
|
|
|
|
// Indicate that placeholder with computed ordinal is present.
|
|
ords[plord] = 1;
|
|
#endif
|
|
|
|
// Store text segment prior to placeholder and placeholder number.
|
|
tsegs.append(trans.mid(spos, ctpos - spos));
|
|
plords.append(plord);
|
|
|
|
// Position of next text segment.
|
|
spos = tpos;
|
|
}
|
|
|
|
tpos = trans.indexOf(plchar, tpos);
|
|
}
|
|
// Store last text segment.
|
|
tsegs.append(trans.mid(spos));
|
|
|
|
#ifndef NDEBUG
|
|
// Perhaps enlarge storage for plural-number ordinal.
|
|
if (!plural.isEmpty() && numberOrd >= ords.size())
|
|
ords.resize(numberOrd + 1);
|
|
|
|
// Message might have plural but without plural placeholder, which is an
|
|
// allowed state. To ease further logic, indicate that plural placeholder
|
|
// is present anyway if message has plural.
|
|
if (!plural.isEmpty())
|
|
ords[numberOrd] = 1;
|
|
#endif
|
|
|
|
// Assemble the final string from text segments and arguments.
|
|
QString final;
|
|
for (int i = 0; i < plords.size(); i++)
|
|
{
|
|
final.append(tsegs.at(i));
|
|
if (plords.at(i) >= args.size())
|
|
// too little arguments
|
|
{
|
|
// put back the placeholder
|
|
final.append(QLatin1Char('%') + QString::number(plords.at(i) + 1));
|
|
#ifndef NDEBUG
|
|
if (!partial)
|
|
// spoof the message
|
|
final.append(QLatin1String("(I18N_ARGUMENT_MISSING)"));
|
|
#endif
|
|
}
|
|
else
|
|
// just fine
|
|
final.append(args.at(plords.at(i)));
|
|
}
|
|
final.append(tsegs.last());
|
|
|
|
#ifndef NDEBUG
|
|
if (!partial)
|
|
{
|
|
// Check that there are no gaps in numbering sequence of placeholders.
|
|
bool gaps = false;
|
|
for (int i = 0; i < ords.size(); i++)
|
|
if (!ords.at(i))
|
|
{
|
|
gaps = true;
|
|
kDebug(173) << QString::fromLatin1("Placeholder %%1 skipped in message {%2}.")
|
|
.arg(QString::number(i + 1), shortenMessage(trans));
|
|
}
|
|
// If no gaps, check for mismatch between number of unique placeholders and
|
|
// actually supplied arguments.
|
|
if (!gaps && ords.size() != args.size())
|
|
kDebug(173) << QString::fromLatin1("%1 instead of %2 arguments to message {%3} supplied before conversion.")
|
|
.arg(args.size()).arg(ords.size()).arg(shortenMessage(trans));
|
|
|
|
// Some spoofs.
|
|
if (gaps)
|
|
final.append(QLatin1String("(I18N_GAPS_IN_PLACEHOLDER_SEQUENCE)"));
|
|
if (ords.size() < args.size())
|
|
final.append(QLatin1String("(I18N_EXCESS_ARGUMENTS_SUPPLIED)"));
|
|
if (!plural.isEmpty() && !numberSet)
|
|
final.append(QLatin1String("(I18N_PLURAL_ARGUMENT_MISSING)"));
|
|
}
|
|
#endif
|
|
|
|
return final;
|
|
}
|
|
|
|
QString KLocalizedStringPrivate::postFormat (const QString &text,
|
|
const QString &lang,
|
|
const QString &ctxt) const
|
|
{
|
|
const KLocalizedStringPrivateStatics *s = staticsKLSP;
|
|
QMutexLocker lock(kLocaleMutex());
|
|
|
|
QString final = text;
|
|
|
|
// Transform any semantic markup into visual formatting.
|
|
if (s->formatters.contains(lang)) {
|
|
final = s->formatters[lang]->format(final, ctxt);
|
|
}
|
|
|
|
return final;
|
|
}
|
|
|
|
int KLocalizedStringPrivate::resolveInterpolation (const QString &strans,
|
|
int pos,
|
|
const QString &lang,
|
|
const QString &ctry,
|
|
const QString &final,
|
|
QString &result,
|
|
bool &fallback) const
|
|
{
|
|
// pos is the position of opening character sequence.
|
|
// Returns the position of first character after closing sequence,
|
|
// or -1 in case of parsing error.
|
|
// result is set to result of Transcript evaluation.
|
|
// fallback is set to true if Transcript evaluation requested so.
|
|
|
|
KLocalizedStringPrivateStatics *s = staticsKLSP;
|
|
QMutexLocker lock(kLocaleMutex());
|
|
|
|
result.clear();
|
|
fallback = false;
|
|
|
|
// Split interpolation into arguments.
|
|
QList<QVariant> iargs;
|
|
int slen = strans.length();
|
|
int islen = s->startInterp.length();
|
|
int ielen = s->endInterp.length();
|
|
int tpos = pos + s->startInterp.length();
|
|
while (1)
|
|
{
|
|
// Skip whitespace.
|
|
while (tpos < slen && strans[tpos].isSpace()) {
|
|
++tpos;
|
|
}
|
|
if (tpos == slen) {
|
|
kDebug(173) << QString::fromLatin1("Unclosed interpolation {%1} in message {%2}.")
|
|
.arg(strans.mid(pos, tpos - pos), shortenMessage(strans));
|
|
return -1;
|
|
}
|
|
if (strans.mid(tpos, ielen) == s->endInterp) {
|
|
break; // no more arguments
|
|
}
|
|
|
|
// Parse argument: may be concatenated from free and quoted text,
|
|
// and sub-interpolations.
|
|
// Free and quoted segments may contain placeholders, substitute them;
|
|
// recurse into sub-interpolations.
|
|
// Free segments may be value references, parse and record for
|
|
// consideration at the end.
|
|
// Mind backslash escapes throughout.
|
|
QStringList segs;
|
|
QVariant vref;
|
|
while ( !strans[tpos].isSpace()
|
|
&& strans.mid(tpos, ielen) != s->endInterp)
|
|
{
|
|
if (strans[tpos] == QLatin1Char('\'')) { // quoted segment
|
|
QString seg;
|
|
++tpos; // skip opening quote
|
|
// Find closing quote.
|
|
while (tpos < slen && strans[tpos] != QLatin1Char('\'')) {
|
|
if (strans[tpos] == QLatin1Char('\\'))
|
|
++tpos; // escape next character
|
|
seg.append(strans[tpos]);
|
|
++tpos;
|
|
}
|
|
if (tpos == slen) {
|
|
kDebug(173) << QString::fromLatin1("Unclosed quote in interpolation {%1} in message {%2}.")
|
|
.arg(strans.mid(pos, tpos - pos), shortenMessage(strans));
|
|
return -1;
|
|
}
|
|
|
|
// Append to list of segments, resolving placeholders.
|
|
segs.append(substituteSimple(seg, s->scriptPlchar, true));
|
|
|
|
++tpos; // skip closing quote
|
|
}
|
|
else if (strans.mid(tpos, islen) == s->startInterp) { // sub-interpolation
|
|
QString resultLocal;
|
|
bool fallbackLocal;
|
|
tpos = resolveInterpolation(strans, tpos, lang, ctry, final,
|
|
resultLocal, fallbackLocal);
|
|
if (tpos < 0) { // unrecoverable problem in sub-interpolation
|
|
// Error reported in the subcall.
|
|
return tpos;
|
|
}
|
|
if (fallbackLocal) { // sub-interpolation requested fallback
|
|
fallback = true;
|
|
}
|
|
segs.append(resultLocal);
|
|
}
|
|
else { // free segment
|
|
QString seg;
|
|
// Find whitespace, quote, opening or closing sequence.
|
|
while ( tpos < slen
|
|
&& !strans[tpos].isSpace() && strans[tpos] != QLatin1Char('\'')
|
|
&& strans.mid(tpos, islen) != s->startInterp
|
|
&& strans.mid(tpos, ielen) != s->endInterp)
|
|
{
|
|
if (strans[tpos] == QLatin1Char('\\'))
|
|
++tpos; // escape next character
|
|
seg.append(strans[tpos]);
|
|
++tpos;
|
|
}
|
|
if (tpos == slen) {
|
|
kDebug(173) << QString::fromLatin1("Non-terminated interpolation {%1} in message {%2}.")
|
|
.arg(strans.mid(pos, tpos - pos), shortenMessage(strans));
|
|
return -1;
|
|
}
|
|
|
|
// The free segment may look like a value reference;
|
|
// in that case, record which value it would reference,
|
|
// and add verbatim to the segment list.
|
|
// Otherwise, do a normal substitution on the segment.
|
|
vref = segmentToValue(seg);
|
|
if (vref.isValid()) {
|
|
segs.append(seg);
|
|
}
|
|
else {
|
|
segs.append(substituteSimple(seg, s->scriptPlchar, true));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Append this argument to rest of the arguments.
|
|
// If the there was a single text segment and it was a proper value
|
|
// reference, add it instead of the joined segments.
|
|
// Otherwise, add the joined segments.
|
|
if (segs.size() == 1 && vref.isValid()) {
|
|
iargs.append(vref);
|
|
}
|
|
else {
|
|
iargs.append(segs.join(QString()));
|
|
}
|
|
}
|
|
tpos += ielen; // skip to first character after closing sequence
|
|
|
|
// NOTE: Why not substitute placeholders (via substituteSimple) in one
|
|
// global pass, then handle interpolations in second pass? Because then
|
|
// there is the danger of substituted text or sub-interpolations producing
|
|
// quotes and escapes themselves, which would mess up the parsing.
|
|
|
|
// Evaluate interpolation.
|
|
QString msgctxt = QString::fromUtf8(ctxt);
|
|
QString msgid = QString::fromUtf8(msg);
|
|
QString scriptError;
|
|
|
|
// s->scriptModulesToLoad will be cleared during the call.
|
|
|
|
if (!scriptError.isEmpty()) { // problem with evaluation
|
|
fallback = true; // also signal fallback
|
|
if (!scriptError.isEmpty()) {
|
|
kDebug(173) << QString::fromLatin1("Interpolation {%1} in {%2} failed: %3")
|
|
.arg(strans.mid(pos, tpos - pos), shortenMessage(strans), scriptError);
|
|
}
|
|
}
|
|
|
|
return tpos;
|
|
}
|
|
|
|
QVariant KLocalizedStringPrivate::segmentToValue (const QString &seg) const
|
|
{
|
|
const KLocalizedStringPrivateStatics *s = staticsKLSP;
|
|
QMutexLocker lock(kLocaleMutex());
|
|
|
|
// Return invalid variant if segment is either not a proper
|
|
// value reference, or the reference is out of bounds.
|
|
|
|
// Value reference must start with a special character.
|
|
if (seg.left(1) != s->scriptVachar) {
|
|
return QVariant();
|
|
}
|
|
|
|
// Reference number must start with 1-9.
|
|
// (If numstr is empty, toInt() will return 0.)
|
|
QString numstr = seg.mid(1);
|
|
if (numstr.left(1).toInt() < 1) {
|
|
return QVariant();
|
|
}
|
|
|
|
// Number must be valid and in bounds.
|
|
bool ok;
|
|
int index = numstr.toInt(&ok) - 1;
|
|
if (!ok || index >= vals.size()) {
|
|
return QVariant();
|
|
}
|
|
|
|
// Passed all hoops.
|
|
return vals.at(index);
|
|
}
|
|
|
|
static QString wrapNum (const QString &tag, const QString &numstr,
|
|
int fieldWidth, const QChar &fillChar)
|
|
{
|
|
QString optag;
|
|
if (fieldWidth != 0) {
|
|
QString fillString = KuitSemantics::escape(fillChar);
|
|
optag = QString::fromLatin1("<%1 width='%2' fill='%3'>")
|
|
.arg(tag, QString::number(fieldWidth), fillString);
|
|
} else {
|
|
optag = QString::fromLatin1("<%1>").arg(tag);
|
|
}
|
|
QString cltag = QString::fromLatin1("</%1>").arg(tag);
|
|
return optag + numstr + cltag;
|
|
}
|
|
|
|
KLocalizedString KLocalizedString::subs (int a, int fieldWidth, int base,
|
|
const QChar &fillChar) const
|
|
{
|
|
KLocalizedString kls(*this);
|
|
if (!kls.d->plural.isEmpty() && !kls.d->numberSet) {
|
|
kls.d->number = static_cast<pluraln>(abs(a));
|
|
kls.d->numberSet = true;
|
|
kls.d->numberOrd = d->args.size();
|
|
}
|
|
kls.d->args.append(wrapNum(QString::fromLatin1(KUIT_NUMINTG), QString::number(a, base),
|
|
fieldWidth, fillChar));
|
|
kls.d->vals.append(static_cast<intn>(a));
|
|
return kls;
|
|
}
|
|
|
|
KLocalizedString KLocalizedString::subs (uint a, int fieldWidth, int base,
|
|
const QChar &fillChar) const
|
|
{
|
|
KLocalizedString kls(*this);
|
|
if (!kls.d->plural.isEmpty() && !kls.d->numberSet) {
|
|
kls.d->number = static_cast<pluraln>(a);
|
|
kls.d->numberSet = true;
|
|
kls.d->numberOrd = d->args.size();
|
|
}
|
|
kls.d->args.append(wrapNum(QString::fromLatin1(KUIT_NUMINTG), QString::number(a, base),
|
|
fieldWidth, fillChar));
|
|
kls.d->vals.append(static_cast<uintn>(a));
|
|
return kls;
|
|
}
|
|
|
|
KLocalizedString KLocalizedString::subs (long a, int fieldWidth, int base,
|
|
const QChar &fillChar) const
|
|
{
|
|
KLocalizedString kls(*this);
|
|
if (!kls.d->plural.isEmpty() && !kls.d->numberSet) {
|
|
kls.d->number = static_cast<pluraln>(abs(a));
|
|
kls.d->numberSet = true;
|
|
kls.d->numberOrd = d->args.size();
|
|
}
|
|
kls.d->args.append(wrapNum(QString::fromLatin1(KUIT_NUMINTG), QString::number(a, base),
|
|
fieldWidth, fillChar));
|
|
kls.d->vals.append(static_cast<intn>(a));
|
|
return kls;
|
|
}
|
|
|
|
KLocalizedString KLocalizedString::subs (ulong a, int fieldWidth, int base,
|
|
const QChar &fillChar) const
|
|
{
|
|
KLocalizedString kls(*this);
|
|
if (!kls.d->plural.isEmpty() && !kls.d->numberSet) {
|
|
kls.d->number = static_cast<pluraln>(a);
|
|
kls.d->numberSet = true;
|
|
kls.d->numberOrd = d->args.size();
|
|
}
|
|
kls.d->args.append(wrapNum(QString::fromLatin1(KUIT_NUMINTG), QString::number(a, base),
|
|
fieldWidth, fillChar));
|
|
kls.d->vals.append(static_cast<uintn>(a));
|
|
return kls;
|
|
}
|
|
|
|
KLocalizedString KLocalizedString::subs (qlonglong a, int fieldWidth, int base,
|
|
const QChar &fillChar) const
|
|
{
|
|
KLocalizedString kls(*this);
|
|
if (!kls.d->plural.isEmpty() && !kls.d->numberSet) {
|
|
kls.d->number = static_cast<pluraln>(qAbs(a));
|
|
kls.d->numberSet = true;
|
|
kls.d->numberOrd = d->args.size();
|
|
}
|
|
kls.d->args.append(wrapNum(QString::fromLatin1(KUIT_NUMINTG), QString::number(a, base),
|
|
fieldWidth, fillChar));
|
|
kls.d->vals.append(static_cast<intn>(a));
|
|
return kls;
|
|
}
|
|
|
|
KLocalizedString KLocalizedString::subs (qulonglong a, int fieldWidth, int base,
|
|
const QChar &fillChar) const
|
|
{
|
|
KLocalizedString kls(*this);
|
|
if (!kls.d->plural.isEmpty() && !kls.d->numberSet) {
|
|
kls.d->number = static_cast<pluraln>(a);
|
|
kls.d->numberSet = true;
|
|
kls.d->numberOrd = d->args.size();
|
|
}
|
|
kls.d->args.append(wrapNum(QString::fromLatin1(KUIT_NUMINTG), QString::number(a, base),
|
|
fieldWidth, fillChar));
|
|
kls.d->vals.append(static_cast<uintn>(a));
|
|
return kls;
|
|
}
|
|
|
|
KLocalizedString KLocalizedString::subs (double a, int fieldWidth,
|
|
char format, int precision,
|
|
const QChar &fillChar) const
|
|
{
|
|
KLocalizedString kls(*this);
|
|
kls.d->args.append(wrapNum(QString::fromLatin1(KUIT_NUMREAL),
|
|
QString::number(a, format, precision),
|
|
fieldWidth, fillChar));
|
|
kls.d->vals.append(static_cast<realn>(a));
|
|
return kls;
|
|
}
|
|
|
|
KLocalizedString KLocalizedString::subs (QChar a, int fieldWidth,
|
|
const QChar &fillChar) const
|
|
{
|
|
KLocalizedString kls(*this);
|
|
kls.d->args.append(QString::fromLatin1("%1").arg(a, fieldWidth, fillChar));
|
|
kls.d->vals.append(QString(a));
|
|
return kls;
|
|
}
|
|
|
|
KLocalizedString KLocalizedString::subs (const QString &a, int fieldWidth,
|
|
const QChar &fillChar) const
|
|
{
|
|
KLocalizedString kls(*this);
|
|
// if (!KuitSemantics::mightBeRichText(a)) { ...
|
|
// Do not try to auto-escape non-rich-text alike arguments;
|
|
// breaks compatibility with 4.0. Perhaps for KDE 5?
|
|
// Perhaps bad idea alltogether (too much surprise)?
|
|
kls.d->args.append(QString::fromLatin1("%1").arg(a, fieldWidth, fillChar));
|
|
kls.d->vals.append(a);
|
|
return kls;
|
|
}
|
|
|
|
KLocalizedString KLocalizedString::inContext (const QString &key,
|
|
const QString &text) const
|
|
{
|
|
KLocalizedString kls(*this);
|
|
kls.d->dynctxt[key] = text;
|
|
return kls;
|
|
}
|
|
|
|
KLocalizedString ki18n (const char* msg)
|
|
{
|
|
return KLocalizedString(NULL, msg, NULL);
|
|
}
|
|
|
|
KLocalizedString ki18nc (const char* ctxt, const char *msg)
|
|
{
|
|
return KLocalizedString(ctxt, msg, NULL);
|
|
}
|
|
|
|
KLocalizedString ki18np (const char* singular, const char* plural)
|
|
{
|
|
return KLocalizedString(NULL, singular, plural);
|
|
}
|
|
|
|
KLocalizedString ki18ncp (const char* ctxt,
|
|
const char* singular, const char* plural)
|
|
{
|
|
return KLocalizedString(ctxt, singular, plural);
|
|
}
|
|
|
|
void KLocalizedString::notifyCatalogsUpdated (const QStringList &languages,
|
|
const QList<KCatalogName> &catalogs)
|
|
{
|
|
KLocalizedStringPrivate::notifyCatalogsUpdated(languages, catalogs);
|
|
}
|
|
|
|
void KLocalizedStringPrivate::notifyCatalogsUpdated (const QStringList &languages,
|
|
const QList<KCatalogName> &catalogs)
|
|
{
|
|
if (staticsKLSP.isDestroyed()) {
|
|
return;
|
|
}
|
|
KLocalizedStringPrivateStatics *s = staticsKLSP;
|
|
// Very important: do not the mutex here.
|
|
//QMutexLocker lock(kLocaleMutex());
|
|
|
|
// Find script modules for all included language/catalogs that have them,
|
|
// and remember their paths.
|
|
// A more specific module may reference the calls from a less specific,
|
|
// and the catalog list is ordered from more to less specific. Therefore,
|
|
// work on reversed list of catalogs.
|
|
foreach (const QString &lang, languages) {
|
|
for (int i = catalogs.size() - 1; i >= 0; --i) {
|
|
const KCatalogName &cat(catalogs[i]);
|
|
|
|
// Assemble module's relative path.
|
|
QString modrpath = lang + QLatin1Char('/') + s->scriptDir + QLatin1Char('/')
|
|
+ cat.name + QLatin1Char('/') + cat.name + QLatin1String(".js");
|
|
|
|
// Try to find this module.
|
|
QString modapath = KStandardDirs::locate("locale", modrpath);
|
|
|
|
// If the module exists and hasn't been already included.
|
|
if ( !modapath.isEmpty()
|
|
&& !s->scriptModules[lang].contains(cat.name))
|
|
{
|
|
// Indicate that the module has been considered.
|
|
s->scriptModules[lang].append(cat.name);
|
|
|
|
// Store the absolute path and language of the module,
|
|
// to load on next script evaluation.
|
|
QStringList mod;
|
|
mod.append(modapath);
|
|
mod.append(lang);
|
|
s->scriptModulesToLoad.append(mod);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create visual formatters for each new language.
|
|
foreach (const QString &lang, languages) {
|
|
if (!s->formatters.contains(lang)) {
|
|
s->formatters.insert(lang, new KuitSemantics(lang));
|
|
}
|
|
}
|
|
}
|