Files
dusk/tools/editor/langtool.py
Dominic Masters 07afc3813a
Some checks failed
Build Dusk / run-tests (push) Successful in 1m55s
Build Dusk / build-linux (push) Successful in 2m0s
Build Dusk / build-psp (push) Failing after 1m40s
Refactor
2026-01-25 21:23:09 -06:00

203 lines
7.0 KiB
Python

#!/usr/bin/env python3
from PyQt5.QtWidgets import QMainWindow, QApplication, QAction, QMenuBar, QMessageBox, QFileDialog, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QTableWidget, QTableWidgetItem, QHeaderView, QPushButton, QTabWidget, QFormLayout
from PyQt5.QtCore import Qt
import sys
import os
import polib
class LangToolWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Dusk Language Editor")
self.setGeometry(100, 100, 800, 600)
self.current_file = None
self.dirty = False
self.init_menu()
self.init_ui()
def init_ui(self):
central = QWidget()
layout = QVBoxLayout(central)
tabs = QTabWidget()
# Header Tab
header_tab = QWidget()
header_layout = QFormLayout(header_tab)
self.language_edit = QLineEdit()
self.language_edit.setMaximumWidth(220)
header_layout.addRow(QLabel("Language:"), self.language_edit)
self.plural_edit = QLineEdit()
self.plural_edit.setMaximumWidth(320)
header_layout.addRow(QLabel("Plural-Forms:"), self.plural_edit)
self.content_type_edit = QLineEdit("text/plain; charset=UTF-8")
self.content_type_edit.setMaximumWidth(320)
header_layout.addRow(QLabel("Content-Type:"), self.content_type_edit)
tabs.addTab(header_tab, "Header")
# Strings Tab
strings_tab = QWidget()
strings_layout = QVBoxLayout(strings_tab)
self.po_table = QTableWidget()
self.po_table.setColumnCount(2)
self.po_table.setHorizontalHeaderLabels(["msgid", "msgstr"])
self.po_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.po_table.verticalHeader().setMinimumWidth(22)
self.po_table.verticalHeader().setDefaultAlignment(Qt.AlignRight | Qt.AlignVCenter)
strings_layout.addWidget(self.po_table)
row_btn_layout = QHBoxLayout()
add_row_btn = QPushButton("Add Row")
remove_row_btn = QPushButton("Remove Row")
row_btn_layout.addWidget(add_row_btn)
row_btn_layout.addWidget(remove_row_btn)
strings_layout.addLayout(row_btn_layout)
tabs.addTab(strings_tab, "Strings")
layout.addWidget(tabs)
add_row_btn.clicked.connect(self.add_row)
remove_row_btn.clicked.connect(self.remove_row)
self.add_row_btn = add_row_btn
self.remove_row_btn = remove_row_btn
self.setCentralWidget(central)
# Connect edits to dirty flag
self.language_edit.textChanged.connect(self.set_dirty)
self.plural_edit.textChanged.connect(self.set_dirty)
self.content_type_edit.textChanged.connect(self.set_dirty)
self.po_table.itemChanged.connect(self.set_dirty)
def set_dirty(self):
self.dirty = True
self.update_save_action()
def init_menu(self):
menubar = self.menuBar()
file_menu = menubar.addMenu("File")
new_action = QAction("New", self)
open_action = QAction("Open", self)
save_action = QAction("Save", self)
save_as_action = QAction("Save As", self)
new_action.triggered.connect(lambda: self.handle_file_action("new"))
open_action.triggered.connect(lambda: self.handle_file_action("open"))
save_action.triggered.connect(self.handle_save)
save_as_action.triggered.connect(self.handle_save_as)
file_menu.addAction(new_action)
file_menu.addAction(open_action)
file_menu.addAction(save_action)
file_menu.addAction(save_as_action)
self.save_action = save_action # Store reference for enabling/disabling
self.update_save_action()
def update_save_action(self):
self.save_action.setEnabled(self.current_file is not None)
def handle_file_action(self, action):
if self.dirty:
reply = QMessageBox.question(self, "Save Changes?", "Do you want to save changes before proceeding?", QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
if reply == QMessageBox.Cancel:
return
elif reply == QMessageBox.Yes:
self.handle_save()
if action == "new":
self.new_file()
elif action == "open":
default_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../assets/locale"))
file_path, _ = QFileDialog.getOpenFileName(self, "Open Language File", default_dir, "PO Files (*.po)")
if file_path:
self.open_file(file_path)
def new_file(self):
self.current_file = None
self.dirty = False
self.language_edit.setText("")
self.plural_edit.setText("")
self.po_table.setRowCount(0)
self.update_save_action()
def open_file(self, file_path):
self.current_file = file_path
self.dirty = False
self.update_save_action()
self.load_po_file(file_path)
def load_po_file(self, file_path):
po = polib.pofile(file_path)
language = po.metadata.get('Language', '')
plural = po.metadata.get('Plural-Forms', '')
content_type = po.metadata.get('Content-Type', 'text/plain; charset=UTF-8')
self.language_edit.setText(language)
self.plural_edit.setText(plural)
self.content_type_edit.setText(content_type)
self.po_table.setRowCount(len(po))
for row, entry in enumerate(po):
self.po_table.setItem(row, 0, QTableWidgetItem(entry.msgid))
self.po_table.setItem(row, 1, QTableWidgetItem(entry.msgstr))
def save_file(self, file_path):
po = polib.POFile()
po.metadata = {
'Language': self.language_edit.text(),
'Content-Type': self.content_type_edit.text(),
'Plural-Forms': self.plural_edit.text(),
}
for row in range(self.po_table.rowCount()):
msgid_item = self.po_table.item(row, 0)
msgstr_item = self.po_table.item(row, 1)
msgid = msgid_item.text() if msgid_item else ''
msgstr = msgstr_item.text() if msgstr_item else ''
if msgid or msgstr:
entry = polib.POEntry(msgid=msgid, msgstr=msgstr)
po.append(entry)
po.save(file_path)
self.dirty = False
self.update_save_action()
def handle_save(self):
if self.current_file:
self.save_file(self.current_file)
else:
self.handle_save_as()
def handle_save_as(self):
default_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../assets/locale"))
file_path, _ = QFileDialog.getSaveFileName(self, "Save Language File As", default_dir, "PO Files (*.po)")
if file_path:
self.current_file = file_path
self.update_save_action()
self.save_file(file_path)
def add_row(self):
row = self.po_table.rowCount()
self.po_table.insertRow(row)
self.po_table.setItem(row, 0, QTableWidgetItem(""))
self.po_table.setItem(row, 1, QTableWidgetItem(""))
self.po_table.setCurrentCell(row, 0)
self.set_dirty()
def remove_row(self):
row = self.po_table.currentRow()
if row >= 0:
self.po_table.removeRow(row)
self.set_dirty()
def closeEvent(self, event):
if self.dirty:
reply = QMessageBox.question(self, "Save Changes?", "Do you want to save changes before exiting?", QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
if reply == QMessageBox.Cancel:
event.ignore()
return
elif reply == QMessageBox.Yes:
self.handle_save()
event.accept()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = LangToolWindow()
window.show()
sys.exit(app.exec_())