From 8b0ac6e81e25742e2558376575dbc32b7ca775cf Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Thu, 18 Feb 2021 19:19:50 +0100 Subject: [PATCH 1/7] Rename projLang to spellLang --- nw/core/project.py | 17 +++++++++++------ nw/gui/doceditor.py | 4 ++-- nw/gui/projsettings.py | 4 ++-- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/nw/core/project.py b/nw/core/project.py index 4019fcbc..cc4914b9 100644 --- a/nw/core/project.py +++ b/nw/core/project.py @@ -79,7 +79,8 @@ class NWProject(): self.projCache = None # The full path to the project's cache folder self.projContent = None # The full path to the project's content folder self.projDict = None # The spell check dictionary - self.projLang = None # The spell check language, if different than default + self.projSpell = None # The spell check language, if different than default + self.projLang = None # The project language, if different than default self.projFile = None # The file name of the project main XML file # Project Meta @@ -195,6 +196,7 @@ class NWProject(): self.projCache = None self.projContent = None self.projDict = None + self.projSpell = None self.projLang = None self.projFile = nwFiles.PROJ_FILE self.projName = "" @@ -533,10 +535,12 @@ class NWProject(): continue if xItem.tag == "doBackup": self.doBackup = checkBool(xItem.text, False) + elif xItem.tag == "language": + self.projLang = checkString(xItem.text, None, True) elif xItem.tag == "spellCheck": self.spellCheck = checkBool(xItem.text, False) elif xItem.tag == "spellLang": - self.projLang = checkString(xItem.text, None, True) + self.projSpell = checkString(xItem.text, None, True) elif xItem.tag == "autoOutline": self.autoOutline = checkBool(xItem.text, True) elif xItem.tag == "lastEdited": @@ -650,8 +654,9 @@ class NWProject(): # Save Project Settings xSettings = etree.SubElement(nwXML, "settings") self._packProjectValue(xSettings, "doBackup", self.doBackup) + self._packProjectValue(xSettings, "language", self.projLang) self._packProjectValue(xSettings, "spellCheck", self.spellCheck) - self._packProjectValue(xSettings, "spellLang", self.projLang) + self._packProjectValue(xSettings, "spellLang", self.projSpell) self._packProjectValue(xSettings, "autoOutline", self.autoOutline) self._packProjectValue(xSettings, "lastEdited", self.lastEdited) self._packProjectValue(xSettings, "lastViewed", self.lastViewed) @@ -1012,9 +1017,9 @@ class NWProject(): """Set the project-specific spell check language. """ theLang = checkString(theLang, None, True) - if self.projLang != theLang: - self.projLang = theLang - self.loadProjectLocalisation(theLang) + if self.projSpell != theLang: + self.projSpell = theLang + # self.loadProjectLocalisation(theLang) self.setProjectChanged(True) return True diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index 7dc80520..9f4949a4 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -599,10 +599,10 @@ class GuiDocEditor(QTextEdit): status bar to show the one actually loaded by the spell checker class. """ - if self.theProject.projLang is None: + if self.theProject.projSpell is None: theLang = self.mainConf.spellLanguage else: - theLang = self.theProject.projLang + theLang = self.theProject.projSpell self.theDict.setLanguage(theLang, self.theProject.projDict) theTag, theProvider = self.theDict.describeDict() diff --git a/nw/gui/projsettings.py b/nw/gui/projsettings.py index c09cbfdb..eeee08e4 100644 --- a/nw/gui/projsettings.py +++ b/nw/gui/projsettings.py @@ -223,8 +223,8 @@ class GuiProjectEditMain(QWidget): ) spellIdx = 0 - if self.theProject.projLang is not None: - spellIdx = self.spellLang.findData(self.theProject.projLang) + if self.theProject.projSpell is not None: + spellIdx = self.spellLang.findData(self.theProject.projSpell) if spellIdx != -1: self.spellLang.setCurrentIndex(spellIdx) From e4d03f95814133086b31a7f56c29b3baa5d113d9 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Thu, 18 Feb 2021 19:21:46 +0100 Subject: [PATCH 2/7] Fix tests for variable change, and change monkeypatches to use the context manager --- sample/nwProject.nwx | 7 +- tests/conftest.py | 18 ++- tests/minimal/nwProject.nwx | 1 + tests/profilestats.py | 15 -- .../coreProject_NewCustomA_nwProject.nwx | 1 + .../coreProject_NewCustomB_nwProject.nwx | 1 + .../coreProject_NewFile_nwProject.nwx | 1 + .../coreProject_NewMinimal_nwProject.nwx | 1 + .../coreProject_NewRoot_nwProject.nwx | 1 + .../guiEditor_Main_Final_nwProject.nwx | 1 + .../guiEditor_Main_Initial_nwProject.nwx | 1 + .../guiItemEditor_Dialog_nwProject.nwx | 1 + .../guiProjSettings_Dialog_nwProject.nwx | 1 + tests/test_base/test_base_config.py | 143 ++++++++-------- tests/test_base/test_base_error.py | 66 ++++---- tests/test_base/test_base_init.py | 6 - tests/test_core/test_core_document.py | 18 +-- tests/test_core/test_core_index.py | 17 +- tests/test_core/test_core_options.py | 8 +- tests/test_core/test_core_project.py | 152 +++++++++--------- tests/test_core/test_core_spell.py | 56 +++---- tests/test_core/test_core_tokenizer.py | 20 ++- tests/test_core/test_core_toodt.py | 4 +- tests/test_core/test_core_tree.py | 2 - tests/test_gui/test_gui_mainmenu.py | 6 +- tests/test_gui/test_gui_projdetails.py | 1 - tests/test_gui/test_gui_projwizard.py | 19 ++- tests/test_gui/test_gui_writingstats.py | 2 - 28 files changed, 274 insertions(+), 296 deletions(-) delete mode 100755 tests/profilestats.py diff --git a/sample/nwProject.nwx b/sample/nwProject.nwx index 52fba625..5f4ff79c 100644 --- a/sample/nwProject.nwx +++ b/sample/nwProject.nwx @@ -1,16 +1,17 @@ - + Sample Project Sample Project Jane Smith Jay Doh - 1021 + 1022 161 - 48283 + 48346 False + None True None True diff --git a/tests/conftest.py b/tests/conftest.py index 59fd5c5d..d7001c1d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -121,17 +121,31 @@ def tmpConf(tmpDir): return theConf @pytest.fixture(scope="function") -def dummyGUI(tmpConf): +def fncConf(fncDir): + """Create a temporary novelWriter configuration object. + """ + confFile = os.path.join(fncDir, "novelwriter.conf") + if os.path.isfile(confFile): + os.unlink(confFile) + theConf = Config() + theConf.initConfig(fncDir, fncDir) + theConf.setLastPath("") + return theConf + +@pytest.fixture(scope="function") +def dummyGUI(monkeypatch, tmpConf): """Create a dummy instance of novelWriter's main GUI class. """ + monkeypatch.setattr("nw.CONFIG", tmpConf) theDummy = DummyMain() theDummy.mainConf = tmpConf return theDummy @pytest.fixture(scope="function") -def nwGUI(qtbot, fncDir): +def nwGUI(qtbot, monkeypatch, fncDir, fncConf): """Create an instance of the novelWriter GUI. """ + monkeypatch.setattr("nw.CONFIG", fncConf) nwGUI = nw.main(["--testmode", "--config=%s" % fncDir, "--data=%s" % fncDir]) qtbot.addWidget(nwGUI) nwGUI.show() diff --git a/tests/minimal/nwProject.nwx b/tests/minimal/nwProject.nwx index d45f9bfa..0405ea25 100644 --- a/tests/minimal/nwProject.nwx +++ b/tests/minimal/nwProject.nwx @@ -11,6 +11,7 @@ True + None False None True diff --git a/tests/profilestats.py b/tests/profilestats.py deleted file mode 100755 index 550aba00..00000000 --- a/tests/profilestats.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import pstats -import os - -profDir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir, "prof")) - -print("") -print("Profiles directory: %s" % profDir) -print("") - -profMainWindows = pstats.Stats(os.path.join(profDir, "testMainWindows.prof")) -profMainWindows.sort_stats("cumtime") -profMainWindows.print_stats("nw/") diff --git a/tests/reference/coreProject_NewCustomA_nwProject.nwx b/tests/reference/coreProject_NewCustomA_nwProject.nwx index fc7e7ab2..7673815d 100644 --- a/tests/reference/coreProject_NewCustomA_nwProject.nwx +++ b/tests/reference/coreProject_NewCustomA_nwProject.nwx @@ -11,6 +11,7 @@ True + None False None True diff --git a/tests/reference/coreProject_NewCustomB_nwProject.nwx b/tests/reference/coreProject_NewCustomB_nwProject.nwx index 11d007aa..baaf3298 100644 --- a/tests/reference/coreProject_NewCustomB_nwProject.nwx +++ b/tests/reference/coreProject_NewCustomB_nwProject.nwx @@ -11,6 +11,7 @@ True + None False None True diff --git a/tests/reference/coreProject_NewFile_nwProject.nwx b/tests/reference/coreProject_NewFile_nwProject.nwx index 66488434..83d80233 100644 --- a/tests/reference/coreProject_NewFile_nwProject.nwx +++ b/tests/reference/coreProject_NewFile_nwProject.nwx @@ -9,6 +9,7 @@ True + None False None True diff --git a/tests/reference/coreProject_NewMinimal_nwProject.nwx b/tests/reference/coreProject_NewMinimal_nwProject.nwx index d8b774bb..ba867df1 100644 --- a/tests/reference/coreProject_NewMinimal_nwProject.nwx +++ b/tests/reference/coreProject_NewMinimal_nwProject.nwx @@ -9,6 +9,7 @@ True + None False None True diff --git a/tests/reference/coreProject_NewRoot_nwProject.nwx b/tests/reference/coreProject_NewRoot_nwProject.nwx index 9ea0617f..d437d4e2 100644 --- a/tests/reference/coreProject_NewRoot_nwProject.nwx +++ b/tests/reference/coreProject_NewRoot_nwProject.nwx @@ -9,6 +9,7 @@ True + None False None True diff --git a/tests/reference/guiEditor_Main_Final_nwProject.nwx b/tests/reference/guiEditor_Main_Final_nwProject.nwx index 764c4610..6e511bca 100644 --- a/tests/reference/guiEditor_Main_Final_nwProject.nwx +++ b/tests/reference/guiEditor_Main_Final_nwProject.nwx @@ -9,6 +9,7 @@ True + None True None True diff --git a/tests/reference/guiEditor_Main_Initial_nwProject.nwx b/tests/reference/guiEditor_Main_Initial_nwProject.nwx index 77eff6e9..96af56bb 100644 --- a/tests/reference/guiEditor_Main_Initial_nwProject.nwx +++ b/tests/reference/guiEditor_Main_Initial_nwProject.nwx @@ -9,6 +9,7 @@ True + None False None True diff --git a/tests/reference/guiItemEditor_Dialog_nwProject.nwx b/tests/reference/guiItemEditor_Dialog_nwProject.nwx index 7bcf7553..89beddc9 100644 --- a/tests/reference/guiItemEditor_Dialog_nwProject.nwx +++ b/tests/reference/guiItemEditor_Dialog_nwProject.nwx @@ -9,6 +9,7 @@ True + None False None True diff --git a/tests/reference/guiProjSettings_Dialog_nwProject.nwx b/tests/reference/guiProjSettings_Dialog_nwProject.nwx index ea349d11..a935bee7 100644 --- a/tests/reference/guiProjSettings_Dialog_nwProject.nwx +++ b/tests/reference/guiProjSettings_Dialog_nwProject.nwx @@ -11,6 +11,7 @@ True + None False en True diff --git a/tests/test_base/test_base_config.py b/tests/test_base/test_base_config.py index 1197ec19..9a0a561b 100644 --- a/tests/test_base/test_base_config.py +++ b/tests/test_base/test_base_config.py @@ -44,7 +44,6 @@ def testBaseConfig_Constructor(monkeypatch): assert tstConf.osDarwin is False assert tstConf.osWindows is False assert tstConf.osUnknown is False - monkeypatch.undo() # macOS monkeypatch.setattr("sys.platform", "darwin") @@ -53,7 +52,6 @@ def testBaseConfig_Constructor(monkeypatch): assert tstConf.osDarwin is True assert tstConf.osWindows is False assert tstConf.osUnknown is False - monkeypatch.undo() # Windows monkeypatch.setattr("sys.platform", "win32") @@ -62,7 +60,6 @@ def testBaseConfig_Constructor(monkeypatch): assert tstConf.osDarwin is False assert tstConf.osWindows is True assert tstConf.osUnknown is False - monkeypatch.undo() # Cygwin monkeypatch.setattr("sys.platform", "cygwin") @@ -71,7 +68,6 @@ def testBaseConfig_Constructor(monkeypatch): assert tstConf.osDarwin is False assert tstConf.osWindows is True assert tstConf.osUnknown is False - monkeypatch.undo() # Other monkeypatch.setattr("sys.platform", "some_other_os") @@ -80,7 +76,6 @@ def testBaseConfig_Constructor(monkeypatch): assert tstConf.osDarwin is False assert tstConf.osWindows is False assert tstConf.osUnknown is True - monkeypatch.undo() # END Test testBaseConfig_Constructor @@ -99,36 +94,35 @@ def testBaseConfig_Init(monkeypatch, tmpDir, fncDir, outDir, refDir, filesDir): os.unlink(confFile) # Let the config class figure out the path - monkeypatch.setattr("PyQt5.QtCore.QStandardPaths.writableLocation", lambda *args: fncDir) - tstConf.verQtValue = 50600 - tstConf.initConfig() - assert tstConf.confPath == os.path.join(fncDir, tstConf.appHandle) - assert tstConf.dataPath == os.path.join(fncDir, tstConf.appHandle) - assert not os.path.isfile(confFile) - tstConf.verQtValue = 50000 - tstConf.initConfig() - assert tstConf.confPath == os.path.join(fncDir, tstConf.appHandle) - assert tstConf.dataPath == os.path.join(fncDir, tstConf.appHandle) - assert not os.path.isfile(confFile) - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("PyQt5.QtCore.QStandardPaths.writableLocation", lambda *args: fncDir) + tstConf.verQtValue = 50600 + tstConf.initConfig() + assert tstConf.confPath == os.path.join(fncDir, tstConf.appHandle) + assert tstConf.dataPath == os.path.join(fncDir, tstConf.appHandle) + assert not os.path.isfile(confFile) + tstConf.verQtValue = 50000 + tstConf.initConfig() + assert tstConf.confPath == os.path.join(fncDir, tstConf.appHandle) + assert tstConf.dataPath == os.path.join(fncDir, tstConf.appHandle) + assert not os.path.isfile(confFile) # Fail to make folders - monkeypatch.setattr("os.mkdir", causeOSError) + with monkeypatch.context() as mp: + mp.setattr("os.mkdir", causeOSError) - tstConfDir = os.path.join(fncDir, "test_conf") - tstConf.initConfig(confPath=tstConfDir, dataPath=tmpDir) - assert tstConf.confPath is None - assert tstConf.dataPath == tmpDir - assert not os.path.isfile(confFile) + tstConfDir = os.path.join(fncDir, "test_conf") + tstConf.initConfig(confPath=tstConfDir, dataPath=tmpDir) + assert tstConf.confPath is None + assert tstConf.dataPath == tmpDir + assert not os.path.isfile(confFile) - tstDataDir = os.path.join(fncDir, "test_data") - tstConf.initConfig(confPath=tmpDir, dataPath=tstDataDir) - assert tstConf.confPath == tmpDir - assert tstConf.dataPath is None - assert os.path.isfile(confFile) - os.unlink(confFile) - - monkeypatch.undo() + tstDataDir = os.path.join(fncDir, "test_data") + tstConf.initConfig(confPath=tmpDir, dataPath=tstDataDir) + assert tstConf.confPath == tmpDir + assert tstConf.dataPath is None + assert os.path.isfile(confFile) + os.unlink(confFile) # Test load/save with no path tstConf.confPath = None @@ -137,35 +131,34 @@ def testBaseConfig_Init(monkeypatch, tmpDir, fncDir, outDir, refDir, filesDir): # Run again and set the paths directly and correctly # This should create a config file as well - monkeypatch.setattr("os.path.expanduser", lambda *args: "") - tstConf.spellTool = nwConst.SP_INTERNAL - tstConf.initConfig(confPath=tmpDir, dataPath=tmpDir) - assert tstConf.confPath == tmpDir - assert tstConf.dataPath == tmpDir - assert os.path.isfile(confFile) + with monkeypatch.context() as mp: + mp.setattr("os.path.expanduser", lambda *args: "") + tstConf.spellTool = nwConst.SP_INTERNAL + tstConf.initConfig(confPath=tmpDir, dataPath=tmpDir) + assert tstConf.confPath == tmpDir + assert tstConf.dataPath == tmpDir + assert os.path.isfile(confFile) - copyfile(confFile, testFile) - assert cmpFiles(testFile, compFile, [2, 9, 10]) - monkeypatch.undo() + copyfile(confFile, testFile) + assert cmpFiles(testFile, compFile, [2, 9, 10]) # Load and save with OSError - monkeypatch.setattr("builtins.open", causeOSError) + with monkeypatch.context() as mp: + mp.setattr("builtins.open", causeOSError) - assert not tstConf.loadConfig() - assert tstConf.hasError is True - assert tstConf.errData != [] - assert tstConf.getErrData().startswith("Could not") - assert tstConf.hasError is False - assert tstConf.errData == [] + assert not tstConf.loadConfig() + assert tstConf.hasError is True + assert tstConf.errData != [] + assert tstConf.getErrData().startswith("Could not") + assert tstConf.hasError is False + assert tstConf.errData == [] - assert not tstConf.saveConfig() - assert tstConf.hasError is True - assert tstConf.errData != [] - assert tstConf.getErrData().startswith("Could not") - assert tstConf.hasError is False - assert tstConf.errData == [] - - monkeypatch.undo() + assert not tstConf.saveConfig() + assert tstConf.hasError is True + assert tstConf.errData != [] + assert tstConf.getErrData().startswith("Could not") + assert tstConf.hasError is False + assert tstConf.errData == [] assert tstConf.loadConfig() assert tstConf.saveConfig() @@ -233,9 +226,9 @@ def testBaseConfig_RecentCache(monkeypatch, tmpConf, tmpDir, fncDir): } # Fail to Save - monkeypatch.setattr("builtins.open", causeOSError) - assert not tmpConf.saveRecentCache() - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("builtins.open", causeOSError) + assert not tmpConf.saveRecentCache() # Save Proper cacheFile = os.path.join(tmpDir, nwFiles.RECENT_FILE) @@ -244,11 +237,11 @@ def testBaseConfig_RecentCache(monkeypatch, tmpConf, tmpDir, fncDir): assert os.path.isfile(cacheFile) # Fail to Load - monkeypatch.setattr("builtins.open", causeOSError) - tmpConf.recentProj = {} - assert not tmpConf.loadRecentCache() - assert tmpConf.recentProj == {} - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("builtins.open", causeOSError) + tmpConf.recentProj = {} + assert not tmpConf.loadRecentCache() + assert tmpConf.recentProj == {} # Load Proper tmpConf.recentProj = {} @@ -555,19 +548,19 @@ def testBaseConfig_Internal(monkeypatch, tmpConf): tmpConf._checkOptionalPackages() assert tmpConf.hasEnchant is True - monkeypatch.setitem(sys.modules, "enchant", None) - tmpConf._checkOptionalPackages() - assert tmpConf.hasEnchant is False - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setitem(sys.modules, "enchant", None) + tmpConf._checkOptionalPackages() + assert tmpConf.hasEnchant is False - monkeypatch.setattr("shutil.which", lambda *args: "dummy") - tmpConf._checkOptionalPackages() - assert tmpConf.hasAssistant is True - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("shutil.which", lambda *args: "dummy") + tmpConf._checkOptionalPackages() + assert tmpConf.hasAssistant is True - monkeypatch.setattr("shutil.which", lambda *args: None) - tmpConf._checkOptionalPackages() - assert tmpConf.hasAssistant is False - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("shutil.which", lambda *args: None) + tmpConf._checkOptionalPackages() + assert tmpConf.hasAssistant is False # END Test testBaseConfig_Internal diff --git a/tests/test_base/test_base_error.py b/tests/test_base/test_base_error.py index 45151413..9976fae2 100644 --- a/tests/test_base/test_base_error.py +++ b/tests/test_base/test_base_error.py @@ -48,22 +48,22 @@ def testBaseError_Dialog(qtbot, monkeypatch, fncDir, tmpDir): assert nwErr.msgBody.toPlainText() == "Failed to generate error report ..." # Valid Error Message - monkeypatch.setattr("PyQt5.QtCore.QSysInfo.kernelVersion", lambda: "1.2.3") - nwErr.setMessage(Exception, "Fine Error", None) - theMessage = nwErr.msgBody.toPlainText() - assert theMessage - assert "Fine Error" in theMessage - assert "Exception" in theMessage - assert "(1.2.3)" in theMessage - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("PyQt5.QtCore.QSysInfo.kernelVersion", lambda: "1.2.3") + nwErr.setMessage(Exception, "Fine Error", None) + theMessage = nwErr.msgBody.toPlainText() + assert theMessage + assert "Fine Error" in theMessage + assert "Exception" in theMessage + assert "(1.2.3)" in theMessage # No kernel version retrieved - monkeypatch.setattr("PyQt5.QtCore.QSysInfo.kernelVersion", causeException) - nwErr.setMessage(Exception, "Almost Fine Error", None) - theMessage = nwErr.msgBody.toPlainText() - assert theMessage - assert "(Unknown)" in theMessage - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("PyQt5.QtCore.QSysInfo.kernelVersion", causeException) + nwErr.setMessage(Exception, "Almost Fine Error", None) + theMessage = nwErr.msgBody.toPlainText() + assert theMessage + assert "(Unknown)" in theMessage nwErr._doClose() nwErr.close() @@ -84,31 +84,31 @@ def testBaseError_Handler(qtbot, monkeypatch, fncDir, tmpDir): qtbot.waitForWindowShown(nwGUI) # Normal shutdown - monkeypatch.setattr(NWErrorMessage, "exec_", lambda *args: None) - monkeypatch.setattr("PyQt5.QtWidgets.qApp.exit", lambda *args: None) - exceptionHandler(Exception, "Error Message", None) - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr(NWErrorMessage, "exec_", lambda *args: None) + mp.setattr("PyQt5.QtWidgets.qApp.exit", lambda *args: None) + exceptionHandler(Exception, "Error Message", None) # Should not crash when no GUI is found - monkeypatch.setattr(NWErrorMessage, "exec_", lambda *args: None) - monkeypatch.setattr("PyQt5.QtWidgets.qApp.exit", lambda *args: None) - monkeypatch.setattr("PyQt5.QtWidgets.qApp.topLevelWidgets", lambda: []) - exceptionHandler(Exception, "Error Message", None) - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr(NWErrorMessage, "exec_", lambda *args: None) + mp.setattr("PyQt5.QtWidgets.qApp.exit", lambda *args: None) + mp.setattr("PyQt5.QtWidgets.qApp.topLevelWidgets", lambda: []) + exceptionHandler(Exception, "Error Message", None) # Should handle qApp failing - monkeypatch.setattr(NWErrorMessage, "exec_", lambda *args: None) - monkeypatch.setattr("PyQt5.QtWidgets.qApp.exit", lambda *args: None) - monkeypatch.setattr("PyQt5.QtWidgets.qApp.topLevelWidgets", causeException) - exceptionHandler(Exception, "Error Message", None) - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr(NWErrorMessage, "exec_", lambda *args: None) + mp.setattr("PyQt5.QtWidgets.qApp.exit", lambda *args: None) + mp.setattr("PyQt5.QtWidgets.qApp.topLevelWidgets", causeException) + exceptionHandler(Exception, "Error Message", None) # Should handle failing to close main GUI - monkeypatch.setattr(NWErrorMessage, "exec_", lambda *args: None) - monkeypatch.setattr("PyQt5.QtWidgets.qApp.exit", lambda *args: None) - monkeypatch.setattr(nwGUI, "closeMain", causeException) - exceptionHandler(Exception, "Error Message", None) - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr(NWErrorMessage, "exec_", lambda *args: None) + mp.setattr("PyQt5.QtWidgets.qApp.exit", lambda *args: None) + mp.setattr(nwGUI, "closeMain", causeException) + exceptionHandler(Exception, "Error Message", None) nwGUI.closeMain() diff --git a/tests/test_base/test_base_init.py b/tests/test_base/test_base_init.py index 923cfd4e..fb906786 100644 --- a/tests/test_base/test_base_init.py +++ b/tests/test_base/test_base_init.py @@ -62,8 +62,6 @@ def testBaseInit_Launch(caplog, monkeypatch, tmpDir): assert ex.value.code == 0 - monkeypatch.undo() - # END Test testBaseInit_Launch @pytest.mark.base @@ -136,8 +134,6 @@ def testBaseInit_Options(monkeypatch, tmpDir): assert nw.CONFIG.cmdOpen == "sample/" assert nwGUI.closeMain() == "closeMain" - monkeypatch.undo() - # END Test testBaseInit_Options @pytest.mark.base @@ -170,6 +166,4 @@ def testBaseInit_Imports(caplog, monkeypatch, tmpDir): assert "At least PyQt5" in caplog.messages[2] assert "lxml" in caplog.messages[3] - monkeypatch.undo() - # END Test testBaseInit_Imports diff --git a/tests/test_core/test_core_document.py b/tests/test_core/test_core_document.py index bbfe658b..f1084f84 100644 --- a/tests/test_core/test_core_document.py +++ b/tests/test_core/test_core_document.py @@ -50,9 +50,9 @@ def testCoreDocument_LoadSave(monkeypatch, dummyGUI, nwMinimal): def dummyOpen(*args, **kwargs): raise OSError - monkeypatch.setattr("builtins.open", dummyOpen) - assert theDoc.openDocument(sHandle) is None - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("builtins.open", dummyOpen) + assert theDoc.openDocument(sHandle) is None # Load the text assert theDoc.openDocument(sHandle) == "### New Scene\n\n" @@ -95,9 +95,9 @@ def testCoreDocument_LoadSave(monkeypatch, dummyGUI, nwMinimal): assert inFile.read() == theText # Cause open() to fail while saving - monkeypatch.setattr("builtins.open", causeOSError) - assert not theDoc.saveDocument(theText) - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("builtins.open", causeOSError) + assert not theDoc.saveDocument(theText) # Saving with no handle theDoc.clearDocument() @@ -108,9 +108,9 @@ def testCoreDocument_LoadSave(monkeypatch, dummyGUI, nwMinimal): assert os.path.isfile(docPath) # Cause the delete to fail - monkeypatch.setattr("os.unlink", causeOSError) - assert not theDoc.deleteDocument(xHandle) - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("os.unlink", causeOSError) + assert not theDoc.deleteDocument(xHandle) # Make the delete pass assert theDoc.deleteDocument(xHandle) diff --git a/tests/test_core/test_core_index.py b/tests/test_core/test_core_index.py index 2d5f8f12..6cec1aab 100644 --- a/tests/test_core/test_core_index.py +++ b/tests/test_core/test_core_index.py @@ -26,6 +26,7 @@ import json from shutil import copyfile +from dummy import causeException from tools import cmpFiles from nw.core.project import NWProject @@ -61,16 +62,12 @@ def testCoreIndex_LoadSave(monkeypatch, nwLipsum, dummyGUI, outDir, refDir): assert not theIndex.reIndexHandle(None) - # Dummy exception function - def doPanic(*arg, **kwargs): - raise Exception - # Make the save fail - monkeypatch.setattr(json, "dump", doPanic) - assert not theIndex.saveIndex() + with monkeypatch.context() as mp: + mp.setattr(json, "dump", causeException) + assert not theIndex.saveIndex() # Make the save pass - monkeypatch.undo() assert theIndex.saveIndex() # Take a copy of the index @@ -100,11 +97,11 @@ def testCoreIndex_LoadSave(monkeypatch, nwLipsum, dummyGUI, outDir, refDir): assert not theIndex._textCounts # Make the load fail - monkeypatch.setattr(json, "load", doPanic) - assert not theIndex.loadIndex() + with monkeypatch.context() as mp: + mp.setattr(json, "load", causeException) + assert not theIndex.loadIndex() # Make the load pass - monkeypatch.undo() assert theIndex.loadIndex() assert str(theIndex._tagIndex) == tagIndex diff --git a/tests/test_core/test_core_options.py b/tests/test_core/test_core_options.py index 20b0b6a3..6bd26b29 100644 --- a/tests/test_core/test_core_options.py +++ b/tests/test_core/test_core_options.py @@ -64,10 +64,10 @@ def testCoreOptions_LoadSave(monkeypatch, dummyGUI, tmpDir): assert theProject.projMeta == tmpDir # Cause open() to fail - monkeypatch.setattr("builtins.open", causeOSError) - assert not theOpts.loadSettings() - assert not theOpts.saveSettings() - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("builtins.open", causeOSError) + assert not theOpts.loadSettings() + assert not theOpts.saveSettings() # Load proper assert theOpts.loadSettings() diff --git a/tests/test_core/test_core_project.py b/tests/test_core/test_core_project.py index d6806984..41026efc 100644 --- a/tests/test_core/test_core_project.py +++ b/tests/test_core/test_core_project.py @@ -180,7 +180,6 @@ def testCoreProject_NewSampleA(fncDir, tmpConf, dummyGUI, tmpDir): } theProject = NWProject(dummyGUI) theProject.projTree.setSeed(42) - theProject.mainConf = tmpConf # Sample set, but no path assert not theProject.newProject({"popSample": True}) @@ -229,7 +228,6 @@ def testCoreProject_NewSampleB(monkeypatch, fncDir, tmpConf, dummyGUI, tmpDir): } theProject = NWProject(dummyGUI) theProject.projTree.setSeed(42) - theProject.mainConf = tmpConf # Make sure we do not pick up the nw/assets/sample.zip file tmpConf.assetPath = tmpDir @@ -330,9 +328,9 @@ def testCoreProject_Open(monkeypatch, nwMinimal, dummyGUI): os.rename(wName, rName) # Fail on folder structure check - monkeypatch.setattr("os.mkdir", causeOSError) - assert theProject.openProject(nwMinimal) is False - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("os.mkdir", causeOSError) + assert theProject.openProject(nwMinimal) is False # Fail on lock file theProject.setProjectPath(nwMinimal) @@ -340,9 +338,9 @@ def testCoreProject_Open(monkeypatch, nwMinimal, dummyGUI): assert theProject.openProject(nwMinimal) is False # Fail to read lockfile (which still opens the project) - monkeypatch.setattr("builtins.open", causeOSError) - assert theProject.openProject(nwMinimal) is True - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("builtins.open", causeOSError) + assert theProject.openProject(nwMinimal) is True assert theProject.closeProject() # Force open with lockfile @@ -452,14 +450,14 @@ def testCoreProject_Save(monkeypatch, nwMinimal, dummyGUI, refDir): assert theProject.openProject(nwMinimal) # Fail on folder structure check - monkeypatch.setattr("os.path.isdir", lambda *args: False) - assert theProject.saveProject() is False - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("os.path.isdir", lambda *args: False) + assert theProject.saveProject() is False # Fail on open file - monkeypatch.setattr("builtins.open", causeOSError) - assert theProject.saveProject() is False - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("builtins.open", causeOSError) + assert theProject.saveProject() is False # Successful save saveCount = theProject.saveCount @@ -501,30 +499,30 @@ def testCoreProject_LockFile(monkeypatch, fncDir, dummyGUI): theProject.mainConf.kernelVer = "1.0" # Block open - monkeypatch.setattr("builtins.open", causeOSError) - assert theProject._writeLockFile() is False - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("builtins.open", causeOSError) + assert theProject._writeLockFile() is False # Write lock file - monkeypatch.setattr("nw.core.project.time", lambda: 123.4) - assert theProject._writeLockFile() is True - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("nw.core.project.time", lambda: 123.4) + assert theProject._writeLockFile() is True assert readFile(lockFile) == "TestHost\nTestOS\n1.0\n123\n" # Block open - monkeypatch.setattr("builtins.open", causeOSError) - assert theProject._readLockFile() == ["ERROR"] - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("builtins.open", causeOSError) + assert theProject._readLockFile() == ["ERROR"] # Read lock file assert theProject._readLockFile() == ["TestHost", "TestOS", "1.0", "123"] # Block unlink - monkeypatch.setattr("os.unlink", causeOSError) - assert os.path.isfile(lockFile) - assert theProject._clearLockFile() is False - assert os.path.isfile(lockFile) - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("os.unlink", causeOSError) + assert os.path.isfile(lockFile) + assert theProject._clearLockFile() is False + assert os.path.isfile(lockFile) # Clear file assert os.path.isfile(lockFile) @@ -554,9 +552,9 @@ def testCoreProject_Helpers(monkeypatch, fncDir, dummyGUI): theProject.projPath = fncDir # Block user's home folder - monkeypatch.setattr("os.path.expanduser", lambda *args, **kwargs: fncDir) - assert theProject.ensureFolderStructure() is False - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("os.path.expanduser", lambda *args, **kwargs: fncDir) + assert theProject.ensureFolderStructure() is False # Create a file to block meta folder metaDir = os.path.join(fncDir, "meta") @@ -702,9 +700,9 @@ def testCoreProject_Methods(monkeypatch, nwMinimal, dummyGUI, tmpDir): # Edit Time theProject.editTime = 1234 theProject.projOpened = 1600000000 - monkeypatch.setattr("nw.core.project.time", lambda: 1600005600) - assert theProject.getCurrentEditTime() == 6834 - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("nw.core.project.time", lambda: 1600005600) + assert theProject.getCurrentEditTime() == 6834 # Trash folder # Should create on first call, and just returned on later calls @@ -735,11 +733,11 @@ def testCoreProject_Methods(monkeypatch, nwMinimal, dummyGUI, tmpDir): # Spell language theProject.projChanged = False assert theProject.setSpellLang(None) - assert theProject.projLang is None + assert theProject.projSpell is None assert theProject.setSpellLang("None") - assert theProject.projLang is None + assert theProject.projSpell is None assert theProject.setSpellLang("en_GB") - assert theProject.projLang == "en_GB" + assert theProject.projSpell == "en_GB" assert theProject.projChanged # Automatic outline update @@ -839,14 +837,14 @@ def testCoreProject_Methods(monkeypatch, nwMinimal, dummyGUI, tmpDir): assert theProject.getSessionWordCount() == 100 # Session stats - monkeypatch.setattr("os.path.isdir", lambda *args, **kwargs: False) - assert not theProject._appendSessionStats(idleTime=0) - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("os.path.isdir", lambda *args, **kwargs: False) + assert not theProject._appendSessionStats(idleTime=0) # Block open - monkeypatch.setattr("builtins.open", causeOSError) - assert not theProject._appendSessionStats(idleTime=0) - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("builtins.open", causeOSError) + assert not theProject._appendSessionStats(idleTime=0) # Write entry assert theProject.projMeta == os.path.join(nwMinimal, "meta") @@ -856,9 +854,9 @@ def testCoreProject_Methods(monkeypatch, nwMinimal, dummyGUI, tmpDir): theProject.novelWCount = 200 theProject.notesWCount = 100 - monkeypatch.setattr("nw.core.project.time", lambda: 1600005600) - assert theProject._appendSessionStats(idleTime=99) - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("nw.core.project.time", lambda: 1600005600) + assert theProject._appendSessionStats(idleTime=99) assert readFile(statsFile) == ( "# Offset 100\n" @@ -1076,9 +1074,9 @@ def testCoreProject_LegacyData(monkeypatch, dummyGUI, fncDir): writeFile(tstFile, "dummy") assert os.path.isfile(tstFile) - monkeypatch.setattr("os.unlink", causeOSError) - assert not theProject._deprecatedFiles() - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("os.unlink", causeOSError) + assert not theProject._deprecatedFiles() assert theProject._deprecatedFiles() assert not os.path.isfile(tstFile) @@ -1101,18 +1099,18 @@ def testCoreProject_LegacyData(monkeypatch, dummyGUI, fncDir): assert os.path.isdir(errItem) # This causes a failure to create the 'junk' folder - monkeypatch.setattr("os.mkdir", causeOSError) - errList = [] - errList = theProject._legacyDataFolder(tstData, errList) - assert len(errList) > 0 - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("os.mkdir", causeOSError) + errList = [] + errList = theProject._legacyDataFolder(tstData, errList) + assert len(errList) > 0 # This causes a failure to move 'stuff' to 'junk' - monkeypatch.setattr("os.rename", causeOSError) - errList = [] - errList = theProject._legacyDataFolder(tstData, errList) - assert len(errList) > 0 - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("os.rename", causeOSError) + errList = [] + errList = theProject._legacyDataFolder(tstData, errList) + assert len(errList) > 0 # This should be successful errList = [] @@ -1138,18 +1136,18 @@ def testCoreProject_LegacyData(monkeypatch, dummyGUI, fncDir): writeFile(tstDoc3b, "dummy") # Make the above fail - monkeypatch.setattr("os.rename", causeOSError) - monkeypatch.setattr("os.unlink", causeOSError) - errList = [] - errList = theProject._legacyDataFolder(tstData, errList) - assert len(errList) > 0 - assert os.path.isfile(tstDoc1m) - assert os.path.isfile(tstDoc1b) - assert os.path.isfile(tstDoc2m) - assert os.path.isfile(tstDoc2b) - assert os.path.isfile(tstDoc3m) - assert os.path.isfile(tstDoc3b) - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("os.rename", causeOSError) + mp.setattr("os.unlink", causeOSError) + errList = [] + errList = theProject._legacyDataFolder(tstData, errList) + assert len(errList) > 0 + assert os.path.isfile(tstDoc1m) + assert os.path.isfile(tstDoc1b) + assert os.path.isfile(tstDoc2m) + assert os.path.isfile(tstDoc2b) + assert os.path.isfile(tstDoc3m) + assert os.path.isfile(tstDoc3b) # And succeed ... errList = [] @@ -1203,14 +1201,14 @@ def testCoreProject_Backup(monkeypatch, dummyGUI, nwMinimal, tmpDir): theProject.mainConf.backupPath = tmpDir # Can't make folder - monkeypatch.setattr("os.mkdir", causeOSError) - assert not theProject.zipIt(doNotify=False) - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("os.mkdir", causeOSError) + assert not theProject.zipIt(doNotify=False) # Can't write archive - monkeypatch.setattr("shutil.make_archive", causeOSError) - assert not theProject.zipIt(doNotify=False) - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("shutil.make_archive", causeOSError) + assert not theProject.zipIt(doNotify=False) # Test correct settings assert theProject.zipIt(doNotify=True) diff --git a/tests/test_core/test_core_spell.py b/tests/test_core/test_core_spell.py index 113ed343..c1d8ae3d 100644 --- a/tests/test_core/test_core_spell.py +++ b/tests/test_core/test_core_spell.py @@ -30,14 +30,13 @@ from tools import readFile, writeFile from nw.core.spellcheck import NWSpellCheck, NWSpellEnchant, NWSpellSimple @pytest.mark.core -def testCoreSpell_Super(monkeypatch, tmpDir, tmpConf): +def testCoreSpell_Super(monkeypatch, tmpDir): """Test the spell checker super class """ wList = os.path.join(tmpDir, "wordlist.txt") writeFile(wList, "a_word\nb_word\nc_word\n") spChk = NWSpellCheck() - spChk.mainConf = tmpConf # Check that dummy functions return results that reflects that spell # checking is effectively disabled @@ -49,16 +48,16 @@ def testCoreSpell_Super(monkeypatch, tmpDir, tmpConf): # Add a word to the user's dictionary assert spChk._readProjectDictionary("dummy") is False - monkeypatch.setattr("builtins.open", causeOSError) - assert spChk._readProjectDictionary(wList) is False - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("builtins.open", causeOSError) + assert spChk._readProjectDictionary(wList) is False assert spChk._readProjectDictionary(wList) is True assert spChk.projectDict == wList # Cannot write to file - monkeypatch.setattr("builtins.open", causeOSError) - assert spChk.addWord("d_word") is False - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("builtins.open", causeOSError) + assert spChk.addWord("d_word") is False assert readFile(wList) == "a_word\nb_word\nc_word\n" # First time, OK @@ -72,28 +71,26 @@ def testCoreSpell_Super(monkeypatch, tmpDir, tmpConf): # END Test testCoreSpell_Super @pytest.mark.core -def testCoreSpell_Enchant(monkeypatch, tmpDir, tmpConf): +def testCoreSpell_Enchant(monkeypatch, tmpDir): """Test the pyenchant spell checker """ wList = os.path.join(tmpDir, "wordlist.txt") writeFile(wList, "a_word\nb_word\nc_word\n") # Block the enchant package (and trigger the dummy class) - monkeypatch.setitem(sys.modules, "enchant", None) - spChk = NWSpellEnchant() + with monkeypatch.context() as mp: + mp.setitem(sys.modules, "enchant", None) + spChk = NWSpellEnchant() - spChk.setLanguage("en", wList) - assert spChk.setLanguage("", "") is None - assert spChk.checkWord("") - assert spChk.suggestWords("") == [] - assert spChk.listDictionaries() == [] - assert spChk.describeDict() == ("", "") - - monkeypatch.undo() + spChk.setLanguage("en", wList) + assert spChk.setLanguage("", "") is None + assert spChk.checkWord("") + assert spChk.suggestWords("") == [] + assert spChk.listDictionaries() == [] + assert spChk.describeDict() == ("", "") # Load the proper enchant package spChk = NWSpellEnchant() - spChk.mainConf = tmpConf spChk.setLanguage("en", wList) assert spChk.checkWord("a_word") @@ -118,7 +115,7 @@ def testCoreSpell_Enchant(monkeypatch, tmpDir, tmpConf): # END Test testCoreSpell_Enchant @pytest.mark.core -def testCoreSpell_Simple(monkeypatch, tmpDir, tmpConf): +def testCoreSpell_Simple(monkeypatch, tmpDir): """Test the fallback simple spell checker """ wList = os.path.join(tmpDir, "wordlist.txt") @@ -127,15 +124,14 @@ def testCoreSpell_Simple(monkeypatch, tmpDir, tmpConf): writeFile(wDict, "# Comment\ne_word\nf_word\ng_word\n") spChk = NWSpellSimple() - spChk.mainConf = tmpConf spChk.mainConf.dictPath = tmpDir # Load dictionary, but fail - monkeypatch.setattr("builtins.open", causeOSError) - spChk.setLanguage("en", wList) - assert spChk.spellLanguage is None - assert spChk.theWords == set(spChk.projDict) - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("builtins.open", causeOSError) + spChk.setLanguage("en", wList) + assert spChk.spellLanguage is None + assert spChk.theWords == set(spChk.projDict) # Load dictionary properly spChk.setLanguage("en", wList) @@ -163,9 +159,9 @@ def testCoreSpell_Simple(monkeypatch, tmpDir, tmpConf): assert "d_word" in wSuggest # Break the matching - monkeypatch.setattr("difflib.get_close_matches", lambda *args, **kwargs: [""]) - assert spChk.suggestWords("word") == [] - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("difflib.get_close_matches", lambda *args, **kwargs: [""]) + assert spChk.suggestWords("word") == [] # Capitalisation wSuggest = spChk.suggestWords("D_wrod") diff --git a/tests/test_core/test_core_tokenizer.py b/tests/test_core/test_core_tokenizer.py index cad615fd..07dc5241 100644 --- a/tests/test_core/test_core_tokenizer.py +++ b/tests/test_core/test_core_tokenizer.py @@ -112,11 +112,10 @@ def testCoreToken_Setters(dummyGUI): # END Test testCoreToken_Setters @pytest.mark.core -def testCoreToken_TextOps(monkeypatch, nwMinimal, dummyGUI, tmpConf): +def testCoreToken_TextOps(monkeypatch, nwMinimal, dummyGUI): """Test handling files and text in the Tokenizer class. """ theProject = NWProject(dummyGUI) - theProject.mainConf = tmpConf theProject.projTree.setSeed(42) theProject.loadProjectLocalisation("en") @@ -157,13 +156,13 @@ def testCoreToken_TextOps(monkeypatch, nwMinimal, dummyGUI, tmpConf): assert theToken.setText(sHandle) is True assert theToken.theText == docText - monkeypatch.setattr("nw.constants.nwConst.MAX_DOCSIZE", 100) - assert theToken.setText(sHandle, docText) is True - assert theToken.theText == ( - "# ERROR\n\n" - "Document 'New Scene' is too big (0.00 MB). Skipping.\n\n" - ) - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("nw.constants.nwConst.MAX_DOCSIZE", 100) + assert theToken.setText(sHandle, docText) is True + assert theToken.theText == ( + "# ERROR\n\n" + "Document 'New Scene' is too big (0.00 MB). Skipping.\n\n" + ) assert theToken.setText(sHandle, docText) is True assert theToken.theText == docText @@ -411,11 +410,10 @@ def testCoreToken_Tokenize(dummyGUI): # END Test testCoreToken_Tokenize @pytest.mark.core -def testCoreToken_Headers(dummyGUI, tmpConf): +def testCoreToken_Headers(dummyGUI): """Test the header and page parser of the Tokenizer class. """ theProject = NWProject(dummyGUI) - theProject.mainConf = tmpConf theProject.loadProjectLocalisation("en") theToken = Tokenizer(theProject, dummyGUI) diff --git a/tests/test_core/test_core_toodt.py b/tests/test_core/test_core_toodt.py index 6126be99..f076c85d 100644 --- a/tests/test_core/test_core_toodt.py +++ b/tests/test_core/test_core_toodt.py @@ -45,11 +45,9 @@ def xmlToText(xElem): return rTxt @pytest.mark.core -def testCoreToOdt_Convert(tmpConf, dummyGUI): +def testCoreToOdt_Convert(dummyGUI): """Test the converter of the ToHtml class. """ - nw.CONFIG = tmpConf - theProject = NWProject(dummyGUI) dummyGUI.theIndex = NWIndex(theProject, dummyGUI) theDoc = ToOdt(theProject, dummyGUI, isFlat=True) diff --git a/tests/test_core/test_core_tree.py b/tests/test_core/test_core_tree.py index d4588b37..b80bb784 100644 --- a/tests/test_core/test_core_tree.py +++ b/tests/test_core/test_core_tree.py @@ -410,8 +410,6 @@ def testCoreTree_MakeHandles(monkeypatch, dummyGUI): theTree._projTree[tHandle] = None assert tHandle == "a79acf4c634a7" - monkeypatch.undo() - # END Test testCoreTree_MakeHandles @pytest.mark.core diff --git a/tests/test_gui/test_gui_mainmenu.py b/tests/test_gui/test_gui_mainmenu.py index f66108d5..3e8886a8 100644 --- a/tests/test_gui/test_gui_mainmenu.py +++ b/tests/test_gui/test_gui_mainmenu.py @@ -534,9 +534,9 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj): # Faulty Keyword Inserts assert not nwGUI.docEditor.insertKeyWord("blabla") - monkeypatch.setattr(QTextBlock, "isValid", lambda *args, **kwards: False) - assert not nwGUI.docEditor.insertKeyWord(nwKeyWords.TAG_KEY) - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr(QTextBlock, "isValid", lambda *args, **kwards: False) + assert not nwGUI.docEditor.insertKeyWord(nwKeyWords.TAG_KEY) nwGUI.docEditor.clear() diff --git a/tests/test_gui/test_gui_projdetails.py b/tests/test_gui/test_gui_projdetails.py index 4919bb8d..e0ead1f9 100644 --- a/tests/test_gui/test_gui_projdetails.py +++ b/tests/test_gui/test_gui_projdetails.py @@ -110,6 +110,5 @@ def testGuiProjDetails_Dialog(qtbot, monkeypatch, nwGUI, nwLipsum): # Clean Up projDet._doClose() nwGUI.closeMain() - monkeypatch.undo() # END Test testGuiProjDetails_Dialog diff --git a/tests/test_gui/test_gui_projwizard.py b/tests/test_gui/test_gui_projwizard.py index 4208bc8f..0825eea6 100644 --- a/tests/test_gui/test_gui_projwizard.py +++ b/tests/test_gui/test_gui_projwizard.py @@ -62,18 +62,17 @@ def testGuiProjectWizard_Main(qtbot, monkeypatch, nwGUI, nwMinimal): # Close project, but call with invalid path assert nwGUI.closeProject() - monkeypatch.setattr(nwGUI, "showNewProjectDialog", lambda *args: None) - assert not nwGUI.newProject() + with monkeypatch.context() as mp: + mp.setattr(nwGUI, "showNewProjectDialog", lambda *args: None) + assert not nwGUI.newProject() - # Now, with an empty dictionary - monkeypatch.setattr(nwGUI, "showNewProjectDialog", lambda *args: {}) - assert not nwGUI.newProject() + # Now, with an empty dictionary + mp.setattr(nwGUI, "showNewProjectDialog", lambda *args: {}) + assert not nwGUI.newProject() - # Now, with a non-empty folder - monkeypatch.setattr(nwGUI, "showNewProjectDialog", lambda *args: {"projPath": nwMinimal}) - assert not nwGUI.newProject() - - monkeypatch.undo() + # Now, with a non-empty folder + mp.setattr(nwGUI, "showNewProjectDialog", lambda *args: {"projPath": nwMinimal}) + assert not nwGUI.newProject() ## # Test the Wizard diff --git a/tests/test_gui/test_gui_writingstats.py b/tests/test_gui/test_gui_writingstats.py index 8fb9d538..bf5a9691 100644 --- a/tests/test_gui/test_gui_writingstats.py +++ b/tests/test_gui/test_gui_writingstats.py @@ -428,6 +428,4 @@ def testGuiWritingStats_Dialog(qtbot, monkeypatch, nwGUI, fncDir, fncProj): assert nwGUI.closeProject() qtbot.wait(stepDelay) - monkeypatch.undo() - # END Test testGuiWritingStats_Dialog From e9763f4ec68a040f90fd8259285b10e5f7f09b07 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Thu, 18 Feb 2021 20:45:18 +0100 Subject: [PATCH 3/7] Add language option to the Build tool --- nw/config.py | 25 ++++++++++++++++++------- nw/core/project.py | 28 ++++++++++++++++++---------- nw/gui/build.py | 21 ++++++++++++++++++++- nw/gui/preferences.py | 8 ++------ 4 files changed, 58 insertions(+), 24 deletions(-) diff --git a/nw/config.py b/nw/config.py index 037520c8..5e532e73 100644 --- a/nw/config.py +++ b/nw/config.py @@ -53,6 +53,9 @@ class Config: CNF_S_LST = 3 CNF_I_LST = 4 + LANG_NW = 1 + LANG_PROJ = 2 + def __init__(self): # Set Application Variables @@ -391,21 +394,29 @@ class Config: return - def listLanguages(self): + def listLanguages(self, lngSet): """List localisation files in the i18n folder. The default GUI language 'en_GB' is British English. """ - langList = { - "en_GB": QLocale("en_GB").nativeLanguageName().title() - } + if lngSet == self.LANG_NW: + fPre = "nw_" + fExt = ".qm" + langList = {"en_GB": QLocale("en_GB").nativeLanguageName().title()} + elif lngSet == self.LANG_PROJ: + fPre = "project_" + fExt = ".json" + langList = {"en": "English"} + else: + return [] + for qmFile in os.listdir(self.nwLangPath): if not os.path.isfile(os.path.join(self.nwLangPath, qmFile)): continue - if not qmFile.startswith("nw_") or not qmFile.endswith(".qm"): + if not qmFile.startswith(fPre) or not qmFile.endswith(fExt): continue - qmLang = qmFile[3:-3] + qmLang = qmFile[len(fPre):-len(fExt)] qmName = QLocale(qmLang).nativeLanguageName().title() - if qmLang and qmName: + if qmLang and qmName and qmLang != "en": langList[qmLang] = qmName return sorted(langList.items(), key=lambda x: x[0]) diff --git a/nw/core/project.py b/nw/core/project.py index cc4914b9..c00d6a18 100644 --- a/nw/core/project.py +++ b/nw/core/project.py @@ -197,7 +197,7 @@ class NWProject(): self.projContent = None self.projDict = None self.projSpell = None - self.projLang = None + self.projLang = self.mainConf.guiLang self.projFile = nwFiles.PROJ_FILE self.projName = "" self.bookTitle = "" @@ -593,7 +593,7 @@ class NWProject(): self.theParent.setStatus(self.tr("Opened Project: {0}").format(self.projName)) self._scanProjectFolder() - self.loadProjectLocalisation(self.projLang) + self._loadProjectLocalisation() self.currWCount = self.lastWCount self.projOpened = time() @@ -1019,7 +1019,16 @@ class NWProject(): theLang = checkString(theLang, None, True) if self.projSpell != theLang: self.projSpell = theLang - # self.loadProjectLocalisation(theLang) + self.setProjectChanged(True) + return True + + def setProjectLang(self, theLang): + """Set the project-specific language. + """ + theLang = checkString(theLang, None, True) + if self.projLang != theLang: + self.projLang = theLang + self._loadProjectLocalisation() self.setProjectChanged(True) return True @@ -1215,13 +1224,16 @@ class NWProject(): theValue = str(theWord) return self.langData.get(theValue, theValue) - def loadProjectLocalisation(self, theLang): + ## + # Internal Functions + ## + + def _loadProjectLocalisation(self): """Load the language data for the current project language. """ + theLang = self.projLang if theLang is None: theLang = self.mainConf.spellLanguage - if theLang is None: - theLang = "en" lngShort = theLang.split("_")[0] loadFile = os.path.join(self.mainConf.nwLangPath, "project_en.json") @@ -1245,10 +1257,6 @@ class NWProject(): return True - ## - # Internal Functions - ## - def _readLockFile(self): """Reads the lock file in the project folder. """ diff --git a/nw/gui/build.py b/nw/gui/build.py index 1e818733..df66fbcb 100644 --- a/nw/gui/build.py +++ b/nw/gui/build.py @@ -41,7 +41,7 @@ from PyQt5.QtWidgets import ( qApp, QDialog, QVBoxLayout, QHBoxLayout, QTextBrowser, QPushButton, QLabel, QLineEdit, QGroupBox, QGridLayout, QProgressBar, QMenu, QAction, QFileDialog, QFontDialog, QSpinBox, QScrollArea, QSplitter, QWidget, - QSizePolicy, QDoubleSpinBox + QSizePolicy, QDoubleSpinBox, QComboBox ) from nw.common import fuzzyTime, makeFileNameSafe @@ -163,6 +163,16 @@ class GuiBuildNovel(QDialog): self._reFmtCodes(self.theProject.titleFormat["section"]) ) + self.buildLang = QComboBox() + self.buildLang.setMinimumWidth(xFmt) + theLangs = self.mainConf.listLanguages(self.mainConf.LANG_PROJ) + for langID, langName in theLangs: + self.buildLang.addItem(langName, langID) + + langIdx = self.buildLang.findData(self.theProject.projLang) + if langIdx != -1: + self.buildLang.setCurrentIndex(langIdx) + # Dummy boxes due to QGridView and QLineEdit expand bug self.boxTitle = QHBoxLayout() self.boxTitle.addWidget(self.fmtTitle) @@ -180,6 +190,7 @@ class GuiBuildNovel(QDialog): unnumbLabel = QLabel(self.tr("Unnumbered")) sceneLabel = QLabel(self.tr("Scene")) sectionLabel = QLabel(self.tr("Section")) + langLabel = QLabel(self.tr("Language")) self.titleForm.addWidget(titleLabel, 0, 0, 1, 1, Qt.AlignLeft) self.titleForm.addLayout(self.boxTitle, 0, 1, 1, 1, Qt.AlignRight) @@ -191,6 +202,8 @@ class GuiBuildNovel(QDialog): self.titleForm.addLayout(self.boxScene, 3, 1, 1, 1, Qt.AlignRight) self.titleForm.addWidget(sectionLabel, 4, 0, 1, 1, Qt.AlignLeft) self.titleForm.addLayout(self.boxSection, 4, 1, 1, 1, Qt.AlignRight) + self.titleForm.addWidget(langLabel, 5, 0, 1, 1, Qt.AlignLeft) + self.titleForm.addWidget(self.buildLang, 5, 1, 1, 1, Qt.AlignRight) self.titleForm.setColumnStretch(0, 0) self.titleForm.setColumnStretch(1, 1) @@ -651,6 +664,9 @@ class GuiBuildNovel(QDialog): includeBody = self.includeBody.isChecked() replaceUCode = self.replaceUCode.isChecked() + # The language lookup dict is reloaded if needed + self.theProject.setProjectLang(self.buildLang.currentData()) + # Get font information fontInfo = QFontInfo(QFont(textFont, textSize)) textFixed = fontInfo.fixedPitch() @@ -1115,6 +1131,7 @@ class GuiBuildNovel(QDialog): "section" : self.fmtSection.text().strip(), }) + buildLang = self.buildLang.currentData() winWidth = self.mainConf.rpxInt(self.width()) winHeight = self.mainConf.rpxInt(self.height()) justifyText = self.justifyText.isChecked() @@ -1136,6 +1153,8 @@ class GuiBuildNovel(QDialog): boxWidth = self.mainConf.rpxInt(mainSplit[0]) docWidth = self.mainConf.rpxInt(mainSplit[1]) + self.theProject.setProjectLang(buildLang) + # GUI Settings self.optState.setValue("GuiBuildNovel", "winWidth", winWidth) self.optState.setValue("GuiBuildNovel", "winHeight", winHeight) diff --git a/nw/gui/preferences.py b/nw/gui/preferences.py index 09a0c3a9..683eeea4 100644 --- a/nw/gui/preferences.py +++ b/nw/gui/preferences.py @@ -91,7 +91,6 @@ class GuiPreferences(PagedDialog): logger.debug("Saving new preferences") needsRestart = self.tabGeneral.saveValues() - prevSpell = self.mainConf.spellLanguage self.tabProjects.saveValues() self.tabDocs.saveValues() @@ -99,9 +98,6 @@ class GuiPreferences(PagedDialog): self.tabSyntax.saveValues() self.tabAuto.saveValues() - if prevSpell != self.mainConf.spellLanguage: - self.theProject.loadProjectLocalisation(self.mainConf.spellLanguage) - if needsRestart: self.theParent.makeAlert( self.tr("Some changes will not be applied until novelWriter has been restarted."), @@ -142,8 +138,8 @@ class GuiPreferencesGeneral(QWidget): ## Select Locale self.guiLang = QComboBox() self.guiLang.setMinimumWidth(minWidth) - self.theLangs = self.mainConf.listLanguages() - for lang, langName in self.theLangs: + theLangs = self.mainConf.listLanguages(self.mainConf.LANG_NW) + for lang, langName in theLangs: self.guiLang.addItem(langName, lang) langIdx = self.guiLang.findData(self.mainConf.guiLang) if langIdx != -1: From 5f3822ac1a8601719796227ab0e1d926aefb70f7 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Thu, 18 Feb 2021 20:46:27 +0100 Subject: [PATCH 4/7] The word "Synopsis" should be translated to GUI language when it appears in document viewer --- i18n/nw_nb_NO.ts | 490 ++++++++++++++++++++++--------------------- i18n/nw_pt.ts | 490 ++++++++++++++++++++++--------------------- nw/core/tohtml.py | 3 +- nw/core/tokenizer.py | 3 + 4 files changed, 505 insertions(+), 481 deletions(-) diff --git a/i18n/nw_nb_NO.ts b/i18n/nw_nb_NO.ts index 7737c09c..2022aec4 100644 --- a/i18n/nw_nb_NO.ts +++ b/i18n/nw_nb_NO.ts @@ -477,300 +477,305 @@ La feltet stå tomt for å hoppe over overskriften, eller sett til en statisk tekst, slik som for eksempel '{0}', for å lage en separator. Separatoren vil bli sentrert automatisk, og bare satt inn mellom scener av samme type. - + Title Tittel - + Chapter Kapittel - + Unnumbered Unumrert - + Scene Scene - + Section Seksjon - + Font Options Skriftvalg - + Font family Skriftfamilie - + Font size Skriftstørrelse - + Line height Linjehøyde - + Justify text Rette marger - + Disable styling Slå av styling - + Include Options Inkluderinger - + Include synopsis Inkluder sammendrag - + Include comments Inkluder kommentarer - + Include keywords Inkluder kodeord - + Include body text Inkluder tekst - + File Filter Options Fil-filtre - + Include files with layouts other than 'Note'. Inkluder filer som ikke er notater. - + Include files with layout 'Note'. Inkluder filer som er notater. - + Ignore the 'Include when building project' setting and include all files in the output. Ignorer 'ta med ved eksport'-instilling og ta med alle filer i resultatet. - + Include novel files Inkluder romanfiler - + Include note files Inkluder notatfiler - + Ignore export flag Ignorer 'ta med'-flagg - + Export Options Eksportvalg - + Replace tabs with spaces Erstatt tab med mellomrom - + Replace Unicode in HTML Erstatt Unicode med HTML - + Build Preview Lag forhåndsvisning - + Print Skriv ut - + Print Preview Forhåndsvisning av utskrift - + Print to PDF Skriv ut til PDF - + Save As Lagre som - + Open Document (.odt) - + Flat Open Document (.fodt) - + novelWriter HTML (.htm) - + novelWriter Markdown (.nwd) - + Standard Markdown (.md) - + GitHub Markdown (.md) - + JSON + novelWriter HTML (.json) - + JSON + novelWriters Markdown (.json) - + Close Lukk - + Failed to generate preview. The result is too big. Kunne ikke generere forhåndsvisning. Resultatet er for stort til å vise. - + There were problems when building the project Det har oppstått en feil under byggingen av prosjektet - + Open Document - + Flat Open Document - + Plain HTML Enkel HTML - + novelWriter Markdown - + Standard Markdown - + GitHub Markdown - + JSON + novelWriter HTML - + JSON + novelWriter Markdown - + PDF - + Save Document As Lagre dokumentet som - + Unknown format Ukjent format - + {0} file successfully written to: Lagring av {0} var vellykket, og filen ble skrevet til: - + Failed to write {0} file. {1} Misslykkes i å skrive {0} til. {1} - + Styling Options Styling + + + Language + Språk + GuiBuildNovelDocView - + This area will show the content of the document to be exported or printed. Press the "Build Preview" button to generate content. Dette området vil vise innholdet av dokumentet som skal eksporteres. Trykk på knappen merket med "Lag forhåndsvisning" for å oppdatere innholdet. - + Unknown Ukjent - + <b>Build Time:</b> {0} <b>Generert den:</b> {0} @@ -2817,7 +2822,7 @@ Avbryt - + Some changes will not be applied until novelWriter has been restarted. Noen endringer vil ikke tas i bruk før neste gang novelWriter startes. @@ -2830,117 +2835,117 @@ GuiPreferencesAutomation - + Automatic Features Automatiske funksjoner - + Auto-select word under cursor Auto-velg ord under markør - + Apply formatting to word under cursor if no selection is made. Hvis ingen tekst er valgt, formatter ordet hvor markøren står. - + Auto-replace text as you type Erstatt mens du skriver - + Allow the editor to replace symbols as you type. La editoren erstatte symboler mens du skriver. - + Replace as You Type Erstatt mens du skriver - + Auto-replace single quotes Erstatt enkle sitattegn - + Try to guess which is an opening or a closing single quote. Forsøker å gjette hva som er venstre og høyre tegn. - + Auto-replace double quotes Erstatt doble sitattegn - + Try to guess which is an opening or a closing double quote. Forsøker å gjette hva som er venstre og høyre tegn. - + Auto-replace dashes Erstatt bindestreker - + Double and triple hyphens become short and long dashes. To og tre bindestreker erstattes med kort og lang bindestrek. - + Auto-replace dots Erstatt tre punktum - + Three consecutive dots become ellipsis. Tre punktum på rad erstattes med ellipsis. - + Quotation Style Sitattegn - + Single quote open style Enkelt sitat, venstre side - + The symbol to use for a leading single quote. Symbol for enkelt sitattegn før et sitat. - + Single quote close style Enkelt sitat, høyre side - + The symbol to use for a trailing single quote. Symbol for enkelt sitattegn etter et sitat. - + Double quote open style Dobbelt sitat, venstre side - + The symbol to use for a leading double quote. Symbol for dobbelt sitattegn før et sitat. - + Double quote close style Dobbelt sitat, høyre side - + The symbol to use for a trailing double quote. Symbol for dobbelt sitattegn etter et sitat. @@ -2948,107 +2953,107 @@ GuiPreferencesDocuments - + Text Style Tekststil - + Font family Skriftfamilie - + Font for the document editor and viewer. Skrifttype til bruk for editor og visning. - + Font size Skriftstørrelse - + Font size for the document editor and viewer. Skriftstørrelse til bruk for editor og visning. - + pt - + Text Flow Tekstflyt - + Maximum text width in "Normal Mode" Maks tekstbredde i "Normal-modues" - + Horizontal margins are scaled automatically. Horisontale marger skalerer da automatisk. - + px - + Maximum text width in "Focus Mode" Maks tekstbredde i "Fokus-modues" - + Disable maximum text width in "Normal Mode" Slå av maks tekstbredde i "Normal-modus" - + Text width is defined by the margins only. Tekstbredden er kun definert av margene. - + Hide document footer in "Focus Mode" Gjem dokumentets bunnlinje i "Fokus-modus" - + Hide the information bar at the bottom of the document. Gjemmer informasjonslinja i bunnen av dokumentet. - + Justify the text margins in editor and viewer Bruk justerte marger i editor og visning - + Lay out text with straight edges in the editor and viewer. Justerte marger gir rette linjeender i avsnitt. - + Text margin Marger - + If maximum width is set, this becomes the minimum margin. Hvis tekstbredde er satt, så blir dette istedet minste marger. - + Tab width Tabulatorens bredde - + The width of a tab key press in the editor and viewer. Hvor langt tabulatoren hopper i editor og visning. @@ -3056,127 +3061,127 @@ GuiPreferencesEditor - + Spell Checking Stavekontroll - + Internal Intern - + Spell check provider Verktøy for stavekontroll - + Note that the internal spell check tool is quite slow. Merk at den interne stavekontrollen er ganske treg. - + Spell check language Språk for stavekontroll - + Available languages are determined by your system. Tilgjengelige språk hentes fra operativystemet ditt. - + Big document limit Grense for store dokumenter - + Full spell checking is disabled above this limit. Automatisk stavekontroll slås av over grensen. - + kB - + Word Count Telling av ord - + Word count interval Telle-intervall - + How often the word count is updated. Hvor ofte antall ord blir oppdatert. - + seconds sekunder - + Writing Guides Hjelpesymboler - + Show tabs and spaces Synlige tabulatorer og mellomrom - + Add symbols to indicate tabs and spaces in the editor. Viser symboler for å indikere disse i editoren. - + Show line endings Synlige linjeender - + Add a symbol to indicate line endings in the editor. Viser symbol for å indikere dette i editoren. - + Scroll Behaviour Rullefelt - + Scroll past end of the document Tillat å rulle forbi slutten av dokumentet - + Also improves trypewriter scrolling for short documents. Forbedrer funksjonen til skrivemaskin-rulling. - + Typewriter style scrolling when you type Skrivemaskin-liknende rulling mens du skriver - + Try to keep the cursor at a fixed vertical position. Prøver å holde markøren på samme sted vertikalt. - + Minimum position for Typewriter scrolling Minste avstand for skrivemaskin-rulling - + Percentage of the editor height from the top. I prosent fra toppen av editor-vinduet. @@ -3184,82 +3189,82 @@ GuiPreferencesGeneral - + Look and Feel Utseende - + Main GUI theme Fargetema - + Changing this requires restarting novelWriter. Endring av dette krever omstart av novelWriter. - + Main icon theme Ikon-tema - + Prefer icons for dark backgrounds Foretrekk ikoner for mørk bakgrunn - + May improve the look of icons on dark themes. Kan forbedre utseende på mørke temaer. - + Font family Skriftfamilie - + Font size Skriftstørrelse - + pt - + GUI Settings Brukergrensesnitt - + Show full path in document header Vis full prosjektbane i dokumenthoder - + Add the parent folder names to the header. Legger til mappene foran dokumentets navn. - + Hide vertical scroll bars in main windows Skjul vertikale rullefelt i hovedvinduer - + Scrolling available with mouse wheel and keys only. Rulling kan bare gjøres med mus og tastatur. - + Hide horizontal scroll bars in main windows Skjul horisontale rullefelt i hovedvinduer - + Main GUI language Programspråk @@ -3267,107 +3272,107 @@ GuiPreferencesProjects - + Automatic Save Automatisk lagring - + Save document interval Interval for lagring av dokument - + How often the open document is automatically saved. Hvor ofte det åpne dokumentet lagres automatisk. - + seconds sekunder - + Save project interval Interval for lagring av prosjekt - + How often the open project is automatically saved. Hvor ofte det åpne prosjektet lagres automatisk. - + Project Backup Sikkerhetskopi - + Browse Bla - + Backup storage location Filbane for sikkerhetskopi - + Path: {0} Filbane: {0} - + Run backup when the project is closed Lag sikkerhetskopi når prosjektet lukkes - + Can be overridden for individual projects in project settings. Kan overstyres fra individuelle prosjektinnstillinger. - + Ask before running backup Spør før sikkerhetskopi tas - + If off, backups will run in the background. Hvis avslått, tas sikkerhetskopi automatisk. - + Session Timer Sesjons-klokke - + Pause the session timer when not writing Sett klokka på pause når du er inaktiv - + Also pauses when the application window does not have focus. Pauses også når du ikke jobber i applikasjonens vindu. - + Editor inactive time before pausing timer Tid uten skriving før klokka settes på pause - + User activity includes typing and changing the content. Dette måler kun endringer i teksteditoren. - + minutes minutter - + Backup Directory Mappe for sikkerhetskopi @@ -3375,67 +3380,67 @@ GuiPreferencesSyntax - + Highlighting Theme Syntaksfremheving - + Highlighting theme Fremhevingstema - + Colour theme to apply to the editor and viewer. Fargetema for editor og visning. - + Quotes & Dialogue Sitattegn & dialog - + Highlight text wrapped in quotes Fremhev tekst mellom sitattegn - + Applies to single, double and straight quotes. Gjelder enkle, doble og rette sitattegn. - + Allow open-ended single quotes Tillat enkle sitattegn som ikke lukkes - + Highlight single-quoted line with no closing quote. Fremhev sitater som ikke er lukket i samme avsnitt. - + Allow open-ended double quotes Tillat doble sitattegn som ikke lukkes - + Highlight double-quoted line with no closing quote. Fremhev sitater som ikke er lukket i samme avsnitt. - + Text Emphasis Fremheving av tekst - + Add highlight colour to emphasised text Fremhev formattert tekst - + Applies to emphasis (italic) and strong (bold). Gjelder kursiv og fet tekst. @@ -4268,297 +4273,297 @@ NWProject - + Trash Søppel - + New Ny - + Note Notat - + Draft Utkast - + Finished Ferdig - + Minor Mindre - + Major Større - + Main Hoved - + New Project Nytt prosjekt - + By Av - + Novel Roman - + Plot Plott - + Characters Karakterer - + World Verden - + Title Page Tittelside - + New Chapter Nytt kapittel - + New Scene Ny scene - + Chapter {0} Kapittel {0} - + Scene {0} Scene {0} - + File not found: {0} Fant ikke filen: {0} - + Failed to parse project xml. Kunne ikke lese prosjektets xml-data. - + Attempting to open backup project file instead. Forsøker å åpne prosjektets sekundære prosjektfil istedet. - + Unknown Ukjent - + Project file does not appear to be a novelWriterXML file. Prosjektfilen later ikke til å være en novelWriterXML-fil. - + Unknown or unsupported novelWriter project file format. The project cannot be opened by this version of novelWriter. The file was saved with novelWriter version {0}. Prosjektfilen har et ukjent eller ikke støttet format, og kan ikke åpnes med denne versjonen av novelWriter. Prosjektet ble lagret av novelWriter versjon {0}. - + Version Conflict Versjonskonflikt - + Opened Project: {0} Åpnet prosjekt: {0} - + Project path not set, cannot save project. Prosjektet mangler filbane, og kan ikke lagres. - + Failed to save project. Kunne ikke lagre prosjektet. - + Saved Project: {0} Lagret prosjekt: {0} - + Backing up project ... Lager sikkerhetskopi ... - + Cannot backup project because no backup path is set. Please set a valid backup location in Tools > Preferences. Kan ikke ta sikkerhetskopi av prosjektet da ingen filbane er satt. Du må først sette en filbane i Verktøy > Innstillinger. - + Cannot backup project because no project name is set. Please set a Working Title in Project > Project Settings. Kan ikke ta sikkerhetskopi av prosjektet da ingen arbeidstittel er satt. Du må først sette en arbeidstittel i Prosjekt > Prosjektinnstillinger. - + Cannot backup project because the backup path does not exist. Please set a valid backup location in Tools > Preferences. Kan ikke ta sikkerhetskopi av prosjektet da filbane ikke finnes. Du må sette en ny filbane i Verktøy > Innstillinger. - + Could not create backup folder. Kunne ikke lage mappe til sikkerhetskopi. - + Cannot backup project because the backup path is within the project folder to be backed up. Please choose a different backup path in Tools > Preferences. Kan ikke ta sikkerhetskopi av prosjektet da filbanen er inne i prosjektmappen. Du må sette en ny filbane i Verktøy > Innstillinger. - + Backup from {0} Sikkerhetskopi fra {0} - + Backup archive file written to: {0} Sikkerhetskopi skrevet til: {0} - + Could not write backup archive. Kunne ikke lage sikkerhetskopi. - + Project backed up to '{0}' Sikkerhetskopi skrevet til '{0}' - + Failed to create a new example project. Kunne ikke lage nytt eksempel-prosjekt. - + Failed to create a new example project. Could not find the necessary files. They seem to be missing from this installation. Kunne ikke lage nytt eksempel-prosjekt. Kunne ikke finne de nødvendige filene. De ser ut til å mangle i denne installasjonen. - + Could not create new project folder. Kunne ikke lage ny prosjekt-mappe. - + New project folder is not empty. Each project requires a dedicated project folder. Ny prosjektmappe er ikke tom. Hvert prosjekt trenger sin egen mappe. - + You must set a valid backup path in preferences to use the automatic project backup feature. Du må sette en gyldig filbane i innstillingene for å kunne bruke automatisk sikkerhetskopi. - + You must set a valid project name in project settings to use the automatic project backup feature. Du må sette en gyldig arbeidstittel i prosjektinnstillingene for å kunne bruke automatisk sikkerhetskopi. - + and og - + Found {0} orphaned file(s) in project folder. Fant {0} tapte filer i prosjektmappen. - + Recovered Gjennopprettet - + [{0}] {1} - + Recovered File {0} Gjennopprettet fil {0} - + One or more orphaned files could not be added back into the project. Make sure at least a Novel root folder exists. Én eller flere gjennopprettede filer kunne ikke bli lagt til i posjektet. Pass på at "Roman"-mappen i det minste eksisterer. - + Not a folder: {0} Ikke en mappe: {0} - + Could not move: {0} Kunne ikke flytte: {0} - + Could not delete: {0} Kunne ikke slette: {0} - + Could not make folder: {0} Kunne ikke lage mappe: {0} - + Could not move item {0} to {1}. Kunne ikke flytte {0} til {1}. - + Chapter Kapittel - + This project was saved by a newer version of novelWriter, version {0}. This is version {1}. If you continue to open the project, some attributes and settings may not be preserved, but the overall project should be fine. Continue opening the project? Dette prosjektet ble lagret av en nyere versjon av novelWriter, versjon {0}. Dette er versjon {1}. Hvis du ønsker å fortsette med å åpne prosjektet, kan noen av innstillingene bli borte, men selve prosjektet vil være i orden. Vil du fortsatt åpne prosjektet? @@ -4749,14 +4754,19 @@ Tokenizer - + Document '{0}' is too big ({1} MB). Skipping. Dokumentet '{0}' er for stort ({1} MB). Hopper over. - + ERROR + + + Synopsis + Sammendrag + diff --git a/i18n/nw_pt.ts b/i18n/nw_pt.ts index 2d45a9fd..6f6de948 100644 --- a/i18n/nw_pt.ts +++ b/i18n/nw_pt.ts @@ -422,47 +422,47 @@ GuiBuildNovel - + Failed to generate preview. The result is too big. A geração do rascunho falhou. O resultado é muito grande. - + Open Document (.odt) Open Document (.odt) - + PDF PDF - + Plain HTML HTML Simples - + Unknown format Formato desconhecido - + novelWriter HTML (.htm) HTML do novelWriter (.htm) - + novelWriter Markdown (.nwd) Markdown do novelWriter (.nwd) - + JSON + novelWriter HTML JSON + HTML do novelWriter - + JSON + novelWriters Markdown (.json) JSON + Markdown do novelWriter (.json) @@ -477,182 +477,182 @@ Formatos de Título para Arquivos do Livro - + Title Título - + Chapter Capítulo - + Unnumbered Sem Numeração - + Section Seção - + Font family Família da fonte - + Font size Tamanho da fonte - + Justify text Texto justificado - + Disable styling Desabilita a estilização - + Include synopsis Inclui a sinopse - + Include comments Inclui comentários - + Include keywords Inclui palavras-chave - + Include body text Inclui o corpo do texto - + File Filter Options Opções de Filtro de Arquivos - + Ignore the 'Include when building project' setting and include all files in the output. Ignora a configuração 'Inclui quando estiver construindo o projeto' e inclui todos os arquivos no resultado. - + Include novel files Inclui arquivos do livro - + Include note files Inclui arquivos de notas - + Ignore export flag Ignora opção de exportação - + Export Options Opções de Exportação - + Replace tabs with spaces Substitui tabulações com espaços - + Print Imprimir - + Close Fechar - + Save Document As Salvar Documento Como - + {0} file successfully written to: Arquivo {0} escrito com sucesso para: - + Failed to write {0} file. {1} Falhou para escrever o arquivo {0}. {1} - + Build Preview Construir Prévia - + Print Preview Imprimir Prévia - + Print to PDF Imprimir para PDF - + Flat Open Document (.fodt) - + Standard Markdown (.md) Markdown Padrão (.md) - + GitHub Markdown (.md) Markdown do GitHub (.md) - + There were problems when building the project Houveram problemas ao construir o projeto - + Scene Cena - + Font Options Opções de Fonte - + Line height Altura da linha - + Include Options Opções de Inclusão - + Replace Unicode in HTML Substituir Unicode no HTML @@ -697,12 +697,12 @@ Deixe em branco para ignorar este cabeçalho ou defina um texto estático como {0}, por exemplo, para fazer um separador. O separador será centralizado automaticamente a aparecerá apenas entre seções do mesmo tipo. - + Include files with layouts other than 'Note'. Inclui arquivos com leiautes diferentes de 'Nota'. - + Include files with layout 'Note'. Inclui arquivos com leiaute 'Nota'. @@ -712,65 +712,70 @@ Códigos de Formatação: - + Save As Salvar Como - + Open Document - + Flat Open Document - + novelWriter Markdown Markdown do novelWriter - + Standard Markdown Markdown Padrão - + GitHub Markdown Markdown do GitHub - + JSON + novelWriter HTML (.json) JSON + HTML do novelWriter (.json) - + JSON + novelWriter Markdown JSON + Markdown do novelWriter - + Styling Options Opções de Estilo + + + Language + + GuiBuildNovelDocView - + Unknown Desconhecido - + This area will show the content of the document to be exported or printed. Press the "Build Preview" button to generate content. Esta área vai mostrar o conteúdo do documento a ser exportado ou impresso. Clique no botão "Construir Prévia" para gerar o conteúdo. - + <b>Build Time:</b> {0} <b>Tempo de Construção:</b> {0} @@ -2777,7 +2782,7 @@ GuiPreferences - + Some changes will not be applied until novelWriter has been restarted. Algumas alterações não serão aplicadas enquanto a aplicação não for reiniciada. @@ -2830,117 +2835,117 @@ GuiPreferencesAutomation - + Automatic Features Funcionalidades Automáticas - + Auto-select word under cursor Selecionar automaticamente a palavra sob o cursor - + Apply formatting to word under cursor if no selection is made. Aplicar a formatação à palavra sob o cursor se nenhuma seleção for feita. - + Auto-replace text as you type Substituir automaticamente o texto enquanto digita - + Allow the editor to replace symbols as you type. Permite que o editor substituia símbolos emquanto você digita. - + Replace as You Type Substituição Durante a Digitação - + Auto-replace single quotes Substituir automaticamente aspas simples - + Try to guess which is an opening or a closing single quote. Tenta adivinhar se a aspa simples é de abertura ou de fechamento. - + Auto-replace double quotes Subsitituir automaticamente aspas duplas - + Try to guess which is an opening or a closing double quote. Tenta adivinhar se a aspa dupla é de abertura ou de fechamento. - + Auto-replace dashes Substituir automaticamente os travessões - + Double and triple hyphens become short and long dashes. Hífens duplos ou triplos se tornam travessões curtos ou longos. - + Auto-replace dots Substituir automaticamente os pontos - + Three consecutive dots become ellipsis. Três pontos consecutivos se tornam uma reticência. - + Quotation Style Estilo de Aspas - + Single quote open style Estilo da aspa de abertura simples - + The symbol to use for a leading single quote. O símbolo usado para a aspa simples à esquerda. - + Single quote close style Estilo da aspa de fechamento simples - + The symbol to use for a trailing single quote. O símbolo usado para a aspa simples à esquerda. - + Double quote open style Estilo da aspa de abertura dupla - + The symbol to use for a leading double quote. O símbolo usado para a aspa dupla à esquerda. - + Double quote close style Estilo da aspa de fechamento dupla - + The symbol to use for a trailing double quote. O símbolo usado para a aspa dupla à direita. @@ -2948,107 +2953,107 @@ GuiPreferencesDocuments - + Text Style Estilo do Texto - + Font family Família da fonte - + Font for the document editor and viewer. Fonte para o editor e visualizador de documentos. - + Font size Tamanho da fonte - + Font size for the document editor and viewer. Tamanho da fonte para o editor e visualizador de documentos. - + Text Flow Fluxo do Texto - + Maximum text width in "Normal Mode" Largura máxima do texto no "Modo Normal" - + Horizontal margins are scaled automatically. Margens horizontais são redimensionadas automaticamente. - + Maximum text width in "Focus Mode" Largura máxima do texto no "Modo Foco" - + Disable maximum text width in "Normal Mode" Desabilita a largura máxima do texto no "Modo Normal" - + Text width is defined by the margins only. A largura do texto é definida apenas pelas margens. - + Hide document footer in "Focus Mode" Oculta o rodapé do documento no "Modo Foco" - + Hide the information bar at the bottom of the document. Oculta a barra de informações na parte de baixo do documento. - + Justify the text margins in editor and viewer Justifica as margens do texto no editor e visualizador - + Lay out text with straight edges in the editor and viewer. Organiza o texto com cantos retos no editor e visualizador. - + Text margin Margem do texto - + If maximum width is set, this becomes the minimum margin. Se a largura máxima for definida, esta se torna a margem mínima. - + Tab width Largura da tabulação - + The width of a tab key press in the editor and viewer. A largura de uma tabulação no editor e visualizador. - + px px - + pt pt @@ -3056,127 +3061,127 @@ GuiPreferencesEditor - + Spell Checking Correção Ortográfica - + Internal Interno - + Spell check provider Provedor de correção ortográfica - + Note that the internal spell check tool is quite slow. Note que o corretor ortográfico interno é significativamente lento. - + Spell check language Idioma do corretor ortográfico - + Available languages are determined by your system. Os idiomas disponíveis são determinados pelo seu sistema. - + Big document limit Limite de documento grande - + Full spell checking is disabled above this limit. A verificação ortográfica é desabilitada acima desse limite. - + Writing Guides Guias de Escrita - + Show tabs and spaces Mostrar tabulações e espaços - + Add symbols to indicate tabs and spaces in the editor. Adiciona símbolos para indicar tabulações e espaços no editor. - + Show line endings Mostrar terminações de linha - + Add a symbol to indicate line endings in the editor. Adiciona um símbolo para indicar a terminação de linha no editor. - + Scroll Behaviour Comportamento da Rolagem - + Scroll past end of the document Rolar após o final do documento - + Also improves trypewriter scrolling for short documents. Também melhora a rolagem de máquina de escrever em documentos curtos. - + Typewriter style scrolling when you type Rolagem no estilo de máquina de escrever quando digita - + Try to keep the cursor at a fixed vertical position. Tenta manter o cursor em uma posição vertical fixa. - + Minimum position for Typewriter scrolling Posição máxima da rolagem de máquina de escrever - + Percentage of the editor height from the top. Porcentagem da altura do editor desde o topo. - + kB kB - + Word Count Contagem de Palavras - + Word count interval Intervalo de contagem de palavras - + How often the word count is updated. Com qual frequência a contagem de palavras é atualizada. - + seconds segundos @@ -3184,82 +3189,82 @@ GuiPreferencesGeneral - + Look and Feel Aparência - + Main GUI theme Tema da interface - + Changing this requires restarting novelWriter. Alterações nessa configuração exigem reinício da aplicação. - + Main icon theme Tema dos ícones - + Prefer icons for dark backgrounds Preferir ícones para fundos escuros - + May improve the look of icons on dark themes. Pode melhorar a aparência dos ícones em temas escuros. - + Font family Família da fonte - + Font size Tamanho da fonte - + GUI Settings Configurações da Interface - + Show full path in document header Mostrar o caminho completo do documento no cabeçalho - + Add the parent folder names to the header. Adiciona o nome dos diretórios-pai ao cabeçalho. - + Hide vertical scroll bars in main windows Ocultar a barra de rolagem vertical nas janelas principais - + Scrolling available with mouse wheel and keys only. A rolagem de tela estará diponível apenas com o mouse ou teclado. - + Hide horizontal scroll bars in main windows Ocultar a barra de rolagem horizontal nas janelas principais - + pt pt - + Main GUI language Idioma da Interface @@ -3267,107 +3272,107 @@ GuiPreferencesProjects - + Automatic Save Salvamento Automático - + Save document interval Intervalo de salvamento do documento - + How often the open document is automatically saved. Com qual frequência o documento aberto é salvo automaticamente. - + Save project interval Intervalo de salvamento do projeto - + How often the open project is automatically saved. Com qual frequencia o projeto aberto é salvo automaticamente. - + Project Backup Cópia de Segurança - + Browse Procurar - + Backup storage location Localização da cópia de segurança - + Run backup when the project is closed Executar a cópia de segurança quando o projeto é fechado - + Can be overridden for individual projects in project settings. Pode ser sobrescrito para projetos individuais nas configurações do projeto. - + Ask before running backup Perguntar antes de executar a cópia de segurança - + If off, backups will run in the background. Se desativado, cópias de segurança serão executadas em segundo plano. - + Backup Directory Diretório das Cópias de Segurança - + seconds segundos - + Session Timer Temporizador da Sessão - + Pause the session timer when not writing Pausa o temporizador da sessão quando não estiver escrevendo - + Also pauses when the application window does not have focus. Também pausa quando a janela da aplicação não estiver em foco. - + Editor inactive time before pausing timer Tempo inativo do editor antes de pausar o temporizador - + User activity includes typing and changing the content. Atividades de usuário incluem escrever e alterar o conteúdo. - + minutes minutos - + Path: {0} Caminho: {0} @@ -3375,67 +3380,67 @@ GuiPreferencesSyntax - + Highlighting Theme Tema do Destaque - + Highlighting theme Tema do destaque - + Colour theme to apply to the editor and viewer. Tema de cores para aplicar ao editor e visualizador. - + Quotes & Dialogue Citações e Diálogos - + Highlight text wrapped in quotes Destaca o texto em citações - + Applies to single, double and straight quotes. Aplica-se a citações com aspas simples, duplas e retas. - + Allow open-ended single quotes Permite citações com aspas simples sem fechamento - + Highlight single-quoted line with no closing quote. Destaca a linha com citação de aspas simples sem aspas de fechamento. - + Allow open-ended double quotes Permite citações com aspas duplas sem fechamento - + Highlight double-quoted line with no closing quote. Destaca a linha com citação de aspas duplas sem aspas de fechamento. - + Text Emphasis Ênfase de Texto - + Add highlight colour to emphasised text Adiciona destaque de cor ao texto enfatizado - + Applies to emphasis (italic) and strong (bold). Aplica-se à ênfase (itálico) e ênfase forte (negrito). @@ -4268,297 +4273,297 @@ NWProject - + Trash Lixeira - + New Novo - + Note Nota - + Draft Rascunho - + Finished Finalizado - + Minor Menor - + Major Maior - + Main Principal - + New Project Novo Projeto - + Novel Livro - + Plot Enredo - + Characters Personagens - + World Mundo - + Title Page Página de Título - + New Chapter Novo Capítulo - + New Scene Nova Cena - + Failed to parse project xml. Houve uma falha ao interpretar o conteúdo XML do projeto. - + Attempting to open backup project file instead. Tentando abrir a cópia de segurança do projeto. - + Unknown Desconhecido - + Project file does not appear to be a novelWriterXML file. O arquivo do projeto não parece ser um arquivo XML do novelWriter. - + Version Conflict Conflito de Versão - + Opened Project: {0} Projeto Aberto: {0} - + Project path not set, cannot save project. O caminho do projeto não foi definido, não é possível salvar o projeto. - + Failed to save project. Houve uma falha ao salvar o projeto. - + Saved Project: {0} Projeto Salvo: {0} - + Backing up project ... Realizando uma cópia de segurança do projeto... - + Cannot backup project because no backup path is set. Please set a valid backup location in Tools > Preferences. Não foi possível realizar uma cópia de segurança do projeto porquê o caminho das cópias de segurança não foi definido. Por favor, defina um caminho válido para as cópias de segurança em Ferramentas > Preferências. - + Cannot backup project because no project name is set. Please set a Working Title in Project > Project Settings. Não foi possível realizar a cópia de segurança do projeto porque o nome do projeto não está definido. Por favor defina o Nome do Projeto em Projeto > Configurações do Projeto. - + Cannot backup project because the backup path does not exist. Please set a valid backup location in Tools > Preferences. Não foi possível realizar a cópia de segurança do projeto porque o caminho das cópias de segurança não exite. Por favor, defina um cainho válido para as cópias de segurança em Ferramentas > Preferências. - + Could not create backup folder. Não foi possível ler o diretório de cópias de segurança. - + Cannot backup project because the backup path is within the project folder to be backed up. Please choose a different backup path in Tools > Preferences. Não foi possível realizar a cópia de segurança do projeto porque o caminho das cópias de segurança está em um caminho dentro do diretório do projeto. Por favor, escolha um caminho diferente para as cópias de segurança em Ferramentas> Preferências. - + Could not write backup archive. Não foi possível escrever o arquivo da cópia de segurança. - + Failed to create a new example project. Houve uma falha ao criar um novo projeto de exemplo. - + Failed to create a new example project. Could not find the necessary files. They seem to be missing from this installation. Houve uma falha ao criar um novo projeto de exemplo. Não foi possível encontrar os arquivos necessários. Eles parecem estar faltando nesta instalação. - + Could not create new project folder. Não foi possível criar o diretório do novo projeto. - + New project folder is not empty. Each project requires a dedicated project folder. O diretório do novo projeto não está vazio. Cada projeto requer um diretório dedicado. - + You must set a valid backup path in preferences to use the automatic project backup feature. Deve ser definido um caminho válido para as cópias de segurança nas preferências para usar a funcionalidade de cópias de segurança automáticas. - + You must set a valid project name in project settings to use the automatic project backup feature. Deve ser definido um nome de projeto válido nas preferências do projeto para usar a funcionalidade de cópias de segurança automáticas. - + Recovered Recuperado - + One or more orphaned files could not be added back into the project. Make sure at least a Novel root folder exists. Um ou mais arquivos-órfãos não puderam ser readicionados ao projeto. Verifique que pelo menos um diretório-raiz de Livro exista. - + Could not move: {0} Não foi possível mover: {0} - + Could not delete: {0} Não foi possível remover: {0} - + Could not make folder: {0} Não foi possível criar o diretório: {0} - + Chapter {0} Capítulo {0} - + Scene {0} Cena {0} - + File not found: {0} Arquivo não encontrado: {0} - + Backup from {0} Cópia de segurança de {0} - + Backup archive file written to: {0} Arquivo da cópia de segurança escrito em: {0} - + Project backed up to '{0}' Cópia de segurança realizada para '{0}' - + Found {0} orphaned file(s) in project folder. Foram encontrados {0} arquivos-órfãos no diretório do projeto. - + Recovered File {0} Arquivo Recuperado {0} - + Not a folder: {0} Não é um diretório: {0} - + Unknown or unsupported novelWriter project file format. The project cannot be opened by this version of novelWriter. The file was saved with novelWriter version {0}. Format de arquivo de projeto do novelWriter desconhecido ou não-suportado. O projeto não pode ser aberto por essa versão do novelWriter. O arquivo foi salvo com a versão {0} do novelWriter. - + [{0}] {1} - + Could not move item {0} to {1}. Não foi possível mover o item {0} para {1}. - + By Por - + and e - + Chapter Capítulo - + This project was saved by a newer version of novelWriter, version {0}. This is version {1}. If you continue to open the project, some attributes and settings may not be preserved, but the overall project should be fine. Continue opening the project? O projeto foi salvo por uma versão mais nova do novelWriter, versão {0}. Esta é a versão {1}. Caso deseje continuar a abrir o projeto, alguns atributos e configurações podem não ser preservados, mas o projeto deve funcionar corretamente. Continuar a abrir o projeto? @@ -4749,14 +4754,19 @@ Tokenizer - + ERROR ERRO - + Document '{0}' is too big ({1} MB). Skipping. O documento '{0}' é muito grande ({1} MB). Ignorando. + + + Synopsis + Sinopse + diff --git a/nw/core/tohtml.py b/nw/core/tohtml.py index 242554bf..9b0e7379 100644 --- a/nw/core/tohtml.py +++ b/nw/core/tohtml.py @@ -405,10 +405,11 @@ class ToHtml(Tokenizer): def _formatSynopsis(self, tText): """Apply HTML formatting to synopsis. """ - sSynop = self._localLookup("Synopsis") if self.genMode == self.M_PREVIEW: + sSynop = self._trSynopsis return f"

{sSynop}: {tText}

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

{sSynop}: {tText}

\n" def _formatComments(self, tText): diff --git a/nw/core/tokenizer.py b/nw/core/tokenizer.py index 989db48f..ff88a274 100644 --- a/nw/core/tokenizer.py +++ b/nw/core/tokenizer.py @@ -147,6 +147,9 @@ class Tokenizer(): self._localLookup = self.theProject.localLookup self.tr = partial(QCoreApplication.translate, "Tokenizer") + # Cached Translations + self._trSynopsis = self.tr("Synopsis") + return ## From 6cc974a8e9acacb0568974648325497278463d26 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Thu, 18 Feb 2021 20:47:22 +0100 Subject: [PATCH 5/7] Fix tests --- sample/nwProject.nwx | 14 +++++++------- tests/conftest.py | 2 ++ .../reference/coreProject_NewCustomA_nwProject.nwx | 2 +- .../reference/coreProject_NewCustomB_nwProject.nwx | 2 +- tests/reference/coreProject_NewFile_nwProject.nwx | 2 +- .../reference/coreProject_NewMinimal_nwProject.nwx | 2 +- tests/reference/coreProject_NewRoot_nwProject.nwx | 2 +- tests/reference/guiEditor_Main_Final_nwProject.nwx | 2 +- .../reference/guiEditor_Main_Initial_nwProject.nwx | 2 +- tests/reference/guiItemEditor_Dialog_nwProject.nwx | 2 +- .../reference/guiProjSettings_Dialog_nwProject.nwx | 2 +- tests/test_base/test_base_config.py | 2 +- tests/test_core/test_core_tokenizer.py | 6 ++++-- 13 files changed, 23 insertions(+), 19 deletions(-) diff --git a/sample/nwProject.nwx b/sample/nwProject.nwx index 5f4ff79c..c4351b24 100644 --- a/sample/nwProject.nwx +++ b/sample/nwProject.nwx @@ -1,17 +1,17 @@ - + Sample Project Sample Project Jane Smith Jay Doh - 1022 - 161 - 48346 + 1054 + 166 + 49435 False - None + en True None True @@ -27,7 +27,7 @@ %title% - Chapter %ch%: %title% + Chapter %chw%: %title% %title% Scene %ch%.%sc%: %title%
@@ -121,7 +121,7 @@ 1810 318 8 - 1112 + 1505 Another Scene diff --git a/tests/conftest.py b/tests/conftest.py index d7001c1d..36bf44d3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -118,6 +118,7 @@ def tmpConf(tmpDir): theConf = Config() theConf.initConfig(tmpDir, tmpDir) theConf.setLastPath("") + theConf.guiLang = "en_GB" return theConf @pytest.fixture(scope="function") @@ -130,6 +131,7 @@ def fncConf(fncDir): theConf = Config() theConf.initConfig(fncDir, fncDir) theConf.setLastPath("") + theConf.guiLang = "en_GB" return theConf @pytest.fixture(scope="function") diff --git a/tests/reference/coreProject_NewCustomA_nwProject.nwx b/tests/reference/coreProject_NewCustomA_nwProject.nwx index 7673815d..53695bd8 100644 --- a/tests/reference/coreProject_NewCustomA_nwProject.nwx +++ b/tests/reference/coreProject_NewCustomA_nwProject.nwx @@ -11,7 +11,7 @@ True - None + en_GB False None True diff --git a/tests/reference/coreProject_NewCustomB_nwProject.nwx b/tests/reference/coreProject_NewCustomB_nwProject.nwx index baaf3298..656001e4 100644 --- a/tests/reference/coreProject_NewCustomB_nwProject.nwx +++ b/tests/reference/coreProject_NewCustomB_nwProject.nwx @@ -11,7 +11,7 @@ True - None + en_GB False None True diff --git a/tests/reference/coreProject_NewFile_nwProject.nwx b/tests/reference/coreProject_NewFile_nwProject.nwx index 83d80233..57f3b0ed 100644 --- a/tests/reference/coreProject_NewFile_nwProject.nwx +++ b/tests/reference/coreProject_NewFile_nwProject.nwx @@ -9,7 +9,7 @@ True - None + en_GB False None True diff --git a/tests/reference/coreProject_NewMinimal_nwProject.nwx b/tests/reference/coreProject_NewMinimal_nwProject.nwx index ba867df1..122efc00 100644 --- a/tests/reference/coreProject_NewMinimal_nwProject.nwx +++ b/tests/reference/coreProject_NewMinimal_nwProject.nwx @@ -9,7 +9,7 @@ True - None + en_GB False None True diff --git a/tests/reference/coreProject_NewRoot_nwProject.nwx b/tests/reference/coreProject_NewRoot_nwProject.nwx index d437d4e2..8a6ae326 100644 --- a/tests/reference/coreProject_NewRoot_nwProject.nwx +++ b/tests/reference/coreProject_NewRoot_nwProject.nwx @@ -9,7 +9,7 @@ True - None + en_GB False None True diff --git a/tests/reference/guiEditor_Main_Final_nwProject.nwx b/tests/reference/guiEditor_Main_Final_nwProject.nwx index 6e511bca..1cd4a051 100644 --- a/tests/reference/guiEditor_Main_Final_nwProject.nwx +++ b/tests/reference/guiEditor_Main_Final_nwProject.nwx @@ -9,7 +9,7 @@ True - None + en_GB True None True diff --git a/tests/reference/guiEditor_Main_Initial_nwProject.nwx b/tests/reference/guiEditor_Main_Initial_nwProject.nwx index 96af56bb..561fd147 100644 --- a/tests/reference/guiEditor_Main_Initial_nwProject.nwx +++ b/tests/reference/guiEditor_Main_Initial_nwProject.nwx @@ -9,7 +9,7 @@ True - None + en_GB False None True diff --git a/tests/reference/guiItemEditor_Dialog_nwProject.nwx b/tests/reference/guiItemEditor_Dialog_nwProject.nwx index 89beddc9..3f581fad 100644 --- a/tests/reference/guiItemEditor_Dialog_nwProject.nwx +++ b/tests/reference/guiItemEditor_Dialog_nwProject.nwx @@ -9,7 +9,7 @@ True - None + en_GB False None True diff --git a/tests/reference/guiProjSettings_Dialog_nwProject.nwx b/tests/reference/guiProjSettings_Dialog_nwProject.nwx index a935bee7..4e8674fe 100644 --- a/tests/reference/guiProjSettings_Dialog_nwProject.nwx +++ b/tests/reference/guiProjSettings_Dialog_nwProject.nwx @@ -11,7 +11,7 @@ True - None + en_GB False en True diff --git a/tests/test_base/test_base_config.py b/tests/test_base/test_base_config.py index 9a0a561b..c755b6a0 100644 --- a/tests/test_base/test_base_config.py +++ b/tests/test_base/test_base_config.py @@ -197,7 +197,7 @@ def testBaseConfig_Init(monkeypatch, tmpDir, fncDir, outDir, refDir, filesDir): tstApp = DummyApp() tstConf.initLocalisation(tstApp) - theList = tstConf.listLanguages() + theList = tstConf.listLanguages(tstConf.LANG_NW) assert theList == [("en_GB", "British English")] copyfile(confFile, testFile) diff --git a/tests/test_core/test_core_tokenizer.py b/tests/test_core/test_core_tokenizer.py index 07dc5241..4062f710 100644 --- a/tests/test_core/test_core_tokenizer.py +++ b/tests/test_core/test_core_tokenizer.py @@ -117,7 +117,8 @@ def testCoreToken_TextOps(monkeypatch, nwMinimal, dummyGUI): """ theProject = NWProject(dummyGUI) theProject.projTree.setSeed(42) - theProject.loadProjectLocalisation("en") + theProject.projLang = "en" + theProject._loadProjectLocalisation() theToken = Tokenizer(theProject, dummyGUI) theToken.setKeepMarkdown(True) @@ -414,7 +415,8 @@ def testCoreToken_Headers(dummyGUI): """Test the header and page parser of the Tokenizer class. """ theProject = NWProject(dummyGUI) - theProject.loadProjectLocalisation("en") + theProject.projLang = "en" + theProject._loadProjectLocalisation() theToken = Tokenizer(theProject, dummyGUI) # Nothing From 97c884de42daba90a1ea3584934084e1c7d482b1 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Thu, 18 Feb 2021 20:53:31 +0100 Subject: [PATCH 6/7] Fix a potential bug --- nw/core/project.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nw/core/project.py b/nw/core/project.py index c00d6a18..f8bfbecd 100644 --- a/nw/core/project.py +++ b/nw/core/project.py @@ -1234,6 +1234,8 @@ class NWProject(): theLang = self.projLang if theLang is None: theLang = self.mainConf.spellLanguage + if theLang is None: + theLang = "en" lngShort = theLang.split("_")[0] loadFile = os.path.join(self.mainConf.nwLangPath, "project_en.json") From 844c3ab26fca1f1458e0b8e5dd2cc60d5eb8e928 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Thu, 18 Feb 2021 21:18:14 +0100 Subject: [PATCH 7/7] Allow Build language to not be set at all --- i18n/nw_nb_NO.ts | 128 +++++++++--------- i18n/nw_pt.ts | 125 +++++++++-------- nw/core/project.py | 18 ++- nw/gui/build.py | 1 + tests/lipsum/nwProject.nwx | 1 + tests/minimal/nwProject.nwx | 2 +- .../coreProject_NewCustomA_nwProject.nwx | 2 +- .../coreProject_NewCustomB_nwProject.nwx | 2 +- .../coreProject_NewFile_nwProject.nwx | 2 +- .../coreProject_NewMinimal_nwProject.nwx | 2 +- .../coreProject_NewRoot_nwProject.nwx | 2 +- .../guiEditor_Main_Final_nwProject.nwx | 2 +- .../guiEditor_Main_Initial_nwProject.nwx | 2 +- .../guiItemEditor_Dialog_nwProject.nwx | 2 +- .../guiProjSettings_Dialog_nwProject.nwx | 2 +- tests/test_core/test_core_toodt.py | 1 - 16 files changed, 152 insertions(+), 142 deletions(-) diff --git a/i18n/nw_nb_NO.ts b/i18n/nw_nb_NO.ts index 2022aec4..5b00cd6d 100644 --- a/i18n/nw_nb_NO.ts +++ b/i18n/nw_nb_NO.ts @@ -1,5 +1,6 @@ - + + Common @@ -477,305 +478,310 @@ La feltet stå tomt for å hoppe over overskriften, eller sett til en statisk tekst, slik som for eksempel '{0}', for å lage en separator. Separatoren vil bli sentrert automatisk, og bare satt inn mellom scener av samme type. - + Title Tittel - + Chapter Kapittel - + Unnumbered Unumrert - + Scene Scene - + Section Seksjon - + Font Options Skriftvalg - + Font family Skriftfamilie - + Font size Skriftstørrelse - + Line height Linjehøyde - + Justify text Rette marger - + Disable styling Slå av styling - + Include Options Inkluderinger - + Include synopsis Inkluder sammendrag - + Include comments Inkluder kommentarer - + Include keywords Inkluder kodeord - + Include body text Inkluder tekst - + File Filter Options Fil-filtre - + Include files with layouts other than 'Note'. Inkluder filer som ikke er notater. - + Include files with layout 'Note'. Inkluder filer som er notater. - + Ignore the 'Include when building project' setting and include all files in the output. Ignorer 'ta med ved eksport'-instilling og ta med alle filer i resultatet. - + Include novel files Inkluder romanfiler - + Include note files Inkluder notatfiler - + Ignore export flag Ignorer 'ta med'-flagg - + Export Options Eksportvalg - + Replace tabs with spaces Erstatt tab med mellomrom - + Replace Unicode in HTML Erstatt Unicode med HTML - + Build Preview Lag forhåndsvisning - + Print Skriv ut - + Print Preview Forhåndsvisning av utskrift - + Print to PDF Skriv ut til PDF - + Save As Lagre som - + Open Document (.odt) - + Flat Open Document (.fodt) - + novelWriter HTML (.htm) - + novelWriter Markdown (.nwd) - + Standard Markdown (.md) - + GitHub Markdown (.md) - + JSON + novelWriter HTML (.json) - + JSON + novelWriters Markdown (.json) - + Close Lukk - + Failed to generate preview. The result is too big. Kunne ikke generere forhåndsvisning. Resultatet er for stort til å vise. - + There were problems when building the project Det har oppstått en feil under byggingen av prosjektet - + Open Document - + Flat Open Document - + Plain HTML Enkel HTML - + novelWriter Markdown - + Standard Markdown - + GitHub Markdown - + JSON + novelWriter HTML - + JSON + novelWriter Markdown - + PDF - + Save Document As Lagre dokumentet som - + Unknown format Ukjent format - + {0} file successfully written to: Lagring av {0} var vellykket, og filen ble skrevet til: - + Failed to write {0} file. {1} Misslykkes i å skrive {0} til. {1} - + Styling Options Styling - + Language Språk + + + Not Set + Ikke satt + GuiBuildNovelDocView - + This area will show the content of the document to be exported or printed. Press the "Build Preview" button to generate content. Dette området vil vise innholdet av dokumentet som skal eksporteres. Trykk på knappen merket med "Lag forhåndsvisning" for å oppdatere innholdet. - + Unknown Ukjent - + <b>Build Time:</b> {0} <b>Generert den:</b> {0} diff --git a/i18n/nw_pt.ts b/i18n/nw_pt.ts index 6f6de948..c0c183be 100644 --- a/i18n/nw_pt.ts +++ b/i18n/nw_pt.ts @@ -422,47 +422,47 @@ GuiBuildNovel - + Failed to generate preview. The result is too big. A geração do rascunho falhou. O resultado é muito grande. - + Open Document (.odt) Open Document (.odt) - + PDF PDF - + Plain HTML HTML Simples - + Unknown format Formato desconhecido - + novelWriter HTML (.htm) HTML do novelWriter (.htm) - + novelWriter Markdown (.nwd) Markdown do novelWriter (.nwd) - + JSON + novelWriter HTML JSON + HTML do novelWriter - + JSON + novelWriters Markdown (.json) JSON + Markdown do novelWriter (.json) @@ -477,182 +477,182 @@ Formatos de Título para Arquivos do Livro - + Title Título - + Chapter Capítulo - + Unnumbered Sem Numeração - + Section Seção - + Font family Família da fonte - + Font size Tamanho da fonte - + Justify text Texto justificado - + Disable styling Desabilita a estilização - + Include synopsis Inclui a sinopse - + Include comments Inclui comentários - + Include keywords Inclui palavras-chave - + Include body text Inclui o corpo do texto - + File Filter Options Opções de Filtro de Arquivos - + Ignore the 'Include when building project' setting and include all files in the output. Ignora a configuração 'Inclui quando estiver construindo o projeto' e inclui todos os arquivos no resultado. - + Include novel files Inclui arquivos do livro - + Include note files Inclui arquivos de notas - + Ignore export flag Ignora opção de exportação - + Export Options Opções de Exportação - + Replace tabs with spaces Substitui tabulações com espaços - + Print Imprimir - + Close Fechar - + Save Document As Salvar Documento Como - + {0} file successfully written to: Arquivo {0} escrito com sucesso para: - + Failed to write {0} file. {1} Falhou para escrever o arquivo {0}. {1} - + Build Preview Construir Prévia - + Print Preview Imprimir Prévia - + Print to PDF Imprimir para PDF - + Flat Open Document (.fodt) - + Standard Markdown (.md) Markdown Padrão (.md) - + GitHub Markdown (.md) Markdown do GitHub (.md) - + There were problems when building the project Houveram problemas ao construir o projeto - + Scene Cena - + Font Options Opções de Fonte - + Line height Altura da linha - + Include Options Opções de Inclusão - + Replace Unicode in HTML Substituir Unicode no HTML @@ -697,12 +697,12 @@ Deixe em branco para ignorar este cabeçalho ou defina um texto estático como {0}, por exemplo, para fazer um separador. O separador será centralizado automaticamente a aparecerá apenas entre seções do mesmo tipo. - + Include files with layouts other than 'Note'. Inclui arquivos com leiautes diferentes de 'Nota'. - + Include files with layout 'Note'. Inclui arquivos com leiaute 'Nota'. @@ -712,70 +712,75 @@ Códigos de Formatação: - + Save As Salvar Como - + Open Document - + Flat Open Document - + novelWriter Markdown Markdown do novelWriter - + Standard Markdown Markdown Padrão - + GitHub Markdown Markdown do GitHub - + JSON + novelWriter HTML (.json) JSON + HTML do novelWriter (.json) - + JSON + novelWriter Markdown JSON + Markdown do novelWriter - + Styling Options Opções de Estilo - + Language + + + Not Set + + GuiBuildNovelDocView - + Unknown Desconhecido - + This area will show the content of the document to be exported or printed. Press the "Build Preview" button to generate content. Esta área vai mostrar o conteúdo do documento a ser exportado ou impresso. Clique no botão "Construir Prévia" para gerar o conteúdo. - + <b>Build Time:</b> {0} <b>Tempo de Construção:</b> {0} diff --git a/nw/core/project.py b/nw/core/project.py index f8bfbecd..ad43e817 100644 --- a/nw/core/project.py +++ b/nw/core/project.py @@ -80,7 +80,7 @@ class NWProject(): self.projContent = None # The full path to the project's content folder self.projDict = None # The spell check dictionary self.projSpell = None # The spell check language, if different than default - self.projLang = None # The project language, if different than default + self.projLang = None # The project language, used for builds self.projFile = None # The file name of the project main XML file # Project Meta @@ -197,7 +197,7 @@ class NWProject(): self.projContent = None self.projDict = None self.projSpell = None - self.projLang = self.mainConf.guiLang + self.projLang = None self.projFile = nwFiles.PROJ_FILE self.projName = "" self.bookTitle = "" @@ -1231,15 +1231,13 @@ class NWProject(): def _loadProjectLocalisation(self): """Load the language data for the current project language. """ - theLang = self.projLang - if theLang is None: - theLang = self.mainConf.spellLanguage - if theLang is None: - theLang = "en" + if self.projLang is None: + self.langData = {} + return False - lngShort = theLang.split("_")[0] + lngShort = self.projLang.split("_")[0] loadFile = os.path.join(self.mainConf.nwLangPath, "project_en.json") - chkFile1 = os.path.join(self.mainConf.nwLangPath, "project_%s.json" % theLang) + chkFile1 = os.path.join(self.mainConf.nwLangPath, "project_%s.json" % self.projLang) chkFile2 = os.path.join(self.mainConf.nwLangPath, "project_%s.json" % lngShort) if os.path.isfile(chkFile1): @@ -1253,7 +1251,7 @@ class NWProject(): logger.debug("Loaded project language file: %s" % os.path.basename(loadFile)) except Exception: - logger.error("Failed to load index file") + logger.error("Failed to project language file") nw.logException() return False diff --git a/nw/gui/build.py b/nw/gui/build.py index df66fbcb..a5234be5 100644 --- a/nw/gui/build.py +++ b/nw/gui/build.py @@ -166,6 +166,7 @@ class GuiBuildNovel(QDialog): self.buildLang = QComboBox() self.buildLang.setMinimumWidth(xFmt) theLangs = self.mainConf.listLanguages(self.mainConf.LANG_PROJ) + self.buildLang.addItem("[%s]" % self.tr("Not Set"), "None") for langID, langName in theLangs: self.buildLang.addItem(langName, langID) diff --git a/tests/lipsum/nwProject.nwx b/tests/lipsum/nwProject.nwx index 5dec55bb..59aee52f 100644 --- a/tests/lipsum/nwProject.nwx +++ b/tests/lipsum/nwProject.nwx @@ -10,6 +10,7 @@ False + en False None True diff --git a/tests/minimal/nwProject.nwx b/tests/minimal/nwProject.nwx index 0405ea25..2ce2a162 100644 --- a/tests/minimal/nwProject.nwx +++ b/tests/minimal/nwProject.nwx @@ -11,7 +11,7 @@ True - None + en False None True diff --git a/tests/reference/coreProject_NewCustomA_nwProject.nwx b/tests/reference/coreProject_NewCustomA_nwProject.nwx index 53695bd8..7673815d 100644 --- a/tests/reference/coreProject_NewCustomA_nwProject.nwx +++ b/tests/reference/coreProject_NewCustomA_nwProject.nwx @@ -11,7 +11,7 @@ True - en_GB + None False None True diff --git a/tests/reference/coreProject_NewCustomB_nwProject.nwx b/tests/reference/coreProject_NewCustomB_nwProject.nwx index 656001e4..baaf3298 100644 --- a/tests/reference/coreProject_NewCustomB_nwProject.nwx +++ b/tests/reference/coreProject_NewCustomB_nwProject.nwx @@ -11,7 +11,7 @@ True - en_GB + None False None True diff --git a/tests/reference/coreProject_NewFile_nwProject.nwx b/tests/reference/coreProject_NewFile_nwProject.nwx index 57f3b0ed..83d80233 100644 --- a/tests/reference/coreProject_NewFile_nwProject.nwx +++ b/tests/reference/coreProject_NewFile_nwProject.nwx @@ -9,7 +9,7 @@ True - en_GB + None False None True diff --git a/tests/reference/coreProject_NewMinimal_nwProject.nwx b/tests/reference/coreProject_NewMinimal_nwProject.nwx index 122efc00..ba867df1 100644 --- a/tests/reference/coreProject_NewMinimal_nwProject.nwx +++ b/tests/reference/coreProject_NewMinimal_nwProject.nwx @@ -9,7 +9,7 @@ True - en_GB + None False None True diff --git a/tests/reference/coreProject_NewRoot_nwProject.nwx b/tests/reference/coreProject_NewRoot_nwProject.nwx index 8a6ae326..d437d4e2 100644 --- a/tests/reference/coreProject_NewRoot_nwProject.nwx +++ b/tests/reference/coreProject_NewRoot_nwProject.nwx @@ -9,7 +9,7 @@ True - en_GB + None False None True diff --git a/tests/reference/guiEditor_Main_Final_nwProject.nwx b/tests/reference/guiEditor_Main_Final_nwProject.nwx index 1cd4a051..6e511bca 100644 --- a/tests/reference/guiEditor_Main_Final_nwProject.nwx +++ b/tests/reference/guiEditor_Main_Final_nwProject.nwx @@ -9,7 +9,7 @@ True - en_GB + None True None True diff --git a/tests/reference/guiEditor_Main_Initial_nwProject.nwx b/tests/reference/guiEditor_Main_Initial_nwProject.nwx index 561fd147..96af56bb 100644 --- a/tests/reference/guiEditor_Main_Initial_nwProject.nwx +++ b/tests/reference/guiEditor_Main_Initial_nwProject.nwx @@ -9,7 +9,7 @@ True - en_GB + None False None True diff --git a/tests/reference/guiItemEditor_Dialog_nwProject.nwx b/tests/reference/guiItemEditor_Dialog_nwProject.nwx index 3f581fad..89beddc9 100644 --- a/tests/reference/guiItemEditor_Dialog_nwProject.nwx +++ b/tests/reference/guiItemEditor_Dialog_nwProject.nwx @@ -9,7 +9,7 @@ True - en_GB + None False None True diff --git a/tests/reference/guiProjSettings_Dialog_nwProject.nwx b/tests/reference/guiProjSettings_Dialog_nwProject.nwx index 4e8674fe..a935bee7 100644 --- a/tests/reference/guiProjSettings_Dialog_nwProject.nwx +++ b/tests/reference/guiProjSettings_Dialog_nwProject.nwx @@ -11,7 +11,7 @@ True - en_GB + None False en True diff --git a/tests/test_core/test_core_toodt.py b/tests/test_core/test_core_toodt.py index f076c85d..9cad2f7d 100644 --- a/tests/test_core/test_core_toodt.py +++ b/tests/test_core/test_core_toodt.py @@ -20,7 +20,6 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ -import nw import pytest from lxml import etree