Füge Schaltfläche zum erneuten Übersetzen hinzu und verbessere das Layout der Einstellungen
This commit is contained in:
122
mainwindow.cpp
122
mainwindow.cpp
@@ -25,6 +25,7 @@
|
||||
#include <QLocale>
|
||||
#include <QMenu>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QPushButton>
|
||||
#include <QScrollArea>
|
||||
#include <QSet>
|
||||
#include <QScrollBar>
|
||||
@@ -45,15 +46,18 @@ public:
|
||||
protected:
|
||||
void contextMenuEvent(QContextMenuEvent *event) override
|
||||
{
|
||||
QTextCursor wordCursor = cursorForPosition(event->pos());
|
||||
wordCursor.select(QTextCursor::WordUnderCursor);
|
||||
if (!wordCursor.hasSelection()) {
|
||||
QTextCursor replacementCursor = textCursor();
|
||||
if (!replacementCursor.hasSelection()) {
|
||||
replacementCursor = cursorForPosition(event->pos());
|
||||
replacementCursor.select(QTextCursor::WordUnderCursor);
|
||||
}
|
||||
if (!replacementCursor.hasSelection()) {
|
||||
QPlainTextEdit::contextMenuEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
const int wordStart = wordCursor.selectionStart();
|
||||
const int wordEnd = wordCursor.selectionEnd();
|
||||
const int wordStart = replacementCursor.selectionStart();
|
||||
const int wordEnd = replacementCursor.selectionEnd();
|
||||
QMenu *menu = createStandardContextMenu();
|
||||
QMenu *replacementMenu = menu->addMenu(tr("Durch Wort aus Original ersetzen"));
|
||||
QSet<QString> seenWords;
|
||||
@@ -185,11 +189,8 @@ QString MainWindow::libreTranslateUrl(const QString &path) const
|
||||
void MainWindow::on_translationProviderCombo_currentIndexChanged(int)
|
||||
{
|
||||
const bool libreTranslate = isLibreTranslateProvider();
|
||||
ui->label_9->setVisible(!libreTranslate);
|
||||
ui->deeplApiKey->setVisible(!libreTranslate);
|
||||
ui->checkDeeplUsageButton->setVisible(!libreTranslate);
|
||||
ui->libreTranslateUrlLabel->setVisible(libreTranslate);
|
||||
ui->libreTranslateUrl->setVisible(libreTranslate);
|
||||
ui->deeplSettingsWidget->setVisible(!libreTranslate);
|
||||
ui->libreSettingsWidget->setVisible(libreTranslate);
|
||||
setConfigValue("translation-provider", ui->translationProviderCombo->currentText());
|
||||
if (libreTranslate) {
|
||||
loadLibreTranslateLanguages();
|
||||
@@ -306,6 +307,8 @@ void MainWindow::populateTreeWidgetFromMap() {
|
||||
QString fileName = QFileInfo(pair.first).fileName();
|
||||
fileItem->setText(0, fileName);
|
||||
fileItem->setData(0, Qt::UserRole, false);
|
||||
// Keep the full path: identical file names can exist in different subdirectories.
|
||||
fileItem->setData(0, Qt::UserRole + 1, pair.first);
|
||||
auto parsedBlocks = parseTextBlock(pair.second);
|
||||
for (const auto &block : std::as_const(parsedBlocks)) {
|
||||
QTreeWidgetItem *blockItem = new QTreeWidgetItem(fileItem);
|
||||
@@ -493,9 +496,11 @@ bool MainWindow::saveFile(QTreeWidgetItem *fileItem, QString &errorMessage)
|
||||
{
|
||||
const QString projectDir = ui->projectDir->text();
|
||||
const QString fileName = fileItem->text(0);
|
||||
const QString filePath = QString("%1/game/tl/%2/%3").arg(projectDir, ui->languageCombo->currentText(), fileName);
|
||||
const QString backupFileName = QString("%1/game/tl/%2/%3.bak.%4")
|
||||
.arg(projectDir, ui->languageCombo->currentText(), fileName, QDateTime::currentDateTime().toString("yyyyMMddHHmmss"));
|
||||
const QString filePath = fileItem->data(0, Qt::UserRole + 1).toString().isEmpty()
|
||||
? QString("%1/game/tl/%2/%3").arg(projectDir, ui->languageCombo->currentText(), fileName)
|
||||
: fileItem->data(0, Qt::UserRole + 1).toString();
|
||||
const QString backupFileName = QString("%1.bak.%2")
|
||||
.arg(filePath, QDateTime::currentDateTime().toString("yyyyMMddHHmmss"));
|
||||
if (!createBackup(filePath, backupFileName)) {
|
||||
errorMessage = filePath + ": Failed to create backup";
|
||||
return false;
|
||||
@@ -543,7 +548,7 @@ bool MainWindow::editAndSaveFile(QTreeWidgetItem *fileItem, const QString &backu
|
||||
bool MainWindow::parseAndEditFile(QFile &backupFile, QTreeWidgetItem *fileItem, QStringList &lines, bool &changed) {
|
||||
QTextStream backupStream(&backupFile);
|
||||
QString content = backupStream.readAll();
|
||||
static QRegularExpression translateRegex(R"(# game\/(.+):(\d+)\ntranslate german (.*?)\n\n\s*#.*?\"(.*?)\"\s*\n\s*(.*?)\s*\"(.*?)\")");
|
||||
static QRegularExpression translateRegex(R"(# game\/(.+):(\d+)\ntranslate\s+[^\s]+\s+(.*?)\n\n\s*#.*?\"(.*?)\"\s*\n\s*(.*?)\s*\"(.*?)\")");
|
||||
static QRegularExpression oldNewRegex(R"(\n\s*#\s*game\/(.+?):(\d+?)\n\s*old\s*\"(.+?)\"\n\s*new\s*\"(.*?)\")");
|
||||
QRegularExpressionMatch match;
|
||||
int offset = 0;
|
||||
@@ -625,6 +630,9 @@ void MainWindow::renderDeeplSources()
|
||||
for (const auto& pair : translationsMap) {
|
||||
ui->deeplTranslateFrom->addItem(pair.first);
|
||||
}
|
||||
if (isLibreTranslateProvider() && ui->deeplTranslateFrom->findText("en") >= 0) {
|
||||
ui->deeplTranslateFrom->setCurrentText("en");
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::onDeeplTranslationPossibilitiesLoaded(QNetworkReply *reply) {
|
||||
@@ -706,6 +714,9 @@ void MainWindow::on_deeplTranslateFrom_currentTextChanged(const QString &sourceL
|
||||
targetLang.replace("\"", "'");
|
||||
ui->deeplTranslateTo->addItem(targetLang);
|
||||
}
|
||||
if (isLibreTranslateProvider() && ui->deeplTranslateTo->findText("de") >= 0) {
|
||||
ui->deeplTranslateTo->setCurrentText("de");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -765,6 +776,15 @@ void MainWindow::on_autoTranslateButton_clicked() {
|
||||
manager->post(request, QJsonDocument(jsonData).toJson());
|
||||
}
|
||||
|
||||
void MainWindow::on_retryTranslationButton_clicked()
|
||||
{
|
||||
if (ui->originalTextEdit->text().trimmed().isEmpty()) {
|
||||
QMessageBox::information(this, tr("Information"), tr("Bitte wähle zuerst einen Übersetzungseintrag aus."));
|
||||
return;
|
||||
}
|
||||
on_autoTranslateButton_clicked();
|
||||
}
|
||||
|
||||
void MainWindow::on_bulkAutoTranslateButton_clicked()
|
||||
{
|
||||
bulkUsesLibreTranslate = isLibreTranslateProvider();
|
||||
@@ -938,6 +958,11 @@ void MainWindow::handleBulkTranslationReply(QNetworkReply *reply, int firstItem,
|
||||
BulkTranslationItem &item = bulkTranslations[firstItem + index];
|
||||
item.translatedText = restoreBulkPlaceholders(translatedText, item.placeholders);
|
||||
item.translatedText.replace('"', '\'');
|
||||
// An unchanged response normally indicates that the selected local model
|
||||
// cannot translate this language pair. Keep it open for a later retry.
|
||||
if (item.translatedText.trimmed() == item.originalText.trimmed()) {
|
||||
item.translatedText.clear();
|
||||
}
|
||||
}
|
||||
ui->statusbar->showMessage(tr("Übersetzt: %1 von %2 …").arg(nextBulkTranslationIndex).arg(bulkTranslations.size()));
|
||||
requestNextBulkTranslationBatch();
|
||||
@@ -960,10 +985,14 @@ void MainWindow::showBulkTranslationDialog()
|
||||
QVBoxLayout *contentLayout = new QVBoxLayout(content);
|
||||
QVector<OriginalWordReplacementEdit *> edits;
|
||||
QVector<QPair<QWidget *, QTreeWidgetItem *>> fileSections;
|
||||
const bool dialogUsesLibreTranslate = bulkUsesLibreTranslate;
|
||||
const QString dialogSourceLanguage = ui->deeplTranslateFrom->currentText();
|
||||
const QString dialogTargetLanguage = ui->deeplTranslateTo->currentText();
|
||||
edits.reserve(bulkTranslations.size());
|
||||
QTreeWidgetItem *currentFile = nullptr;
|
||||
QVBoxLayout *fileLayout = nullptr;
|
||||
for (const BulkTranslationItem &item : std::as_const(bulkTranslations)) {
|
||||
for (int bulkIndex = 0; bulkIndex < bulkTranslations.size(); ++bulkIndex) {
|
||||
BulkTranslationItem &item = bulkTranslations[bulkIndex];
|
||||
QTreeWidgetItem *fileItem = item.item->parent();
|
||||
if (fileItem != currentFile) {
|
||||
if (currentFile) {
|
||||
@@ -984,15 +1013,72 @@ void MainWindow::showBulkTranslationDialog()
|
||||
fileSections.append(qMakePair(fileSection, fileItem));
|
||||
}
|
||||
|
||||
QLabel *originalLabel = new QLabel(item.originalText, content);
|
||||
auto *originalRow = new QWidget(content);
|
||||
auto *originalRowLayout = new QHBoxLayout(originalRow);
|
||||
originalRowLayout->setContentsMargins(0, 0, 0, 0);
|
||||
QLabel *originalLabel = new QLabel(item.originalText, originalRow);
|
||||
originalLabel->setWordWrap(true);
|
||||
originalLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
originalLabel->setStyleSheet(QStringLiteral("font-weight: 600; padding-top: 6px;"));
|
||||
fileLayout->addWidget(originalLabel);
|
||||
originalRowLayout->addWidget(originalLabel, 1);
|
||||
|
||||
auto *translationEdit = new OriginalWordReplacementEdit(item.originalText, content);
|
||||
translationEdit->setPlainText(item.translatedText);
|
||||
translationEdit->setPlaceholderText(tr("Übersetzung"));
|
||||
translationEdit->setPlaceholderText(tr("Keine Übersetzung erhalten – später erneut übersetzen oder manuell eintragen."));
|
||||
auto *retryButton = new QPushButton(tr("Neu übersetzen"), originalRow);
|
||||
retryButton->setToolTip(tr("Fordert für diesen einzelnen Text einen neuen Vorschlag an."));
|
||||
originalRowLayout->addWidget(retryButton);
|
||||
connect(retryButton, &QPushButton::clicked, &dialog,
|
||||
[this, retryButton, translationEdit, bulkIndex, dialogUsesLibreTranslate, dialogSourceLanguage, dialogTargetLanguage, &dialog] {
|
||||
BulkTranslationItem &retryItem = bulkTranslations[bulkIndex];
|
||||
QVector<QString> placeholders;
|
||||
const QString protectedText = protectBulkPlaceholders(retryItem.originalText, placeholders);
|
||||
retryButton->setEnabled(false);
|
||||
|
||||
QJsonObject payload;
|
||||
if (dialogUsesLibreTranslate) {
|
||||
payload["q"] = protectedText;
|
||||
payload["source"] = dialogSourceLanguage;
|
||||
payload["target"] = dialogTargetLanguage;
|
||||
payload["format"] = "text";
|
||||
} else {
|
||||
payload["text"] = QJsonArray::fromStringList({protectedText});
|
||||
payload["source_lang"] = dialogSourceLanguage;
|
||||
payload["target_lang"] = dialogTargetLanguage;
|
||||
payload["formality"] = "less";
|
||||
payload["tag_handling"] = "xml";
|
||||
payload["tag_handling_version"] = "v2";
|
||||
}
|
||||
QNetworkRequest request(QUrl(dialogUsesLibreTranslate
|
||||
? libreTranslateUrl("/translate")
|
||||
: QStringLiteral("https://api-free.deepl.com/v2/translate")));
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
if (!dialogUsesLibreTranslate) {
|
||||
request.setRawHeader("Authorization", "DeepL-Auth-Key " + ui->deeplApiKey->text().toUtf8());
|
||||
}
|
||||
auto *manager = new QNetworkAccessManager(&dialog);
|
||||
connect(manager, &QNetworkAccessManager::finished, &dialog,
|
||||
[this, manager, retryButton, translationEdit, bulkIndex, placeholders, dialogUsesLibreTranslate](QNetworkReply *reply) {
|
||||
retryButton->setEnabled(true);
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
QMessageBox::warning(this, tr("Übersetzung fehlgeschlagen"), reply->errorString());
|
||||
} else {
|
||||
const QJsonObject response = QJsonDocument::fromJson(reply->readAll()).object();
|
||||
const QJsonArray translations = response.value("translations").toArray();
|
||||
QString translatedText = dialogUsesLibreTranslate
|
||||
? response.value("translatedText").toString()
|
||||
: (!translations.isEmpty() ? translations.at(0).toObject().value("text").toString() : QString());
|
||||
translatedText = restoreBulkPlaceholders(translatedText, placeholders);
|
||||
translatedText.replace('"', '\'');
|
||||
bulkTranslations[bulkIndex].translatedText = translatedText;
|
||||
translationEdit->setPlainText(translatedText);
|
||||
}
|
||||
reply->deleteLater();
|
||||
manager->deleteLater();
|
||||
});
|
||||
manager->post(request, QJsonDocument(payload).toJson());
|
||||
});
|
||||
fileLayout->addWidget(originalRow);
|
||||
fileLayout->addWidget(translationEdit);
|
||||
edits.append(translationEdit);
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ private slots:
|
||||
void onDeeplTranslationPossibilitiesLoaded(QNetworkReply *reply);
|
||||
void on_deeplTranslateFrom_currentTextChanged(const QString &sourceLanguage);
|
||||
void on_autoTranslateButton_clicked();
|
||||
void on_retryTranslationButton_clicked();
|
||||
void on_bulkAutoTranslateButton_clicked();
|
||||
void on_checkDeeplUsageButton_clicked();
|
||||
void on_translationProviderCombo_currentIndexChanged(int index);
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="textFormLayout">
|
||||
<layout class="QGridLayout" name="textFormLayout">
|
||||
<item row="0" column="0"><widget class="QLabel" name="originalTextLabel"><property name="text"><string>Originaltext</string></property></widget></item>
|
||||
<item row="0" column="1"><widget class="QLineEdit" name="originalTextEdit"/></item>
|
||||
<item row="0" column="2"><widget class="QPushButton" name="copyButton"><property name="text"><string>Kopieren</string></property></widget></item>
|
||||
@@ -63,6 +63,7 @@
|
||||
<item><widget class="QPushButton" name="setTranslationButton"><property name="text"><string>Übernehmen</string></property></widget></item>
|
||||
<item><widget class="QPushButton" name="setTranslationAndJumpNextButton"><property name="text"><string>Übernehmen && weiter</string></property></widget></item>
|
||||
<item><widget class="QPushButton" name="setAndNextAndAutoTranslate"><property name="text"><string>Übernehmen, weiter && automatisch übersetzen</string></property></widget></item>
|
||||
<item><widget class="QPushButton" name="retryTranslationButton"><property name="toolTip"><string>Fordert für den aktuellen Originaltext einen neuen Übersetzungsvorschlag an.</string></property><property name="text"><string>Neu übersetzen</string></property></widget></item>
|
||||
<item><spacer name="translationActionSpacer"><property name="orientation"><enum>Qt::Horizontal</enum></property><property name="sizeHint" stdset="0"><size><width>40</width><height>20</height></size></property></spacer></item>
|
||||
</layout>
|
||||
</item>
|
||||
@@ -79,13 +80,38 @@
|
||||
<item row="0" column="3"><widget class="QComboBox" name="deeplTranslateFrom"/></item>
|
||||
<item row="0" column="4"><widget class="QLabel" name="label_5"><property name="text"><string>Nach</string></property></widget></item>
|
||||
<item row="0" column="5"><widget class="QComboBox" name="deeplTranslateTo"/></item>
|
||||
<item row="0" column="6"><widget class="QPushButton" name="autoTranslateButton"><property name="text"><string>Aktuellen Text übersetzen</string></property></widget></item>
|
||||
<item row="1" column="0"><widget class="QLabel" name="label_9"><property name="text"><string>DeepL-API-Key</string></property></widget></item>
|
||||
<item row="1" column="1" colspan="3"><widget class="QLineEdit" name="deeplApiKey"/></item>
|
||||
<item row="1" column="4"><widget class="QPushButton" name="checkDeeplUsageButton"><property name="text"><string>Kontingent prüfen</string></property></widget></item>
|
||||
<item row="1" column="5" colspan="2"><widget class="QPushButton" name="bulkAutoTranslateButton"><property name="text"><string>Alle offenen Texte übersetzen</string></property></widget></item>
|
||||
<item row="2" column="0"><widget class="QLabel" name="libreTranslateUrlLabel"><property name="text"><string>Lokale URL</string></property></widget></item>
|
||||
<item row="2" column="1" colspan="5"><widget class="QLineEdit" name="libreTranslateUrl"><property name="text"><string>http://localhost:5000</string></property></widget></item>
|
||||
<item row="1" column="0" colspan="6">
|
||||
<layout class="QHBoxLayout" name="automaticActionLayout">
|
||||
<item><widget class="QPushButton" name="autoTranslateButton"><property name="text"><string>Aktuellen Text übersetzen</string></property></widget></item>
|
||||
<item><widget class="QPushButton" name="bulkAutoTranslateButton"><property name="text"><string>Alle offenen Texte übersetzen</string></property></widget></item>
|
||||
<item><spacer name="automaticActionSpacer"><property name="orientation"><enum>Qt::Horizontal</enum></property><property name="sizeHint" stdset="0"><size><width>40</width><height>20</height></size></property></spacer></item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="6">
|
||||
<widget class="QWidget" name="deeplSettingsWidget">
|
||||
<layout class="QHBoxLayout" name="deeplSettingsLayout">
|
||||
<property name="leftMargin"><number>0</number></property>
|
||||
<property name="topMargin"><number>0</number></property>
|
||||
<property name="rightMargin"><number>0</number></property>
|
||||
<property name="bottomMargin"><number>0</number></property>
|
||||
<item><widget class="QLabel" name="label_9"><property name="text"><string>DeepL-API-Key</string></property></widget></item>
|
||||
<item><widget class="QLineEdit" name="deeplApiKey"/></item>
|
||||
<item><widget class="QPushButton" name="checkDeeplUsageButton"><property name="text"><string>Kontingent prüfen</string></property></widget></item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="6">
|
||||
<widget class="QWidget" name="libreSettingsWidget">
|
||||
<layout class="QHBoxLayout" name="libreSettingsLayout">
|
||||
<property name="leftMargin"><number>0</number></property>
|
||||
<property name="topMargin"><number>0</number></property>
|
||||
<property name="rightMargin"><number>0</number></property>
|
||||
<property name="bottomMargin"><number>0</number></property>
|
||||
<item><widget class="QLabel" name="libreTranslateUrlLabel"><property name="text"><string>Lokale URL</string></property></widget></item>
|
||||
<item><widget class="QLineEdit" name="libreTranslateUrl"><property name="text"><string>http://localhost:5000</string></property></widget></item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
Reference in New Issue
Block a user