I18N Code Work done.
This commit is contained in:
@@ -3,6 +3,7 @@ import os
|
||||
import xml.dom.minidom
|
||||
from typing import Dict, List, cast
|
||||
|
||||
from PyQt6.QtCore import QCoreApplication
|
||||
from PyQt6.QtSql import QSqlQuery
|
||||
|
||||
from main import query_error
|
||||
@@ -20,13 +21,16 @@ class Book:
|
||||
return
|
||||
|
||||
def load(self, book_id: int) -> None:
|
||||
translate = QCoreApplication.translate
|
||||
query = QSqlQuery()
|
||||
query.prepare("SELECT * FROM books where book_id = :book_id")
|
||||
query.bindValue(":book_id", book_id)
|
||||
if not query.exec():
|
||||
query_error(query)
|
||||
if not query.next():
|
||||
raise Exception(f"Missing book? book_id={book_id}")
|
||||
raise Exception(
|
||||
translate("Book", "Missing book? book_id=") + f"{book_id}"
|
||||
)
|
||||
self.metadata = {
|
||||
"title": query.value("title"),
|
||||
"creator": query.value("author"),
|
||||
|
||||
@@ -17,7 +17,7 @@ from PyQt6.QtSql import QSqlQuery, QSqlQueryModel
|
||||
from PyQt6.QtWidgets import QDialog, QDialogButtonBox, QStyledItemDelegate
|
||||
|
||||
from main import query_error
|
||||
from ui.PeopleDialog import Ui_Dialog
|
||||
from ui.PeopleDialog import Ui_PersonDialog
|
||||
|
||||
|
||||
class blockHandler(HTMLParser):
|
||||
@@ -104,7 +104,7 @@ class MLStripper(HTMLParser):
|
||||
return self.text.getvalue()
|
||||
|
||||
|
||||
class PersonDialog(QDialog, Ui_Dialog):
|
||||
class PersonDialog(QDialog, Ui_PersonDialog):
|
||||
SectionIdRole = Qt.ItemDataRole.UserRole
|
||||
SectionSequenceRole = Qt.ItemDataRole.UserRole + 1
|
||||
BookIdRole = Qt.ItemDataRole.UserRole + 2
|
||||
@@ -125,12 +125,12 @@ class PersonDialog(QDialog, Ui_Dialog):
|
||||
if not query.exec():
|
||||
query_error(query)
|
||||
model.setQuery(query)
|
||||
self.bookCombo.setPlaceholderText("Select A Book")
|
||||
self.bookCombo.setPlaceholderText(self.tr("Select A Book"))
|
||||
self.bookCombo.setModel(model)
|
||||
self.bookCombo.setModelColumn(1)
|
||||
self.bookCombo.setCurrentIndex(-1)
|
||||
model: QStandardItemModel = QStandardItemModel() # type: ignore[no-redef]
|
||||
self.sectionCombo.setPlaceholderText("Select A Section")
|
||||
self.sectionCombo.setPlaceholderText(self.tr("Select A Section"))
|
||||
self.sectionCombo.setModel(model)
|
||||
self.sectionCombo.setEnabled(False)
|
||||
self.sectionCombo.setCurrentIndex(-1)
|
||||
@@ -168,7 +168,7 @@ class PersonDialog(QDialog, Ui_Dialog):
|
||||
if not query.exec():
|
||||
query_error(query)
|
||||
if not query.next():
|
||||
raise Exception(f"No person record for {person_id}")
|
||||
raise Exception(self.tr("No person record for ") + f"{person_id}")
|
||||
self.person_id = person_id
|
||||
self.nameEdit.setText(query.value("name"))
|
||||
self.orgEdit.setText(query.value("organization"))
|
||||
@@ -183,7 +183,8 @@ class PersonDialog(QDialog, Ui_Dialog):
|
||||
)
|
||||
if len(matches) != 1:
|
||||
raise Exception(
|
||||
f"Match failed looking for book_id: {query.value('book_id')}"
|
||||
self.tr("Match failed looking for book_id: ")
|
||||
+ f"{query.value('book_id')}"
|
||||
)
|
||||
row = int(matches[0].row())
|
||||
self.bookCombo.setCurrentIndex(row)
|
||||
@@ -215,10 +216,14 @@ class PersonDialog(QDialog, Ui_Dialog):
|
||||
)
|
||||
html += "</head><body>\n"
|
||||
html += f"<h1>{title}</h1>\n"
|
||||
html += self.makeNotes()
|
||||
html += self.makeDefinitions()
|
||||
html += self.makeText()
|
||||
html += "</body>\n</html>\n"
|
||||
|
||||
#
|
||||
# XXX - Use the sound module, don't do this by hand
|
||||
#
|
||||
if self.sender() == self.printBtn:
|
||||
dev = None
|
||||
for output in QMediaDevices.audioOutputs():
|
||||
@@ -312,9 +317,8 @@ class PersonDialog(QDialog, Ui_Dialog):
|
||||
book_id = model.data(model.createIndex(row, 0))
|
||||
query.bindValue(":book_id", book_id)
|
||||
section_id = self.sectionCombo.currentData(PersonDialog.SectionIdRole)
|
||||
print(f"section_id: {section_id}")
|
||||
if not section_id:
|
||||
raise Exception(f"Section id is null")
|
||||
raise Exception(self.tr("Section id is null"))
|
||||
if not query.exec():
|
||||
query_error(query)
|
||||
if self.person_id <= 0:
|
||||
@@ -426,7 +430,9 @@ class PersonDialog(QDialog, Ui_Dialog):
|
||||
if not section_query.exec():
|
||||
query_error(section_query)
|
||||
if not section_query.next():
|
||||
raise Exception(f"Missing section {section_id}")
|
||||
raise Exception(
|
||||
self.tr("Missing section ") + f"{section_id}"
|
||||
)
|
||||
section = blockHandler()
|
||||
section.feed(section_query.value("content"))
|
||||
html += section.get_block(query.value("block")) + "\n"
|
||||
@@ -437,3 +443,24 @@ class PersonDialog(QDialog, Ui_Dialog):
|
||||
html = '<div class="stats">'
|
||||
html += "</div>\n"
|
||||
return html
|
||||
|
||||
def makeNotes(self) -> str:
|
||||
html = '<div class="notes">'
|
||||
query = QSqlQuery()
|
||||
query.prepare(
|
||||
"SELECT * FROM sessions " "WHERE session_id = :session_id"
|
||||
)
|
||||
row = self.sessionCombo.currentIndex()
|
||||
model = self.sessionCombo.model()
|
||||
index = model.index(row, 0)
|
||||
session_id = index.data()
|
||||
query.bindValue(":session_id", session_id)
|
||||
if not query.exec():
|
||||
query_error(query)
|
||||
first = True
|
||||
if not query.next():
|
||||
return ""
|
||||
html += "<h3>" + self.tr("Notes") + "</h3>\n"
|
||||
html += query.value("notes")
|
||||
html += "</div>"
|
||||
return html
|
||||
|
||||
@@ -1,19 +1,28 @@
|
||||
import json
|
||||
import os
|
||||
from typing import Any, Dict, List, Optional, Type, cast
|
||||
import re
|
||||
from typing import Any, Dict, List, Optional, Self, Type, cast
|
||||
|
||||
from PyQt6.QtCore import Qt, pyqtSlot
|
||||
from PyQt6.QtCore import (
|
||||
QCoreApplication,
|
||||
QLocale,
|
||||
QResource,
|
||||
Qt,
|
||||
QTranslator,
|
||||
pyqtSlot,
|
||||
)
|
||||
from PyQt6.QtMultimedia import QMediaDevices
|
||||
from PyQt6.QtWidgets import QAbstractItemView, QDialog, QListWidgetItem, QWidget
|
||||
|
||||
from ui.Preferences import Ui_Dialog
|
||||
from ui.Preferences import Ui_Preferences
|
||||
|
||||
|
||||
class Preferences(QDialog, Ui_Dialog):
|
||||
class Preferences(QDialog, Ui_Preferences):
|
||||
translator: Optional[QTranslator]
|
||||
_instance = None
|
||||
preferences: Dict[str, str | List[str]]
|
||||
|
||||
def __new__(cls: Type[Preferences]) -> Preferences:
|
||||
def __new__(cls: Type[Self]) -> Self:
|
||||
if cls._instance:
|
||||
return cls._instance
|
||||
cls._instance = super(Preferences, cls).__new__(cls)
|
||||
@@ -59,6 +68,15 @@ class Preferences(QDialog, Ui_Dialog):
|
||||
description = output.description()
|
||||
self.alertList.addItem(description)
|
||||
self.playerList.addItem(description)
|
||||
resource = QResource("/translate")
|
||||
translator = QTranslator()
|
||||
for child in resource.children():
|
||||
fn = f":/translate/{child}"
|
||||
if translator.load(fn):
|
||||
locale = QLocale(translator.language())
|
||||
lang_code = locale.language()
|
||||
language = QLocale.languageToString(lang_code)
|
||||
self.languageCombo.addItem(language, translator.language())
|
||||
self.setCurrent()
|
||||
return
|
||||
|
||||
@@ -72,6 +90,7 @@ class Preferences(QDialog, Ui_Dialog):
|
||||
"phoneticFont": "Gentium",
|
||||
"alertOutputs": ["default"],
|
||||
"playerOutputs": ["Feed for virtual microphone"],
|
||||
"language": "pt",
|
||||
}
|
||||
for output in self.preferences["alertOutputs"]:
|
||||
if output == "default":
|
||||
@@ -87,6 +106,17 @@ class Preferences(QDialog, Ui_Dialog):
|
||||
output, Qt.MatchFlag.MatchExactly
|
||||
):
|
||||
item.setSelected(True)
|
||||
index = self.languageCombo.findData(self.preferences["language"])
|
||||
if index >= 0:
|
||||
self.languageCombo.setCurrentIndex(index)
|
||||
self.translator = QTranslator()
|
||||
if self.translator.load(
|
||||
f":/translate/esl_reader_{self.preferences['language']}"
|
||||
):
|
||||
assert self.translator is not None
|
||||
QCoreApplication.installTranslator(self.translator)
|
||||
else:
|
||||
self.translator = None
|
||||
index = self.readerCombo.findText(self.preferences["readerFont"])
|
||||
if index >= 0:
|
||||
self.readerCombo.setCurrentIndex(index)
|
||||
@@ -107,6 +137,7 @@ class Preferences(QDialog, Ui_Dialog):
|
||||
self.preferences[
|
||||
"phoneticFont"
|
||||
] = self.phoneticsCombo.currentFont().family()
|
||||
self.preferences["language"] = self.languageCombo.currentData()
|
||||
self.preferences["alertOutputs"] = [
|
||||
x.data(Qt.ItemDataRole.DisplayRole)
|
||||
for x in self.alertList.selectedItems()
|
||||
@@ -117,5 +148,22 @@ class Preferences(QDialog, Ui_Dialog):
|
||||
]
|
||||
with open("preferences.json", "w") as f:
|
||||
json.dump(self.preferences, f, indent=2)
|
||||
if self.translator:
|
||||
if self.translator.language() != self.preferences["language"]:
|
||||
full_code: str = cast(str, self.preferences["language"])
|
||||
locale = QLocale(full_code)
|
||||
language = locale.language()
|
||||
code = QLocale.languageToCode(
|
||||
language, QLocale.LanguageCodeType.ISO639Part1
|
||||
)
|
||||
print(language, code)
|
||||
fn = f":/translate/esl_reader_{code}.qm"
|
||||
translator = QTranslator()
|
||||
if translator.load(fn):
|
||||
QCoreApplication.removeTranslator(self.translator)
|
||||
self.translator = translator
|
||||
QCoreApplication.installTranslator(self.translator)
|
||||
else:
|
||||
print(self.tr("Unable to load translation file ") + f"{fn}")
|
||||
super().accept()
|
||||
return
|
||||
|
||||
46
lib/read.py
46
lib/read.py
@@ -36,10 +36,10 @@ from lib.preferences import Preferences
|
||||
from lib.session import SessionDialog
|
||||
from lib.sounds import SoundOff
|
||||
from main import query_error
|
||||
from ui.ReadDialog import Ui_Dialog
|
||||
from ui.ReadDialog import Ui_ReadDialog
|
||||
|
||||
|
||||
class ReadDialog(QDialog, Ui_Dialog):
|
||||
class ReadDialog(QDialog, Ui_ReadDialog):
|
||||
playSound = pyqtSignal(str)
|
||||
playAlert = pyqtSignal()
|
||||
block: int
|
||||
@@ -112,9 +112,9 @@ class ReadDialog(QDialog, Ui_Dialog):
|
||||
@pyqtSlot()
|
||||
def timerAction(self) -> None:
|
||||
if self.session.isActive(): # We are stopping
|
||||
self.sessionBtn.setText("Start")
|
||||
self.sessionBtn.setText(self.tr("Start"))
|
||||
else:
|
||||
self.sessionBtn.setText("Stop")
|
||||
self.sessionBtn.setText(self.tr("Stop"))
|
||||
self.session.timerAction()
|
||||
self.newParagraph.emit(self.section_id, self.block)
|
||||
return
|
||||
@@ -153,7 +153,6 @@ class ReadDialog(QDialog, Ui_Dialog):
|
||||
|
||||
@pyqtSlot()
|
||||
def playAction(self) -> None:
|
||||
print("playAction")
|
||||
idx = self.stackedWidget.currentIndex()
|
||||
if idx == 0: # Reading
|
||||
# find word
|
||||
@@ -188,11 +187,11 @@ class ReadDialog(QDialog, Ui_Dialog):
|
||||
self.phonetics = None
|
||||
if not self.phonetics:
|
||||
return
|
||||
print("Searching for audio file")
|
||||
print(self.tr("Searching for audio file"))
|
||||
for entry in self.phonetics:
|
||||
if len(entry["audio"]) > 0:
|
||||
# self.parent().playAlert.emit()
|
||||
print(f"playing {entry['audio']}")
|
||||
print(self.tr("playing ") + f"{entry['audio']}")
|
||||
self.playSound.emit(entry["audio"])
|
||||
return
|
||||
|
||||
@@ -279,16 +278,23 @@ class ReadDialog(QDialog, Ui_Dialog):
|
||||
return
|
||||
|
||||
def defToHtml(self, word: str, definition: Dict[str, Any]) -> str:
|
||||
SPEAKER = "\U0001F508"
|
||||
html = f'<h1 class="def-word">{word}</h1>' + "\n"
|
||||
try:
|
||||
words: List[str] = []
|
||||
for phonetic in definition["phonetics"]:
|
||||
# XXX - Some phonetics have text and audio but audio is empty,
|
||||
# some have just text and some have just audio
|
||||
if phonetic["text"] in words:
|
||||
continue
|
||||
words.append(phonetic["text"])
|
||||
html += f'<p class="phonetic">{phonetic["text"]}</p>' + "\n"
|
||||
html += f'<p class="phonetic">{phonetic["text"]}'
|
||||
if "audio" in phonetic:
|
||||
html += f'<a href="{phonetic["audio"]}">{SPEAKER}</a>'
|
||||
html += "</p>\n"
|
||||
except Exception:
|
||||
pass
|
||||
print(html + "\n")
|
||||
html += '<ul class="outer">' + "\n"
|
||||
for meaning in definition["meanings"]:
|
||||
html += f"<li>{meaning['partOfSpeech']}"
|
||||
@@ -391,17 +397,17 @@ class ReadDialog(QDialog, Ui_Dialog):
|
||||
# Event handlers
|
||||
#
|
||||
def keyReleaseEvent(self, event: Optional[QKeyEvent]) -> None:
|
||||
self.nextBtn.setText("Next Para")
|
||||
self.prevBtn.setText("Prev Para")
|
||||
self.defineBtn.setText("Show Def")
|
||||
self.nextBtn.setText(self.tr("Next Paragraph"))
|
||||
self.prevBtn.setText(self.tr("Previous Paragraph"))
|
||||
self.defineBtn.setText(self.tr("Definition"))
|
||||
self.paragraphs = True
|
||||
super().keyReleaseEvent(event)
|
||||
return
|
||||
|
||||
def keyPressEvent(self, event: Optional[QKeyEvent]) -> None:
|
||||
self.nextBtn.setText("Next Sect")
|
||||
self.prevBtn.setText("Prev Sect")
|
||||
self.defineBtn.setText("Add Def")
|
||||
self.nextBtn.setText(self.tr("Next Section"))
|
||||
self.prevBtn.setText(self.tr("Previous Secttion"))
|
||||
self.defineBtn.setText(self.tr("Definition"))
|
||||
self.paragraphs = False
|
||||
super().keyPressEvent(event)
|
||||
return
|
||||
@@ -601,11 +607,11 @@ class ReadDialog(QDialog, Ui_Dialog):
|
||||
def returnAction(self) -> None:
|
||||
self.returnBtn.setVisible(False)
|
||||
if self.paragraphs:
|
||||
self.nextBtn.setText("Next Para")
|
||||
self.prevBtn.setText("Prev Para")
|
||||
self.nextBtn.setText(self.tr("Next Paragraph"))
|
||||
self.prevBtn.setText(self.tr("Previous Paragraph"))
|
||||
else:
|
||||
self.nextBtn.setText("Next Sect")
|
||||
self.prevBtn.setText("Prev Sect")
|
||||
self.nextBtn.setText(self.tr("Next Section"))
|
||||
self.prevBtn.setText(self.tr("Previous Section"))
|
||||
self.stackedWidget.setCurrentIndex(0)
|
||||
self.update()
|
||||
return
|
||||
@@ -613,8 +619,8 @@ class ReadDialog(QDialog, Ui_Dialog):
|
||||
def showDefinition(self) -> None:
|
||||
idx = self.stackedWidget.currentIndex()
|
||||
self.returnBtn.setVisible(True)
|
||||
self.nextBtn.setText("Next Def")
|
||||
self.prevBtn.setText("Prev Def")
|
||||
self.nextBtn.setText(self.tr("Next Definition"))
|
||||
self.prevBtn.setText(self.tr("Previous Definition"))
|
||||
if not self.display_definition(idx):
|
||||
return
|
||||
self.stackedWidget.setCurrentIndex(1)
|
||||
|
||||
@@ -15,10 +15,10 @@ from PyQt6.QtSql import QSqlQuery
|
||||
from PyQt6.QtWidgets import QCheckBox, QDialog, QListView, QMessageBox
|
||||
|
||||
from main import query_error
|
||||
from ui.SessionDialog import Ui_Dialog
|
||||
from ui.SessionDialog import Ui_SessionDialog
|
||||
|
||||
|
||||
class SessionDialog(QDialog, Ui_Dialog):
|
||||
class SessionDialog(QDialog, Ui_SessionDialog):
|
||||
WordIdRole = Qt.ItemDataRole.UserRole
|
||||
SectionIdRole = Qt.ItemDataRole.UserRole + 1
|
||||
BlockRole = Qt.ItemDataRole.UserRole + 2
|
||||
@@ -90,8 +90,8 @@ class SessionDialog(QDialog, Ui_Dialog):
|
||||
@pyqtSlot()
|
||||
def rejected(self): # type: ignore[no-untyped-def]
|
||||
msg = QMessageBox()
|
||||
msg.setText("There is unsaved data.")
|
||||
msg.setInformativeText("Do you want to save the session?")
|
||||
msg.setText(self.tr("There is unsaved data."))
|
||||
msg.setInformativeText(self.tr("Do you want to save the session?"))
|
||||
msg.setStandardButtons(
|
||||
QMessageBox.StandardButton.Save
|
||||
| QMessageBox.StandardButton.Discard
|
||||
@@ -212,7 +212,7 @@ class SessionDialog(QDialog, Ui_Dialog):
|
||||
if not query.exec():
|
||||
query_error(query)
|
||||
else:
|
||||
print(f"Not active: {word_id}")
|
||||
print(self.tr("Not active: ") + f"{word_id}")
|
||||
return
|
||||
|
||||
@pyqtSlot(int, int)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Optional, Type, cast
|
||||
from typing import Optional, Self, Type, cast
|
||||
|
||||
from PyQt6.QtCore import QObject, Qt, QUrl, pyqtSlot
|
||||
from PyQt6.QtMultimedia import (
|
||||
@@ -15,7 +15,7 @@ from PyQt6.QtMultimedia import (
|
||||
class SoundOff(QObject):
|
||||
_instance = None
|
||||
|
||||
def __new__(cls: Type[SoundOff]) -> SoundOff:
|
||||
def __new__(cls: Type[Self]) -> Self:
|
||||
if cls._instance:
|
||||
return cls._instance
|
||||
cls._instance = super(SoundOff, cls).__new__(cls)
|
||||
|
||||
Reference in New Issue
Block a user