5#include "ui_machinetranslationdialog.h"
8#include "auto-translation/machinetranslator.h"
11#include <QtCore/qsettings.h>
12#include <QtWidgets/qmessagebox.h>
14using namespace Qt::Literals::StringLiterals;
18MachineTranslationDialog::MachineTranslationDialog(QWidget *parent)
20 m_ui(std::make_unique<Ui::MachineTranslationDialog>()),
21 m_translator(std::make_unique<MachineTranslator>())
25 connect(m_ui->toolBox, &QToolBox::currentChanged,
this, [
this](
int index) {
26 for (
int i = 0; i < m_ui->toolBox->count(); ++i) {
27 const QString baseText = m_ui->toolBox->itemText(i).mid(2);
28 m_ui->toolBox->setItemText(i, (i == index ?
"- "_L1 :
"+ "_L1) + baseText);
32 m_ui->statusLabel->setWordWrap(
true);
33 m_ui->statusLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
34 connect(m_ui->translateButton, &QPushButton::clicked,
this,
35 &MachineTranslationDialog::translateSelection);
36 connect(m_ui->filesComboBox, &QComboBox::currentIndexChanged,
this, [
this] {
37 m_ui->filterComboBox->setCurrentIndex(0);
40 connect(m_ui->groupListWidget, &QListWidget::itemSelectionChanged,
this,
41 [
this] { updateStatus(); });
42 connect(m_translator.get(), &MachineTranslator::batchTranslated,
this,
43 &MachineTranslationDialog::onBatchTranslated);
44 connect(m_translator.get(), &MachineTranslator::translationFailed,
this,
45 &MachineTranslationDialog::onTranslationFailed);
46 connect(m_ui->doneButton, &QPushButton::clicked,
this, [
this] {
47 if (discardTranslations())
50 connect(m_ui->cancelButton, &QPushButton::clicked,
this, [
this] {
51 if (discardTranslations())
54 connect(m_ui->applyButton, &QPushButton::clicked,
this,
55 &MachineTranslationDialog::applyTranslations);
57 connect(m_ui->stopButton, &QToolButton::clicked,
this, &MachineTranslationDialog::stop);
58 connect(m_ui->connectButton, &QPushButton::clicked,
this,
59 &MachineTranslationDialog::connectToOllama);
60 connect(m_translator.get(), &MachineTranslator::modelsReceived,
this,
61 [
this](
const QStringList &models) {
63 QString savedModel = config.value(settingPath(selectedModelSettingsKey)).toString();
64 m_ui->modelComboBox->clear();
65 m_ui->modelComboBox->addItems(models);
68 if (!savedModel.isEmpty()) {
69 int index = m_ui->modelComboBox->findText(savedModel);
71 m_ui->modelComboBox->setCurrentIndex(index);
74 connect(m_ui->modelComboBox, &QComboBox::currentTextChanged,
this, [](
const QString &text) {
76 QSettings().setValue(settingPath(selectedModelSettingsKey), text);
78 connect(
this, &QDialog::finished, m_translator.get(), &MachineTranslator::stop);
79 connect(m_ui->filterComboBox, &QComboBox::currentIndexChanged,
this,
80 &MachineTranslationDialog::onFilterChanged);
92 m_ui->toolBox->setCurrentIndex(0);
93 m_ui->filesComboBox->clear();
94 m_ui->filesComboBox->addItems(m_dataModel->srcFileNames());
95 m_ui->filesComboBox->setCurrentIndex(0);
96 m_ui->translationLog->setText(tr(
"Translation Log"));
97 m_ui->translateButton->setEnabled(
true);
98 m_ui->stopButton->setEnabled(
false);
102 m_failedTranslations = 0;
103 m_receivedTranslations.clear();
104 m_ongoingTranslations.clear();
105 m_ui->applyButton->setEnabled(
false);
106 m_ui->progressBar->setVisible(
false);
107 m_translator->start();
112 const qsizetype receivedCount = m_receivedTranslations.size();
113 m_ui->statusLabel->setText(
114 tr(
"Translation status: %1/%2 source texts translated, %3/%2 failed.")
117 .arg(m_failedTranslations));
118 m_ui->progressBar->setValue((receivedCount + m_failedTranslations) * 100 / m_sentTexts);
119 if (!table.empty()) {
120 QString html =
"<hr/><table cellpadding=\"4\""
125 for (
const QStringList &row : table) {
127 for (
const QString &col : row)
128 html +=
"<td>%1</td>"_L1.arg(col);
131 html +=
"</table>"_L1;
132 m_ui->translationLog->append(html);
135 if (receivedCount + m_failedTranslations == m_sentTexts) {
136 m_ui->translationLog->append(
137 tr(
"<hr/><b>Translation completed: %1/%2 translated, %3/%2 failed.</b>")
140 .arg(m_failedTranslations));
141 m_ui->translateButton->setEnabled(
true);
142 m_ui->stopButton->setEnabled(
false);
143 m_ui->applyButton->setEnabled(
true);
144 m_ui->progressBar->setVisible(
false);
146 m_ui->translateButton->setEnabled(
false);
147 m_ui->stopButton->setEnabled(
true);
148 m_ui->progressBar->setVisible(
true);
154 m_ui->translationLog->append(
"<hr/>"_L1);
155 m_ui->translationLog->append(info);
160 m_ui->translationLog->append(
"<hr/>"_L1);
161 m_ui->translationLog->append(
162 "<span style=\"color:red; font-weight: bold; \">%1</span>"_L1.arg(error));
167 return (m_receivedTranslations.empty()
168 || QMessageBox::warning(
169 this, tr(
"Qt Linguist"),
170 tr(
"The already %n translated item(s) will be discarded. Continue?", 0,
171 m_receivedTranslations.size()),
172 QMessageBox::Yes | QMessageBox::No)
173 == QMessageBox::Yes);
178 m_translator->stop();
179 m_ui->stopButton->setEnabled(
false);
180 m_ui->translateButton->setEnabled(
true);
182 logError(tr(
"Translation Stopped."));
187 m_ui->toolBox->setCurrentIndex(2);
188 const QString model = m_ui->modelComboBox->currentText();
189 const int id = m_ui->filesComboBox->currentIndex();
190 if (model.isEmpty()) {
191 logError(tr(
"Please verify the service URL is valid, "
192 "then select a translation model."));
196 logError(tr(
"Please select a file for translation."));
199 if (!discardTranslations())
203 const int filter = m_ui->filterComboBox->currentIndex();
207 QMutexLocker lock(&m_mutex);
210 if (tm->translation().isEmpty()) {
211 messages.items.append(tm);
212 m_ongoingTranslations[tm] =
213 MultiDataIndex{ it.translationType(), id, it.group(), it.message() };
218 if (tm->translation().isEmpty()) {
219 messages.items.append(tm);
220 m_ongoingTranslations[tm] =
221 MultiDataIndex{ it.translationType(), id, it.group(), it.message() };
225 const QList<QListWidgetItem *> selectedItems = m_ui->groupListWidget->selectedItems();
227 if (selectedItems.isEmpty()) {
228 logError(tr(
"Please select at least one context/label to translate."));
232 QMutexLocker lock(&m_mutex);
234 for (QListWidgetItem *item : selectedItems) {
235 const int groupIdx = item->data(Qt::UserRole).toInt();
236 const GroupItem *g = dm->groupItem(groupIdx, type);
237 for (
int i = 0; i < g->messageCount(); i++) {
238 const TranslatorMessage *tm = &g->messageItem(i)->message();
239 if (tm->translation().isEmpty()) {
240 messages.items.append(tm);
241 m_ongoingTranslations[tm] = MultiDataIndex{ type, id, groupIdx, i };
246 messages.srcLang = QLocale::languageToString(dm->sourceLanguage());
247 messages.tgtLang = QLocale::languageToString(dm->language());
248 m_sentTexts += messages.items.size();
249 m_translator->activateTranslationModel(model);
250 m_translator->translate(messages, m_ui->contextEdit->toPlainText().trimmed());
251 logInfo(tr(
"Translation Started"));
256 QHash<
const TranslatorMessage *, QString> translations)
258 QList<QStringList> log;
259 log.reserve(translations.size());
260 QMutexLocker lock(&m_mutex);
261 for (
const auto &[msg, translation] : translations.asKeyValueRange()) {
262 log.append({ msg->sourceText().simplified(), translation.simplified() });
263 m_receivedTranslations.append(std::make_pair(m_ongoingTranslations.take(msg), translation));
265 logInfo(tr(
"Translation Batch:"));
271 m_ui->groupLabel->setEnabled(id != 0);
272 m_ui->groupListWidget->setEnabled(id != 0);
273 m_ui->groupListWidget->clear();
274 int modelId = m_ui->filesComboBox->currentIndex();
278 QList<QPair<QString,
int>> groupsWithIndices;
281 groupsWithIndices.append(
283 }
else if (id == 2) {
285 groupsWithIndices.append(
289 std::sort(groupsWithIndices.begin(), groupsWithIndices.end(),
290 [](
const QPair<QString,
int> &a,
const QPair<QString,
int> &b) {
291 return a.first.compare(b.first, Qt::CaseInsensitive) < 0;
294 for (
const auto &group : groupsWithIndices) {
295 QListWidgetItem *item =
new QListWidgetItem(group.first);
296 item->setData(Qt::UserRole, group.second);
297 m_ui->groupListWidget->addItem(item);
303 QMutexLocker lock(&m_mutex);
304 for (
const auto &[item, translation] : std::as_const(m_receivedTranslations))
305 m_dataModel->setTranslation(item, translation);
307 logInfo(tr(
"Translations Applied."));
312 QList<QStringList> log;
313 log.reserve(failed.size() + 1);
315 QMutexLocker lock(&m_mutex);
316 m_failedTranslations += failed.size();
317 for (
const TranslatorMessage *m : failed) {
318 log << QStringList{ m->sourceText().simplified() };
319 m_ongoingTranslations.remove(m);
321 logError(tr(
"Failed Translation(s):"));
327 const int model = m_ui->filesComboBox->currentIndex();
328 const int filter = m_ui->filterComboBox->currentIndex();
330 QList<QListWidgetItem *> selectedItems;
332 selectedItems = m_ui->groupListWidget->selectedItems();
334 if (model < 0 || filter < 0 || (filter > 0 && selectedItems.isEmpty())) {
335 m_ui->selectionLabel->setText(tr(
"Selection status: -"));
336 }
else if (filter == 0) {
345 m_ui->selectionLabel->setText(tr(
"Selected %n item(s).", 0, count));
346 }
else if (!selectedItems.isEmpty()) {
349 for (QListWidgetItem *item : std::as_const(selectedItems)) {
350 const int groupIdx = item->data(Qt::UserRole).toInt();
351 const GroupItem *g = m_dataModel->model(model)->groupItem(groupIdx, type);
352 for (
int i = 0; i < g->messageCount(); i++)
353 if (g->messageItem(i)->message().translation().isEmpty())
356 m_ui->selectionLabel->setText(
357 tr(
"Selected %n item(s) in %1 group(s).", 0, count).arg(selectedItems.size()));
363 if (m_ui->serverText->text().isEmpty())
365 m_translator->setUrl(m_ui->serverText->text());
366 m_translator->requestModels();
DataModelIterator(TranslationType type, const DataModel *model=0, int groupNo=0, int messageNo=0)
MessageItem * current() const
GroupItem * groupItem(int index, TranslationType type) const
~MachineTranslationDialog()
void setDataModel(MultiDataModel *dm)
const TranslatorMessage & message() const