Commit 20ca5fbe authored by Simon Depiets's avatar Simon Depiets
Browse files

Adding LanguageTool support

This patch adds LanguageTool support in the Unit Metadata widget.
You can launch a LanguageTool verification by pressing Ctrl+J at the moment.
I will add a timer feature which will launch a check automatically after X seconds of inactivity.
This also fixes a %1 escaping i18n issue in a tooltip
And refactoring of UnitMetadata pology feature to make it independent from temporaryNotes.

BUG: 410068
parent 165beb09
......@@ -27,6 +27,7 @@ include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/tm
${CMAKE_CURRENT_SOURCE_DIR}/filesearch
${CMAKE_CURRENT_SOURCE_DIR}/mergemode
${CMAKE_CURRENT_SOURCE_DIR}/languagetool
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
)
......@@ -88,6 +89,12 @@ set(liblokalize_SRCS
webquery/webqueryview.cpp
webquery/webquerycontroller.cpp
webquery/myactioncollectionview.cpp
languagetool/languagetoolresultjob.cpp
languagetool/languagetoolmanager.cpp
languagetool/languagetoolparser.cpp
languagetool/languagetoolgrammarerror.cpp
tools/widgettextcaptureconfig.cpp
filesearch/filesearchtab.cpp
......@@ -137,6 +144,7 @@ ki18n_wrap_ui(liblokalize_SRCS
prefs/prefs_appearance.ui
prefs/prefs_tm.ui
prefs/prefs_pology.ui
prefs/prefs_languagetool.ui
project/prefs_project_advanced.ui
project/prefs_project_local.ui
project/prefs_projectmain.ui
......
......@@ -278,6 +278,8 @@ void EditorTab::setupActions()
connect(m_view, &EditorView::signalChanged, m_notesView, &MsgCtxtView::removeErrorNotes);
connect(m_notesView, &MsgCtxtView::escaped, this, &EditorTab::setProperFocus);
connect((const TranslationUnitTextEdit*)m_view->viewPort(), &TranslationUnitTextEdit::languageToolChanged, m_notesView, &MsgCtxtView::languageTool);
action = edit->addAction(QStringLiteral("edit_addnote"), m_notesView, SLOT(addNoteUI()));
//action->setShortcut(Qt::CTRL+glist[i]);
action->setText(i18nc("@action:inmenu", "Add a note"));
......@@ -541,6 +543,10 @@ void EditorTab::setupActions()
ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_T));
action->setText(i18nc("@action:inmenu", "Insert Tag"));
action = edit->addAction(QStringLiteral("edit_languagetool"), m_view->viewPort(), SLOT(launchLanguageTool()));
ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_J));
action->setText(i18nc("@action:inmenu", "Check this unit using LanguageTool"));
action = edit->addAction(QStringLiteral("edit_tagimmediate"), m_view->viewPort(), SLOT(tagImmediate()));
ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_M));
action->setText(i18nc("@action:inmenu", "Insert Next Tag"));
......
......@@ -64,6 +64,7 @@
<Action name="edit_tagmenu"/>
<Action name="edit_tagimmediate"/>
<Action name="edit_spellreplace"/>
<Action name="edit_languagetool"/>
<Action name="edit_addnote"/>
<Menu name="glossary"><text>&amp;Glossary</text>
<Action name="glossary_define"/>
......
/*
Copyright (C) 2019-2020 Laurent Montel <montel@kde.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "languagetoolgrammarerror.h"
#include "lokalize_debug.h"
#include "languagetoolmanager.h"
#include <klocalizedstring.h>
#include <QJsonArray>
LanguageToolGrammarError::LanguageToolGrammarError()
{
}
LanguageToolGrammarError::~LanguageToolGrammarError()
{
}
QString LanguageToolGrammarError::parse(const QJsonObject &obj, const QString &text)
{
QString mError = obj[QStringLiteral("message")].toString();
int mStart = obj[QStringLiteral("offset")].toInt(-1);
int mLength = obj[QStringLiteral("length")].toInt(-1);
QStringList mSuggestions = parseSuggestion(obj);
/*
const QJsonObject rulesObj = obj[QStringLiteral("rule")].toObject();
if (!rulesObj.isEmpty()) {
QString mRule = rulesObj[QStringLiteral("id")].toString();
const QJsonArray urlArray = rulesObj[QStringLiteral("urls")].toArray();
if (!urlArray.isEmpty()) {
if (urlArray.count() > 1) {
qCWarning(LOKALIZE_LOG) << "LanguageToolGrammarError::parse : more than 1 url found. Perhaps need to adapt api ";
}
QString mUrl = urlArray.at(0)[QLatin1String("value")].toString();
//qDebug() << " mUrl" << mUrl;
}
}*/
QString result = mError;
if (mLength > 0)
result = result + QStringLiteral(" (") + text.mid(mStart, mLength) + QStringLiteral(")");
if (mSuggestions.count() > 0)
result = result + QStringLiteral("\n") + i18n("Suggestions:") + QStringLiteral(" ") + mSuggestions.join(QStringLiteral(", "));
return result + QStringLiteral("\n\n");
}
void LanguageToolGrammarError::setTesting(bool b)
{
mTesting = b;
}
QStringList LanguageToolGrammarError::parseSuggestion(const QJsonObject &obj)
{
QStringList lst;
const QJsonArray array = obj[QStringLiteral("replacements")].toArray();
for (const QJsonValue &current : array) {
if (current.type() == QJsonValue::Object) {
const QJsonObject suggestionObject = current.toObject();
lst.append(suggestionObject[QLatin1String("value")].toString());
}
}
//qDebug() << " lst : " << lst;
return lst;
}
/*
Copyright (C) 2019-2020 Laurent Montel <montel@kde.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef LANGUAGETOOLGRAMMARERROR_H
#define LANGUAGETOOLGRAMMARERROR_H
#include <QJsonObject>
#include <QStringList>
class LanguageToolGrammarError
{
public:
LanguageToolGrammarError();
~LanguageToolGrammarError();
QString parse(const QJsonObject &obj, const QString &text);
void setTesting(bool b);
private:
static QStringList parseSuggestion(const QJsonObject &obj);
bool mTesting = false;
};
#endif // LANGUAGETOOLGRAMMARERROR_H
/*
Copyright (C) 2019-2020 Laurent Montel <montel@kde.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "languagetoolmanager.h"
#include "prefs_lokalize.h"
#include <KConfigGroup>
#include <KSharedConfig>
#include <QNetworkAccessManager>
#include <QColor>
#include <QRandomGenerator>
LanguageToolManager::LanguageToolManager(QObject *parent)
: QObject(parent)
, mNetworkAccessManager(new QNetworkAccessManager(this))
{
mNetworkAccessManager->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
mNetworkAccessManager->setStrictTransportSecurityEnabled(true);
mNetworkAccessManager->enableStrictTransportSecurityStore(true);
}
LanguageToolManager::~LanguageToolManager()
{
}
LanguageToolManager *LanguageToolManager::self()
{
static LanguageToolManager s_self;
return &s_self;
}
QNetworkAccessManager *LanguageToolManager::networkAccessManager() const
{
return mNetworkAccessManager;
}
QString LanguageToolManager::languageToolCheckPath() const
{
return (Settings::self()->languageToolCustom() ?
Settings::self()->languageToolInstancePath() :
QStringLiteral("https://languagetool.org/api/v2")
) + QStringLiteral("/check");
}
/*
Copyright (C) 2019-2020 Laurent Montel <montel@kde.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef LANGUAGETOOLMANAGER_H
#define LANGUAGETOOLMANAGER_H
#include <QObject>
#include <QHash>
class QColor;
class QNetworkAccessManager;
class LanguageToolManager : public QObject
{
Q_OBJECT
public:
explicit LanguageToolManager(QObject *parent = nullptr);
~LanguageToolManager();
static LanguageToolManager *self();
QNetworkAccessManager *networkAccessManager() const;
Q_REQUIRED_RESULT QString languageToolCheckPath() const;
private:
Q_DISABLE_COPY(LanguageToolManager)
QNetworkAccessManager *mNetworkAccessManager = nullptr;
};
#endif // LANGUAGETOOLMANAGER_H
/*
Copyright (C) 2019-2020 Laurent Montel <montel@kde.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "languagetoolgrammarerror.h"
#include "languagetoolparser.h"
#include "lokalize_debug.h"
#include <klocalizedstring.h>
#include <QJsonArray>
LanguageToolParser::LanguageToolParser()
{
}
LanguageToolParser::~LanguageToolParser()
{
}
QString LanguageToolParser::parseResult(const QJsonObject &obj, const QString &text) const
{
QString infos;
const QJsonArray array = obj.value(QLatin1String("matches")).toArray();
for (const QJsonValue &current : array) {
//qDebug() << " current " << current;
if (current.type() == QJsonValue::Object) {
const QJsonObject languageToolObject = current.toObject();
LanguageToolGrammarError error;
infos.append(error.parse(languageToolObject, text));
}
}
if (infos.length() == 0)
infos = i18n("No errors");
return infos;
}
/*QVector<GrammarError> LanguageToolParser::parseResult(const QJsonObject &obj) const
{
QVector<GrammarError> infos;
const QJsonArray array = obj.value(QLatin1String("matches")).toArray();
for (const QJsonValue &current : array) {
//qDebug() << " current " << current;
if (current.type() == QJsonValue::Object) {
const QJsonObject languageToolObject = current.toObject();
LanguageToolGrammarError error;
error.parse(languageToolObject, 0);
if (error.isValid()) {
infos.append(error);
}
}
}
return infos;
}*/
/*
Copyright (C) 2019-2020 Laurent Montel <montel@kde.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef LANGUAGETOOLPARSER_H
#define LANGUAGETOOLPARSER_H
class LanguageToolParser
{
public:
LanguageToolParser();
~LanguageToolParser();
QString parseResult(const QJsonObject &obj, const QString &text) const;
};
#endif // LANGUAGETOOLPARSER_H
/*
Copyright (C) 2019-2020 Laurent Montel <montel@kde.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "languagetoolresultjob.h"
#include "lokalize_debug.h"
#include <QNetworkAccessManager>
#include <QNetworkReply>
LanguageToolResultJob::LanguageToolResultJob(QObject *parent)
: QObject(parent)
{
}
LanguageToolResultJob::~LanguageToolResultJob()
{
}
static bool hasNotEmptyText(const QString &text)
{
for (int i = 0; i < text.length(); ++i) {
if (!text.at(i).isSpace()) {
return true;
}
}
return false;
}
bool LanguageToolResultJob::canStart() const
{
return canStartError() == LanguageToolResultJob::JobError::NotError;
}
LanguageToolResultJob::JobError LanguageToolResultJob::canStartError() const
{
if (!mNetworkAccessManager) {
return LanguageToolResultJob::JobError::NetworkManagerNotDefined;
}
if (!hasNotEmptyText(mText)) {
return LanguageToolResultJob::JobError::EmptyText;
}
if (mUrl.isEmpty()) {
return LanguageToolResultJob::JobError::UrlNotDefined;
}
if (mLanguage.isEmpty()) {
return LanguageToolResultJob::JobError::LanguageNotDefined;
}
return LanguageToolResultJob::JobError::NotError;
}
void LanguageToolResultJob::start()
{
const LanguageToolResultJob::JobError errorType = canStartError();
switch (errorType) {
case LanguageToolResultJob::JobError::EmptyText:
return;
case LanguageToolResultJob::JobError::UrlNotDefined:
case LanguageToolResultJob::JobError::NetworkManagerNotDefined:
case LanguageToolResultJob::JobError::LanguageNotDefined:
qCWarning(LOKALIZE_LOG) << "Impossible to start language tool";
return;
case LanguageToolResultJob::JobError::NotError:
break;
}
QNetworkRequest request(QUrl::fromUserInput(mUrl));
addRequestAttribute(request);
const QByteArray ba = "text=" + mText.toUtf8() + "&language=" + mLanguage.toLatin1();
QNetworkReply *reply = mNetworkAccessManager->post(request, ba);
connect(reply, &QNetworkReply::finished, this, &LanguageToolResultJob::slotCheckGrammarFinished);
connect(mNetworkAccessManager, &QNetworkAccessManager::finished, this, &LanguageToolResultJob::slotFinish);
}
void LanguageToolResultJob::slotFinish(QNetworkReply *reply)
{
#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0))
if (reply->error() != QNetworkReply::NoError) {
#else
if (reply->networkError() != QNetworkReply::NoError) {
#endif
qCWarning(LOKALIZE_LOG) << " Error reply - "<<reply->errorString();
Q_EMIT error(reply->errorString());
}
}
void LanguageToolResultJob::slotCheckGrammarFinished()
{
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
if (reply) {
const QByteArray data = reply->readAll();
Q_EMIT finished(QString::fromUtf8(data));
}
reply->deleteLater();
deleteLater();
}
void LanguageToolResultJob::addRequestAttribute(QNetworkRequest &request) const
{
request.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/x-www-form-urlencoded"));
}
QString LanguageToolResultJob::language() const
{
return mLanguage;
}
void LanguageToolResultJob::setLanguage(const QString &language)
{
mLanguage = language;
}
QString LanguageToolResultJob::url() const
{
return mUrl;
}
void LanguageToolResultJob::setUrl(const QString &url)
{
mUrl = url;
}
QStringList LanguageToolResultJob::arguments() const
{
return mArguments;
}
void LanguageToolResultJob::setArguments(const QStringList &arguments)
{
mArguments = arguments;
}
QNetworkAccessManager *LanguageToolResultJob::networkAccessManager() const
{
return mNetworkAccessManager;
}
void LanguageToolResultJob::setNetworkAccessManager(QNetworkAccessManager *networkAccessManager)
{
mNetworkAccessManager = networkAccessManager;
}
QString LanguageToolResultJob::text() const
{
return mText;
}
void LanguageToolResultJob::setText(const QString &text)
{
mText = text;
}
/*
Copyright (C) 2019-2020 Laurent Montel <montel@kde.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef LANGUAGETOOLRESULTJOB_H
#define LANGUAGETOOLRESULTJOB_H
#include <QObject>
class QNetworkRequest;
class QNetworkReply;
class QNetworkAccessManager;
class LanguageToolResultJob : public QObject
{
Q_OBJECT
public:
explicit LanguageToolResultJob(QObject *parent = nullptr);
~LanguageToolResultJob();
bool canStart() const;
void start();
Q_REQUIRED_RESULT QStringList arguments() const;
void setArguments(const QStringList &arguments);
QNetworkAccessManager *networkAccessManager() const;
void setNetworkAccessManager(QNetworkAccessManager *networkAccessManager);
Q_REQUIRED_RESULT QString text() const;
void setText(const QString &text);
Q_REQUIRED_RESULT QString url() const;
void setUrl(const QString &url);
Q_REQUIRED_RESULT QString language() const;
void setLanguage(const QString &language);
Q_SIGNALS:
void finished(const QString &result);
void error(const QString &errorStr);
private:
Q_DISABLE_COPY(LanguageToolResultJob)
enum class JobError {
NotError,
EmptyText,