feat(translate): add per-series glossary with auto extraction

This commit was merged in pull request #2.
This commit is contained in:
tri committed 2026-11-07 13:28:32 +07:00
1 parent 18ba92a6e3
commit 9e55dba687
14 files changed
+380 -12

No files matched your search

+8
View File
@@ -35,6 +35,7 @@ Item {
signal headerToggleRequested()
signal contextMenuRequested()
signal translateRequested()
signal editGlossaryRequested()
function seekBy(seconds: real) {
mpv.seekBy(seconds)
@@ -364,6 +365,13 @@ Item {
onTriggered: root.translateRequested()
}
property Action editGlossary: Action {
enabled: root.shortcutsEnabled && root.hasFile && root.mpv.currentUrl.toString().startsWith("file:")
text: KI18n.i18nc("@action", "Edit Translation Glossary…")
icon.name: "document-edit"
onTriggered: root.editGlossaryRequested()
}
property Action cancelTranslation: Action {
enabled: root.translator.busy
text: KI18n.i18nc("@action", "Cancel Translation")
+41
View File
@@ -5,6 +5,7 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Dialogs
import QtQuick.Layouts
import org.kde.ki18n
import org.kde.kirigami as Kirigami
import org.kde.config as KConfig
@@ -108,6 +109,10 @@ ApplicationWindow {
onPlaylistToggleRequested: playlistPanel.toggle()
onContextMenuRequested: contextMenu.popup()
onTranslateRequested: appWindow.translateCurrentSubtitle()
onEditGlossaryRequested: {
translationService.loadGlossary(video.currentUrl)
glossaryDialog.open()
}
onMenuBarToggleRequested: {
QtPotatoSettings.showMenuBar = !QtPotatoSettings.showMenuBar
QtPotatoSettings.save()
@@ -312,4 +317,40 @@ ApplicationWindow {
]
onAccepted: video.addSubtitleFile(selectedFile)
}
Kirigami.Dialog {
id: glossaryDialog
title: KI18n.i18nc("@title:window", "Translation Glossary")
standardButtons: Kirigami.Dialog.Save | Kirigami.Dialog.Cancel
width: Math.min(appWindow.width * 0.9, 650)
contentItem: ColumnLayout {
ListView {
id: glossaryList
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredHeight: Kirigami.Units.gridUnit * 15
clip: true
model: ListModel { id: glossaryModel }
delegate: RowLayout {
width: glossaryList.width
TextField { text: source; Layout.fillWidth: true; onTextChanged: model.source = text }
TextField { text: target; Layout.fillWidth: true; onTextChanged: model.target = text }
ToolButton { icon.name: "edit-delete-remove"; onClicked: glossaryModel.remove(index) }
}
}
RowLayout {
Button { text: KI18n.i18nc("@action:button", "Add"); onClicked: glossaryModel.append({source: "", target: ""}) }
Button { text: KI18n.i18nc("@action:button", "Clear"); onClicked: glossaryModel.clear() }
}
}
onOpened: {
glossaryModel.clear()
for (const entry of translationService.glossaryEntries) glossaryModel.append(entry)
}
onAccepted: {
const entries = []
for (let i = 0; i < glossaryModel.count; ++i) entries.push(glossaryModel.get(i))
translationService.saveGlossary(entries)
}
}
}
+1
View File
@@ -26,6 +26,7 @@ Menu {
}
MenuSeparator {}
MenuItem { action: root.actions.translateSubtitle }
MenuItem { action: root.actions.editGlossary }
MenuItem {
action: root.actions.cancelTranslation
visible: root.actions.cancelTranslation.enabled
+7
View File
@@ -190,6 +190,13 @@ SettingsBasePage {
onEditingFinished: QtPotatoSettings.save()
}
CheckBox {
text: KI18n.i18nc("@option:check", "Build a glossary automatically before translating")
checked: QtPotatoSettings.autoBuildGlossary
Layout.columnSpan: 2
onToggled: { QtPotatoSettings.autoBuildGlossary = checked; QtPotatoSettings.save() }
}
Label {
text: KI18n.i18nc("@label:spinbox", "Lines per request")
Layout.alignment: Qt.AlignRight