self._stack.addWidget(self._detail_view)
self._page_detail = self._stack.count() - 1
+ from ui.translation_view import TranslationView
+
+ self._translation_view = TranslationView(
+ self.config.transart_script,
+ self.config.typora_bin,
+ parent=self,
+ )
+ self._translation_view.push_master.connect(lambda: self._do_git_push("master"))
+ self._translation_view.publish.connect(lambda: self._do_git_push("production"))
+ self._stack.addWidget(self._translation_view)
+ self._page_translations = self._stack.count() - 1
+
# Remaining placeholders
- for name in ["taxonomy", "media", "translations", "git", "hugo"]:
+ for name in ["taxonomy", "media", "git", "hugo"]:
w = QLabel(f"[{name}]")
w.setAlignment(Qt.AlignmentFlag.AlignCenter)
self._stack.addWidget(w)
subprocess.Popen([self.config.typora_bin, str(dlg.created_path)])
def _do_translate(self, article: Article):
- pass # implemented in Task 15
+ self._translation_view.start_translation(article)
+ self._stack.setCurrentIndex(self._page_translations)
def _do_git_push(self, branch: str):
pass # implemented in Task 16
--- /dev/null
+from __future__ import annotations
+import subprocess
+from pathlib import Path
+from PyQt6.QtWidgets import (
+ QWidget, QVBoxLayout, QHBoxLayout, QLabel,
+ QPushButton, QPlainTextEdit, QTextBrowser,
+)
+from PyQt6.QtCore import pyqtSignal
+from core.models import Article
+from core.frontmatter import parse_frontmatter
+from workers.translation_worker import TranslationWorker
+import re
+
+def _strip_shortcodes(text: str) -> str:
+ return re.sub(r'\{\{[^}]+\}\}', '[shortcode]', text)
+
+class TranslationView(QWidget):
+ push_master = pyqtSignal()
+ publish = pyqtSignal()
+
+ def __init__(self, transart_script: str, typora_bin: str, parent=None):
+ super().__init__(parent)
+ self._transart_script = transart_script
+ self._typora_bin = typora_bin
+ self._article: Article | None = None
+ self._worker: TranslationWorker | None = None
+ self._output_path: str = ""
+ self._build_ui()
+
+ def _build_ui(self):
+ layout = QVBoxLayout(self)
+
+ self._header = QLabel("")
+ self._header.setStyleSheet("color:#fff;font-weight:bold;font-size:13px;padding:10px;")
+ layout.addWidget(self._header)
+
+ log_label = QLabel("Output traduzione:")
+ log_label.setStyleSheet("color:#a855f7;font-size:9px;text-transform:uppercase;letter-spacing:1px;padding:0 10px;")
+ layout.addWidget(log_label)
+ self._log = QPlainTextEdit()
+ self._log.setReadOnly(True)
+ self._log.setStyleSheet("background:#0a0a12;color:#00ff88;font-family:monospace;font-size:11px;")
+ self._log.setMaximumHeight(180)
+ layout.addWidget(self._log)
+
+ preview_label = QLabel("Preview traduzione:")
+ preview_label.setStyleSheet("color:#a855f7;font-size:9px;text-transform:uppercase;letter-spacing:1px;padding:0 10px;")
+ layout.addWidget(preview_label)
+ self._preview = QTextBrowser()
+ self._preview.setStyleSheet("background:#0d0d1a;color:#ccc;border:none;")
+ layout.addWidget(self._preview, stretch=1)
+
+ self._action_bar = QWidget()
+ btns = QHBoxLayout(self._action_bar)
+ btns.setContentsMargins(10, 6, 10, 6)
+
+ self._btn_typora = QPushButton("โ๏ธ Apri in Typora")
+ self._btn_typora.clicked.connect(self._open_typora)
+ self._btn_retry = QPushButton("๐ Rigenera")
+ self._btn_retry.clicked.connect(self._retry)
+ self._btn_master = QPushButton("๐งช Push master")
+ self._btn_master.clicked.connect(self.push_master.emit)
+ self._btn_publish = QPushButton("๐ข Pubblica")
+ self._btn_publish.clicked.connect(self.publish.emit)
+
+ for btn in (self._btn_typora, self._btn_retry, self._btn_master, self._btn_publish):
+ btns.addWidget(btn)
+ btns.addStretch()
+
+ self._action_bar.setVisible(False)
+ layout.addWidget(self._action_bar)
+
+ def start_translation(self, article: Article):
+ self._article = article
+ self._log.clear()
+ self._preview.clear()
+ self._action_bar.setVisible(False)
+
+ direction = "it-en" if article.lang == "it" else "en-it"
+ self._header.setText(f"Traduzione: {article.slug} ({article.lang.upper()} โ {('EN' if article.lang == 'it' else 'IT')})")
+
+ self._worker = TranslationWorker(
+ Path(self._transart_script),
+ article.path,
+ direction,
+ parent=self,
+ )
+ self._worker.log_line.connect(self._log.appendPlainText)
+ self._worker.finished.connect(self._on_finished)
+ self._worker.error.connect(lambda e: self._log.appendPlainText(f"[ERRORE] {e}"))
+ self._worker.start()
+
+ def _on_finished(self, success: bool, output_path: str):
+ self._output_path = output_path
+ if success and Path(output_path).exists():
+ try:
+ _, body = parse_frontmatter(Path(output_path))
+ self._preview.setMarkdown(_strip_shortcodes(body))
+ except Exception as e:
+ self._preview.setPlainText(f"[Errore preview: {e}]")
+ self._action_bar.setVisible(True)
+
+ def _open_typora(self):
+ if self._output_path:
+ subprocess.Popen([self._typora_bin, self._output_path])
+
+ def _retry(self):
+ if self._article:
+ self.start_translation(self._article)