Some bug fixes and enhancements

This commit is contained in:
Torsten Schulz
2025-02-14 09:51:01 +01:00
parent c9bf7db38c
commit 2d03c47d33
4 changed files with 133 additions and 68 deletions

View File

@@ -9,8 +9,10 @@ set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(QT NAMES Qt6 REQUIRED COMPONENTS Widgets) #target_include_directories(RenpyTranslationHelper PRIVATE /usr/include/qt6)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets)
find_package(QT NAMES Qt6 REQUIRED COMPONENTS Widgets Network)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Network)
set(TS_FILES RenpyTranslationHelper_en_GB.ts) set(TS_FILES RenpyTranslationHelper_en_GB.ts)
@@ -27,19 +29,11 @@ if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
MANUAL_FINALIZATION MANUAL_FINALIZATION
${PROJECT_SOURCES} ${PROJECT_SOURCES}
) )
# Define target properties for Android with Qt 6 as:
# set_property(TARGET RenpyTranslationHelper APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
# ${CMAKE_CURRENT_SOURCE_DIR}/android)
# For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation
# qt_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
else() else()
if(ANDROID) if(ANDROID)
add_library(RenpyTranslationHelper SHARED add_library(RenpyTranslationHelper SHARED
${PROJECT_SOURCES} ${PROJECT_SOURCES}
) )
# Define properties for Android with Qt 5 after find_package() calls as:
# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
else() else()
add_executable(RenpyTranslationHelper add_executable(RenpyTranslationHelper
${PROJECT_SOURCES} ${PROJECT_SOURCES}
@@ -51,12 +45,9 @@ endif()
target_link_libraries(RenpyTranslationHelper PRIVATE target_link_libraries(RenpyTranslationHelper PRIVATE
Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Widgets
Qt6Network Qt${QT_VERSION_MAJOR}::Network
) )
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
# If you are developing for iOS or macOS you should consider setting an
# explicit, fixed bundle identifier manually though.
if(${QT_VERSION} VERSION_LESS 6.1.0) if(${QT_VERSION} VERSION_LESS 6.1.0)
set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.RenpyTranslationHelper) set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.RenpyTranslationHelper)
endif() endif()

View File

@@ -16,6 +16,8 @@
#include <QJsonObject> #include <QJsonObject>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QInputDialog> #include <QInputDialog>
#include <QClipboard>
#include <QTimer>
MainWindow::MainWindow(QWidget *parent) MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent) : QMainWindow(parent)
@@ -117,29 +119,33 @@ void MainWindow::on_languageCombo_currentTextChanged(const QString &selectedLang
} }
void MainWindow::populateTreeWidgetFromMap() { void MainWindow::populateTreeWidgetFromMap() {
ui->treeWidget->clear(); try {
foreach (const auto &pair, fileContentsMap) { ui->treeWidget->clear();
QTreeWidgetItem *fileItem = new QTreeWidgetItem(ui->treeWidget); foreach (const auto &pair, fileContentsMap) {
QString fileName = QFileInfo(pair.first).fileName(); QTreeWidgetItem *fileItem = new QTreeWidgetItem(ui->treeWidget);
fileItem->setText(0, fileName); QString fileName = QFileInfo(pair.first).fileName();
fileItem->setData(0, Qt::UserRole, false); fileItem->setText(0, fileName);
auto parsedBlocks = parseTextBlock(pair.second); fileItem->setData(0, Qt::UserRole, false);
for (const auto& block : std::as_const(parsedBlocks)) { auto parsedBlocks = parseTextBlock(pair.second);
QTreeWidgetItem *blockItem = new QTreeWidgetItem(fileItem); for (const auto &block : std::as_const(parsedBlocks)) {
blockItem->setText(0, QString::number(block.line)); QTreeWidgetItem *blockItem = new QTreeWidgetItem(fileItem);
blockItem->setText(1, block.oldText); blockItem->setText(0, QString::number(block.line));
blockItem->setText(2, block.newText); blockItem->setText(1, QString("%1").arg(block.character));
blockItem->setText(3, block.character); blockItem->setText(2, block.oldText);
blockItem->setText(3, block.newText);
}
} }
ui->treeWidget->resizeColumnToContents(0);
int firstColumnWidth = ui->treeWidget->columnWidth(0);
int secondColumnWidth = ui->treeWidget->columnWidth(1);
int remainingWidth = ui->treeWidget->viewport()->width() - firstColumnWidth - secondColumnWidth;
int columnWidth = remainingWidth / 2;
ui->treeWidget->setColumnWidth(2, columnWidth);
ui->treeWidget->setColumnWidth(3, columnWidth);
countAndShowUntranslated();
} catch (const std::exception &e) {
qDebug() << e.what();
} }
ui->treeWidget->resizeColumnToContents(0);
int firstColumnWidth = ui->treeWidget->columnWidth(0);
int remainingWidth = ui->treeWidget->viewport()->width() - firstColumnWidth;
int columnWidth = remainingWidth / 2;
ui->treeWidget->setColumnWidth(1, columnWidth);
ui->treeWidget->setColumnWidth(2, columnWidth);
countAndShowUntranslated();
} }
QVector<MainWindow::TranslationItem> MainWindow::parseTextBlock(const QString& input) { QVector<MainWindow::TranslationItem> MainWindow::parseTextBlock(const QString& input) {
@@ -196,17 +202,18 @@ void MainWindow::on_reloadFilesButton_clicked()
populateTreeWidgetFromMap(); populateTreeWidgetFromMap();
} }
void MainWindow::on_nextUntranslatedButton_clicked() void MainWindow::on_nextUntranslatedButton_clicked() {
{
for (int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i) { for (int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i) {
QTreeWidgetItem *topLevelItem = ui->treeWidget->topLevelItem(i); QTreeWidgetItem *topLevelItem = ui->treeWidget->topLevelItem(i);
for (int j = 0; j < topLevelItem->childCount(); ++j) { for (int j = 0; j < topLevelItem->childCount(); ++j) {
QTreeWidgetItem *childItem = topLevelItem->child(j); QTreeWidgetItem *childItem = topLevelItem->child(j);
if (childItem->text(2).isEmpty()) { // Prüfe, ob Übersetzung (Spalte 3) leer ist
ui->originalTextEdit->setText(childItem->text(1)); if (childItem->text(3).isEmpty()) {
ui->originalTextEdit->setText(childItem->text(2)); // Originaltext aus Spalte 2
ui->translationEdit->clear(); ui->translationEdit->clear();
ui->treeWidget->scrollToItem(childItem); ui->treeWidget->scrollToItem(childItem);
ui->treeWidget->setCurrentItem(childItem); ui->treeWidget->setCurrentItem(childItem);
on_copyButton_clicked();
return; return;
} }
} }
@@ -214,34 +221,39 @@ void MainWindow::on_nextUntranslatedButton_clicked()
QMessageBox::information(this, tr("Information"), tr("No untranslated item found.")); QMessageBox::information(this, tr("Information"), tr("No untranslated item found."));
} }
void MainWindow::on_treeWidget_itemSelectionChanged() void MainWindow::on_treeWidget_itemSelectionChanged() {
{
QTreeWidgetItem *selectedItem = ui->treeWidget->currentItem(); QTreeWidgetItem *selectedItem = ui->treeWidget->currentItem();
if (selectedItem) { if (selectedItem) {
if (selectedItem->treeWidget()->indexOfTopLevelItem(selectedItem) < 0) { if (selectedItem->treeWidget()->indexOfTopLevelItem(selectedItem) < 0) {
ui->originalTextEdit->setText(selectedItem->text(1)); ui->originalTextEdit->setText(selectedItem->text(2)); // Originaltext
ui->translationEdit->setText(selectedItem->text(2)); ui->translationEdit->setText(selectedItem->text(3)); // Übersetzung
} }
} }
} }
void MainWindow::on_setTranslationButton_clicked() {
void MainWindow::on_setTranslationButton_clicked()
{
QTreeWidgetItem *selectedItem = ui->treeWidget->currentItem(); QTreeWidgetItem *selectedItem = ui->treeWidget->currentItem();
if (selectedItem) { if (selectedItem) {
if (selectedItem->treeWidget()->indexOfTopLevelItem(selectedItem) < 0) { if (selectedItem->treeWidget()->indexOfTopLevelItem(selectedItem) < 0) {
selectedItem->setText(2, ui->translationEdit->text()); // Setze Übersetzung in Spalte 3
selectedItem->setText(3, ui->translationEdit->text());
QTreeWidgetItem *parentItem = selectedItem->parent(); QTreeWidgetItem *parentItem = selectedItem->parent();
if (parentItem) { if (parentItem) {
parentItem->setData(0, Qt::UserRole, true); parentItem->setData(0, Qt::UserRole, true);
for (int index = 0; index < parentItem->childCount(); ++index) {
auto item = parentItem->child(index);
// Vergleiche den Originaltext (Spalte 2)
if (item->text(2) == ui->originalTextEdit->text()) {
item->setText(3, ui->translationEdit->text());
}
}
} }
} }
} }
ui->originalTextEdit->setFocus();
countAndShowUntranslated(); countAndShowUntranslated();
} }
void MainWindow::on_setTranslationAndJumpNextButton_clicked() void MainWindow::on_setTranslationAndJumpNextButton_clicked()
{ {
on_setTranslationButton_clicked(); on_setTranslationButton_clicked();
@@ -352,7 +364,7 @@ bool MainWindow::parseAndEditFile(QFile &backupFile, QTreeWidgetItem *fileItem,
auto oldText = match.captured(4); auto oldText = match.captured(4);
auto newText = match.captured(6); auto newText = match.captured(6);
auto replacedText = findNewText(fileItem, lineNumber, oldText, speaker); auto replacedText = findNewText(fileItem, lineNumber, oldText, speaker);
QString toReplace = QString("\"%1\"").arg(newText); QString toReplace = QString("\"%1\"").arg(newText).trimmed();
QString replacement = QString("\"%1\"").arg(replacedText.isEmpty() ? newText : replacedText); QString replacement = QString("\"%1\"").arg(replacedText.isEmpty() ? newText : replacedText);
auto replacedBlock = block.replace(toReplace, replacement); auto replacedBlock = block.replace(toReplace, replacement);
content.replace(match.capturedStart(0), match.capturedLength(0), replacedBlock); content.replace(match.capturedStart(0), match.capturedLength(0), replacedBlock);
@@ -381,16 +393,37 @@ bool MainWindow::parseAndEditFile(QFile &backupFile, QTreeWidgetItem *fileItem,
QString MainWindow::findNewText(QTreeWidgetItem *fileItem, const QString &lineNumber, const QString &originalText, const QString &speaker) { QString MainWindow::findNewText(QTreeWidgetItem *fileItem, const QString &lineNumber, const QString &originalText, const QString &speaker) {
for (int i = 0; i < fileItem->childCount(); ++i) { for (int i = 0; i < fileItem->childCount(); ++i) {
QTreeWidgetItem *childItem = fileItem->child(i); QTreeWidgetItem *childItem = fileItem->child(i);
if (childItem->text(0) == lineNumber && childItem->text(1) == originalText && childItem->text(3) == speaker) { if (childItem->text(0) == lineNumber &&
return childItem->text(2); childItem->text(1) == speaker &&
childItem->text(2) == originalText) {
return childItem->text(3);
} }
} }
return QString(); return QString();
} }
void MainWindow::loadDeeplTranslationPossibilities() { void MainWindow::loadDeeplTranslationPossibilities() {
static int attempt = 1;
QNetworkAccessManager *manager = new QNetworkAccessManager(this); QNetworkAccessManager *manager = new QNetworkAccessManager(this);
connect(manager, &QNetworkAccessManager::finished, this, &MainWindow::onDeeplTranslationPossibilitiesLoaded); connect(manager, &QNetworkAccessManager::finished, this, [this, manager](QNetworkReply *reply) {
if (reply->error() != QNetworkReply::NoError) {
qDebug() << "Network error:" << reply->errorString();
qDebug() << "Attempt:" << attempt;
if (attempt < 5) {
attempt++;
QTimer::singleShot(250, this, &MainWindow::loadDeeplTranslationPossibilities);
} else {
qDebug() << "Max attempts reached, giving up.";
attempt = 1;
}
} else {
attempt = 1;
onDeeplTranslationPossibilitiesLoaded(reply);
}
reply->deleteLater();
manager->deleteLater();
});
QNetworkRequest request(QUrl("https://api-free.deepl.com/v2/glossary-language-pairs")); QNetworkRequest request(QUrl("https://api-free.deepl.com/v2/glossary-language-pairs"));
request.setRawHeader("Authorization", "DeepL-Auth-Key " + ui->deeplApiKey->text().toUtf8()); request.setRawHeader("Authorization", "DeepL-Auth-Key " + ui->deeplApiKey->text().toUtf8());
manager->get(request); manager->get(request);
@@ -483,6 +516,8 @@ void MainWindow::on_deeplTranslateFrom_currentTextChanged(const QString &sourceL
} }
} }
#include <QTimer>
void MainWindow::on_autoTranslateButton_clicked() { void MainWindow::on_autoTranslateButton_clicked() {
if (ui->deeplApiKey->text().isEmpty()) { if (ui->deeplApiKey->text().isEmpty()) {
QMessageBox::information(this, "Deepl error", "For auto translation, you need to add a deepl API key."); QMessageBox::information(this, "Deepl error", "For auto translation, you need to add a deepl API key.");
@@ -495,12 +530,32 @@ void MainWindow::on_autoTranslateButton_clicked() {
jsonData["text"] = QJsonArray::fromStringList({originalText}); jsonData["text"] = QJsonArray::fromStringList({originalText});
jsonData["source_lang"] = sourceLang; jsonData["source_lang"] = sourceLang;
jsonData["target_lang"] = targetLang; jsonData["target_lang"] = targetLang;
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
connect(manager, &QNetworkAccessManager::finished, this, &MainWindow::translationRequestFinished);
QUrl url("https://api-free.deepl.com/v2/translate"); QUrl url("https://api-free.deepl.com/v2/translate");
QNetworkRequest request(url); QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader("Authorization", "DeepL-Auth-Key " + ui->deeplApiKey->text().toUtf8()); request.setRawHeader("Authorization", "DeepL-Auth-Key " + ui->deeplApiKey->text().toUtf8());
static int attempt = 1;
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
connect(manager, &QNetworkAccessManager::finished, this,
[this, manager, jsonData, request](QNetworkReply *reply) mutable {
if (reply->error() != QNetworkReply::NoError) {
qDebug() << "Translation request failed:" << reply->errorString();
qDebug() << "Attempt:" << attempt;
if (attempt < 5) {
attempt++;
QTimer::singleShot(250, this, &MainWindow::on_autoTranslateButton_clicked);
} else {
qDebug() << "Max attempts reached for auto translation.";
attempt = 1;
}
} else {
attempt = 1;
translationRequestFinished(reply);
}
reply->deleteLater();
manager->deleteLater();
});
manager->post(request, QJsonDocument(jsonData).toJson()); manager->post(request, QJsonDocument(jsonData).toJson());
} }
@@ -524,21 +579,24 @@ void MainWindow::translationRequestFinished(QNetworkReply *reply) {
reply->deleteLater(); reply->deleteLater();
} }
void MainWindow::countAndShowUntranslated() void MainWindow::countAndShowUntranslated() {
{
int untranslatedCount = 0; int untranslatedCount = 0;
std::function<void(QTreeWidgetItem *)> countUntranslated = [&](QTreeWidgetItem *parentItem) { std::function<int(QTreeWidgetItem *)> countUntranslated = [&](QTreeWidgetItem *parentItem) {
int untranslatedInSectionCount = 0;
for (int i = 0; i < parentItem->childCount(); ++i) { for (int i = 0; i < parentItem->childCount(); ++i) {
QTreeWidgetItem *childItem = parentItem->child(i); QTreeWidgetItem *childItem = parentItem->child(i);
if (!childItem->text(1).isEmpty() && childItem->text(2).isEmpty()) { if (!childItem->text(2).isEmpty() && childItem->text(3).isEmpty()) {
untranslatedCount++; untranslatedCount++;
untranslatedInSectionCount++;
} }
countUntranslated(childItem); countUntranslated(childItem);
} }
return untranslatedInSectionCount;
}; };
for (int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i) { for (int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i) {
QTreeWidgetItem *topLevelItem = ui->treeWidget->topLevelItem(i); QTreeWidgetItem *topLevelItem = ui->treeWidget->topLevelItem(i);
countUntranslated(topLevelItem); int sectionCount = countUntranslated(topLevelItem);
topLevelItem->setText(1, QString("%1").arg(sectionCount));
} }
ui->untranslatedLabel->setText(QString("%1").arg(untranslatedCount)); ui->untranslatedLabel->setText(QString("%1").arg(untranslatedCount));
} }
@@ -567,8 +625,8 @@ void MainWindow::searchNext() {
auto fileItem {ui->treeWidget->invisibleRootItem()->child(fileItemPos)}; auto fileItem {ui->treeWidget->invisibleRootItem()->child(fileItemPos)};
for (int linePos = 0; linePos < fileItem->childCount(); ++linePos) { for (int linePos = 0; linePos < fileItem->childCount(); ++linePos) {
auto line {fileItem->child(linePos)}; auto line {fileItem->child(linePos)};
if (currentItem == NULL && (line->text(1).contains(searchQuery, Qt::CaseInsensitive) if (currentItem == NULL && (line->text(2).contains(searchQuery, Qt::CaseInsensitive)
|| line->text(2).contains(searchQuery, Qt::CaseInsensitive))) { || line->text(3).contains(searchQuery, Qt::CaseInsensitive))) {
ui->treeWidget->setCurrentItem(line); ui->treeWidget->setCurrentItem(line);
ui->treeWidget->scrollToItem(line); ui->treeWidget->scrollToItem(line);
return; return;
@@ -602,3 +660,7 @@ void MainWindow::on_deeplApiKey_editingFinished()
loadDeeplTranslationPossibilities(); loadDeeplTranslationPossibilities();
} }
void MainWindow::on_copyButton_clicked() {
QGuiApplication::clipboard()->setText(ui->originalTextEdit->text());
}

View File

@@ -3,6 +3,8 @@
#include <QMainWindow> #include <QMainWindow>
#include <QJsonObject> #include <QJsonObject>
#include <memory>
#include <QNetworkReply>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; } namespace Ui { class MainWindow; }
@@ -11,6 +13,7 @@ QT_END_NAMESPACE
class QTreeWidgetItem; class QTreeWidgetItem;
class QFile; class QFile;
class QNetworkReply; class QNetworkReply;
class QAuthenticator;
class MainWindow : public QMainWindow class MainWindow : public QMainWindow
{ {
@@ -40,8 +43,8 @@ private slots:
void on_autoTranslateButton_clicked(); void on_autoTranslateButton_clicked();
void on_searchButton_clicked(); void on_searchButton_clicked();
void on_searchNextButton_clicked(); void on_searchNextButton_clicked();
void on_deeplApiKey_editingFinished(); void on_deeplApiKey_editingFinished();
void on_copyButton_clicked();
private: private:
struct TranslationItem { struct TranslationItem {
@@ -55,6 +58,7 @@ private:
QString oldText; QString oldText;
QString newText; QString newText;
}; };
std::unique_ptr<QNetworkAccessManager> networkManager;
Ui::MainWindow *ui; Ui::MainWindow *ui;
QJsonObject configuration; QJsonObject configuration;
bool noConfigChange{false}; bool noConfigChange{false};
@@ -72,6 +76,7 @@ private:
void loadDeeplTranslationPossibilities(); void loadDeeplTranslationPossibilities();
void renderDeeplSources(); void renderDeeplSources();
void translationRequestFinished(QNetworkReply *reply); void translationRequestFinished(QNetworkReply *reply);
void onDeeplTranslationAuthenticationError(QNetworkReply *reply, QAuthenticator *authenticator);
void countAndShowUntranslated(); void countAndShowUntranslated();
void searchNext(); void searchNext();
QVector<TranslationItem> parseTextBlock(const QString &block); QVector<TranslationItem> parseTextBlock(const QString &block);

View File

@@ -102,6 +102,11 @@
<string>File</string> <string>File</string>
</property> </property>
</column> </column>
<column>
<property name="text">
<string>Speaker</string>
</property>
</column>
<column> <column>
<property name="text"> <property name="text">
<string>Original</string> <string>Original</string>
@@ -112,11 +117,6 @@
<string>Translation</string> <string>Translation</string>
</property> </property>
</column> </column>
<column>
<property name="text">
<string>Speaker</string>
</property>
</column>
<item> <item>
<property name="text"> <property name="text">
<string>All items</string> <string>All items</string>
@@ -230,7 +230,14 @@
<item> <item>
<widget class="QLineEdit" name="originalTextEdit"> <widget class="QLineEdit" name="originalTextEdit">
<property name="enabled"> <property name="enabled">
<bool>false</bool> <bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="copyButton">
<property name="text">
<string>Copy</string>
</property> </property>
</widget> </widget>
</item> </item>