diff options
Diffstat (limited to 'tools/linguist')
25 files changed, 494 insertions, 348 deletions
diff --git a/tools/linguist/lconvert/main.cpp b/tools/linguist/lconvert/main.cpp index 2842bc7..9ccc60e 100644 --- a/tools/linguist/lconvert/main.cpp +++ b/tools/linguist/lconvert/main.cpp @@ -210,6 +210,7 @@ int main(int argc, char *argv[]) qWarning() << qPrintable(cd.error()); return 2; } + Translator::reportDuplicates(tr.resolveDuplicates(), inFiles[0].name, verbose); for (int i = 1; i < inFiles.size(); ++i) { Translator tr2; @@ -217,6 +218,7 @@ int main(int argc, char *argv[]) qWarning() << qPrintable(cd.error()); return 2; } + Translator::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/formpreviewview.cpp b/tools/linguist/linguist/formpreviewview.cpp index 990414b..184f01b 100644 --- a/tools/linguist/linguist/formpreviewview.cpp +++ b/tools/linguist/linguist/formpreviewview.cpp @@ -473,7 +473,7 @@ void FormPreviewView::setSourceContext(int model, MessageItem *messageItem) } QDir dir = QFileInfo(m_dataModel->srcFileName(model)).dir(); - QString fileName = dir.absoluteFilePath(messageItem->fileName()); + QString fileName = QDir::cleanPath(dir.absoluteFilePath(messageItem->fileName())); if (m_lastFormName != fileName) { delete m_form; m_form = 0; diff --git a/tools/linguist/linguist/linguist.pro b/tools/linguist/linguist/linguist.pro index 417ef67..968293a 100644 --- a/tools/linguist/linguist/linguist.pro +++ b/tools/linguist/linguist/linguist.pro @@ -100,7 +100,6 @@ RESOURCES += linguist.qrc TRANSLATIONS=$$[QT_INSTALL_TRANSLATIONS]/linguist_ja.ts \ $$[QT_INSTALL_TRANSLATIONS]/linguist_pl.ts \ $$[QT_INSTALL_TRANSLATIONS]/linguist_untranslated.ts \ - $$[QT_INSTALL_TRANSLATIONS]/linguist_tr_TR.ts \ $$[QT_INSTALL_TRANSLATIONS]/linguist_zh_CN.ts \ $$[QT_INSTALL_TRANSLATIONS]/linguist_zh_TW.ts \ $$[QT_INSTALL_TRANSLATIONS]/linguist_de.ts \ diff --git a/tools/linguist/linguist/mainwindow.cpp b/tools/linguist/linguist/mainwindow.cpp index e0a8a56..5157fbe 100644 --- a/tools/linguist/linguist/mainwindow.cpp +++ b/tools/linguist/linguist/mainwindow.cpp @@ -188,7 +188,7 @@ private: static const QVariant &pxObsolete() { - static const QVariant v = + static const QVariant v = qVariantFromValue(QPixmap(QLatin1String(":/images/s_check_obsolete.png"))); return v; } @@ -1340,30 +1340,10 @@ void MainWindow::about() QMessageBox box(this); box.setTextFormat(Qt::RichText); QString version = tr("Version %1"); -#if QT_EDITION == QT_EDITION_OPENSOURCE - QString open = tr(" Open Source Edition"); - version.append(open); -#endif version = version.arg(QLatin1String(QT_VERSION_STR)); - QString edition = -#if QT_EDITION == QT_EDITION_OPENSOURCE - tr("This version of Qt Linguist is part of the Qt Open Source Edition, for use " - "in the development of Open Source applications. " - "Qt is a comprehensive C++ framework for cross-platform application " - "development.<br/><br/>" - "You need a commercial Qt license for development of proprietary (closed " - "source) applications. Please see <tt>http://qtsoftware.com/company/model" - ".html</tt> for an overview of Qt licensing."); -#elif defined(QT_PRODUCT_LICENSE) - tr("This program is licensed to you under the terms of the " - "Qt %1 License Agreement. For details, see the license file " - "that came with this software distribution.").arg(QLatin1String(QT_PRODUCT_LICENSE)); -#else - tr("This program is licensed to you under the terms of the " - "Qt Commercial License Agreement. For details, see the file LICENSE " - "that came with this software distribution."); -#endif + // TODO: Remove this variable for 4.6.0. Must keep this way for 4.5.x due to string freeze. + QString edition; box.setText(tr("<center><img src=\":/images/splash.png\"/></img><p>%1</p></center>" "<p>Qt Linguist is a tool for adding translations to Qt " @@ -1535,7 +1515,7 @@ void MainWindow::selectedMessageChanged(const QModelIndex &sortedIndex, const QM } else { m_sourceAndFormView->setCurrentWidget(m_sourceCodeView); QDir dir = QFileInfo(m_dataModel->srcFileName(model)).dir(); - QString fileName = dir.absoluteFilePath(m->fileName()); + QString fileName = QDir::cleanPath(dir.absoluteFilePath(m->fileName())); m_sourceCodeView->setSourceContext(fileName, m->lineNumber()); } m_errorsView->setEnabled(true); diff --git a/tools/linguist/linguist/messageeditor.cpp b/tools/linguist/linguist/messageeditor.cpp index f8c679c..53cbbea 100644 --- a/tools/linguist/linguist/messageeditor.cpp +++ b/tools/linguist/linguist/messageeditor.cpp @@ -67,7 +67,7 @@ QT_BEGIN_NAMESPACE // Allow translators to provide localized names for QLocale::languageToString // At least the own language should be translated ... This is a "hack" until // functionality is provided within Qt (see task 196275). -static const char * language_strings[] = +static const char * language_strings[] = { QT_TRANSLATE_NOOP("MessageEditor", "German"), QT_TRANSLATE_NOOP("MessageEditor", "Japanese"), @@ -133,19 +133,12 @@ void MessageEditor::setupEditorPage() QFrame *editorPage = new QFrame; editorPage->setObjectName(QLatin1String("editorPage")); - // Due to CSS being rather broken on the Mac style at the moment, only - // use the border-image on non-Mac systems. editorPage->setStyleSheet(QLatin1String( -#ifndef Q_WS_MAC "QFrame#editorPage { border-image: url(:/images/transbox.png) 12 16 16 12 repeat;" " border-width: 12px 16px 16px 12px; }" -#endif "QFrame#editorPage { background-color: white; }" "QLabel { font-weight: bold; }" )); -#ifdef Q_WS_MAC - editorPage->setFrameStyle(QFrame::StyledPanel | QFrame::Raised); -#endif editorPage->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); m_source = new FormWidget(tr("Source text"), false); @@ -786,6 +779,7 @@ void MessageEditor::selectAll() void MessageEditor::emitTranslationChanged() { + static_cast<FormWidget *>(sender())->getEditor()->setFocus(); // DND proofness updateBeginFromSource(); updateUndoRedo(); emit translationChanged(translations(m_currentModel)); @@ -793,6 +787,7 @@ void MessageEditor::emitTranslationChanged() void MessageEditor::emitTranslatorCommentChanged() { + static_cast<FormWidget *>(sender())->getEditor()->setFocus(); // DND proofness updateUndoRedo(); emit translatorCommentChanged(m_editors[m_currentModel].transCommentText->getTranslation()); } diff --git a/tools/linguist/linguist/messagemodel.cpp b/tools/linguist/linguist/messagemodel.cpp index a7053cf..7a8063b 100644 --- a/tools/linguist/linguist/messagemodel.cpp +++ b/tools/linguist/linguist/messagemodel.cpp @@ -209,19 +209,19 @@ bool DataModel::load(const QString &fileName, bool *langGuessed, QWidget *parent return false; } - QList<TranslatorMessage> dupes = tor.findDuplicates(); + QSet<TranslatorMessagePtr> dupes = tor.resolveDuplicates(); if (!dupes.isEmpty()) { QString err = tr("<qt>Duplicate messages found in '%1':").arg(Qt::escape(fileName)); int numdups = 0; - foreach (const TranslatorMessage &msg, dupes) { + foreach (const TranslatorMessagePtr &msg, dupes) { 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())); } QMessageBox::warning(parent, QObject::tr("Qt Linguist"), err); } @@ -460,7 +460,7 @@ QString DataModel::prettifyFileName(const QString &fn) /****************************************************************************** * - * DataModelIterator + * DataModelIterator * *****************************************************************************/ @@ -1109,7 +1109,7 @@ void MultiDataModel::updateCountsOnRemove(int model, bool writable) /****************************************************************************** * - * MultiDataModelIterator + * MultiDataModelIterator * *****************************************************************************/ @@ -1214,17 +1214,17 @@ int MessageModel::columnCount(const QModelIndex &) const QVariant MessageModel::data(const QModelIndex &index, int role) const { - static QVariant pxOn = + static QVariant pxOn = qVariantFromValue(QPixmap(QLatin1String(":/images/s_check_on.png"))); - static QVariant pxOff = + static QVariant pxOff = qVariantFromValue(QPixmap(QLatin1String(":/images/s_check_off.png"))); - static QVariant pxObsolete = + static QVariant pxObsolete = qVariantFromValue(QPixmap(QLatin1String(":/images/s_check_obsolete.png"))); static QVariant pxDanger = qVariantFromValue(QPixmap(QLatin1String(":/images/s_check_danger.png"))); static QVariant pxWarning = qVariantFromValue(QPixmap(QLatin1String(":/images/s_check_warning.png"))); - static QVariant pxEmpty = + static QVariant pxEmpty = qVariantFromValue(QPixmap(QLatin1String(":/images/s_check_empty.png"))); int row = index.row(); diff --git a/tools/linguist/linguist/phrase.cpp b/tools/linguist/linguist/phrase.cpp index 563c72d..300f6e8 100644 --- a/tools/linguist/linguist/phrase.cpp +++ b/tools/linguist/linguist/phrase.cpp @@ -243,7 +243,7 @@ bool PhraseBook::load(const QString &fileName, bool *langGuessed) // don't click on these! reader.setFeature(QLatin1String("http://xml.org/sax/features/namespaces"), false); reader.setFeature(QLatin1String("http://xml.org/sax/features/namespace-prefixes"), true); - reader.setFeature(QLatin1String("http://qtsoftware.com/xml/features/report-whitespace" + reader.setFeature(QLatin1String("http://trolltech.com/xml/features/report-whitespace" "-only-CharData"), false); QphHandler *hand = new QphHandler(this); reader.setContentHandler(hand); diff --git a/tools/linguist/linguist/phraseview.cpp b/tools/linguist/linguist/phraseview.cpp index 78c9151..72c27dc 100644 --- a/tools/linguist/linguist/phraseview.cpp +++ b/tools/linguist/linguist/phraseview.cpp @@ -135,7 +135,7 @@ void PhraseView::contextMenuEvent(QContextMenuEvent *event) void PhraseView::mouseDoubleClickEvent(QMouseEvent *event) { QModelIndex index = indexAt(event->pos()); - if (!index.isValid()) + if (!index.isValid()) return; emit phraseSelected(m_modelIndex, m_phraseModel->phrase(index)->target()); diff --git a/tools/linguist/linguist/translationsettingsdialog.h b/tools/linguist/linguist/translationsettingsdialog.h index 8a633d9..a7ed1b4 100644 --- a/tools/linguist/linguist/translationsettingsdialog.h +++ b/tools/linguist/linguist/translationsettingsdialog.h @@ -76,4 +76,4 @@ private: QT_END_NAMESPACE -#endif // TRANSLATIONSETTINGSDIALOG_H +#endif // TRANSLATIONSETTINGSDIALOG_H diff --git a/tools/linguist/lrelease/main.cpp b/tools/linguist/lrelease/main.cpp index f1fdb3a..3bcc998 100644 --- a/tools/linguist/lrelease/main.cpp +++ b/tools/linguist/lrelease/main.cpp @@ -94,19 +94,6 @@ static bool loadTsFile(Translator &tor, const QString &tsFileName, bool /* verbo } else { if (!cd.errors().isEmpty()) printOut(cd.error()); - const QList<TranslatorMessage> dupes = tor.findDuplicates(); - if (!dupes.isEmpty()) { - qWarning("lrelease error: duplicate messages found in '%s':", - qPrintable(tsFileName)); - foreach (const TranslatorMessage &msg, dupes) { - qWarning("\n* Context: %s\n* Source: %s", - qPrintable(msg.context()), - qPrintable(msg.sourceText())); - if (!msg.comment().isEmpty()) - qWarning("\n* Comment: %s", qPrintable(msg.comment())); - } - ok = false; - } } return ok; } @@ -115,6 +102,8 @@ static bool releaseTranslator(Translator &tor, const QString &qmFileName, bool verbose, bool ignoreUnfinished, bool removeIdentical, TranslatorSaveMode mode) { + Translator::reportDuplicates(tor.resolveDuplicates(), qmFileName, verbose); + if (verbose) printOut(QCoreApplication::tr( "Updating '%1'...\n").arg(qmFileName)); if (removeIdentical) { diff --git a/tools/linguist/lupdate/main.cpp b/tools/linguist/lupdate/main.cpp index e7865d5..b537b6e 100644 --- a/tools/linguist/lupdate/main.cpp +++ b/tools/linguist/lupdate/main.cpp @@ -73,7 +73,7 @@ static void recursiveFileInfoList(const QDir &dir, if (fname != QLatin1String(".") && fname != QLatin1String("..")) { if (it->isDir()) recursiveFileInfoList(QDir(it->absoluteFilePath()), nameFilters, filter, recursive, fileinfolist); - else + else fileinfolist->append(*it); } } @@ -146,6 +146,7 @@ static void updateTsFiles(const Translator &fetchedTor, const QStringList &tsFil *fail = true; continue; } + tor.resolveDuplicates(); cd.clearErrors(); if (!codecForTr.isEmpty() && codecForTr != tor.codecName()) qWarning("lupdate warning: Codec for tr() '%s' disagrees with " diff --git a/tools/linguist/shared/cpp.cpp b/tools/linguist/shared/cpp.cpp index 28616cc..2e137cf 100644 --- a/tools/linguist/shared/cpp.cpp +++ b/tools/linguist/shared/cpp.cpp @@ -130,12 +130,19 @@ static int yyInPos; static uint getChar() { - if (yyInPos >= yyInStr.size()) - return EOF; - QChar c = yyInStr[yyInPos++]; - if (c.unicode() == '\n') - ++yyCurLineNo; - return c.unicode(); + forever { + if (yyInPos >= yyInStr.size()) + return EOF; + uint c = yyInStr[yyInPos++].unicode(); + if (c == '\\' && yyInPos < yyInStr.size() && yyInStr[yyInPos].unicode() == '\n') { + ++yyCurLineNo; + ++yyInPos; + continue; + } + if (c == '\n') + ++yyCurLineNo; + return c; + } } static uint getToken() @@ -784,7 +791,7 @@ static void parse(Translator *tor, const QString &initialContext, const QString if (yyTok == Tok_ColonColon) fullName.append(QString()); while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) { - if (yyTok == Tok_Ident) + if (yyTok == Tok_Ident) fullName.append(yyIdent); yyTok = getToken(); } diff --git a/tools/linguist/shared/po.cpp b/tools/linguist/shared/po.cpp index e9375e9..5842771 100644 --- a/tools/linguist/shared/po.cpp +++ b/tools/linguist/shared/po.cpp @@ -389,7 +389,7 @@ bool loadPO(Translator &translator, QIODevice &dev, ConversionData &cd) for (; l != lines.size(); ++l) { QString line = lines.at(l); if (line.isEmpty()) - continue; + continue; if (isTranslationLine(line)) { bool isObsolete = line.startsWith(QLatin1String("#~ msgstr")); const QString prefix = QLatin1String(isObsolete ? "#~ " : ""); diff --git a/tools/linguist/shared/profileevaluator.cpp b/tools/linguist/shared/profileevaluator.cpp index 1e91f92..5440752 100644 --- a/tools/linguist/shared/profileevaluator.cpp +++ b/tools/linguist/shared/profileevaluator.cpp @@ -157,11 +157,6 @@ ProFileEvaluator::Private::Private(ProFileEvaluator *q_) m_prevLineNo = 0; m_prevProFile = 0; m_verbose = true; - m_block = 0; - m_commentItem = 0; - m_syntaxError = 0; - m_lineNo = 0; - m_contNextLine = false; } bool ProFileEvaluator::Private::read(ProFile *pro) @@ -172,8 +167,12 @@ bool ProFileEvaluator::Private::read(ProFile *pro) return false; } + m_block = 0; + m_commentItem = 0; + m_contNextLine = false; m_syntaxError = false; m_lineNo = 1; + m_blockstack.clear(); m_blockstack.push(pro); QTextStream ts(&file); @@ -276,6 +275,9 @@ void ProFileEvaluator::Private::insertVariable(const QString &line, int *i) { ProVariable::VariableOperator opkind; + if (m_proitem.isEmpty()) // Line starting with '=', like a conflict marker + return; + switch (m_proitem.at(m_proitem.length() - 1).unicode()) { case '+': m_proitem.chop(1); @@ -711,7 +713,7 @@ QStringList ProFileEvaluator::Private::qmakeFeaturePaths() // if (!specdir.cdUp() || specdir.isRoot()) // break; // if (QFile::exists(specdir.path() + QDir::separator() + "features")) { - // foreach (const QString &concat_it, concat) + // foreach (const QString &concat_it, concat) // feature_roots << (specdir.path() + concat_it); // break; // } @@ -1002,9 +1004,10 @@ bool ProFileEvaluator::Private::isActiveConfig(const QString &config, bool regex QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &func, const QString &arguments) { QStringList argumentsList = split_arg_list(arguments); + QStringList args; for (int i = 0; i < argumentsList.count(); ++i) - args += expandVariableReferences(argumentsList[i]); + args += expandVariableReferences(argumentsList[i]).join(Option::field_sep); enum ExpandFunc { E_MEMBER=1, E_FIRST, E_LAST, E_CAT, E_FROMFILE, E_EVAL, E_LIST, E_SPRINTF, E_JOIN, E_SPLIT, E_BASENAME, E_DIRNAME, E_SECTION, @@ -1265,10 +1268,11 @@ bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &funct for (int mut = 0; mut < mutuals.count(); mut++) { if (configs[i] == mutuals[mut].trimmed()) { cond = (configs[i] == args[0]); - break; + goto done_T_CONFIG; } } } + done_T_CONFIG: break; } case CF_CONTAINS: { @@ -1295,12 +1299,12 @@ bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &funct for (int mut = 0; mut < mutuals.count(); mut++) { if (val == mutuals[mut].trimmed()) { cond = (regx.exactMatch(val) || val == args[1]); - break; + goto done_T_CONTAINS; } } } } - + done_T_CONTAINS: break; } case CF_COUNT: { @@ -1373,7 +1377,7 @@ bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &funct parents.append(proFile->fileName()); if (!parents.isEmpty()) parents.takeLast(); - if (parents.isEmpty()) + if (parents.isEmpty()) q->fileMessage(format("Project ERROR: %1").arg(msg)); else q->fileMessage(format("Project ERROR: %1. File was included from: '%2'") @@ -1472,7 +1476,13 @@ ProFile *ProFileEvaluator::parsedProFile(const QString &fileName) { QFileInfo fi(fileName); if (fi.exists()) { - ProFile *pro = new ProFile(fi.absoluteFilePath()); + QString fn = QDir::cleanPath(fi.absoluteFilePath()); + foreach (const ProFile *pf, d->m_profileStack) + if (pf->fileName() == fn) { + errorMessage(d->format("circular inclusion of %1").arg(fn)); + return 0; + } + ProFile *pro = new ProFile(fn); if (d->read(pro)) return pro; delete pro; diff --git a/tools/linguist/shared/qm.cpp b/tools/linguist/shared/qm.cpp index c197e2b..5563ac5 100644 --- a/tools/linguist/shared/qm.cpp +++ b/tools/linguist/shared/qm.cpp @@ -103,6 +103,43 @@ static uint elfHash(const QByteArray &ba) return h; } +class ByteTranslatorMessage +{ +public: + ByteTranslatorMessage( + const QByteArray &context, + const QByteArray &sourceText, + const QByteArray &comment, + const QStringList &translations) : + m_context(context), + m_sourcetext(sourceText), + m_comment(comment), + m_translations(translations) + {} + const QByteArray &context() const { return m_context; } + const QByteArray &sourceText() const { return m_sourcetext; } + const QByteArray &comment() const { return m_comment; } + const QStringList &translations() const { return m_translations; } + bool operator<(const ByteTranslatorMessage& m) const; + +private: + QByteArray m_context; + QByteArray m_sourcetext; + QByteArray m_comment; + QStringList m_translations; +}; + +Q_DECLARE_TYPEINFO(ByteTranslatorMessage, Q_MOVABLE_TYPE); + +bool ByteTranslatorMessage::operator<(const ByteTranslatorMessage& 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; + return m_comment < m.m_comment; +} + class Releaser { public: @@ -133,27 +170,12 @@ public: m_codec = QTextCodec::codecForName(codecName); } - TranslatorMessage findMessage(const QString &context, - const QString &sourceText, const QString &comment, - const QString &fileName = QString(), int lineNumber = -1) const; - bool save(QIODevice *iod); - void insert(const TranslatorMessage &); - void remove(const TranslatorMessage &); - - bool contains(const QString &context, const QString &sourceText, - const QString & comment) const; - - bool contains(const QString &context, const QString &comment, - const QString &fileName, int lineNumber) const; + void insert(const TranslatorMessage &msg, bool forceComment); void squeeze(TranslatorSaveMode mode); - QList<TranslatorMessage> messages() const; - - bool isEmpty() const; - void setNumerusRules(const QByteArray &rules); private: @@ -163,18 +185,20 @@ private: // on turn should be the same as passed to the actual tr(...) calls QByteArray originalBytes(const QString &str, bool isUtf8) const; - Prefix commonPrefix(const TranslatorMessage &m1, const TranslatorMessage &m2) const; + void insertInternal(const TranslatorMessage &message, bool forceComment, bool isUtf8); + + static Prefix commonPrefix(const ByteTranslatorMessage &m1, const ByteTranslatorMessage &m2); - uint msgHash(const TranslatorMessage &msg) const; + static uint msgHash(const ByteTranslatorMessage &msg); - void writeMessage(const TranslatorMessage & msg, QDataStream & stream, + void writeMessage(const ByteTranslatorMessage & msg, QDataStream & stream, TranslatorSaveMode strip, Prefix prefix) const; // for squeezed but non-file data, this is what needs to be deleted QByteArray m_messageArray; QByteArray m_offsetArray; QByteArray m_contextArray; - QMap<TranslatorMessage, void *> m_messages; + QMap<ByteTranslatorMessage, void *> m_messages; QByteArray m_numerusRules; // Used to reproduce the original bytes @@ -193,12 +217,12 @@ QByteArray Releaser::originalBytes(const QString &str, bool isUtf8) const return m_codec ? m_codec->fromUnicode(str) : str.toLatin1(); } -uint Releaser::msgHash(const TranslatorMessage &msg) const +uint Releaser::msgHash(const ByteTranslatorMessage &msg) { - return elfHash(originalBytes(msg.sourceText() + msg.comment(), msg.isUtf8())); + return elfHash(msg.sourceText() + msg.comment()); } -Prefix Releaser::commonPrefix(const TranslatorMessage &m1, const TranslatorMessage &m2) const +Prefix Releaser::commonPrefix(const ByteTranslatorMessage &m1, const ByteTranslatorMessage &m2) { if (msgHash(m1) != msgHash(m2)) return NoPrefix; @@ -211,7 +235,7 @@ Prefix Releaser::commonPrefix(const TranslatorMessage &m1, const TranslatorMessa return HashContextSourceTextComment; } -void Releaser::writeMessage(const TranslatorMessage & msg, QDataStream & stream, +void Releaser::writeMessage(const ByteTranslatorMessage &msg, QDataStream &stream, TranslatorSaveMode mode, Prefix prefix) const { for (int i = 0; i < msg.translations().count(); ++i) { @@ -228,14 +252,14 @@ void Releaser::writeMessage(const TranslatorMessage & msg, QDataStream & stream, switch (prefix) { default: case HashContextSourceTextComment: - stream << quint8(Tag_Comment) << originalBytes(msg.comment(), msg.isUtf8()); + stream << quint8(Tag_Comment) << msg.comment(); // fall through case HashContextSourceText: - stream << quint8(Tag_SourceText) << originalBytes(msg.sourceText(), msg.isUtf8()); + stream << quint8(Tag_SourceText) << msg.sourceText(); // fall through case HashContext: - stream << quint8(Tag_Context) << originalBytes(msg.context(), msg.isUtf8()); - ; + stream << quint8(Tag_Context) << msg.context(); + break; } stream << quint8(Tag_End); @@ -275,7 +299,7 @@ void Releaser::squeeze(TranslatorSaveMode mode) if (m_messages.isEmpty() && mode == SaveEverything) return; - QMap<TranslatorMessage, void *> messages = m_messages; + QMap<ByteTranslatorMessage, void *> messages = m_messages; // re-build contents m_messageArray.clear(); @@ -286,7 +310,7 @@ void Releaser::squeeze(TranslatorSaveMode mode) QMap<Offset, void *> offsets; QDataStream ms(&m_messageArray, QIODevice::WriteOnly); - QMap<TranslatorMessage, void *>::const_iterator it, next; + QMap<ByteTranslatorMessage, void *>::const_iterator it, next; int cpPrev = 0, cpNext = 0; for (it = messages.constBegin(); it != messages.constEnd(); ++it) { cpPrev = cpNext; @@ -310,7 +334,7 @@ void Releaser::squeeze(TranslatorSaveMode mode) } if (mode == SaveStripped) { - QMap<QString, int> contextSet; + QMap<QByteArray, int> contextSet; for (it = messages.constBegin(); it != messages.constEnd(); ++it) ++contextSet[it.key().context()]; @@ -322,10 +346,10 @@ void Releaser::squeeze(TranslatorSaveMode mode) else hTableSize = (contextSet.size() < 10000) ? 15013 : 3 * contextSet.size() / 2; - QMultiMap<int, QString> hashMap; - QMap<QString, int>::const_iterator c; + QMultiMap<int, QByteArray> hashMap; + QMap<QByteArray, int>::const_iterator c; for (c = contextSet.constBegin(); c != contextSet.constEnd(); ++c) - hashMap.insert(elfHash(originalBytes(c.key(), false /*FIXME*/)) % hTableSize, c.key()); + hashMap.insert(elfHash(c.key()) % hTableSize, c.key()); /* The contexts found in this translator are stored in a hash @@ -360,16 +384,14 @@ void Releaser::squeeze(TranslatorSaveMode mode) t << quint16(0); // the entry at offset 0 cannot be used uint upto = 2; - QMap<int, QString>::const_iterator entry = hashMap.constBegin(); + QMap<int, QByteArray>::const_iterator entry = hashMap.constBegin(); while (entry != hashMap.constEnd()) { int i = entry.key(); hTable[i] = quint16(upto >> 1); do { - QString context = entry.value(); - QByteArray ba = context.toUtf8(); - const char *con = ba.data(); - uint len = uint(qstrlen(con)); + const char *con = entry.value().constData(); + uint len = uint(entry.value().length()); len = qMin(len, 255u); t << quint8(len); t.writeRawData(con, len); @@ -394,68 +416,28 @@ void Releaser::squeeze(TranslatorSaveMode mode) } } -bool Releaser::contains(const QString &context, const QString &sourceText, - const QString &comment) const -{ - return !findMessage(context, sourceText, comment).translation().isNull(); -} - -bool Releaser::contains(const QString &context, const QString &comment, - const QString &fileName, int lineNumber) const -{ - return !findMessage(context, QString(), comment, fileName, lineNumber).isNull(); -} - -void Releaser::insert(const TranslatorMessage &message) -{ - m_messages.insert(message, 0); -} - -void Releaser::remove(const TranslatorMessage &message) +void Releaser::insertInternal(const TranslatorMessage &message, bool forceComment, bool isUtf8) { - m_messages.remove(message); -} - - -TranslatorMessage Releaser::findMessage(const QString &context, - const QString &sourceText, const QString &comment, - const QString &fileName, int lineNumber) const -{ - if (m_messages.isEmpty()) - return TranslatorMessage(); - - QMap<TranslatorMessage, void *>::const_iterator it; - - // Either we want to find an item that matches context, sourcetext - // (and optionally comment) Or we want to find an item that - // matches context, filename, linenumber (and optionally comment) - TranslatorMessage msg(context, sourceText, comment, QString(), fileName, lineNumber); - it = m_messages.constFind(msg); - if (it != m_messages.constEnd()) - return it.key(); - - if (!comment.isEmpty()) { - it = m_messages.constFind(TranslatorMessage(context, sourceText, QString(), QString(), fileName, lineNumber)); - if (it != m_messages.constEnd()) - return it.key(); + ByteTranslatorMessage bmsg(originalBytes(message.context(), isUtf8), + originalBytes(message.sourceText(), isUtf8), + originalBytes(message.comment(), isUtf8), + message.translations()); + if (!forceComment) { + ByteTranslatorMessage bmsg2( + bmsg.context(), bmsg.sourceText(), QByteArray(""), bmsg.translations()); + if (!m_messages.contains(bmsg2)) { + m_messages.insert(bmsg2, 0); + return; + } } - - it = m_messages.constFind(TranslatorMessage(context, QString(), comment, QString(), fileName, lineNumber)); - if (it != m_messages.constEnd()) - return it.key(); - if (comment.isEmpty()) - return TranslatorMessage(); - - it = m_messages.constFind(TranslatorMessage(context, QString(), QString(), QString(), fileName, lineNumber)); - if (it != m_messages.constEnd()) - return it.key(); - return TranslatorMessage(); + m_messages.insert(bmsg, 0); } -bool Releaser::isEmpty() const +void Releaser::insert(const TranslatorMessage &message, bool forceComment) { - return m_messageArray.isEmpty() && m_offsetArray.isEmpty() - && m_contextArray.isEmpty() && m_messages.isEmpty(); + insertInternal(message, forceComment, message.isUtf8()); + if (message.isUtf8() && message.isNonUtf8()) + insertInternal(message, forceComment, false); } void Releaser::setNumerusRules(const QByteArray &rules) @@ -463,11 +445,6 @@ void Releaser::setNumerusRules(const QByteArray &rules) m_numerusRules = rules; } -QList<TranslatorMessage> Releaser::messages() const -{ - return m_messages.keys(); -} - static quint8 read8(const uchar *data) { return *data; @@ -478,6 +455,31 @@ static quint32 read32(const uchar *data) return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | (data[3]); } +static void fromBytes(const char *str, int len, QTextCodec *codec, QTextCodec *utf8Codec, + QString *out, QString *utf8Out, + bool *isSystem, bool *isUtf8, bool *needs8Bit) +{ + for (int i = 0; i < len; ++i) + if (str[i] & 0x80) { + if (utf8Codec) { + QTextCodec::ConverterState cvtState; + *utf8Out = utf8Codec->toUnicode(str, len, &cvtState); + *isUtf8 = !cvtState.invalidChars; + } + QTextCodec::ConverterState cvtState; + *out = codec->toUnicode(str, len, &cvtState); + *isSystem = !cvtState.invalidChars; + *needs8Bit = true; + return; + } + *out = QString::fromLatin1(str, len); + *isSystem = true; + if (utf8Codec) { + *utf8Out = *out; + *isUtf8 = true; + } + *needs8Bit = false; +} bool loadQM(Translator &translator, QIODevice &dev, ConversionData &cd) { @@ -543,10 +545,19 @@ bool loadQM(Translator &translator, QIODevice &dev, ConversionData &cd) size_t numItems = offsetLength / (2 * sizeof(quint32)); //qDebug() << "NUMITEMS: " << numItems; - TranslatorMessage msg; - // FIXME: that's just a guess, the original locale data is lost... QTextCodec *codec = QTextCodec::codecForLocale(); + QTextCodec *utf8Codec = 0; + if (codec->name() != "UTF-8") + utf8Codec = QTextCodec::codecForName("UTF-8"); + + QString context, contextUtf8; + bool contextIsSystem, contextIsUtf8, contextNeeds8Bit; + QString sourcetext, sourcetextUtf8; + bool sourcetextIsSystem, sourcetextIsUtf8, sourcetextNeeds8Bit; + QString comment, commentUtf8; + bool commentIsSystem, commentIsUtf8, commentNeeds8Bit; + QStringList translations; for (const uchar *start = offsetArray; start != offsetArray + (numItems << 3); start += 8) { //quint32 hash = read32(start); @@ -575,7 +586,7 @@ bool loadQM(Translator &translator, QIODevice &dev, ConversionData &cd) } str.replace(QChar(Translator::InternalVariantSeparator), QChar(Translator::DefaultVariantSeparator)); - msg.appendTranslation(str); + translations << str; m += len; break; } @@ -588,7 +599,9 @@ bool loadQM(Translator &translator, QIODevice &dev, ConversionData &cd) m += 4; //qDebug() << "SOURCE LEN: " << len; //qDebug() << "SOURCE: " << QByteArray((const char*)m, len); - msg.setSourceText(codec->toUnicode(QByteArray((const char*)m, len))); + fromBytes((const char*)m, len, codec, utf8Codec, + &sourcetext, &sourcetextUtf8, + &sourcetextIsSystem, &sourcetextIsUtf8, &sourcetextNeeds8Bit); m += len; break; } @@ -597,7 +610,9 @@ bool loadQM(Translator &translator, QIODevice &dev, ConversionData &cd) m += 4; //qDebug() << "CONTEXT LEN: " << len; //qDebug() << "CONTEXT: " << QByteArray((const char*)m, len); - msg.setContext(codec->toUnicode(QByteArray((const char*)m, len))); + fromBytes((const char*)m, len, codec, utf8Codec, + &context, &contextUtf8, + &contextIsSystem, &contextIsUtf8, &contextNeeds8Bit); m += len; break; } @@ -606,7 +621,9 @@ bool loadQM(Translator &translator, QIODevice &dev, ConversionData &cd) m += 4; //qDebug() << "COMMENT LEN: " << len; //qDebug() << "COMMENT: " << QByteArray((const char*)m, len); - msg.setComment(codec->toUnicode(QByteArray((const char*)m, len))); + fromBytes((const char*)m, len, codec, utf8Codec, + &comment, &commentUtf8, + &commentIsSystem, &commentIsUtf8, &commentNeeds8Bit); m += len; break; } @@ -616,10 +633,31 @@ bool loadQM(Translator &translator, QIODevice &dev, ConversionData &cd) } } end:; + TranslatorMessage msg; msg.setType(TranslatorMessage::Finished); + msg.setTranslations(translations); + translations.clear(); + if (contextNeeds8Bit || sourcetextNeeds8Bit || commentNeeds8Bit) { + if (utf8Codec && contextIsUtf8 && sourcetextIsUtf8 && commentIsUtf8) { + // The message is utf-8, but file is not. + msg.setUtf8(true); + msg.setContext(contextUtf8); + msg.setSourceText(sourcetextUtf8); + msg.setComment(commentUtf8); + translator.append(msg); + continue; + } + if (!(contextIsSystem && sourcetextIsSystem && commentIsSystem)) { + cd.appendError(QLatin1String( + "Cannot read file with current system character codec")); + return false; + } + // The message is 8-bit in the file's encoding (utf-8 or not). + } + msg.setContext(context); + msg.setSourceText(sourcetext); + msg.setComment(comment); translator.append(msg); - //qDebug() << "\nHASH:" << hash << msg.sourceText() << msg.context(); - msg.setTranslations(QStringList()); } return ok; } @@ -646,39 +684,27 @@ static bool saveQM(const Translator &translator, QIODevice &dev, ConversionData TranslatorMessage::Type typ = msg.type(); if (typ != TranslatorMessage::Obsolete) { if (typ == TranslatorMessage::Unfinished) { - if (msg.translation().isEmpty()) + if (msg.translation().isEmpty()) { ++untranslated; - else + continue; + } else { + if (cd.ignoreUnfinished()) + continue; ++unfinished; + } } else { ++finished; } - QString context = msg.context(); - QString sourceText = msg.sourceText(); - QString comment = msg.comment(); - QStringList translations = msg.translations(); - - if (!cd.ignoreUnfinished() || typ != TranslatorMessage::Unfinished) { - /* - Drop the comment in (context, sourceText, comment), - unless the context is empty, - unless (context, sourceText, "") already exists or - unless we already dropped the comment of (context, - sourceText, comment0). - */ - if (comment.isEmpty() - || context.isEmpty() - || translator.contains(context, sourceText, QString()) - || !releaser.findMessage(context, sourceText, QString()).translation() - .isNull() ) { - releaser.insert(msg); - } else { - TranslatorMessage tm(context, sourceText, QString(), - QString(), QString(), -1, translations); - //filename and lineNumbers will be ignored from now. - releaser.insert(tm); - } - } + // Drop the comment in (context, sourceText, comment), + // unless the context is empty, + // unless (context, sourceText, "") already exists or + // unless we already dropped the comment of (context, + // sourceText, comment0). + bool forceComment = + msg.comment().isEmpty() + || msg.context().isEmpty() + || translator.contains(msg.context(), msg.sourceText(), QString()); + releaser.insert(msg, forceComment); } } diff --git a/tools/linguist/shared/qph.cpp b/tools/linguist/shared/qph.cpp index 45d3a20..799bf7d 100644 --- a/tools/linguist/shared/qph.cpp +++ b/tools/linguist/shared/qph.cpp @@ -101,7 +101,7 @@ bool QPHReader::read(Translator &translator) m_currentField = TargetField; else if (name() == QLatin1String("definition")) m_currentField = DefinitionField; - else + else m_currentField = NoField; } else if (isWhiteSpace()) { // ignore these @@ -133,14 +133,47 @@ static bool loadQPH(Translator &translator, QIODevice &dev, ConversionData &cd) return reader.read(translator); } -static bool saveQPH(const Translator &translator, QIODevice &dev, ConversionData &cd) +static QString protect(const QString &str) +{ + QString result; + result.reserve(str.length() * 12 / 10); + for (int i = 0; i != str.size(); ++i) { + uint c = str.at(i).unicode(); + switch (c) { + case '\"': + result += QLatin1String("""); + break; + case '&': + result += QLatin1String("&"); + break; + case '>': + result += QLatin1String(">"); + break; + case '<': + result += QLatin1String("<"); + break; + case '\'': + result += QLatin1String("'"); + break; + default: + if (c < 0x20 && c != '\r' && c != '\n' && c != '\t') + result += QString(QLatin1String("&#%1;")).arg(c); + else // this also covers surrogates + result += QChar(c); + } + } + return result; +} + +static bool saveQPH(const Translator &translator, QIODevice &dev, ConversionData &) { QTextStream t(&dev); - t << "<!DOCTYPE QPH><QPH>\n"; + t.setCodec(QTextCodec::codecForName("UTF-8")); + t << "<!DOCTYPE QPH>\n<QPH>\n"; foreach (const TranslatorMessage &msg, translator.messages()) { t << "<phrase>\n"; - t << " <source>" << msg.sourceText() << "</source>\n"; - t << " <target>" << msg.translations().join(QLatin1String("@")) + t << " <source>" << protect(msg.sourceText()) << "</source>\n"; + t << " <target>" << protect(msg.translations().join(QLatin1String("@"))) << "</target>\n"; if (!msg.context().isEmpty() || !msg.comment().isEmpty()) t << " <definition>" << msg.context() << msg.comment() diff --git a/tools/linguist/shared/qscript.cpp b/tools/linguist/shared/qscript.cpp index 9ab5e16..7377cba 100644 --- a/tools/linguist/shared/qscript.cpp +++ b/tools/linguist/shared/qscript.cpp @@ -2382,7 +2382,7 @@ bool loadQScript(Translator &translator, QIODevice &dev, ConversionData &cd) return true; } -bool saveQScript(const Translator &translator, QIODevice &dev, ConversionData &cd) +bool saveQScript(const Translator &translator, QIODevice &dev, ConversionData &cd) { Q_UNUSED(dev); Q_UNUSED(translator); diff --git a/tools/linguist/shared/qscript.g b/tools/linguist/shared/qscript.g index c7ee80d..8d33277 100644 --- a/tools/linguist/shared/qscript.g +++ b/tools/linguist/shared/qscript.g @@ -2012,7 +2012,7 @@ bool loadQScript(Translator &translator, QIODevice &dev, ConversionData &cd) return true; } -bool saveQScript(const Translator &translator, QIODevice &dev, ConversionData &cd) +bool saveQScript(const Translator &translator, QIODevice &dev, ConversionData &cd) { Q_UNUSED(dev); Q_UNUSED(translator); diff --git a/tools/linguist/shared/translator.cpp b/tools/linguist/shared/translator.cpp index 3b5e8f3..312bb71 100644 --- a/tools/linguist/shared/translator.cpp +++ b/tools/linguist/shared/translator.cpp @@ -105,13 +105,18 @@ void Translator::extend(const TranslatorMessage &msg) if (index == -1) { m_messages.append(msg); } else { - m_messages[index].addReferenceUniq(msg.fileName(), msg.lineNumber()); + TranslatorMessage &emsg = m_messages[index]; + emsg.addReferenceUniq(msg.fileName(), msg.lineNumber()); if (!msg.extraComment().isEmpty()) { - QString cmt = m_messages[index].extraComment(); + QString cmt = emsg.extraComment(); if (!cmt.isEmpty()) cmt.append(QLatin1String("\n----------\n")); cmt.append(msg.extraComment()); - m_messages[index].setExtraComment(cmt); + emsg.setExtraComment(cmt); + } + if (msg.isUtf8() != emsg.isUtf8()) { + emsg.setUtf8(true); + emsg.setNonUtf8(true); } } } @@ -411,17 +416,53 @@ void Translator::dropTranslations() } } -QList<TranslatorMessage> Translator::findDuplicates() const +QSet<TranslatorMessagePtr> Translator::resolveDuplicates() +{ + QSet<TranslatorMessagePtr> dups; + QHash<TranslatorMessagePtr, int> refs; + 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); + } + if (!omsg.isTranslated() && msg.isTranslated()) + omsg.setTranslations(msg.translations()); + m_messages.removeAt(i); + } else { + refs[msg] = i; + ++i; + } + } + return dups; +} + +void Translator::reportDuplicates(const QSet<TranslatorMessagePtr> &dupes, + const QString &fileName, bool verbose) { - QHash<TranslatorMessage, int> dups; - foreach (const TranslatorMessage &msg, m_messages) - dups[msg]++; - QList<TranslatorMessage> ret; - QHash<TranslatorMessage, int>::ConstIterator it = dups.constBegin(), end = dups.constEnd(); - for (; it != end; ++it) - if (it.value() > 1) - ret.append(it.key()); - return ret; + if (!dupes.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) { + qWarning("\n* Context: %s\n* Source: %s", + qPrintable(msg->context()), + qPrintable(msg->sourceText())); + if (!msg->comment().isEmpty()) + qWarning("* Comment: %s", qPrintable(msg->comment())); + } + qWarning(); + } + } } // Used by lupdate to be able to search using absolute paths during merging @@ -544,7 +585,7 @@ void Translator::setCodecName(const QByteArray &name) if (!codec) { if (!name.isEmpty()) qWarning("No QTextCodec for %s available. Using Latin1\n", name.constData()); - m_codecName.clear(); + m_codecName = "ISO-8859-1"; } else { m_codecName = codec->name(); } diff --git a/tools/linguist/shared/translator.h b/tools/linguist/shared/translator.h index f4279da..6b88b23 100644 --- a/tools/linguist/shared/translator.h +++ b/tools/linguist/shared/translator.h @@ -52,8 +52,6 @@ QT_BEGIN_NAMESPACE -Q_DECLARE_TYPEINFO(TranslatorMessage, Q_MOVABLE_TYPE); - class QIODevice; // A struct of "interesting" data passed to and from the load and save routines @@ -88,7 +86,7 @@ public: QString m_targetFileName; QDir m_sourceDir; QDir m_targetDir; // FIXME: TS spefic - QStringList m_dropTags; // tags to be dropped + QStringList m_dropTags; // tags to be dropped QStringList m_errors; bool m_verbose; bool m_ignoreUnfinished; @@ -129,8 +127,10 @@ public: void stripNonPluralForms(); void stripIdenticalSourceTranslations(); void dropTranslations(); - QList<TranslatorMessage> findDuplicates() const; void makeFileNamesAbsolute(const QDir &originalPath); + QSet<TranslatorMessagePtr> resolveDuplicates(); + static void reportDuplicates(const QSet<TranslatorMessagePtr> &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 ab4301f..afe66fe 100644 --- a/tools/linguist/shared/translatormessage.cpp +++ b/tools/linguist/shared/translatormessage.cpp @@ -54,7 +54,7 @@ QT_BEGIN_NAMESPACE TranslatorMessage::TranslatorMessage() - : m_lineNumber(-1), m_type(Unfinished), m_utf8(false), m_plural(false) + : m_lineNumber(-1), m_type(Unfinished), m_utf8(false), m_nonUtf8(false), m_plural(false) { } @@ -66,7 +66,7 @@ TranslatorMessage::TranslatorMessage(const QString &context, : m_context(context), m_sourcetext(sourceText), m_comment(comment), m_userData(userData), m_translations(translations), m_fileName(fileName), m_lineNumber(lineNumber), - m_type(type), m_utf8(false), m_plural(plural) + m_type(type), m_utf8(false), m_nonUtf8(false), m_plural(plural) { } diff --git a/tools/linguist/shared/translatormessage.h b/tools/linguist/shared/translatormessage.h index fa37ff5..9f847b0 100644 --- a/tools/linguist/shared/translatormessage.h +++ b/tools/linguist/shared/translatormessage.h @@ -136,6 +136,8 @@ public: void setType(Type t) { m_type = t; } bool isUtf8() const { return m_utf8; } // codecForTr override void setUtf8(bool on) { m_utf8 = on; } + bool isNonUtf8() const { return m_nonUtf8; } // codecForTr override + void setNonUtf8(bool on) { m_nonUtf8 = on; } bool isPlural() const { return m_plural; } void setPlural(bool isplural) { m_plural = isplural; } @@ -169,11 +171,40 @@ private: Type m_type; bool m_utf8; + bool m_nonUtf8; bool m_plural; }; +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 diff --git a/tools/linguist/shared/translatortools.cpp b/tools/linguist/shared/translatortools.cpp index dcff546..96301d5 100644 --- a/tools/linguist/shared/translatortools.cpp +++ b/tools/linguist/shared/translatortools.cpp @@ -69,7 +69,7 @@ static int numberLength(const QString &s, int i) int pos = i; do { ++i; - } while (i < s.size() + } while (i < s.size() && (s.at(i).isDigit() || (isDigitFriendly(s[i]) && i + 1 < s.size() @@ -445,7 +445,7 @@ Translator merge(const Translator &tor, const Translator &virginTor, if (mv.sourceText().isEmpty()) { if (tor.contains(mv.context())) continue; - } else { + } else { if (tor.contains(mv.context(), mv.sourceText(), mv.comment())) continue; if (options & HeuristicSimilarText) { diff --git a/tools/linguist/shared/ts.cpp b/tools/linguist/shared/ts.cpp index 2e7d40f..22f2a1b 100644 --- a/tools/linguist/shared/ts.cpp +++ b/tools/linguist/shared/ts.cpp @@ -212,6 +212,7 @@ QString TSReader::readTransContents() bool TSReader::read(Translator &translator) { + STRING(both); STRING(byte); STRING(comment); STRING(context); @@ -241,7 +242,7 @@ bool TSReader::read(Translator &translator) STRING(userdata); STRING(utf8); STRING(value); - STRING(version); + //STRING(version); STRING(yes); static const QString strextrans(QLatin1String("extra-")); @@ -277,7 +278,9 @@ bool TSReader::read(Translator &translator) // ignore these, just whitespace } else if (elementStarts(strdefaultcodec)) { // <defaultcodec> - translator.setCodecName(readElementText().toLatin1()); + const QString &codec = readElementText(); + if (!codec.isEmpty()) + translator.setCodecName(codec.toLatin1()); // </defaultcodec> } else if (isStartElement() && name().toString().startsWith(strextrans)) { @@ -309,7 +312,9 @@ bool TSReader::read(Translator &translator) msg.setContext(context); msg.setType(TranslatorMessage::Finished); msg.setPlural(attributes().value(strnumerus) == stryes); - msg.setUtf8(attributes().value(strutf8) == strtrue + const QStringRef &utf8Attr = attributes().value(strutf8); + msg.setNonUtf8(utf8Attr == strboth); + msg.setUtf8(msg.isNonUtf8() || utf8Attr == strtrue || attributes().value(strencoding) == strUtf8); while (!atEnd()) { readNext(); @@ -373,7 +378,7 @@ bool TSReader::read(Translator &translator) } else if (elementStarts(strtranslation)) { // <translation> QXmlStreamAttributes atts = attributes(); - QStringRef type = atts.value(strtype); + QStringRef type = atts.value(strtype); if (type == strunfinished) msg.setType(TranslatorMessage::Unfinished); else if (type == strobsolete) @@ -440,7 +445,7 @@ static QString protect(const QString &str) QString result; result.reserve(str.length() * 12 / 10); for (int i = 0; i != str.size(); ++i) { - uint c = str.at(i).unicode(); + uint c = str.at(i).unicode(); switch (c) { case '\"': result += QLatin1String("""); @@ -592,107 +597,126 @@ bool saveTS(const Translator &translator, QIODevice &dev, ConversionData &cd, in foreach (const TranslatorMessage &msg, messageOrder[context]) { //msg.dump(); - t << " <message"; - if (!msg.id().isEmpty()) - t << " id=\"" << msg.id() << "\""; - if (format == 11 && !trIsUtf8 && msg.isUtf8()) - t << " encoding=\"UTF-8\""; - if (format == 20 && !trIsUtf8 && msg.isUtf8()) - t << " utf8=\"true\""; - if (msg.isPlural()) - t << " numerus=\"yes\""; - t << ">\n"; - if (translator.locationsType() != Translator::NoLocations) { - QString cfile = currentFile; - bool first = true; - foreach (const TranslatorMessage::Reference &ref, msg.allReferences()) { - QString fn = cd.m_targetDir.relativeFilePath(ref.fileName()) - .replace(QLatin1Char('\\'),QLatin1Char('/')); - int ln = ref.lineNumber(); - QString ld; - if (translator.locationsType() == Translator::RelativeLocations) { - if (ln != -1) { - int dlt = ln - currentLine[fn]; - if (dlt >= 0) - ld.append(QLatin1Char('+')); - ld.append(QString::number(dlt)); - currentLine[fn] = ln; + bool isUtf8 = msg.isUtf8(); + bool second = false; + forever { + + t << " <message"; + if (!msg.id().isEmpty()) + t << " id=\"" << msg.id() << "\""; + if (!trIsUtf8) { + if (format == 11) { + if (isUtf8) + t << " encoding=\"UTF-8\""; + } else { + if (msg.isUtf8()) { + if (msg.isNonUtf8()) + t << " utf8=\"both\""; + else + t << " utf8=\"true\""; } + } + } + if (msg.isPlural()) + t << " numerus=\"yes\""; + t << ">\n"; + if (translator.locationsType() != Translator::NoLocations) { + QString cfile = currentFile; + bool first = true; + foreach (const TranslatorMessage::Reference &ref, msg.allReferences()) { + QString fn = cd.m_targetDir.relativeFilePath(ref.fileName()) + .replace(QLatin1Char('\\'),QLatin1Char('/')); + int ln = ref.lineNumber(); + QString ld; + if (translator.locationsType() == Translator::RelativeLocations) { + if (ln != -1) { + int dlt = ln - currentLine[fn]; + if (dlt >= 0) + ld.append(QLatin1Char('+')); + ld.append(QString::number(dlt)); + currentLine[fn] = ln; + } - if (fn != cfile) { - if (first) - currentFile = fn; - cfile = fn; + if (fn != cfile) { + if (first) + currentFile = fn; + cfile = fn; + } else { + fn.clear(); + } + first = false; } else { - fn.clear(); + if (ln != -1) + ld = QString::number(ln); } - first = false; - } else { - if (ln != -1) - ld = QString::number(ln); + t << " <location"; + if (!fn.isEmpty()) + t << " filename=\"" << fn << "\""; + if (!ld.isEmpty()) + t << " line=\"" << ld << "\""; + t << "/>\n"; } - t << " <location"; - if (!fn.isEmpty()) - t << " filename=\"" << fn << "\""; - if (!ld.isEmpty()) - t << " line=\"" << ld << "\""; - t << "/>\n"; } - } - t << " <source>" - << evilBytes(msg.sourceText(), msg.isUtf8(), format, codecName) - << "</source>\n"; + t << " <source>" + << evilBytes(msg.sourceText(), isUtf8, format, codecName) + << "</source>\n"; - if (format != 11 && !msg.oldSourceText().isEmpty()) - t << " <oldsource>" << protect(msg.oldSourceText()) << "</oldsource>\n"; + if (format != 11 && !msg.oldSourceText().isEmpty()) + t << " <oldsource>" << protect(msg.oldSourceText()) << "</oldsource>\n"; - if (!msg.comment().isEmpty()) { - t << " <comment>" - << evilBytes(msg.comment(), msg.isUtf8(), format, codecName) - << "</comment>\n"; - } + if (!msg.comment().isEmpty()) { + t << " <comment>" + << evilBytes(msg.comment(), isUtf8, format, codecName) + << "</comment>\n"; + } - if (format != 11) { + if (format != 11) { - if (!msg.oldComment().isEmpty()) - t << " <oldcomment>" << protect(msg.oldComment()) << "</oldcomment>\n"; + if (!msg.oldComment().isEmpty()) + t << " <oldcomment>" << protect(msg.oldComment()) << "</oldcomment>\n"; - if (!msg.extraComment().isEmpty()) - t << " <extracomment>" << protect(msg.extraComment()) - << "</extracomment>\n"; + if (!msg.extraComment().isEmpty()) + t << " <extracomment>" << protect(msg.extraComment()) + << "</extracomment>\n"; - if (!msg.translatorComment().isEmpty()) - t << " <translatorcomment>" << protect(msg.translatorComment()) - << "</translatorcomment>\n"; + if (!msg.translatorComment().isEmpty()) + t << " <translatorcomment>" << protect(msg.translatorComment()) + << "</translatorcomment>\n"; - } + } - t << " <translation"; - if (msg.type() == TranslatorMessage::Unfinished) - t << " type=\"unfinished\""; - else if (msg.type() == TranslatorMessage::Obsolete) - t << " type=\"obsolete\""; - if (msg.isPlural()) { - t << ">"; - QStringList translns = translator.normalizedTranslations(msg, cd, &result); - for (int j = 0; j < qMax(1, translns.count()); ++j) { - t << "\n <numerusform"; - writeVariants(t, " ", translns[j]); - t << "</numerusform>"; + t << " <translation"; + if (msg.type() == TranslatorMessage::Unfinished) + t << " type=\"unfinished\""; + else if (msg.type() == TranslatorMessage::Obsolete) + t << " type=\"obsolete\""; + if (msg.isPlural()) { + t << ">"; + QStringList translns = translator.normalizedTranslations(msg, cd, &result); + for (int j = 0; j < qMax(1, translns.count()); ++j) { + t << "\n <numerusform"; + writeVariants(t, " ", translns[j]); + t << "</numerusform>"; + } + t << "\n "; + } else { + writeVariants(t, " ", msg.translation()); } - t << "\n "; - } else { - writeVariants(t, " ", msg.translation()); - } - t << "</translation>\n"; + t << "</translation>\n"; + + if (format != 11) + writeExtras(t, " ", msg.extras(), drops); - if (format != 11) - writeExtras(t, " ", msg.extras(), drops); + if (!msg.userData().isEmpty()) + t << " <userdata>" << msg.userData() << "</userdata>\n"; + t << " </message>\n"; - if (!msg.userData().isEmpty()) - t << " <userdata>" << msg.userData() << "</userdata>\n"; - t << " </message>\n"; + if (format != 11 || second || !msg.isUtf8() || !msg.isNonUtf8()) + break; + isUtf8 = false; + second = true; + } } t << "</context>\n"; } @@ -708,12 +732,12 @@ bool loadTS(Translator &translator, QIODevice &dev, ConversionData &cd) return reader.read(translator); } -bool saveTS11(const Translator &translator, QIODevice &dev, ConversionData &cd) +bool saveTS11(const Translator &translator, QIODevice &dev, ConversionData &cd) { return saveTS(translator, dev, cd, 11); } -bool saveTS20(const Translator &translator, QIODevice &dev, ConversionData &cd) +bool saveTS20(const Translator &translator, QIODevice &dev, ConversionData &cd) { return saveTS(translator, dev, cd, 20); } diff --git a/tools/linguist/shared/ui.cpp b/tools/linguist/shared/ui.cpp index 4b86714..ff98a90 100644 --- a/tools/linguist/shared/ui.cpp +++ b/tools/linguist/shared/ui.cpp @@ -60,7 +60,7 @@ class UiReader : public QXmlDefaultHandler { public: UiReader(Translator &translator, ConversionData &cd) - : m_translator(translator), m_cd(cd), m_lineNumber(-1), + : m_translator(translator), m_cd(cd), m_lineNumber(-1), m_isTrString(false), m_needUtf8(translator.codecName() != "UTF-8") {} @@ -81,6 +81,7 @@ private: QString m_context; QString m_source; QString m_comment; + QString m_extracomment; QXmlLocator *m_locator; QString m_accum; @@ -95,22 +96,27 @@ bool UiReader::startElement(const QString &namespaceURI, Q_UNUSED(namespaceURI); Q_UNUSED(localName); - if (qName == QLatin1String("item")) { + if (qName == QLatin1String("item")) { // UI3 menu entries flush(); - if (!atts.value(QLatin1String("text")).isEmpty()) + if (!atts.value(QLatin1String("text")).isEmpty()) { m_source = atts.value(QLatin1String("text")); + m_isTrString = true; + if (!m_cd.m_noUiLines) + m_lineNumber = m_locator->lineNumber(); + } } else if (qName == QLatin1String("string")) { flush(); if (atts.value(QLatin1String("notr")).isEmpty() || atts.value(QLatin1String("notr")) != QLatin1String("true")) { m_isTrString = true; m_comment = atts.value(QLatin1String("comment")); + m_extracomment = atts.value(QLatin1String("extracomment")); + if (!m_cd.m_noUiLines) + m_lineNumber = m_locator->lineNumber(); } else { m_isTrString = false; } } - if (m_isTrString && !m_cd.m_noUiLines) - m_lineNumber = m_locator->lineNumber(); m_accum.clear(); return true; } @@ -123,15 +129,15 @@ bool UiReader::endElement(const QString &namespaceURI, m_accum.replace(QLatin1String("\r\n"), QLatin1String("\n")); - if (qName == QLatin1String("class")) { + if (qName == QLatin1String("class")) { // UI "header" if (m_context.isEmpty()) m_context = m_accum; } else if (qName == QLatin1String("string") && m_isTrString) { m_source = m_accum; - } else if (qName == QLatin1String("comment")) { + } else if (qName == QLatin1String("comment")) { // FIXME: what's that? m_comment = m_accum; flush(); - } else if (qName == QLatin1String("function")) { + } else if (qName == QLatin1String("function")) { // UI3 embedded code fetchtrInlinedCpp(m_accum, m_translator, m_context); } else { flush(); @@ -151,7 +157,7 @@ bool UiReader::fatalError(const QXmlParseException &exception) msg.sprintf("XML error: Parse error at line %d, column %d (%s).", exception.lineNumber(), exception.columnNumber(), exception.message().toLatin1().data()); - m_cd.appendError(msg); + m_cd.appendError(msg); return false; } @@ -161,12 +167,14 @@ void UiReader::flush() TranslatorMessage msg(m_context, m_source, m_comment, QString(), m_cd.m_sourceFileName, m_lineNumber, QStringList()); + msg.setExtraComment(m_extracomment); if (m_needUtf8 && msg.needs8Bit()) msg.setUtf8(true); m_translator.extend(msg); } m_source.clear(); m_comment.clear(); + m_extracomment.clear(); } bool loadUI(Translator &translator, QIODevice &dev, ConversionData &cd) @@ -188,7 +196,7 @@ bool loadUI(Translator &translator, QIODevice &dev, ConversionData &cd) return result; } -bool saveUI(const Translator &translator, QIODevice &dev, ConversionData &cd) +bool saveUI(const Translator &translator, QIODevice &dev, ConversionData &cd) { Q_UNUSED(dev); Q_UNUSED(translator); |