Index: src/catalog/gettext/gettextexport.h =================================================================== --- src/catalog/gettext/gettextexport.h +++ src/catalog/gettext/gettextexport.h @@ -69,7 +69,12 @@ * Write a PO keyword (msgctxt, msgid, msgstr, msgstr_plural, msgstr[0]) and the corresponding text. * This includes wrapping the text. */ - void writeKeyword(QTextStream& stream, const QString& keyword, QString text, bool containsHtml = true, bool startedWithEmptyLine = false) const; + void writeKeyword(QTextStream& stream, const QString& keyword, QString text, bool startedWithEmptyLine = false) const; + + /** + * Tokenize text into list of tokens and delimiters. List may contain empty tokens. + */ + static QStringList tokenize(const QString &text); public: /** Index: src/catalog/gettext/gettextexport.cpp =================================================================== --- src/catalog/gettext/gettextexport.cpp +++ src/catalog/gettext/gettextexport.cpp @@ -45,8 +45,8 @@ #include #include #include -#include #include +#include using namespace GettextCatalog; @@ -102,19 +102,19 @@ if (! msgctxt.isEmpty() || catalogItem.keepEmptyMsgCtxt()) writeKeyword(stream, QStringLiteral("msgctxt"), msgctxt); - writeKeyword(stream, QStringLiteral("msgid"), catalogItem.msgid(), true, catalogItem.prependEmptyForMsgid()); + writeKeyword(stream, QStringLiteral("msgid"), catalogItem.msgid(), catalogItem.prependEmptyForMsgid()); if (catalogItem.isPlural()) - writeKeyword(stream, QStringLiteral("msgid_plural"), catalogItem.msgid(1), true, catalogItem.prependEmptyForMsgid()); + writeKeyword(stream, QStringLiteral("msgid_plural"), catalogItem.msgid(1), catalogItem.prependEmptyForMsgid()); if (!catalogItem.isPlural()) - writeKeyword(stream, QStringLiteral("msgstr"), catalogItem.msgstr(), true, catalogItem.prependEmptyForMsgstr()); + writeKeyword(stream, QStringLiteral("msgstr"), catalogItem.msgstr(), catalogItem.prependEmptyForMsgstr()); else { qCDebug(LOKALIZE_LOG) << "Saving gettext plural form"; //TODO check len of the actual stringlist?? const int forms = catalog->numberOfPluralForms(); for (int i = 0; i < forms; ++i) { QString keyword = QStringLiteral("msgstr[") + QString::number(i) + ']'; - writeKeyword(stream, keyword, catalogItem.msgstr(i), true, catalogItem.prependEmptyForMsgstr()); + writeKeyword(stream, keyword, catalogItem.msgstr(i), catalogItem.prependEmptyForMsgstr()); } } } @@ -142,6 +142,24 @@ return OK; } +QStringList GettextExportPlugin::tokenize(const QString &text) { + QStringList tokens; + int lastpos = 0; + + auto finder = new QTextBoundaryFinder(QTextBoundaryFinder::Line, text); + while (finder->toNextBoundary() != -1) { + QString token = text.mid(lastpos, finder->position() - lastpos); + // Hack to mocking gettext's write-po.c wrapping algorithm: + // "Don't break immediately before the "\n" at the end." + if (token.indexOf(QStringLiteral("\\n")) == 0 && !tokens.isEmpty()) { + token = tokens.takeLast() + token; + } + tokens << token; + lastpos = finder->position(); + } + return(tokens); +} + void GettextExportPlugin::writeComment(QTextStream& stream, const QString& comment) const { if (!comment.isEmpty()) { @@ -182,7 +200,7 @@ } } -void GettextExportPlugin::writeKeyword(QTextStream& stream, const QString& keyword, QString text, bool containsHtml, bool startedWithEmptyLine) const +void GettextExportPlugin::writeKeyword(QTextStream& stream, const QString& keyword, QString text, bool startedWithEmptyLine) const { if (text.isEmpty()) { // Whatever the wrapping mode, an empty line is an empty line @@ -224,78 +242,48 @@ } else if (m_wrapWidth <= 3) { // No change in wrapping QStringList list = text.split('\n'); - if (list.count() > 1 || startedWithEmptyLine /* || keyword.length()+3+text.length()>=80*/) + if (list.count() > 1 || startedWithEmptyLine) list.prepend(QString()); stream << keyword << QStringLiteral(" "); - QStringList::const_iterator it; - for (it = list.constBegin(); it != list.constEnd(); ++it) - stream << QStringLiteral("\"") << (*it) << QStringLiteral("\"\n"); + for (QString& it : list) + stream << QStringLiteral("\"") << (it) << QStringLiteral("\"\n"); return; } - // lazy wrapping - QStringList list = text.split('\n', QString::SkipEmptyParts); - - if (text.startsWith('\n')) - list.prepend(QString()); - - if (list.isEmpty()) - list.append(QString()); + int max = m_wrapWidth - 2; - //static QRegExp breakStopReForHtml("[ >.%/:,]", Qt::CaseSensitive, QRegExp::Wildcard); - //static QRegExp breakStopReForText("[ .%/:,]", Qt::CaseSensitive, QRegExp::Wildcard); - static QRegExp breakStopReForHtml(QStringLiteral("[ >%]"), Qt::CaseSensitive, QRegExp::Wildcard); - static QRegExp breakStopReForText(QStringLiteral("[ &%]"), Qt::CaseSensitive, QRegExp::Wildcard); - QRegExp breakStopRe = containsHtml ? breakStopReForHtml : breakStopReForText; + // Remove newlines and re-add them where they needed + text.remove(QStringLiteral("\n")); + text.replace(QStringLiteral("\\n"), QStringLiteral("\\n\n")); + QStringList list = text.split(QStringLiteral("\n"), QString::KeepEmptyParts); + QStringList wrapped_list; - int max = m_wrapWidth - 2; - bool prependedEmptyLine = false; - QStringList::iterator itm; - for (itm = list.begin(); itm != list.end(); ++itm) { - if (list.count() == 1 && keyword.length() + 1 + itm->length() >= max) { - prependedEmptyLine = true; - itm = list.insert(itm, QString()); - } + // Iterate each newline string + for (const QString& it : list) { + QStringList words = tokenize(it); + QString wrapped_string; - if (itm->length() > max) { - int pos = itm->lastIndexOf(breakStopRe, max - 1); - if (pos > (max / 2)) { - int pos2 = itm->indexOf(QLatin1Char('<'), pos); - if (pos2 > 0 && pos2 < max - 1) { - pos = itm->indexOf(QLatin1Char('<'), pos); - ++pos; - } + for (const QString& word : words) { + if (wrapped_string.length() + word.length() < max) { + wrapped_string += word; } else { - if (itm->at(max - 1) == QLatin1Char('\\')) { - do { - --max; - } while (max >= 2 && itm->at(max - 1) == QLatin1Char('\\')); - } - pos = max; - //Restore the max variable to the m_wordWrap - 2 value - max = m_wrapWidth - 2; - } - //itm=list.insert(itm,itm->left(pos)); - QString t = *itm; - itm = list.insert(itm, t); - ++itm; - if (itm != list.end()) { - (*itm) = itm->remove(0, pos); - --itm; - if (itm != list.end()) - itm->truncate(pos); + wrapped_list.append(wrapped_string); + wrapped_string = word; } } + if (!wrapped_string.isEmpty()) { + wrapped_list.append(wrapped_string); + } } - if (!prependedEmptyLine && list.count() > 1) - list.prepend(QString()); + if (wrapped_list.count() > 1 || keyword.length() + 1 + wrapped_list.first().length() >= max) { + wrapped_list.prepend(QString()); + } stream << keyword << QStringLiteral(" "); - - QStringList::const_iterator it; - for (it = list.constBegin(); it != list.constEnd(); ++it) - stream << QStringLiteral("\"") << (*it) << QStringLiteral("\"\n"); + for (const QString& it : wrapped_list) { + stream << QStringLiteral("\"") << (it) << QStringLiteral("\"\n"); + } }