From 3f0024a6a384c4fc20ad079f5c85158fa59e87d0 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Thu, 9 Nov 2023 18:02:33 +0100 Subject: [PATCH 1/6] Use GUI language when translating meta data for the viewer --- novelwriter/core/tohtml.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/novelwriter/core/tohtml.py b/novelwriter/core/tohtml.py index 41201412..2dd1eed9 100644 --- a/novelwriter/core/tohtml.py +++ b/novelwriter/core/tohtml.py @@ -31,7 +31,7 @@ from pathlib import Path from novelwriter import CONFIG from novelwriter.common import formatTimeStamp -from novelwriter.constants import nwHeadFmt, nwKeyWords, nwLabels, nwHtmlUnicode +from novelwriter.constants import nwHeadFmt, nwKeyWords, nwLabels, nwHtmlUnicode, trConst from novelwriter.core.project import NWProject from novelwriter.core.tokenizer import Tokenizer, stripEscape @@ -477,7 +477,11 @@ class ToHtml(Tokenizer): if not valid or not bits or bits[0] not in nwLabels.KEY_NAME: return "" - result = f"{self._localLookup(nwLabels.KEY_NAME[bits[0]])}: " + if self._genMode == self.M_PREVIEW: + result = f"{trConst(nwLabels.KEY_NAME[bits[0]])}: " + else: + result = f"{self._localLookup(nwLabels.KEY_NAME[bits[0]])}: " + if len(bits) > 1: if bits[0] == nwKeyWords.TAG_KEY: result += f"{bits[1]}" From 27f3b8e0f203b8e41d86f4664cf41d0f65d52ac9 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Thu, 9 Nov 2023 18:02:53 +0100 Subject: [PATCH 2/6] Add project translations for Norwegian nynorsk --- novelwriter/assets/i18n/project_nn_NO.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/novelwriter/assets/i18n/project_nn_NO.json b/novelwriter/assets/i18n/project_nn_NO.json index 09b99a4e..aff847e2 100644 --- a/novelwriter/assets/i18n/project_nn_NO.json +++ b/novelwriter/assets/i18n/project_nn_NO.json @@ -2,6 +2,16 @@ "Synopsis": "Samandrag", "Comment": "Kommentar", "Notes": "Notat", + "Tag": "Knagg", + "Point of View": "Perspektiv", + "Focus": "Fokus", + "Characters": "Karakterar", + "Plot": "Plott", + "Timeline": "Tidslinje", + "Locations": "Lokasjonar", + "Objects": "Objekt", + "Entities": "Eining", + "Custom": "Anna", "0": "null", "1": "ein", "2": "to", From 2cde1b845ee9fb2d3241db9ba65434b4e118ddca Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Thu, 9 Nov 2023 18:26:33 +0100 Subject: [PATCH 3/6] Clean up project settings class --- novelwriter/core/projectdata.py | 4 +- novelwriter/dialogs/projsettings.py | 180 +++++++++----------- tests/test_dialogs/test_dlg_projsettings.py | 34 ++-- 3 files changed, 95 insertions(+), 123 deletions(-) diff --git a/novelwriter/core/projectdata.py b/novelwriter/core/projectdata.py index 9b0cc36c..d248a7a1 100644 --- a/novelwriter/core/projectdata.py +++ b/novelwriter/core/projectdata.py @@ -152,14 +152,14 @@ class NWProjectData: """Return the initial count of words for novel and note documents. """ - return tuple(self._initCounts) + return self._initCounts[0], self._initCounts[1] @property def currCounts(self) -> tuple[int, int]: """Return the current count of words for novel and note documents. """ - return tuple(self._currCounts) + return self._currCounts[0], self._currCounts[1] @property def lastHandle(self) -> dict[str, str | None]: diff --git a/novelwriter/dialogs/projsettings.py b/novelwriter/dialogs/projsettings.py index ac79a986..337c823a 100644 --- a/novelwriter/dialogs/projsettings.py +++ b/novelwriter/dialogs/projsettings.py @@ -96,17 +96,17 @@ class GuiProjectSettings(NPagedDialog): return - def __del__(self): # pragma: no cover + def __del__(self) -> None: # pragma: no cover logger.debug("Delete: GuiProjectSettings") return ## - # Slots + # Private Slots ## - def _doSave(self): - """Save settings and close dialog. - """ + @pyqtSlot() + def _doSave(self) -> None: + """Save settings and close dialog.""" project = SHARED.project projName = self.tabMain.editName.text() bookTitle = self.tabMain.editTitle.text() @@ -140,9 +140,9 @@ class GuiProjectSettings(NPagedDialog): return - def _doClose(self): - """Save settings and close the dialog. - """ + @pyqtSlot() + def _doClose(self) -> None: + """Save settings and close the dialog.""" self._saveGuiSettings() self.reject() return @@ -151,9 +151,8 @@ class GuiProjectSettings(NPagedDialog): # Internal Functions ## - def _focusTab(self, tab): - """Change which is the focused tab. - """ + def _focusTab(self, tab: int) -> None: + """Change which is the focused tab.""" if tab == self.TAB_MAIN: self.setCurrentWidget(self.tabMain) elif tab == self.TAB_STATUS: @@ -164,9 +163,8 @@ class GuiProjectSettings(NPagedDialog): self.setCurrentWidget(self.tabReplace) return - def _saveGuiSettings(self): - """Save GUI settings. - """ + def _saveGuiSettings(self) -> None: + """Save GUI settings.""" winWidth = CONFIG.rpxInt(self.width()) winHeight = CONFIG.rpxInt(self.height()) replaceColW = CONFIG.rpxInt(self.tabReplace.listBox.columnWidth(0)) @@ -187,8 +185,8 @@ class GuiProjectSettings(NPagedDialog): class GuiProjectEditMain(QWidget): - def __init__(self, projGui): - super().__init__(parent=projGui) + def __init__(self, parent: QWidget) -> None: + super().__init__(parent=parent) # The Form self.mainForm = NConfigLayout() @@ -271,8 +269,8 @@ class GuiProjectEditStatus(QWidget): COL_ROLE = Qt.ItemDataRole.UserRole + 1 NUM_ROLE = Qt.ItemDataRole.UserRole + 2 - def __init__(self, projGui, isStatus): - super().__init__(parent=projGui) + def __init__(self, parent: QWidget, isStatus: bool) -> None: + super().__init__(parent=parent) if isStatus: self.theStatus = SHARED.project.data.itemStatus @@ -372,9 +370,8 @@ class GuiProjectEditStatus(QWidget): return - def getNewList(self): - """Return list of entries. - """ + def getNewList(self) -> tuple[list, list]: + """Return list of entries.""" if self.colChanged: newList = [] for n in range(self.listBox.topLevelItemCount()): @@ -394,9 +391,8 @@ class GuiProjectEditStatus(QWidget): ## @pyqtSlot() - def _selectColour(self): - """Open a dialog to select the status icon colour. - """ + def _selectColour(self) -> None: + """Open a dialog to select the status icon colour.""" if self.selColour is not None: newCol = QColorDialog.getColor( self.selColour, self, self.tr("Select Colour") @@ -410,17 +406,15 @@ class GuiProjectEditStatus(QWidget): return @pyqtSlot() - def _newItem(self): - """Create a new status item. - """ + def _newItem(self) -> None: + """Create a new status item.""" self._addItem(None, self.tr("New Item"), (100, 100, 100), 0) self.colChanged = True return @pyqtSlot() - def _delItem(self): - """Delete a status item. - """ + def _delItem(self) -> None: + """Delete a status item.""" selItem = self._getSelectedItem() if isinstance(selItem, QTreeWidgetItem): iRow = self.listBox.indexOfTopLevelItem(selItem) @@ -433,9 +427,8 @@ class GuiProjectEditStatus(QWidget): return @pyqtSlot() - def _saveItem(self): - """Save changes made to a status item. - """ + def _saveItem(self) -> None: + """Save changes made to a status item.""" selItem = self._getSelectedItem() if isinstance(selItem, QTreeWidgetItem): selItem.setText(self.COL_LABEL, simplified(self.editName.text())) @@ -444,11 +437,10 @@ class GuiProjectEditStatus(QWidget): self.selColour.red(), self.selColour.green(), self.selColour.blue() )) self.colChanged = True - return @pyqtSlot() - def _selectedItem(self): + def _selectedItem(self) -> None: """Extract the info of a selected item and populate the settings boxes and button. If no item is selected, clear the form. """ @@ -456,7 +448,6 @@ class GuiProjectEditStatus(QWidget): if isinstance(selItem, QTreeWidgetItem): cols = selItem.data(self.COL_LABEL, self.COL_ROLE) name = selItem.text(self.COL_LABEL) - pixmap = QPixmap(self.iPx, self.iPx) pixmap.fill(QColor(*cols)) self.selColour = QColor(*cols) @@ -464,31 +455,27 @@ class GuiProjectEditStatus(QWidget): self.colButton.setIcon(QIcon(pixmap)) self.editName.selectAll() self.editName.setFocus() - self.editName.setEnabled(True) self.colButton.setEnabled(True) self.saveButton.setEnabled(True) - else: pixmap = QPixmap(self.iPx, self.iPx) pixmap.fill(QColor(100, 100, 100)) self.selColour = QColor(100, 100, 100) self.editName.setText("") self.colButton.setIcon(QIcon(pixmap)) - self.editName.setEnabled(False) self.colButton.setEnabled(False) self.saveButton.setEnabled(False) - return ## # Internal Functions ## - def _addItem(self, key, name, cols, count): - """Add a status item to the list. - """ + def _addItem(self, key: str | None, name: str, + cols: tuple[int, int, int], count: int) -> None: + """Add a status item to the list.""" pixmap = QPixmap(self.iPx, self.iPx) pixmap.fill(QColor(*cols)) @@ -504,9 +491,8 @@ class GuiProjectEditStatus(QWidget): return - def _moveItem(self, step): - """Move and item up or down step. - """ + def _moveItem(self, step: int) -> None: + """Move and item up or down step.""" selItem = self._getSelectedItem() if selItem is None: return @@ -527,17 +513,15 @@ class GuiProjectEditStatus(QWidget): return - def _getSelectedItem(self): - """Get the currently selected item. - """ + def _getSelectedItem(self) -> QTreeWidgetItem | None: + """Get the currently selected item.""" selItem = self.listBox.selectedItems() if len(selItem) > 0: return selItem[0] return None - def _usageString(self, nUse): - """Generate usage string. - """ + def _usageString(self, nUse: int) -> str: + """Generate usage string.""" if nUse == 0: return self.tr("Not in use") elif nUse == 1: @@ -553,8 +537,8 @@ class GuiProjectEditReplace(QWidget): COL_KEY = 0 COL_REPL = 1 - def __init__(self, projGui): - super().__init__(parent=projGui) + def __init__(self, parent: QWidget) -> None: + super().__init__(parent=parent) self.arChanged = False @@ -635,25 +619,23 @@ class GuiProjectEditReplace(QWidget): return - def getNewList(self): - """Extract the list from the widget. - """ - newList = {} + def getNewList(self) -> dict: + """Extract the list from the widget.""" + new = {} for n in range(self.listBox.topLevelItemCount()): tItem = self.listBox.topLevelItem(n) if tItem is not None: aKey = self._stripNotAllowed(tItem.text(0)) aVal = tItem.text(1) if len(aKey) > 0: - newList[aKey] = aVal - - return newList + new[aKey] = aVal + return new ## # Internal Functions ## - def _selectedItem(self): + def _selectedItem(self) -> bool: """Extract the details from the selected item and populate the edit form. """ @@ -670,63 +652,53 @@ class GuiProjectEditReplace(QWidget): self.editKey.setFocus() return True - def _saveEntry(self): - """Save the form data into the list widget. - """ + def _saveEntry(self) -> None: + """Save the form data into the list widget.""" selItem = self._getSelectedItem() - if selItem is None: - return False - - newKey = self.editKey.text() - newVal = self.editValue.text() - saveKey = self._stripNotAllowed(newKey) - - if len(saveKey) > 0 and len(newVal) > 0: - selItem.setText(self.COL_KEY, "<%s>" % saveKey) - selItem.setText(self.COL_REPL, newVal) - self.editKey.clear() - self.editValue.clear() - self.editKey.setEnabled(False) - self.editValue.setEnabled(False) - self.listBox.clearSelection() - self.arChanged = True - + if selItem: + newKey = self.editKey.text() + newVal = self.editValue.text() + saveKey = self._stripNotAllowed(newKey) + if len(saveKey) > 0 and len(newVal) > 0: + selItem.setText(self.COL_KEY, "<%s>" % saveKey) + selItem.setText(self.COL_REPL, newVal) + self.editKey.clear() + self.editValue.clear() + self.editKey.setEnabled(False) + self.editValue.setEnabled(False) + self.listBox.clearSelection() + self.arChanged = True return - def _addEntry(self): - """Add a new list entry. - """ + def _addEntry(self) -> None: + """Add a new list entry.""" saveKey = "" % (self.listBox.topLevelItemCount() + 1) newVal = "" newItem = QTreeWidgetItem([saveKey, newVal]) self.listBox.addTopLevelItem(newItem) - return True + return - def _delEntry(self): - """Delete the selected entry. - """ + def _delEntry(self) -> None: + """Delete the selected entry.""" selItem = self._getSelectedItem() - if selItem is None: - return False - self.listBox.takeTopLevelItem(self.listBox.indexOfTopLevelItem(selItem)) - self.arChanged = True - return True + if selItem: + self.listBox.takeTopLevelItem(self.listBox.indexOfTopLevelItem(selItem)) + self.arChanged = True + return - def _getSelectedItem(self): - """Extract the currently selected item. - """ + def _getSelectedItem(self) -> QTreeWidgetItem | None: + """Extract the currently selected item.""" selItem = self.listBox.selectedItems() if len(selItem) == 0: return None return selItem[0] - def _stripNotAllowed(self, theKey): - """Clean up the replace key string. - """ - retKey = "" - for c in theKey: + def _stripNotAllowed(self, key: str) -> str: + """Clean up the replace key string.""" + result = "" + for c in key: if c.isalnum(): - retKey += c - return retKey + result += c + return result # END Class GuiProjectEditReplace diff --git a/tests/test_dialogs/test_dlg_projsettings.py b/tests/test_dialogs/test_dlg_projsettings.py index f56f01bf..824d0403 100644 --- a/tests/test_dialogs/test_dlg_projsettings.py +++ b/tests/test_dialogs/test_dlg_projsettings.py @@ -155,9 +155,9 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, fncPath, projPat # Set some values theProject = SHARED.project - theProject.tree[C.hTitlePage].setStatus(C.sFinished) - theProject.tree[C.hChapterDoc].setStatus(C.sDraft) - theProject.tree[C.hSceneDoc].setStatus(C.sDraft) + theProject.tree[C.hTitlePage].setStatus(C.sFinished) # type: ignore + theProject.tree[C.hChapterDoc].setStatus(C.sDraft) # type: ignore + theProject.tree[C.hSceneDoc].setStatus(C.sDraft) # type: ignore nwGUI.projView.projTree.setSelectedHandle(C.hPlotRoot) nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, hLevel=1, isNote=True) @@ -170,9 +170,9 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, fncPath, projPat hCharNote = "0000000000011" hWorldNote = "0000000000012" - theProject.tree[hPlotNote].setImport(C.iMajor) - theProject.tree[hCharNote].setImport(C.iMajor) - theProject.tree[hWorldNote].setImport(C.iMain) + theProject.tree[hPlotNote].setImport(C.iMajor) # type: ignore + theProject.tree[hCharNote].setImport(C.iMajor) # type: ignore + theProject.tree[hWorldNote].setImport(C.iMain) # type: ignore # Create Dialog projSettings = GuiProjectSettings(nwGUI, GuiProjectSettings.TAB_STATUS) @@ -367,23 +367,23 @@ def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, fncPath, projPath, mo tabReplace = projSettings.tabReplace - assert tabReplace.listBox.topLevelItem(0).text(0) == "" - assert tabReplace.listBox.topLevelItem(0).text(1) == "B" - assert tabReplace.listBox.topLevelItem(1).text(0) == "" - assert tabReplace.listBox.topLevelItem(1).text(1) == "D" + assert tabReplace.listBox.topLevelItem(0).text(0) == "" # type: ignore + assert tabReplace.listBox.topLevelItem(0).text(1) == "B" # type: ignore + assert tabReplace.listBox.topLevelItem(1).text(0) == "" # type: ignore + assert tabReplace.listBox.topLevelItem(1).text(1) == "D" # type: ignore assert tabReplace.listBox.topLevelItemCount() == 2 # Nothing to save or delete tabReplace.listBox.clearSelection() - assert tabReplace._saveEntry() is False - assert tabReplace._delEntry() is False + tabReplace._saveEntry() + tabReplace._delEntry() assert tabReplace.listBox.topLevelItemCount() == 2 # Create a new entry qtbot.mouseClick(tabReplace.addButton, Qt.LeftButton) assert tabReplace.listBox.topLevelItemCount() == 3 - assert tabReplace.listBox.topLevelItem(2).text(0) == "" - assert tabReplace.listBox.topLevelItem(2).text(1) == "" + assert tabReplace.listBox.topLevelItem(2).text(0) == "" # type: ignore + assert tabReplace.listBox.topLevelItem(2).text(1) == "" # type: ignore # Edit the entry tabReplace.listBox.setCurrentItem(tabReplace.listBox.topLevelItem(2)) @@ -394,8 +394,8 @@ def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, fncPath, projPath, mo for c in "With This Stuff ": qtbot.keyClick(tabReplace.editValue, c, delay=KEY_DELAY) qtbot.mouseClick(tabReplace.saveButton, Qt.LeftButton) - assert tabReplace.listBox.topLevelItem(2).text(0) == "" - assert tabReplace.listBox.topLevelItem(2).text(1) == "With This Stuff " + assert tabReplace.listBox.topLevelItem(2).text(0) == "" # type: ignore + assert tabReplace.listBox.topLevelItem(2).text(1) == "With This Stuff " # type: ignore # Create a new entry again tabReplace.listBox.clearSelection() @@ -405,7 +405,7 @@ def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, fncPath, projPath, mo # The list is sorted, so we must find it newIdx = -1 for i in range(tabReplace.listBox.topLevelItemCount()): - if tabReplace.listBox.topLevelItem(i).text(0) == "": + if tabReplace.listBox.topLevelItem(i).text(0) == "": # type: ignore newIdx = i break assert newIdx >= 0 From c7bfb1b21ed0ec3fc7d6b1fe3b682807a14e2dd1 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Thu, 9 Nov 2023 18:44:08 +0100 Subject: [PATCH 4/6] Change to always use project language for generating documents --- novelwriter/core/docbuild.py | 6 +----- novelwriter/core/tohtml.py | 16 ++++----------- novelwriter/core/tokenizer.py | 3 --- novelwriter/dialogs/projsettings.py | 32 +++++++++++++++++++++++------ novelwriter/tools/manussettings.py | 14 ------------- 5 files changed, 31 insertions(+), 40 deletions(-) diff --git a/novelwriter/core/docbuild.py b/novelwriter/core/docbuild.py index 8c4b4ca5..75906d9a 100644 --- a/novelwriter/core/docbuild.py +++ b/novelwriter/core/docbuild.py @@ -248,13 +248,9 @@ class NWBuildDocument: def _setupBuild(self, bldObj: Tokenizer) -> dict: """Configure the build object.""" # Get Settings - buildLang = self._build.getStr("format.buildLang") textFont = self._build.getStr("format.textFont") textSize = self._build.getInt("format.textSize") - # The language lookup dict is reloaded if needed - self._project.setProjectLang(buildLang) - fontFamily = textFont or CONFIG.textFont bldFont = QFont(fontFamily, textSize) fontInfo = QFontInfo(bldFont) @@ -287,7 +283,7 @@ class NWBuildDocument: if isinstance(bldObj, ToOdt): bldObj.setColourHeaders(self._build.getBool("odt.addColours")) - bldObj.setLanguage(buildLang) + bldObj.setLanguage(self._project.data.language) scale = nwLabels.UNIT_SCALE.get(self._build.getStr("format.pageUnit"), 1.0) pW, pH = nwLabels.PAPER_SIZE.get(self._build.getStr("format.pageSize"), (-1.0, -1.0)) diff --git a/novelwriter/core/tohtml.py b/novelwriter/core/tohtml.py index 2dd1eed9..ac73418d 100644 --- a/novelwriter/core/tohtml.py +++ b/novelwriter/core/tohtml.py @@ -31,7 +31,7 @@ from pathlib import Path from novelwriter import CONFIG from novelwriter.common import formatTimeStamp -from novelwriter.constants import nwHeadFmt, nwKeyWords, nwLabels, nwHtmlUnicode, trConst +from novelwriter.constants import nwHeadFmt, nwKeyWords, nwLabels, nwHtmlUnicode from novelwriter.core.project import NWProject from novelwriter.core.tokenizer import Tokenizer, stripEscape @@ -456,12 +456,8 @@ class ToHtml(Tokenizer): def _formatSynopsis(self, text: str) -> str: """Apply HTML formatting to synopsis.""" - if self._genMode == self.M_PREVIEW: - sSynop = self._trSynopsis - return f"

{sSynop}: {text}

\n" - else: - sSynop = self._localLookup("Synopsis") - return f"

{sSynop}: {text}

\n" + sSynop = self._localLookup("Synopsis") + return f"

{sSynop}: {text}

\n" def _formatComments(self, text: str) -> str: """Apply HTML formatting to comments.""" @@ -477,11 +473,7 @@ class ToHtml(Tokenizer): if not valid or not bits or bits[0] not in nwLabels.KEY_NAME: return "" - if self._genMode == self.M_PREVIEW: - result = f"{trConst(nwLabels.KEY_NAME[bits[0]])}: " - else: - result = f"{self._localLookup(nwLabels.KEY_NAME[bits[0]])}: " - + result = f"{self._localLookup(nwLabels.KEY_NAME[bits[0]])}: " if len(bits) > 1: if bits[0] == nwKeyWords.TAG_KEY: result += f"
{bits[1]}" diff --git a/novelwriter/core/tokenizer.py b/novelwriter/core/tokenizer.py index 63518ca7..4f4a8378 100644 --- a/novelwriter/core/tokenizer.py +++ b/novelwriter/core/tokenizer.py @@ -167,9 +167,6 @@ class Tokenizer(ABC): self._localLookup = self._project.localLookup self.tr = partial(QCoreApplication.translate, "Tokenizer") - # Cached Translations - self._trSynopsis = self.tr("Synopsis") - # Format RegEx self._rxMarkdown = [ (QRegularExpression(nwRegEx.FMT_EI), [0, self.FMT_I_B, 0, self.FMT_I_E]), diff --git a/novelwriter/dialogs/projsettings.py b/novelwriter/dialogs/projsettings.py index 337c823a..4accfed0 100644 --- a/novelwriter/dialogs/projsettings.py +++ b/novelwriter/dialogs/projsettings.py @@ -107,10 +107,11 @@ class GuiProjectSettings(NPagedDialog): @pyqtSlot() def _doSave(self) -> None: """Save settings and close dialog.""" - project = SHARED.project + project = SHARED.project projName = self.tabMain.editName.text() bookTitle = self.tabMain.editTitle.text() bookAuthor = self.tabMain.editAuthor.text() + projLang = self.tabMain.projLang.currentData() spellLang = self.tabMain.spellLang.currentData() doBackup = not self.tabMain.doBackup.isChecked() @@ -119,6 +120,7 @@ class GuiProjectSettings(NPagedDialog): project.data.setAuthor(bookAuthor) project.data.setDoBackup(doBackup) project.data.setSpellLang(spellLang) + project.setProjectLang(projLang) if self.tabStatus.colChanged: newList, delList = self.tabStatus.getNewList() @@ -228,24 +230,42 @@ class GuiProjectEditMain(QWidget): self.tr("Change whenever you want!") ) + self.projLang = QComboBox(self) + self.projLang.setMaximumWidth(xW) + if CONFIG.hasEnchant: + for tag, language in CONFIG.listLanguages(CONFIG.LANG_PROJ): + self.projLang.addItem(language, tag) + self.mainForm.addRow( + self.tr("Project language"), + self.projLang, + self.tr("Used when building the manuscript.") + ) + + langIdx = 0 + if pData.language is not None: + langIdx = self.projLang.findData(pData.language) + if langIdx == -1: + langIdx = self.projLang.findData("en_GB") + if langIdx != -1: + self.projLang.setCurrentIndex(langIdx) + self.spellLang = QComboBox(self) self.spellLang.setMaximumWidth(xW) self.spellLang.addItem(self.tr("Default"), "None") if CONFIG.hasEnchant: for tag, language in SHARED.spelling.listDictionaries(): self.spellLang.addItem(language, tag) - self.mainForm.addRow( self.tr("Spell check language"), self.spellLang, self.tr("Overrides main preferences.") ) - spellIdx = 0 + langIdx = 0 if pData.spellLang is not None: - spellIdx = self.spellLang.findData(pData.spellLang) - if spellIdx != -1: - self.spellLang.setCurrentIndex(spellIdx) + langIdx = self.spellLang.findData(pData.spellLang) + if langIdx != -1: + self.spellLang.setCurrentIndex(langIdx) self.doBackup = NSwitch(self) self.doBackup.setChecked(not pData.doBackup) diff --git a/novelwriter/tools/manussettings.py b/novelwriter/tools/manussettings.py index cfa1b038..95235424 100644 --- a/novelwriter/tools/manussettings.py +++ b/novelwriter/tools/manussettings.py @@ -974,15 +974,6 @@ class _FormatTab(QWidget): self.formFormat = NConfigLayout() self.formFormat.addGroupLabel(self._build.getLabel("format.grpFormat")) - # Build Language - self.buildLang = QComboBox() - langauges = CONFIG.listLanguages(CONFIG.LANG_PROJ) - self.buildLang.addItem("[%s]" % self.tr("Not Set"), "None") - for langID, langName in langauges: - self.buildLang.addItem(langName, langID) - - self.formFormat.addRow(self._build.getLabel("format.buildLang"), self.buildLang) - # Font Family self.textFont = QLineEdit() self.textFont.setReadOnly(True) @@ -1103,10 +1094,6 @@ class _FormatTab(QWidget): def loadContent(self) -> None: """Populate the widgets.""" - langIdx = self.buildLang.findData(self._build.getStr("format.buildLang")) - if langIdx != -1: - self.buildLang.setCurrentIndex(langIdx) - textFont = self._build.getStr("format.textFont") if not textFont: textFont = str(CONFIG.textFont) @@ -1146,7 +1133,6 @@ class _FormatTab(QWidget): def saveContent(self) -> None: """Save choices back into build object.""" - self._build.setValue("format.buildLang", str(self.buildLang.currentData())) self._build.setValue("format.textFont", self.textFont.text()) self._build.setValue("format.textSize", self.textSize.value()) self._build.setValue("format.lineHeight", self.lineHeight.value()) From e3997b53aa6687d809557202e9b8a6b3576b33e5 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Thu, 9 Nov 2023 18:55:02 +0100 Subject: [PATCH 5/6] Add language setting to wizard --- novelwriter/core/coretools.py | 2 ++ novelwriter/dialogs/projsettings.py | 5 ++--- novelwriter/guimain.py | 6 ++++++ novelwriter/tools/projwizard.py | 18 ++++++++++++++++-- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/novelwriter/core/coretools.py b/novelwriter/core/coretools.py index 017ea082..81803576 100644 --- a/novelwriter/core/coretools.py +++ b/novelwriter/core/coretools.py @@ -353,11 +353,13 @@ class ProjectBuilder: projName = data.get("projName", lblNewProject) projTitle = data.get("projTitle", lblNewProject) projAuthor = data.get("projAuthor", "") + projLang = data.get("projLang", "en_GB") project.data.setUuid(None) project.data.setName(projName) project.data.setTitle(projTitle) project.data.setAuthor(projAuthor) + project.data.setLanguage(projLang) project.setDefaultStatusImport() project.session.startSession() diff --git a/novelwriter/dialogs/projsettings.py b/novelwriter/dialogs/projsettings.py index 4accfed0..20b11c53 100644 --- a/novelwriter/dialogs/projsettings.py +++ b/novelwriter/dialogs/projsettings.py @@ -232,9 +232,8 @@ class GuiProjectEditMain(QWidget): self.projLang = QComboBox(self) self.projLang.setMaximumWidth(xW) - if CONFIG.hasEnchant: - for tag, language in CONFIG.listLanguages(CONFIG.LANG_PROJ): - self.projLang.addItem(language, tag) + for tag, language in CONFIG.listLanguages(CONFIG.LANG_PROJ): + self.projLang.addItem(language, tag) self.mainForm.addRow( self.tr("Project language"), self.projLang, diff --git a/novelwriter/guimain.py b/novelwriter/guimain.py index 41301476..519a40cf 100644 --- a/novelwriter/guimain.py +++ b/novelwriter/guimain.py @@ -1482,6 +1482,12 @@ class GuiMain(QMainWindow): projData["numChapters"] = newProj.field("numChapters") projData["numScenes"] = newProj.field("numScenes") + try: + langIdx = newProj.field("projLang") + projData["projLang"] = CONFIG.listLanguages(CONFIG.LANG_PROJ)[langIdx][0] + except Exception: + projData["projLang"] = "en_GB" + return projData def _getTagSource(self, tag: str) -> tuple[str | None, str | None]: diff --git a/novelwriter/tools/projwizard.py b/novelwriter/tools/projwizard.py index ef715847..b47da1cf 100644 --- a/novelwriter/tools/projwizard.py +++ b/novelwriter/tools/projwizard.py @@ -28,8 +28,9 @@ import logging from PyQt5.QtCore import Qt from PyQt5.QtWidgets import ( - QFileDialog, QFormLayout, QGridLayout, QHBoxLayout, QLabel, QLineEdit, - QPushButton, QRadioButton, QSpinBox, QVBoxLayout, QWizard, QWizardPage + QComboBox, QFileDialog, QFormLayout, QGridLayout, QHBoxLayout, QLabel, + QLineEdit, QPushButton, QRadioButton, QSpinBox, QVBoxLayout, QWizard, + QWizardPage ) from novelwriter import CONFIG, SHARED @@ -127,15 +128,28 @@ class ProjWizardIntroPage(QWizardPage): self.projAuthor.setFixedWidth(xW) self.projAuthor.setPlaceholderText(self.tr("Optional")) + self.projLang = QComboBox(self) + self.projLang.setMaximumWidth(xW) + for tag, language in CONFIG.listLanguages(CONFIG.LANG_PROJ): + self.projLang.addItem(language, tag) + + langIdx = self.projLang.findData(CONFIG.guiLocale) + if langIdx == -1: + langIdx = self.projLang.findData("en_GB") + if langIdx != -1: + self.projLang.setCurrentIndex(langIdx) + self.mainForm = QFormLayout() self.mainForm.addRow(self.tr("Project Name"), self.projName) self.mainForm.addRow(self.tr("Novel Title"), self.projTitle) self.mainForm.addRow(self.tr("Author(s)"), self.projAuthor) + self.mainForm.addRow(self.tr("Language"), self.projLang) self.mainForm.setVerticalSpacing(fS) self.registerField("projName*", self.projName) self.registerField("projTitle", self.projTitle) self.registerField("projAuthor", self.projAuthor) + self.registerField("projLang", self.projLang) # Assemble self.outerBox = QVBoxLayout() From 76d67dd0aaaf46f2339038fd3ba836b07db7767d Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Thu, 9 Nov 2023 19:04:36 +0100 Subject: [PATCH 6/6] Clean up build settings and update tests --- novelwriter/core/buildsettings.py | 2 -- novelwriter/core/tohtml.py | 5 ++++- tests/reference/coreTools_NewMinimal_nwProject.nwx | 2 +- tests/test_core/test_core_coretools.py | 2 ++ tests/test_tools/test_tools_manussettings.py | 3 --- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/novelwriter/core/buildsettings.py b/novelwriter/core/buildsettings.py index 931525ea..0698c2e0 100644 --- a/novelwriter/core/buildsettings.py +++ b/novelwriter/core/buildsettings.py @@ -64,7 +64,6 @@ SETTINGS_TEMPLATE = { "text.includeKeywords": (bool, False), "text.includeBodyText": (bool, True), "text.addNoteHeadings": (bool, True), - "format.buildLang": (str, "en_GB"), "format.textFont": (str, CONFIG.textFont), "format.textSize": (int, 12), "format.lineHeight": (float, 1.15, 0.75, 3.0), @@ -107,7 +106,6 @@ SETTINGS_LABELS = { "text.addNoteHeadings": QT_TRANSLATE_NOOP("Builds", "Add Titles for Notes"), "format.grpFormat": QT_TRANSLATE_NOOP("Builds", "Text Format"), - "format.buildLang": QT_TRANSLATE_NOOP("Builds", "Language"), "format.textFont": QT_TRANSLATE_NOOP("Builds", "Font Family"), "format.textSize": QT_TRANSLATE_NOOP("Builds", "Font Size"), "format.lineHeight": QT_TRANSLATE_NOOP("Builds", "Line Height"), diff --git a/novelwriter/core/tohtml.py b/novelwriter/core/tohtml.py index ac73418d..7443ed34 100644 --- a/novelwriter/core/tohtml.py +++ b/novelwriter/core/tohtml.py @@ -457,7 +457,10 @@ class ToHtml(Tokenizer): def _formatSynopsis(self, text: str) -> str: """Apply HTML formatting to synopsis.""" sSynop = self._localLookup("Synopsis") - return f"

{sSynop}: {text}

\n" + if self._genMode == self.M_PREVIEW: + return f"

{sSynop}: {text}

\n" + else: + return f"

{sSynop}: {text}

\n" def _formatComments(self, text: str) -> str: """Apply HTML formatting to comments.""" diff --git a/tests/reference/coreTools_NewMinimal_nwProject.nwx b/tests/reference/coreTools_NewMinimal_nwProject.nwx index 23a377f5..a38b56b0 100644 --- a/tests/reference/coreTools_NewMinimal_nwProject.nwx +++ b/tests/reference/coreTools_NewMinimal_nwProject.nwx @@ -7,7 +7,7 @@ yes - None + en_GB None None diff --git a/tests/test_core/test_core_coretools.py b/tests/test_core/test_core_coretools.py index a3f4e11c..0f127e05 100644 --- a/tests/test_core/test_core_coretools.py +++ b/tests/test_core/test_core_coretools.py @@ -446,6 +446,7 @@ def testCoreTools_NewCustomA(monkeypatch, fncPath, tstPaths, mockRnd): "projName": "Test Custom", "projTitle": "Test Novel", "projAuthor": "Jane Doe", + "projLang": -1, "projPath": fncPath, "popSample": False, "popMinimal": False, @@ -484,6 +485,7 @@ def testCoreTools_NewCustomB(monkeypatch, fncPath, tstPaths, mockRnd): "projName": "Test Custom", "projTitle": "Test Novel", "projAuthor": "Jane Doe", + "projLang": -1, "projPath": fncPath, "popSample": False, "popMinimal": False, diff --git a/tests/test_tools/test_tools_manussettings.py b/tests/test_tools/test_tools_manussettings.py index 1371caf0..0a5f9c5a 100644 --- a/tests/test_tools/test_tools_manussettings.py +++ b/tests/test_tools/test_tools_manussettings.py @@ -562,7 +562,6 @@ def testBuildSettings_Format(monkeypatch, qtbot: QtBot, nwGUI: GuiMain): assert bSettings.toolStack.currentWidget() is fmtTab # Check initial values - assert fmtTab.buildLang.currentData() == "en_US" assert fmtTab.textFont.text() == textFont assert fmtTab.textSize.value() == 12 assert fmtTab.lineHeight.value() == 1.2 @@ -581,7 +580,6 @@ def testBuildSettings_Format(monkeypatch, qtbot: QtBot, nwGUI: GuiMain): assert fmtTab.rightMargin.value() == 15.0 # Change values - fmtTab.buildLang.setCurrentIndex(fmtTab.buildLang.findData("en_GB")) fmtTab.textFont.setText("Arial") fmtTab.textSize.setValue(11) fmtTab.lineHeight.setValue(1.15) @@ -596,7 +594,6 @@ def testBuildSettings_Format(monkeypatch, qtbot: QtBot, nwGUI: GuiMain): # Save values fmtTab.saveContent() - assert build.getStr("format.buildLang") == "en_GB" assert build.getStr("format.textFont") == "Arial" assert build.getInt("format.textSize") == 11 assert build.getFloat("format.lineHeight") == 1.15