summaryrefslogtreecommitdiffstats
path: root/tools/linguist
diff options
context:
space:
mode:
Diffstat (limited to 'tools/linguist')
-rw-r--r--tools/linguist/lconvert/main.cpp4
-rw-r--r--tools/linguist/linguist/messagemodel.cpp21
-rw-r--r--tools/linguist/lrelease/main.cpp83
-rw-r--r--tools/linguist/lupdate/cpp.cpp102
-rw-r--r--tools/linguist/lupdate/merge.cpp85
-rw-r--r--tools/linguist/lupdate/qscript.cpp2
-rw-r--r--tools/linguist/phrasebooks/french.qph20
-rw-r--r--tools/linguist/shared/qm.cpp47
-rw-r--r--tools/linguist/shared/translator.cpp189
-rw-r--r--tools/linguist/shared/translator.h15
-rw-r--r--tools/linguist/shared/translatormessage.cpp34
-rw-r--r--tools/linguist/shared/translatormessage.h31
12 files changed, 360 insertions, 273 deletions
diff --git a/tools/linguist/lconvert/main.cpp b/tools/linguist/lconvert/main.cpp
index 6f5f86a..7807761 100644
--- a/tools/linguist/lconvert/main.cpp
+++ b/tools/linguist/lconvert/main.cpp
@@ -239,7 +239,7 @@ int main(int argc, char *argv[])
qWarning() << qPrintable(cd.error());
return 2;
}
- Translator::reportDuplicates(tr.resolveDuplicates(), inFiles[0].name, verbose);
+ tr.reportDuplicates(tr.resolveDuplicates(), inFiles[0].name, verbose);
for (int i = 1; i < inFiles.size(); ++i) {
Translator tr2;
@@ -247,7 +247,7 @@ int main(int argc, char *argv[])
qWarning() << qPrintable(cd.error());
return 2;
}
- Translator::reportDuplicates(tr2.resolveDuplicates(), inFiles[i].name, verbose);
+ tr2.reportDuplicates(tr2.resolveDuplicates(), inFiles[i].name, verbose);
for (int j = 0; j < tr2.messageCount(); ++j)
tr.replaceSorted(tr2.message(j));
}
diff --git a/tools/linguist/linguist/messagemodel.cpp b/tools/linguist/linguist/messagemodel.cpp
index e6bb9af..024fd91 100644
--- a/tools/linguist/linguist/messagemodel.cpp
+++ b/tools/linguist/linguist/messagemodel.cpp
@@ -209,20 +209,29 @@ bool DataModel::load(const QString &fileName, bool *langGuessed, QWidget *parent
return false;
}
- QSet<TranslatorMessagePtr> dupes = tor.resolveDuplicates();
- if (!dupes.isEmpty()) {
+ Translator::Duplicates dupes = tor.resolveDuplicates();
+ if (!dupes.byId.isEmpty() || !dupes.byContents.isEmpty()) {
QString err = tr("<qt>Duplicate messages found in '%1':").arg(Qt::escape(fileName));
int numdups = 0;
- foreach (const TranslatorMessagePtr &msg, dupes) {
+ foreach (int i, dupes.byId) {
+ if (++numdups >= 5) {
+ err += tr("<p>[more duplicates omitted]");
+ goto doWarn;
+ }
+ err += tr("<p>* ID: %1").arg(Qt::escape(tor.message(i).id()));
+ }
+ foreach (int j, dupes.byContents) {
+ const TranslatorMessage &msg = tor.message(j);
if (++numdups >= 5) {
err += tr("<p>[more duplicates omitted]");
break;
}
err += tr("<p>* Context: %1<br>* Source: %2")
- .arg(Qt::escape(msg->context()), Qt::escape(msg->sourceText()));
- if (!msg->comment().isEmpty())
- err += tr("<br>* Comment: %3").arg(Qt::escape(msg->comment()));
+ .arg(Qt::escape(msg.context()), Qt::escape(msg.sourceText()));
+ if (!msg.comment().isEmpty())
+ err += tr("<br>* Comment: %3").arg(Qt::escape(msg.comment()));
}
+ doWarn:
QMessageBox::warning(parent, QObject::tr("Qt Linguist"), err);
}
diff --git a/tools/linguist/lrelease/main.cpp b/tools/linguist/lrelease/main.cpp
index 2867849..742c2e6 100644
--- a/tools/linguist/lrelease/main.cpp
+++ b/tools/linguist/lrelease/main.cpp
@@ -79,6 +79,9 @@ static void printUsage()
" -removeidentical\n"
" If the translated text is the same as\n"
" the source text, do not include the message\n"
+ " -markuntranslated <prefix>\n"
+ " If a message has no real translation, use the source text\n"
+ " prefixed with the given string instead\n"
" -silent\n"
" Do not explain what is being done\n"
" -version\n"
@@ -100,15 +103,14 @@ static bool loadTsFile(Translator &tor, const QString &tsFileName, bool /* verbo
}
static bool releaseTranslator(Translator &tor, const QString &qmFileName,
- bool verbose, bool ignoreUnfinished,
- bool removeIdentical, bool idBased, TranslatorSaveMode mode)
+ ConversionData &cd, bool removeIdentical)
{
- Translator::reportDuplicates(tor.resolveDuplicates(), qmFileName, verbose);
+ tor.reportDuplicates(tor.resolveDuplicates(), qmFileName, cd.isVerbose());
- if (verbose)
+ if (cd.isVerbose())
printOut(QCoreApplication::tr( "Updating '%1'...\n").arg(qmFileName));
if (removeIdentical) {
- if ( verbose )
+ if (cd.isVerbose())
printOut(QCoreApplication::tr( "Removing translations equal to source text in '%1'...\n").arg(qmFileName));
tor.stripIdenticalSourceTranslations();
}
@@ -120,12 +122,7 @@ static bool releaseTranslator(Translator &tor, const QString &qmFileName,
return false;
}
- ConversionData cd;
tor.normalizeTranslations(cd);
- cd.m_verbose = verbose;
- cd.m_ignoreUnfinished = ignoreUnfinished;
- cd.m_idBased = idBased;
- cd.m_saveMode = mode;
bool ok = tor.release(&file, cd);
file.close();
@@ -139,11 +136,11 @@ static bool releaseTranslator(Translator &tor, const QString &qmFileName,
return true;
}
-static bool releaseTsFile(const QString& tsFileName, bool verbose,
- bool ignoreUnfinished, bool removeIdentical, bool idBased, TranslatorSaveMode mode)
+static bool releaseTsFile(const QString& tsFileName,
+ ConversionData &cd, bool removeIdentical)
{
Translator tor;
- if (!loadTsFile(tor, tsFileName, verbose))
+ if (!loadTsFile(tor, tsFileName, cd.isVerbose()))
return false;
QString qmFileName = tsFileName;
@@ -155,7 +152,7 @@ static bool releaseTsFile(const QString& tsFileName, bool verbose,
}
qmFileName += QLatin1String(".qm");
- return releaseTranslator(tor, qmFileName, verbose, ignoreUnfinished, removeIdentical, idBased, mode);
+ return releaseTranslator(tor, qmFileName, cd, removeIdentical);
}
int main(int argc, char **argv)
@@ -166,37 +163,40 @@ int main(int argc, char **argv)
if (translator.load(QLatin1String("lrelease_") + QLocale::system().name()))
app.installTranslator(&translator);
- bool verbose = true; // the default is true starting with Qt 4.2
- bool ignoreUnfinished = false;
- bool idBased = false;
- // the default mode is SaveEverything starting with Qt 4.2
- TranslatorSaveMode mode = SaveEverything;
+ ConversionData cd;
+ cd.m_verbose = true; // the default is true starting with Qt 4.2
bool removeIdentical = false;
Translator tor;
+ QStringList inputFiles;
QString outputFile;
- int numFiles = 0;
for (int i = 1; i < argc; ++i) {
if (args[i] == QLatin1String("-compress")) {
- mode = SaveStripped;
+ cd.m_saveMode = SaveStripped;
continue;
} else if (args[i] == QLatin1String("-idbased")) {
- idBased = true;
+ cd.m_idBased = true;
continue;
} else if (args[i] == QLatin1String("-nocompress")) {
- mode = SaveEverything;
+ cd.m_saveMode = SaveEverything;
continue;
} else if (args[i] == QLatin1String("-removeidentical")) {
removeIdentical = true;
continue;
} else if (args[i] == QLatin1String("-nounfinished")) {
- ignoreUnfinished = true;
+ cd.m_ignoreUnfinished = true;
continue;
+ } else if (args[i] == QLatin1String("-markuntranslated")) {
+ if (i == argc - 1) {
+ printUsage();
+ return 1;
+ }
+ cd.m_unTrPrefix = args[++i];
} else if (args[i] == QLatin1String("-silent")) {
- verbose = false;
+ cd.m_verbose = false;
continue;
} else if (args[i] == QLatin1String("-verbose")) {
- verbose = true;
+ cd.m_verbose = true;
continue;
} else if (args[i] == QLatin1String("-version")) {
printOut(QCoreApplication::tr( "lrelease version %1\n").arg(QLatin1String(QT_VERSION_STR)) );
@@ -206,41 +206,37 @@ int main(int argc, char **argv)
printUsage();
return 1;
}
- i++;
- outputFile = args[i];
+ outputFile = args[++i];
} else if (args[i] == QLatin1String("-help")) {
printUsage();
return 0;
- } else if (args[i][0] == QLatin1Char('-')) {
+ } else if (args[i].startsWith(QLatin1Char('-'))) {
printUsage();
return 1;
} else {
- numFiles++;
+ inputFiles << args[i];
}
}
- if (numFiles == 0) {
+ if (inputFiles.isEmpty()) {
printUsage();
return 1;
}
- for (int i = 1; i < argc; ++i) {
- if (args[i][0] == QLatin1Char('-') || args[i] == outputFile)
- continue;
-
- if (args[i].endsWith(QLatin1String(".pro"), Qt::CaseInsensitive)
- || args[i].endsWith(QLatin1String(".pri"), Qt::CaseInsensitive)) {
+ foreach (const QString &inputFile, inputFiles) {
+ if (inputFile.endsWith(QLatin1String(".pro"), Qt::CaseInsensitive)
+ || inputFile.endsWith(QLatin1String(".pri"), Qt::CaseInsensitive)) {
QHash<QByteArray, QStringList> varMap;
- bool ok = evaluateProFile(args[i], verbose, &varMap );
+ bool ok = evaluateProFile(inputFile, cd.isVerbose(), &varMap);
if (ok) {
QStringList translations = varMap.value("TRANSLATIONS");
if (translations.isEmpty()) {
qWarning("lrelease warning: Met no 'TRANSLATIONS' entry in"
" project file '%s'\n",
- qPrintable(args[i]));
+ qPrintable(inputFile));
} else {
foreach (const QString &trans, translations)
- if (!releaseTsFile(trans, verbose, ignoreUnfinished, removeIdentical, idBased, mode))
+ if (!releaseTsFile(trans, cd, removeIdentical))
return 1;
}
} else {
@@ -251,18 +247,17 @@ int main(int argc, char **argv)
}
} else {
if (outputFile.isEmpty()) {
- if (!releaseTsFile(args[i], verbose, ignoreUnfinished, removeIdentical, idBased, mode))
+ if (!releaseTsFile(inputFile, cd, removeIdentical))
return 1;
} else {
- if (!loadTsFile(tor, args[i], verbose))
+ if (!loadTsFile(tor, inputFile, cd.isVerbose()))
return 1;
}
}
}
if (!outputFile.isEmpty())
- return releaseTranslator(tor, outputFile, verbose, ignoreUnfinished,
- removeIdentical, idBased, mode) ? 0 : 1;
+ return releaseTranslator(tor, outputFile, cd, removeIdentical) ? 0 : 1;
return 0;
}
diff --git a/tools/linguist/lupdate/cpp.cpp b/tools/linguist/lupdate/cpp.cpp
index 4d89156..443abd0 100644
--- a/tools/linguist/lupdate/cpp.cpp
+++ b/tools/linguist/lupdate/cpp.cpp
@@ -135,6 +135,11 @@ struct Namespace {
// Nested classes may be forward-declared inside a definition, and defined in another file.
// The latter will detach the class' child list, so clones need a backlink to the original
// definition (either one in case of multiple definitions).
+ // Namespaces can have tr() functions as well, so we need to track parent definitions for
+ // them as well. The complication is that we may have to deal with a forrest instead of
+ // a tree - in that case the parent will be arbitrary. However, it seem likely that
+ // Q_DECLARE_TR_FUNCTIONS would be used either in "class-like" namespaces with a central
+ // header or only locally in a file.
Namespace *classDef;
QString trQualification;
@@ -256,17 +261,20 @@ private:
bool qualifyOneCallbackUsing(const Namespace *ns, void *context) const;
bool qualifyOne(const NamespaceList &namespaces, int nsCnt, const HashString &segment,
NamespaceList *resolved) const;
- bool fullyQualify(const NamespaceList &namespaces, const QList<HashString> &segments,
- bool isDeclaration,
+ bool fullyQualify(const NamespaceList &namespaces, int nsCnt,
+ const QList<HashString> &segments, bool isDeclaration,
NamespaceList *resolved, QStringList *unresolved) const;
- bool fullyQualify(const NamespaceList &namespaces, const QString &segments,
- bool isDeclaration,
+ bool fullyQualify(const NamespaceList &namespaces,
+ const QList<HashString> &segments, bool isDeclaration,
+ NamespaceList *resolved, QStringList *unresolved) const;
+ bool fullyQualify(const NamespaceList &namespaces,
+ const QString &segments, bool isDeclaration,
NamespaceList *resolved, QStringList *unresolved) const;
bool findNamespaceCallback(const Namespace *ns, void *context) const;
const Namespace *findNamespace(const NamespaceList &namespaces, int nsCount = -1) const;
void enterNamespace(NamespaceList *namespaces, const HashString &name);
void truncateNamespaces(NamespaceList *namespaces, int lenght);
- Namespace *modifyNamespace(NamespaceList *namespaces, bool tryOrigin = true);
+ Namespace *modifyNamespace(NamespaceList *namespaces, bool haveLast = true);
enum {
Tok_Eof, Tok_class, Tok_friend, Tok_namespace, Tok_using, Tok_return,
@@ -780,7 +788,7 @@ uint CppParser::getToken()
if (yyCh == EOF) {
qWarning("%s:%d: Unterminated C++ comment\n",
qPrintable(yyFileName), yyLineNo);
- return Tok_Comment;
+ break;
}
*ptr++ = yyCh;
@@ -958,7 +966,7 @@ void CppParser::loadState(const SavedState *state)
pendingContext = state->pendingContext;
}
-Namespace *CppParser::modifyNamespace(NamespaceList *namespaces, bool tryOrigin)
+Namespace *CppParser::modifyNamespace(NamespaceList *namespaces, bool haveLast)
{
Namespace *pns, *ns = &results->rootNamespace;
for (int i = 1; i < namespaces->count(); ++i) {
@@ -966,7 +974,7 @@ Namespace *CppParser::modifyNamespace(NamespaceList *namespaces, bool tryOrigin)
if (!(ns = pns->children.value(namespaces->at(i)))) {
do {
ns = new Namespace;
- if (tryOrigin)
+ if (haveLast || i < namespaces->count() - 1)
if (const Namespace *ons = findNamespace(*namespaces, i + 1))
ns->classDef = ons->classDef;
pns->children.insert(namespaces->at(i), ns);
@@ -1052,7 +1060,18 @@ bool CppParser::qualifyOneCallbackOwn(const Namespace *ns, void *context) const
}
QHash<HashString, NamespaceList>::ConstIterator nsai = ns->aliases.constFind(data->segment);
if (nsai != ns->aliases.constEnd()) {
- *data->resolved = *nsai;
+ const NamespaceList &nsl = *nsai;
+ if (nsl.last().value().isEmpty()) { // Delayed alias resolution
+ NamespaceList &nslIn = *const_cast<NamespaceList *>(&nsl);
+ nslIn.removeLast();
+ NamespaceList nslOut;
+ if (!fullyQualify(data->namespaces, data->nsCount, nslIn, false, &nslOut, 0)) {
+ const_cast<Namespace *>(ns)->aliases.remove(data->segment);
+ return false;
+ }
+ nslIn = nslOut;
+ }
+ *data->resolved = nsl;
return true;
}
return false;
@@ -1081,8 +1100,8 @@ bool CppParser::qualifyOne(const NamespaceList &namespaces, int nsCnt, const Has
return visitNamespace(namespaces, nsCnt, &CppParser::qualifyOneCallbackUsing, &data);
}
-bool CppParser::fullyQualify(const NamespaceList &namespaces, const QList<HashString> &segments,
- bool isDeclaration,
+bool CppParser::fullyQualify(const NamespaceList &namespaces, int nsCnt,
+ const QList<HashString> &segments, bool isDeclaration,
NamespaceList *resolved, QStringList *unresolved) const
{
int nsIdx;
@@ -1099,7 +1118,7 @@ bool CppParser::fullyQualify(const NamespaceList &namespaces, const QList<HashSt
nsIdx = 0;
} else {
initSegIdx = 0;
- nsIdx = namespaces.count() - 1;
+ nsIdx = nsCnt - 1;
}
do {
@@ -1122,8 +1141,16 @@ bool CppParser::fullyQualify(const NamespaceList &namespaces, const QList<HashSt
return false;
}
-bool CppParser::fullyQualify(const NamespaceList &namespaces, const QString &quali,
- bool isDeclaration,
+bool CppParser::fullyQualify(const NamespaceList &namespaces,
+ const QList<HashString> &segments, bool isDeclaration,
+ NamespaceList *resolved, QStringList *unresolved) const
+{
+ return fullyQualify(namespaces, namespaces.count(),
+ segments, isDeclaration, resolved, unresolved);
+}
+
+bool CppParser::fullyQualify(const NamespaceList &namespaces,
+ const QString &quali, bool isDeclaration,
NamespaceList *resolved, QStringList *unresolved) const
{
static QString strColons(QLatin1String("::"));
@@ -1633,9 +1660,8 @@ void CppParser::parseInternal(ConversionData &cd, QSet<QString> &inclusions)
}
if (fullName.isEmpty())
break;
- NamespaceList nsl;
- if (fullyQualify(namespaces, fullName, false, &nsl, 0))
- modifyNamespace(&namespaces, false)->aliases[ns] = nsl;
+ fullName.append(HashString(QString())); // Mark as unresolved
+ modifyNamespace(&namespaces)->aliases[ns] = fullName;
}
} else if (yyTok == Tok_LeftBrace) {
// Anonymous namespace
@@ -1661,7 +1687,7 @@ void CppParser::parseInternal(ConversionData &cd, QSet<QString> &inclusions)
}
NamespaceList nsl;
if (fullyQualify(namespaces, fullName, false, &nsl, 0))
- modifyNamespace(&namespaces, false)->usings << HashStringList(nsl);
+ modifyNamespace(&namespaces)->usings << HashStringList(nsl);
} else {
QList<HashString> fullName;
if (yyTok == Tok_ColonColon)
@@ -1676,9 +1702,13 @@ void CppParser::parseInternal(ConversionData &cd, QSet<QString> &inclusions)
}
if (fullName.isEmpty())
break;
- NamespaceList nsl;
- if (fullyQualify(namespaces, fullName, false, &nsl, 0))
- modifyNamespace(&namespaces, true)->aliases[nsl.last()] = nsl;
+ // using-declarations cannot rename classes, so the last element of
+ // fullName is already the resolved name we actually want.
+ // As we do no resolution here, we'll collect useless usings of data
+ // members and methods as well. This is no big deal.
+ HashString &ns = fullName.last();
+ fullName.append(HashString(QString())); // Mark as unresolved
+ modifyNamespace(&namespaces)->aliases[ns] = fullName;
}
break;
case Tok_tr:
@@ -1853,29 +1883,25 @@ void CppParser::parseInternal(ConversionData &cd, QSet<QString> &inclusions)
case Tok_trid:
if (!tor)
goto case_default;
- if (sourcetext.isEmpty()) {
- yyTok = getToken();
- } else {
- if (!msgid.isEmpty())
- qWarning("%s:%d: //= cannot be used with qtTrId() / QT_TRID_NOOP(). Ignoring\n",
- qPrintable(yyFileName), yyLineNo);
- //utf8 = false; // Maybe use //%% or something like that
- line = yyLineNo;
- yyTok = getToken();
- if (match(Tok_LeftParen) && matchString(&msgid) && !msgid.isEmpty()) {
- bool plural = match(Tok_Comma);
- recordMessage(line, QString(), sourcetext, QString(), extracomment,
- msgid, extra, false, plural);
- }
- sourcetext.clear();
+ if (!msgid.isEmpty())
+ qWarning("%s:%d: //= cannot be used with qtTrId() / QT_TRID_NOOP(). Ignoring\n",
+ qPrintable(yyFileName), yyLineNo);
+ //utf8 = false; // Maybe use //%% or something like that
+ line = yyLineNo;
+ yyTok = getToken();
+ if (match(Tok_LeftParen) && matchString(&msgid) && !msgid.isEmpty()) {
+ bool plural = match(Tok_Comma);
+ recordMessage(line, QString(), sourcetext, QString(), extracomment,
+ msgid, extra, false, plural);
}
+ sourcetext.clear();
extracomment.clear();
msgid.clear();
extra.clear();
break;
case Tok_Q_DECLARE_TR_FUNCTIONS:
if (getMacroArgs()) {
- Namespace *ns = modifyNamespace(&namespaces, true);
+ Namespace *ns = modifyNamespace(&namespaces);
ns->hasTrFunctions = true;
ns->trQualification = yyWord;
ns->trQualification.detach();
@@ -1883,7 +1909,7 @@ void CppParser::parseInternal(ConversionData &cd, QSet<QString> &inclusions)
yyTok = getToken();
break;
case Tok_Q_OBJECT:
- modifyNamespace(&namespaces, true)->hasTrFunctions = true;
+ modifyNamespace(&namespaces)->hasTrFunctions = true;
yyTok = getToken();
break;
case Tok_Ident:
diff --git a/tools/linguist/lupdate/merge.cpp b/tools/linguist/lupdate/merge.cpp
index b5f77cd..fa0dd3d 100644
--- a/tools/linguist/lupdate/merge.cpp
+++ b/tools/linguist/lupdate/merge.cpp
@@ -247,10 +247,8 @@ int applyNumberHeuristic(Translator &tor)
t = translated.find(zeroKey((*u).sourceText()));
if (t != translated.end() && !t.key().isEmpty()
&& t->sourceText() != u->sourceText()) {
- TranslatorMessage m = *u;
- m.setTranslation(translationAttempt(t->translation(), t->sourceText(),
- u->sourceText()));
- tor.replace(m);
+ u->setTranslation(translationAttempt(t->translation(), t->sourceText(),
+ u->sourceText()));
inserted++;
}
}
@@ -305,9 +303,7 @@ int applySameTextHeuristic(Translator &tor)
QString key = u->sourceText();
t = translated.find(key);
if (t != translated.end()) {
- TranslatorMessage m = *u;
- m.setTranslations(t->translations());
- tor.replace(m);
+ u->setTranslations(t->translations());
++inserted;
}
}
@@ -345,15 +341,17 @@ Translator merge(const Translator &tor, const Translator &virginTor,
foreach (TranslatorMessage m, tor.messages()) {
TranslatorMessage::Type newType = TranslatorMessage::Finished;
- if (m.sourceText().isEmpty()) {
+ if (m.sourceText().isEmpty() && m.id().isEmpty()) {
// context/file comment
TranslatorMessage mv = virginTor.find(m.context());
if (!mv.isNull())
m.setComment(mv.comment());
} else {
- TranslatorMessage mv = virginTor.find(m.context(), m.sourceText(), m.comment());
- if (mv.isNull()) {
+ TranslatorMessage mv;
+ int mvi = virginTor.find(m);
+ if (mvi < 0) {
if (!(options & HeuristicSimilarText)) {
+ makeObsolete:
newType = TranslatorMessage::Obsolete;
if (m.type() != TranslatorMessage::Obsolete)
obsoleted++;
@@ -362,10 +360,7 @@ Translator merge(const Translator &tor, const Translator &virginTor,
mv = virginTor.find(m.context(), m.comment(), m.allReferences());
if (mv.isNull()) {
// did not find it in the virgin, mark it as obsolete
- newType = TranslatorMessage::Obsolete;
- if (m.type() != TranslatorMessage::Obsolete)
- obsoleted++;
- m.clearReferences();
+ goto makeObsolete;
} else {
// Do not just accept it if its on the same line number,
// but different source text.
@@ -380,6 +375,7 @@ Translator merge(const Translator &tor, const Translator &virginTor,
++similarTextHeuristicCount;
neww++;
+ outdateSource:
m.setOldSourceText(m.sourceText());
m.setSourceText(mv.sourceText());
const QString &oldpluralsource = m.extra(QLatin1String("po-msgid_plural"));
@@ -387,38 +383,45 @@ Translator merge(const Translator &tor, const Translator &virginTor,
m.setExtra(QLatin1String("po-old_msgid_plural"), oldpluralsource);
m.unsetExtra(QLatin1String("po-msgid_plural"));
}
- m.setReferences(mv.allReferences()); // Update secondary references
- m.setPlural(mv.isPlural());
- m.setUtf8(mv.isUtf8());
- m.setExtraComment(mv.extraComment());
+ goto copyAttribs; // Update secondary references
} else {
// The virgin and vernacular sourceTexts are so
// different that we could not find it.
- newType = TranslatorMessage::Obsolete;
- if (m.type() != TranslatorMessage::Obsolete)
- obsoleted++;
- m.clearReferences();
+ goto makeObsolete;
}
}
}
} else {
- switch (m.type()) {
- case TranslatorMessage::Finished:
- default:
- if (m.isPlural() == mv.isPlural()) {
- newType = TranslatorMessage::Finished;
- } else {
- newType = TranslatorMessage::Unfinished;
- }
+ mv = virginTor.message(mvi);
+ if (!mv.id().isEmpty()
+ && (mv.context() != m.context()
+ || mv.sourceText() != m.sourceText()
+ || mv.comment() != m.comment())) {
known++;
- break;
- case TranslatorMessage::Unfinished:
newType = TranslatorMessage::Unfinished;
- known++;
- break;
- case TranslatorMessage::Obsolete:
- newType = TranslatorMessage::Unfinished;
- neww++;
+ m.setContext(mv.context());
+ m.setComment(mv.comment());
+ if (mv.sourceText() != m.sourceText())
+ goto outdateSource;
+ } else {
+ switch (m.type()) {
+ case TranslatorMessage::Finished:
+ default:
+ if (m.isPlural() == mv.isPlural()) {
+ newType = TranslatorMessage::Finished;
+ } else {
+ newType = TranslatorMessage::Unfinished;
+ }
+ known++;
+ break;
+ case TranslatorMessage::Unfinished:
+ newType = TranslatorMessage::Unfinished;
+ known++;
+ break;
+ case TranslatorMessage::Obsolete:
+ newType = TranslatorMessage::Unfinished;
+ neww++;
+ }
}
// Always get the filename and linenumber info from the
@@ -426,10 +429,12 @@ Translator merge(const Translator &tor, const Translator &virginTor,
// This should also enable us to read a file that does not
// have the <location> element.
// why not use operator=()? Because it overwrites e.g. userData.
+ copyAttribs:
m.setReferences(mv.allReferences());
m.setPlural(mv.isPlural());
m.setUtf8(mv.isUtf8());
m.setExtraComment(mv.extraComment());
+ m.setId(mv.id());
}
}
@@ -442,11 +447,11 @@ Translator merge(const Translator &tor, const Translator &virginTor,
vernacular translator.
*/
foreach (const TranslatorMessage &mv, virginTor.messages()) {
- if (mv.sourceText().isEmpty()) {
+ if (mv.sourceText().isEmpty() && mv.id().isEmpty()) {
if (tor.contains(mv.context()))
continue;
} else {
- if (tor.contains(mv.context(), mv.sourceText(), mv.comment()))
+ if (tor.find(mv) >= 0)
continue;
if (options & HeuristicSimilarText) {
TranslatorMessage m = tor.find(mv.context(), mv.comment(), mv.allReferences());
@@ -460,7 +465,7 @@ Translator merge(const Translator &tor, const Translator &virginTor,
outTor.append(mv);
else
outTor.appendSorted(mv);
- if (!mv.sourceText().isEmpty())
+ if (!mv.sourceText().isEmpty() || !mv.id().isEmpty())
++neww;
}
diff --git a/tools/linguist/lupdate/qscript.cpp b/tools/linguist/lupdate/qscript.cpp
index 4600656..6c94588 100644
--- a/tools/linguist/lupdate/qscript.cpp
+++ b/tools/linguist/lupdate/qscript.cpp
@@ -776,7 +776,7 @@ static void recordMessage(
fileName, lineNo, QStringList(),
TranslatorMessage::Unfinished, plural);
msg.setExtraComment(extracomment.simplified());
- tor->replace(msg);
+ tor->extend(msg);
}
diff --git a/tools/linguist/phrasebooks/french.qph b/tools/linguist/phrasebooks/french.qph
index 9440345..a34effe 100644
--- a/tools/linguist/phrasebooks/french.qph
+++ b/tools/linguist/phrasebooks/french.qph
@@ -1346,4 +1346,24 @@
<source>Select All</source>
<target>Sélectionner tout</target>
</phrase>
+<phrase>
+ <source>Cannot create directory: %1</source>
+ <target>Impossible de créer le répertoire : %1</target>
+</phrase>
+<phrase>
+ <source>&amp;Case sensitive</source>
+ <target>&amp;Sensible à la casse</target>
+</phrase>
+<phrase>
+ <source>Whole &amp;words</source>
+ <target>M&amp;ots complets</target>
+</phrase>
+<phrase>
+ <source>Title:</source>
+ <target>Titre :</target>
+</phrase>
+<phrase>
+ <source>Fonts</source>
+ <target>Polices</target>
+</phrase>
</QPH>
diff --git a/tools/linguist/shared/qm.cpp b/tools/linguist/shared/qm.cpp
index 317a07e..99aedef 100644
--- a/tools/linguist/shared/qm.cpp
+++ b/tools/linguist/shared/qm.cpp
@@ -172,8 +172,8 @@ public:
bool save(QIODevice *iod);
- void insert(const TranslatorMessage &msg, bool forceComment);
- void insertIdBased(const TranslatorMessage &message);
+ void insert(const TranslatorMessage &msg, const QStringList &tlns, bool forceComment);
+ void insertIdBased(const TranslatorMessage &message, const QStringList &tlns);
void squeeze(TranslatorSaveMode mode);
@@ -186,7 +186,8 @@ private:
// on turn should be the same as passed to the actual tr(...) calls
QByteArray originalBytes(const QString &str, bool isUtf8) const;
- void insertInternal(const TranslatorMessage &message, bool forceComment, bool isUtf8);
+ void insertInternal(const TranslatorMessage &message, const QStringList &tlns,
+ bool forceComment, bool isUtf8);
static Prefix commonPrefix(const ByteTranslatorMessage &m1, const ByteTranslatorMessage &m2);
@@ -413,12 +414,13 @@ void Releaser::squeeze(TranslatorSaveMode mode)
}
}
-void Releaser::insertInternal(const TranslatorMessage &message, bool forceComment, bool isUtf8)
+void Releaser::insertInternal(const TranslatorMessage &message, const QStringList &tlns,
+ bool forceComment, bool isUtf8)
{
ByteTranslatorMessage bmsg(originalBytes(message.context(), isUtf8),
originalBytes(message.sourceText(), isUtf8),
originalBytes(message.comment(), isUtf8),
- message.translations());
+ tlns);
if (!forceComment) {
ByteTranslatorMessage bmsg2(
bmsg.context(), bmsg.sourceText(), QByteArray(""), bmsg.translations());
@@ -430,20 +432,15 @@ void Releaser::insertInternal(const TranslatorMessage &message, bool forceCommen
m_messages.insert(bmsg, 0);
}
-void Releaser::insert(const TranslatorMessage &message, bool forceComment)
+void Releaser::insert(const TranslatorMessage &message, const QStringList &tlns, bool forceComment)
{
- insertInternal(message, forceComment, message.isUtf8());
+ insertInternal(message, tlns, forceComment, message.isUtf8());
if (message.isUtf8() && message.isNonUtf8())
- insertInternal(message, forceComment, false);
+ insertInternal(message, tlns, forceComment, false);
}
-void Releaser::insertIdBased(const TranslatorMessage &message)
+void Releaser::insertIdBased(const TranslatorMessage &message, const QStringList &tlns)
{
- QStringList tlns = message.translations();
- if (message.type() == TranslatorMessage::Unfinished)
- for (int i = 0; i < tlns.size(); ++i)
- if (tlns.at(i).isEmpty())
- tlns[i] = message.sourceText();
ByteTranslatorMessage bmsg("", originalBytes(message.id(), false), "", tlns);
m_messages.insert(bmsg, 0);
}
@@ -688,6 +685,16 @@ bool loadQM(Translator &translator, QIODevice &dev, ConversionData &cd)
+static bool containsStripped(const Translator &translator, const TranslatorMessage &msg)
+{
+ foreach (const TranslatorMessage &tmsg, translator.messages())
+ if (tmsg.sourceText() == msg.sourceText()
+ && tmsg.context() == msg.context()
+ && tmsg.comment().isEmpty())
+ return true;
+ return false;
+}
+
static bool saveQM(const Translator &translator, QIODevice &dev, ConversionData &cd)
{
Releaser releaser;
@@ -725,10 +732,16 @@ static bool saveQM(const Translator &translator, QIODevice &dev, ConversionData
} else {
++finished;
}
+ QStringList tlns = msg.translations();
+ if (msg.type() == TranslatorMessage::Unfinished
+ && (cd.m_idBased || !cd.m_unTrPrefix.isEmpty()))
+ for (int j = 0; j < tlns.size(); ++j)
+ if (tlns.at(j).isEmpty())
+ tlns[j] = cd.m_unTrPrefix + msg.sourceText();
if (cd.m_idBased) {
if (!msg.context().isEmpty() || !msg.comment().isEmpty())
++droppedData;
- releaser.insertIdBased(msg);
+ releaser.insertIdBased(msg, tlns);
} else {
// Drop the comment in (context, sourceText, comment),
// unless the context is empty,
@@ -738,8 +751,8 @@ static bool saveQM(const Translator &translator, QIODevice &dev, ConversionData
bool forceComment =
msg.comment().isEmpty()
|| msg.context().isEmpty()
- || translator.contains(msg.context(), msg.sourceText(), QString());
- releaser.insert(msg, forceComment);
+ || containsStripped(translator, msg);
+ releaser.insert(msg, tlns, forceComment);
}
}
}
diff --git a/tools/linguist/shared/translator.cpp b/tools/linguist/shared/translator.cpp
index cd670cc..bc27daf 100644
--- a/tools/linguist/shared/translator.cpp
+++ b/tools/linguist/shared/translator.cpp
@@ -80,18 +80,9 @@ QList<Translator::FileFormat> &Translator::registeredFileFormats()
return theFormats;
}
-void Translator::replace(const TranslatorMessage &msg)
-{
- int index = m_messages.indexOf(msg);
- if (index == -1)
- m_messages.append(msg);
- else
- m_messages[index] = msg;
-}
-
void Translator::replaceSorted(const TranslatorMessage &msg)
{
- int index = m_messages.indexOf(msg);
+ int index = find(msg);
if (index == -1)
appendSorted(msg);
else
@@ -100,7 +91,7 @@ void Translator::replaceSorted(const TranslatorMessage &msg)
void Translator::extend(const TranslatorMessage &msg)
{
- int index = m_messages.indexOf(msg);
+ int index = find(msg);
if (index == -1) {
m_messages.append(msg);
} else {
@@ -145,7 +136,7 @@ void Translator::appendSorted(const TranslatorMessage &msg)
int prevLine = 0;
int curIdx = 0;
foreach (const TranslatorMessage &mit, m_messages) {
- bool sameFile = mit.fileName() == msg.fileName();
+ bool sameFile = mit.fileName() == msg.fileName() && mit.context() == msg.context();
int curLine;
if (sameFile && (curLine = mit.lineNumber()) >= prevLine) {
if (msgLine >= prevLine && msgLine < curLine) {
@@ -311,19 +302,21 @@ bool Translator::release(QFile *iod, ConversionData &cd) const
return false;
}
-bool Translator::contains(const QString &context,
- const QString &sourceText, const QString &comment) const
-{
- return m_messages.contains(TranslatorMessage(context, sourceText, comment,
- QString(), QString(), 0));
-}
-
-TranslatorMessage Translator::find(const QString &context,
- const QString &sourceText, const QString &comment) const
+int Translator::find(const TranslatorMessage &msg) const
{
- TranslatorMessage needle(context, sourceText, comment, QString(), QString(), 0);
- int index = m_messages.indexOf(needle);
- return index == -1 ? TranslatorMessage() : m_messages.at(index);
+ for (int i = 0; i < m_messages.count(); ++i) {
+ const TranslatorMessage &tmsg = m_messages.at(i);
+ if (msg.id().isEmpty() || tmsg.id().isEmpty()) {
+ if (msg.context() == tmsg.context()
+ && msg.sourceText() == tmsg.sourceText()
+ && msg.comment() == tmsg.comment())
+ return i;
+ } else {
+ if (msg.id() == tmsg.id())
+ return i;
+ }
+ }
+ return -1;
}
TranslatorMessage Translator::find(const QString &context,
@@ -344,7 +337,7 @@ TranslatorMessage Translator::find(const QString &context,
bool Translator::contains(const QString &context) const
{
foreach (const TranslatorMessage &msg, m_messages)
- if (msg.context() == context && msg.sourceText().isEmpty())
+ if (msg.context() == context && msg.sourceText().isEmpty() && msg.id().isEmpty())
return true;
return false;
}
@@ -352,7 +345,7 @@ bool Translator::contains(const QString &context) const
TranslatorMessage Translator::find(const QString &context) const
{
foreach (const TranslatorMessage &msg, m_messages)
- if (msg.context() == context && msg.sourceText().isEmpty())
+ if (msg.context() == context && msg.sourceText().isEmpty() && msg.id().isEmpty())
return msg;
return TranslatorMessage();
}
@@ -435,49 +428,143 @@ void Translator::dropUiLines()
}
}
-QSet<TranslatorMessagePtr> Translator::resolveDuplicates()
+struct TranslatorMessageIdPtr {
+ explicit TranslatorMessageIdPtr(const TranslatorMessage &tm)
+ {
+ ptr = &tm;
+ }
+
+ inline const TranslatorMessage *operator->() const
+ {
+ return ptr;
+ }
+
+ const TranslatorMessage *ptr;
+};
+
+Q_DECLARE_TYPEINFO(TranslatorMessageIdPtr, Q_MOVABLE_TYPE);
+
+inline int qHash(TranslatorMessageIdPtr tmp)
+{
+ return qHash(tmp->id());
+}
+
+inline bool operator==(TranslatorMessageIdPtr tmp1, TranslatorMessageIdPtr tmp2)
+{
+ return tmp1->id() == tmp2->id();
+}
+
+struct TranslatorMessageContentPtr {
+ explicit TranslatorMessageContentPtr(const TranslatorMessage &tm)
+ {
+ ptr = &tm;
+ }
+
+ inline const TranslatorMessage *operator->() const
+ {
+ return ptr;
+ }
+
+ const TranslatorMessage *ptr;
+};
+
+Q_DECLARE_TYPEINFO(TranslatorMessageContentPtr, Q_MOVABLE_TYPE);
+
+inline int qHash(TranslatorMessageContentPtr tmp)
+{
+ int hash = qHash(tmp->context()) ^ qHash(tmp->sourceText());
+ if (!tmp->sourceText().isEmpty())
+ // Special treatment for context comments (empty source).
+ hash ^= qHash(tmp->comment());
+ return hash;
+}
+
+inline bool operator==(TranslatorMessageContentPtr tmp1, TranslatorMessageContentPtr tmp2)
+{
+ if (tmp1->context() != tmp2->context() || tmp1->sourceText() != tmp2->sourceText())
+ return false;
+ // Special treatment for context comments (empty source).
+ if (tmp1->sourceText().isEmpty())
+ return true;
+ return tmp1->comment() == tmp2->comment();
+}
+
+Translator::Duplicates Translator::resolveDuplicates()
{
- QSet<TranslatorMessagePtr> dups;
- QHash<TranslatorMessagePtr, int> refs;
+ Duplicates dups;
+ QHash<TranslatorMessageIdPtr, int> idRefs;
+ QHash<TranslatorMessageContentPtr, int> contentRefs;
for (int i = 0; i < m_messages.count();) {
const TranslatorMessage &msg = m_messages.at(i);
- QHash<TranslatorMessagePtr, int>::ConstIterator it = refs.constFind(msg);
- if (it != refs.constEnd()) {
- TranslatorMessage &omsg = m_messages[*it];
- if (omsg.isUtf8() != msg.isUtf8() && !omsg.isNonUtf8()) {
- // Dual-encoded message
- omsg.setUtf8(true);
- omsg.setNonUtf8(true);
- } else {
- // Duplicate
- dups.insert(omsg);
+ TranslatorMessage *omsg;
+ int oi;
+ QSet<int> *pDup;
+ if (!msg.id().isEmpty()) {
+ QHash<TranslatorMessageIdPtr, int>::ConstIterator it =
+ idRefs.constFind(TranslatorMessageIdPtr(msg));
+ if (it != idRefs.constEnd()) {
+ oi = *it;
+ omsg = &m_messages[oi];
+ pDup = &dups.byId;
+ goto gotDupe;
}
- if (!omsg.isTranslated() && msg.isTranslated())
- omsg.setTranslations(msg.translations());
- m_messages.removeAt(i);
+ }
+ {
+ QHash<TranslatorMessageContentPtr, int>::ConstIterator it =
+ contentRefs.constFind(TranslatorMessageContentPtr(msg));
+ if (it != contentRefs.constEnd()) {
+ oi = *it;
+ omsg = &m_messages[oi];
+ if (msg.id().isEmpty() || omsg->id().isEmpty()) {
+ if (!msg.id().isEmpty() && omsg->id().isEmpty()) {
+ omsg->setId(msg.id());
+ idRefs[TranslatorMessageIdPtr(*omsg)] = oi;
+ }
+ pDup = &dups.byContents;
+ goto gotDupe;
+ }
+ // This is really a content dupe, but with two distinct IDs.
+ }
+ }
+ if (!msg.id().isEmpty())
+ idRefs[TranslatorMessageIdPtr(msg)] = i;
+ contentRefs[TranslatorMessageContentPtr(msg)] = i;
+ ++i;
+ continue;
+ gotDupe:
+ if (omsg->isUtf8() != msg.isUtf8() && !omsg->isNonUtf8()) {
+ // Dual-encoded message
+ omsg->setUtf8(true);
+ omsg->setNonUtf8(true);
} else {
- refs[msg] = i;
- ++i;
+ // Duplicate
+ pDup->insert(oi);
}
+ if (!omsg->isTranslated() && msg.isTranslated())
+ omsg->setTranslations(msg.translations());
+ m_messages.removeAt(i);
}
return dups;
}
-void Translator::reportDuplicates(const QSet<TranslatorMessagePtr> &dupes,
+void Translator::reportDuplicates(const Duplicates &dupes,
const QString &fileName, bool verbose)
{
- if (!dupes.isEmpty()) {
+ if (!dupes.byId.isEmpty() || !dupes.byContents.isEmpty()) {
if (!verbose) {
qWarning("Warning: dropping duplicate messages in '%s'\n(try -verbose for more info).",
qPrintable(fileName));
} else {
qWarning("Warning: dropping duplicate messages in '%s':", qPrintable(fileName));
- foreach (const TranslatorMessagePtr &msg, dupes) {
+ foreach (int i, dupes.byId)
+ qWarning("\n* ID: %s", qPrintable(message(i).id()));
+ foreach (int j, dupes.byContents) {
+ const TranslatorMessage &msg = message(j);
qWarning("\n* Context: %s\n* Source: %s",
- qPrintable(msg->context()),
- qPrintable(msg->sourceText()));
- if (!msg->comment().isEmpty())
- qWarning("* Comment: %s", qPrintable(msg->comment()));
+ qPrintable(msg.context()),
+ qPrintable(msg.sourceText()));
+ if (!msg.comment().isEmpty())
+ qWarning("* Comment: %s", qPrintable(msg.comment()));
}
qWarning();
}
diff --git a/tools/linguist/shared/translator.h b/tools/linguist/shared/translator.h
index 1dd6a59..eec704a 100644
--- a/tools/linguist/shared/translator.h
+++ b/tools/linguist/shared/translator.h
@@ -86,6 +86,7 @@ public:
QString m_defaultContext;
QByteArray m_codecForSource; // CPP, PO & QM specific
QByteArray m_outputCodec; // PO specific
+ QString m_unTrPrefix; // QM specific
QString m_sourceFileName;
QString m_targetFileName;
QDir m_sourceDir;
@@ -112,18 +113,13 @@ public:
bool save(const QString &filename, ConversionData &err, const QString &format /*= "auto"*/) const;
bool release(QFile *iod, ConversionData &cd) const;
- bool contains(const QString &context, const QString &sourceText,
- const QString &comment) const;
- TranslatorMessage find(const QString &context,
- const QString &sourceText, const QString &comment) const;
-
+ int find(const TranslatorMessage &msg) const;
TranslatorMessage find(const QString &context,
const QString &comment, const TranslatorMessage::References &refs) const;
bool contains(const QString &context) const;
TranslatorMessage find(const QString &context) const;
- void replace(const TranslatorMessage &msg);
void replaceSorted(const TranslatorMessage &msg);
void extend(const TranslatorMessage &msg); // Only for single-location messages
void append(const TranslatorMessage &msg);
@@ -137,9 +133,10 @@ public:
void dropTranslations();
void dropUiLines();
void makeFileNamesAbsolute(const QDir &originalPath);
- QSet<TranslatorMessagePtr> resolveDuplicates();
- static void reportDuplicates(const QSet<TranslatorMessagePtr> &dupes,
- const QString &fileName, bool verbose);
+
+ struct Duplicates { QSet<int> byId, byContents; };
+ Duplicates resolveDuplicates();
+ void reportDuplicates(const Duplicates &dupes, const QString &fileName, bool verbose);
void setCodecName(const QByteArray &name);
QByteArray codecName() const { return m_codecName; }
diff --git a/tools/linguist/shared/translatormessage.cpp b/tools/linguist/shared/translatormessage.cpp
index db6f333..5151ebd 100644
--- a/tools/linguist/shared/translatormessage.cpp
+++ b/tools/linguist/shared/translatormessage.cpp
@@ -143,40 +143,6 @@ bool TranslatorMessage::needs8Bit() const
}
-bool TranslatorMessage::operator==(const TranslatorMessage& m) const
-{
- static QString msgIdPlural = QLatin1String("po-msgid_plural");
-
- // Special treatment for context comments (empty source).
- return (m_context == m.m_context)
- && m_sourcetext == m.m_sourcetext
- && m_extra[msgIdPlural] == m.m_extra[msgIdPlural]
- && m_id == m.m_id
- && (m_sourcetext.isEmpty() || m_comment == m.m_comment);
-}
-
-
-bool TranslatorMessage::operator<(const TranslatorMessage& m) const
-{
- if (m_context != m.m_context)
- return m_context < m.m_context;
- if (m_sourcetext != m.m_sourcetext)
- return m_sourcetext < m.m_sourcetext;
- if (m_comment != m.m_comment)
- return m_comment < m.m_comment;
- return m_id < m.m_id;
-}
-
-int qHash(const TranslatorMessage &msg)
-{
- return
- qHash(msg.context()) ^
- qHash(msg.sourceText()) ^
- qHash(msg.extra(QLatin1String("po-msgid_plural"))) ^
- qHash(msg.comment()) ^
- qHash(msg.id());
-}
-
bool TranslatorMessage::hasExtra(const QString &key) const
{
return m_extra.contains(key);
diff --git a/tools/linguist/shared/translatormessage.h b/tools/linguist/shared/translatormessage.h
index 675bba7..60b60c5 100644
--- a/tools/linguist/shared/translatormessage.h
+++ b/tools/linguist/shared/translatormessage.h
@@ -109,9 +109,6 @@ public:
return false;
}
- bool operator==(const TranslatorMessage& m) const;
- bool operator<(const TranslatorMessage& m) const;
-
QString fileName() const { return m_fileName; }
void setFileName(const QString &fileName) { m_fileName = fileName; }
int lineNumber() const { return m_lineNumber; }
@@ -177,34 +174,6 @@ private:
Q_DECLARE_TYPEINFO(TranslatorMessage, Q_MOVABLE_TYPE);
-int qHash(const TranslatorMessage &msg);
-
-struct TranslatorMessagePtr {
- TranslatorMessagePtr(const TranslatorMessage &tm)
- {
- ptr = &tm;
- }
-
- inline const TranslatorMessage *operator->() const
- {
- return ptr;
- }
-
- const TranslatorMessage *ptr;
-};
-
-Q_DECLARE_TYPEINFO(TranslatorMessagePtr, Q_MOVABLE_TYPE);
-
-inline int qHash(TranslatorMessagePtr tmp)
-{
- return qHash(*tmp.ptr);
-}
-
-inline bool operator==(TranslatorMessagePtr tmp1, TranslatorMessagePtr tmp2)
-{
- return *tmp1.ptr == *tmp2.ptr;
-}
-
QT_END_NAMESPACE
#endif // QT_NO_TRANSLATION