From df19ced50cea50173e9bc87c6c03e9b150574bb4 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Mon, 15 Feb 2021 16:47:57 +0100 Subject: [PATCH] Rewrap self.tr and fix a few bugs --- nw/constants/__init__.py | 5 +- nw/constants/constants.py | 126 +++++++++++---------- nw/core/document.py | 18 ++- nw/core/index.py | 7 +- nw/core/project.py | 226 ++++++++++++++++++++++---------------- nw/core/tohtml.py | 10 +- nw/core/tokenizer.py | 13 ++- nw/core/tree.py | 7 +- nw/gui/about.py | 121 +++++++++++--------- nw/gui/build.py | 96 +++++++++------- nw/gui/doceditor.py | 89 ++++++++------- nw/gui/docmerge.py | 24 ++-- nw/gui/docsplit.py | 52 +++++---- nw/gui/docviewer.py | 16 ++- nw/gui/itemdetails.py | 16 ++- nw/gui/itemeditor.py | 27 +++-- nw/gui/mainmenu.py | 36 +++--- nw/gui/outline.py | 13 +-- nw/gui/outlinedetails.py | 40 +++---- nw/gui/projdetails.py | 22 ++-- nw/gui/projload.py | 9 +- nw/gui/projsettings.py | 8 +- nw/gui/projtree.py | 33 +++--- nw/gui/projwizard.py | 69 +++++++----- nw/gui/theme.py | 13 ++- nw/gui/writingstats.py | 21 +++- nw/guimain.py | 73 +++++++----- sample/nwProject.nwx | 8 +- 28 files changed, 682 insertions(+), 516 deletions(-) diff --git a/nw/constants/__init__.py b/nw/constants/__init__.py index 7c458d2e..0aeb0ae4 100644 --- a/nw/constants/__init__.py +++ b/nw/constants/__init__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from nw.constants.constants import ( - nwConst, nwLists, nwRegEx, nwFiles, nwKeyWords, nwLabels, nwQuotes, - nwUnicode, nwHtmlUnicode + trConst, nwConst, nwLists, nwRegEx, nwFiles, nwKeyWords, nwLabels, + nwQuotes, nwUnicode, nwHtmlUnicode ) from nw.constants.enum import ( nwAlert, nwDocAction, nwItemClass, nwItemLayout, nwItemType, nwOutline, @@ -9,6 +9,7 @@ from nw.constants.enum import ( ) __all__ = [ + "trConst", "nwConst", "nwLists", "nwRegEx", diff --git a/nw/constants/constants.py b/nw/constants/constants.py index 892fcf6f..2135dce4 100644 --- a/nw/constants/constants.py +++ b/nw/constants/constants.py @@ -24,12 +24,18 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ -from PyQt5.QtCore import QT_TRANSLATE_NOOP +from PyQt5.QtCore import QCoreApplication +from PyQt5.QtCore import QT_TRANSLATE_NOOP as QT_TRN from nw.constants.enum import ( nwItemClass, nwItemLayout, nwItemType, nwOutline ) +def trConst(tString): + """Wrapper function for locally translating constants. + """ + return QCoreApplication.translate("Constant", tString) + class nwConst(): # Date and Time Formats @@ -121,17 +127,17 @@ class nwKeyWords: class nwLabels(): CLASS_NAME = { - nwItemClass.NO_CLASS : QT_TRANSLATE_NOOP("Constant", "None"), - nwItemClass.NOVEL : QT_TRANSLATE_NOOP("Constant", "Novel"), - nwItemClass.PLOT : QT_TRANSLATE_NOOP("Constant", "Plot"), - nwItemClass.CHARACTER : QT_TRANSLATE_NOOP("Constant", "Characters"), - nwItemClass.WORLD : QT_TRANSLATE_NOOP("Constant", "Locations"), - nwItemClass.TIMELINE : QT_TRANSLATE_NOOP("Constant", "Timeline"), - nwItemClass.OBJECT : QT_TRANSLATE_NOOP("Constant", "Objects"), - nwItemClass.ENTITY : QT_TRANSLATE_NOOP("Constant", "Entity"), - nwItemClass.CUSTOM : QT_TRANSLATE_NOOP("Constant", "Custom"), - nwItemClass.ARCHIVE : QT_TRANSLATE_NOOP("Constant", "Outtakes"), - nwItemClass.TRASH : QT_TRANSLATE_NOOP("Constant", "Trash"), + nwItemClass.NO_CLASS : QT_TRN("Constant", "None"), + nwItemClass.NOVEL : QT_TRN("Constant", "Novel"), + nwItemClass.PLOT : QT_TRN("Constant", "Plot"), + nwItemClass.CHARACTER : QT_TRN("Constant", "Characters"), + nwItemClass.WORLD : QT_TRN("Constant", "Locations"), + nwItemClass.TIMELINE : QT_TRN("Constant", "Timeline"), + nwItemClass.OBJECT : QT_TRN("Constant", "Objects"), + nwItemClass.ENTITY : QT_TRN("Constant", "Entity"), + nwItemClass.CUSTOM : QT_TRN("Constant", "Custom"), + nwItemClass.ARCHIVE : QT_TRN("Constant", "Outtakes"), + nwItemClass.TRASH : QT_TRN("Constant", "Trash"), } CLASS_FLAG = { nwItemClass.NO_CLASS : "0", @@ -160,15 +166,15 @@ class nwLabels(): nwItemClass.TRASH : "cls_trash", } LAYOUT_NAME = { - nwItemLayout.NO_LAYOUT : QT_TRANSLATE_NOOP("Constant", "None"), - nwItemLayout.TITLE : QT_TRANSLATE_NOOP("Constant", "Title Page"), - nwItemLayout.BOOK : QT_TRANSLATE_NOOP("Constant", "Book"), - nwItemLayout.PAGE : QT_TRANSLATE_NOOP("Constant", "Plain Page"), - nwItemLayout.PARTITION : QT_TRANSLATE_NOOP("Constant", "Partition"), - nwItemLayout.UNNUMBERED : QT_TRANSLATE_NOOP("Constant", "Unnumbered"), - nwItemLayout.CHAPTER : QT_TRANSLATE_NOOP("Constant", "Chapter"), - nwItemLayout.SCENE : QT_TRANSLATE_NOOP("Constant", "Scene"), - nwItemLayout.NOTE : QT_TRANSLATE_NOOP("Constant", "Note"), + nwItemLayout.NO_LAYOUT : QT_TRN("Constant", "None"), + nwItemLayout.TITLE : QT_TRN("Constant", "Title Page"), + nwItemLayout.BOOK : QT_TRN("Constant", "Book"), + nwItemLayout.PAGE : QT_TRN("Constant", "Plain Page"), + nwItemLayout.PARTITION : QT_TRN("Constant", "Partition"), + nwItemLayout.UNNUMBERED : QT_TRN("Constant", "Unnumbered"), + nwItemLayout.CHAPTER : QT_TRN("Constant", "Chapter"), + nwItemLayout.SCENE : QT_TRN("Constant", "Scene"), + nwItemLayout.NOTE : QT_TRN("Constant", "Note"), } LAYOUT_FLAG = { nwItemLayout.NO_LAYOUT : "Xo", @@ -182,27 +188,27 @@ class nwLabels(): nwItemLayout.NOTE : "Nt", } KEY_NAME = { - nwKeyWords.TAG_KEY : QT_TRANSLATE_NOOP("Constant", "Tag"), - nwKeyWords.POV_KEY : QT_TRANSLATE_NOOP("Constant", "Point of View"), - nwKeyWords.FOCUS_KEY : QT_TRANSLATE_NOOP("Constant", "Focus"), - nwKeyWords.CHAR_KEY : QT_TRANSLATE_NOOP("Constant", "Characters"), - nwKeyWords.PLOT_KEY : QT_TRANSLATE_NOOP("Constant", "Plot"), - nwKeyWords.TIME_KEY : QT_TRANSLATE_NOOP("Constant", "Timeline"), - nwKeyWords.WORLD_KEY : QT_TRANSLATE_NOOP("Constant", "Locations"), - nwKeyWords.OBJECT_KEY : QT_TRANSLATE_NOOP("Constant", "Objects"), - nwKeyWords.ENTITY_KEY : QT_TRANSLATE_NOOP("Constant", "Entities"), - nwKeyWords.CUSTOM_KEY : QT_TRANSLATE_NOOP("Constant", "Custom"), + nwKeyWords.TAG_KEY : QT_TRN("Constant", "Tag"), + nwKeyWords.POV_KEY : QT_TRN("Constant", "Point of View"), + nwKeyWords.FOCUS_KEY : QT_TRN("Constant", "Focus"), + nwKeyWords.CHAR_KEY : QT_TRN("Constant", "Characters"), + nwKeyWords.PLOT_KEY : QT_TRN("Constant", "Plot"), + nwKeyWords.TIME_KEY : QT_TRN("Constant", "Timeline"), + nwKeyWords.WORLD_KEY : QT_TRN("Constant", "Locations"), + nwKeyWords.OBJECT_KEY : QT_TRN("Constant", "Objects"), + nwKeyWords.ENTITY_KEY : QT_TRN("Constant", "Entities"), + nwKeyWords.CUSTOM_KEY : QT_TRN("Constant", "Custom"), } OUTLINE_COLS = { - nwOutline.TITLE : QT_TRANSLATE_NOOP("Constant", "Title"), - nwOutline.LEVEL : QT_TRANSLATE_NOOP("Constant", "Level"), - nwOutline.LABEL : QT_TRANSLATE_NOOP("Constant", "Document"), - nwOutline.LINE : QT_TRANSLATE_NOOP("Constant", "Line"), - nwOutline.CCOUNT : QT_TRANSLATE_NOOP("Constant", "Chars"), - nwOutline.WCOUNT : QT_TRANSLATE_NOOP("Constant", "Words"), - nwOutline.PCOUNT : QT_TRANSLATE_NOOP("Constant", "Pars"), - nwOutline.POV : QT_TRANSLATE_NOOP("Constant", "POV"), - nwOutline.FOCUS : QT_TRANSLATE_NOOP("Constant", "Focus"), + nwOutline.TITLE : QT_TRN("Constant", "Title"), + nwOutline.LEVEL : QT_TRN("Constant", "Level"), + nwOutline.LABEL : QT_TRN("Constant", "Document"), + nwOutline.LINE : QT_TRN("Constant", "Line"), + nwOutline.CCOUNT : QT_TRN("Constant", "Chars"), + nwOutline.WCOUNT : QT_TRN("Constant", "Words"), + nwOutline.PCOUNT : QT_TRN("Constant", "Pars"), + nwOutline.POV : QT_TRN("Constant", "POV"), + nwOutline.FOCUS : QT_TRN("Constant", "Focus"), nwOutline.CHAR : KEY_NAME[nwKeyWords.CHAR_KEY], nwOutline.PLOT : KEY_NAME[nwKeyWords.PLOT_KEY], nwOutline.TIME : KEY_NAME[nwKeyWords.TIME_KEY], @@ -210,7 +216,7 @@ class nwLabels(): nwOutline.OBJECT : KEY_NAME[nwKeyWords.OBJECT_KEY], nwOutline.ENTITY : KEY_NAME[nwKeyWords.ENTITY_KEY], nwOutline.CUSTOM : KEY_NAME[nwKeyWords.CUSTOM_KEY], - nwOutline.SYNOP : QT_TRANSLATE_NOOP("Constant", "Synopsis"), + nwOutline.SYNOP : QT_TRN("Constant", "Synopsis"), } # END Class nwLabels @@ -220,28 +226,28 @@ class nwQuotes(): Source: https://en.wikipedia.org/wiki/Quotation_mark """ SYMBOLS = { - "\u0027" : QT_TRANSLATE_NOOP("Constant", "Straight single quotation mark"), - "\u0022" : QT_TRANSLATE_NOOP("Constant", "Straight double quotation mark"), + "\u0027" : QT_TRN("Constant", "Straight single quotation mark"), + "\u0022" : QT_TRN("Constant", "Straight double quotation mark"), - "\u2018" : QT_TRANSLATE_NOOP("Constant", "Left single quotation mark"), - "\u2019" : QT_TRANSLATE_NOOP("Constant", "Right single quotation mark"), - "\u201a" : QT_TRANSLATE_NOOP("Constant", "Single low-9 quotation mark"), - "\u201b" : QT_TRANSLATE_NOOP("Constant", "Single high-reversed-9 quotation mark"), - "\u201c" : QT_TRANSLATE_NOOP("Constant", "Left double quotation mark"), - "\u201d" : QT_TRANSLATE_NOOP("Constant", "Right double quotation mark"), - "\u201e" : QT_TRANSLATE_NOOP("Constant", "Double low-9 quotation mark"), - "\u201f" : QT_TRANSLATE_NOOP("Constant", "Double high-reversed-9 quotation mark"), - "\u2e42" : QT_TRANSLATE_NOOP("Constant", "Double low-reversed-9 quotation mark"), + "\u2018" : QT_TRN("Constant", "Left single quotation mark"), + "\u2019" : QT_TRN("Constant", "Right single quotation mark"), + "\u201a" : QT_TRN("Constant", "Single low-9 quotation mark"), + "\u201b" : QT_TRN("Constant", "Single high-reversed-9 quotation mark"), + "\u201c" : QT_TRN("Constant", "Left double quotation mark"), + "\u201d" : QT_TRN("Constant", "Right double quotation mark"), + "\u201e" : QT_TRN("Constant", "Double low-9 quotation mark"), + "\u201f" : QT_TRN("Constant", "Double high-reversed-9 quotation mark"), + "\u2e42" : QT_TRN("Constant", "Double low-reversed-9 quotation mark"), - "\u2039" : QT_TRANSLATE_NOOP("Constant", "Single left-pointing angle quotation mark"), - "\u203a" : QT_TRANSLATE_NOOP("Constant", "Single right-pointing angle quotation mark"), - "\u00ab" : QT_TRANSLATE_NOOP("Constant", "Left-pointing double angle quotation mark"), - "\u00bb" : QT_TRANSLATE_NOOP("Constant", "Right-pointing double angle quotation mark"), + "\u2039" : QT_TRN("Constant", "Single left-pointing angle quotation mark"), + "\u203a" : QT_TRN("Constant", "Single right-pointing angle quotation mark"), + "\u00ab" : QT_TRN("Constant", "Left-pointing double angle quotation mark"), + "\u00bb" : QT_TRN("Constant", "Right-pointing double angle quotation mark"), - "\u300c" : QT_TRANSLATE_NOOP("Constant", "Left corner bracket"), - "\u300d" : QT_TRANSLATE_NOOP("Constant", "Right corner bracket"), - "\u300e" : QT_TRANSLATE_NOOP("Constant", "Left white corner bracket"), - "\u300f" : QT_TRANSLATE_NOOP("Constant", "Right white corner bracket"), + "\u300c" : QT_TRN("Constant", "Left corner bracket"), + "\u300d" : QT_TRN("Constant", "Right corner bracket"), + "\u300e" : QT_TRN("Constant", "Left white corner bracket"), + "\u300f" : QT_TRN("Constant", "Right white corner bracket"), } # END Class nwQuotes diff --git a/nw/core/document.py b/nw/core/document.py index 15437ff6..ce91ddd8 100644 --- a/nw/core/document.py +++ b/nw/core/document.py @@ -24,10 +24,11 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ -from functools import partial import logging import os +from functools import partial + from PyQt5.QtCore import QCoreApplication from nw.constants import nwAlert @@ -51,7 +52,7 @@ class NWDoc(): # Internal Mapping self.makeAlert = self.theParent.makeAlert - self.tr = partial(QCoreApplication.translate, self.__class__.__name__) + self.tr = partial(QCoreApplication.translate, "NWDoc") return @@ -131,7 +132,9 @@ class NWDoc(): self.theParent.setStatus( self.tr("{0}: {1}").format( self.tr("Opened Document"), - self._theItem.itemName)) + self._theItem.itemName + ) + ) return theText @@ -178,7 +181,9 @@ class NWDoc(): self.theParent.setStatus( self.tr("{0}: {1}").format( self.tr("Saved Document"), - self._theItem.itemName)) + self._theItem.itemName + ) + ) return True @@ -201,8 +206,9 @@ class NWDoc(): os.unlink(chkFile) logger.debug("Deleted: %s" % chkFile) except Exception as e: - self.makeAlert([self.tr("Could not delete document file."), str(e)], - nwAlert.ERROR) + self.makeAlert( + [self.tr("Could not delete document file."), str(e)], nwAlert.ERROR + ) return False return True diff --git a/nw/core/index.py b/nw/core/index.py index 9ac2ac45..6af04e3b 100644 --- a/nw/core/index.py +++ b/nw/core/index.py @@ -24,13 +24,13 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ -from functools import partial import nw import logging import json import os from time import time +from functools import partial from PyQt5.QtCore import QCoreApplication @@ -68,10 +68,11 @@ class NWIndex(): self._timeNotes = 0 self._timeIndex = 0 - self.tr = partial(QCoreApplication.translate, self.__class__.__name__) - self.clearIndex() + # Internal Mappings + self.tr = partial(QCoreApplication.translate, "NWIndex") + return ## diff --git a/nw/core/project.py b/nw/core/project.py index 62cba7ca..8649e978 100644 --- a/nw/core/project.py +++ b/nw/core/project.py @@ -24,9 +24,6 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ -from functools import partial - -from PyQt5.QtCore import QCoreApplication import nw import logging import os @@ -34,6 +31,9 @@ import shutil from lxml import etree from time import time +from functools import partial + +from PyQt5.QtCore import QCoreApplication from nw.core.tree import NWTree from nw.core.item import NWItem @@ -45,7 +45,7 @@ from nw.common import ( makeFileNameSafe, hexToInt ) from nw.constants import ( - nwFiles, nwItemType, nwItemClass, nwItemLayout, nwLabels, nwAlert + trConst, nwFiles, nwItemType, nwItemClass, nwItemLayout, nwLabels, nwAlert ) logger = logging.getLogger(__name__) @@ -102,7 +102,7 @@ class NWProject(): # Internal Mapping self.makeAlert = self.theParent.makeAlert - self.tr = partial(QCoreApplication.translate, self.__class__.__name__) + self.tr = partial(QCoreApplication.translate, "NWProject") # Set Defaults self.clearProject() @@ -302,8 +302,8 @@ class NWProject(): nHandle = self.newRoot(self.tr("Novel"), nwItemClass.NOVEL) for newRoot in projData.get("addRoots", []): if newRoot in nwItemClass: - self.newRoot(QCoreApplication.translate( - "Constant", nwLabels.CLASS_NAME[newRoot]), newRoot) + self.newRoot(trConst(nwLabels.CLASS_NAME[newRoot]), newRoot + ) # Create a title page tHandle = self.newFile(self.tr("Title Page"), nwItemClass.NOVEL, nHandle) @@ -424,13 +424,15 @@ class NWProject(): # Trying to open backup file instead backFile = fileName[:-3]+"bak" if os.path.isfile(backFile): - self.makeAlert(self.tr("Attempting to open backup project file instead."), - nwAlert.INFO) + self.makeAlert( + self.tr("Attempting to open backup project file instead."), nwAlert.INFO + ) try: nwXML = etree.parse(backFile) except Exception as e: - self.makeAlert([self.tr("Failed to parse project xml."), str(e)], - nwAlert.ERROR) + self.makeAlert( + [self.tr("Failed to parse project xml."), str(e)], nwAlert.ERROR + ) self.clearProject() return False else: @@ -477,23 +479,31 @@ class NWProject(): # read the file. Introduced in version 0.10. if fileVersion == "1.0": - msgYes = self.theParent.askQuestion(self.tr("Old Project Version"), ( + msgYes = self.theParent.askQuestion( + self.tr("Old Project Version"), "%s

%s" % ( - self.tr("The project file and data is created by a novelWriter version " - "lower than 0.7. Do you want to upgrade the project to the " - "most recent format?"), - self.tr("Note that after the upgrade, you " - "cannot open the project with an older version of novelWriter " - "any more, so make sure you have a recent backup.")))) + self.tr( + "The project file and data is created by a novelWriter version " + "lower than 0.7. Do you want to upgrade the project to the " + "most recent format?" + ), + self.tr( + "Note that after the upgrade, you cannot open the project with " + "an older version of novelWriter any more, so make sure you " + "have a recent backup.") + ) + ) if not msgYes: self.clearProject() return False elif fileVersion != "1.1" and fileVersion != "1.2": self.makeAlert(( - self.tr("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(appVersion) + self.tr( + "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(appVersion) ), nwAlert.ERROR) self.clearProject() return False @@ -502,15 +512,18 @@ class NWProject(): # ========================= if hexToInt(hexVersion) > hexToInt(nw.__hexversion__): - msgYes = self.theParent.askQuestion(self.tr("Version Conflict"), ( - self.tr("This project was saved by a newer version of novelWriter, version " - "{new_version}. This is version {version}. 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?") - ).format( - new_version = appVersion, - version = nw.__version__ - )) + msgYes = self.theParent.askQuestion( + self.tr("Version Conflict"), + self.tr( + "This project was saved by a newer version of novelWriter, version " + "{new_version}. This is version {version}. 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?" + ).format( + new_version = appVersion, + version = nw.__version__ + ) + ) if not msgYes: self.clearProject() return False @@ -610,8 +623,8 @@ class NWProject(): self.mainConf.saveRecentCache() self.theParent.setStatus(self.tr("{0}: {1}").format( - self.tr("Opened Project"), - self.projName)) + self.tr("Opened Project"), self.projName) + ) self._scanProjectFolder() @@ -731,8 +744,8 @@ class NWProject(): self._writeLockFile() self.theParent.setStatus(self.tr("{0}: {1}").format( - self.tr("Saved Project"), - self.projName)) + self.tr("Saved Project"), self.projName) + ) self.setProjectChanged(False) return True @@ -787,24 +800,30 @@ class NWProject(): self.theParent.setStatus(self.tr("Backing up project ...")) if self.mainConf.backupPath is None or self.mainConf.backupPath == "": - self.theParent.makeAlert(( - self.tr("Cannot backup project because no backup path is set. " - "Please set a valid backup location in Tools > Preferences.") - ), nwAlert.ERROR) + self.theParent.makeAlert( + self.tr( + "Cannot backup project because no backup path is set. " + "Please set a valid backup location in Tools > Preferences." + ), nwAlert.ERROR + ) return False if self.projName is None or self.projName == "": - self.theParent.makeAlert(( - self.tr("Cannot backup project because no project name is set. " - "Please set a Working Title in Project > Project Settings.") - ), nwAlert.ERROR) + self.theParent.makeAlert( + self.tr( + "Cannot backup project because no project name is set. " + "Please set a Working Title in Project > Project Settings." + ), nwAlert.ERROR + ) return False if not os.path.isdir(self.mainConf.backupPath): - self.theParent.makeAlert(( - self.tr("Cannot backup project because the backup path does not exist. " - "Please set a valid backup location in Tools > Preferences.") - ), nwAlert.ERROR) + self.theParent.makeAlert( + self.tr( + "Cannot backup project because the backup path does not exist. " + "Please set a valid backup location in Tools > Preferences." + ), nwAlert.ERROR + ) return False cleanName = makeFileNameSafe(self.projName) @@ -821,11 +840,13 @@ class NWProject(): return False if os.path.commonpath([self.projPath, baseDir]) == self.projPath: - self.theParent.makeAlert(( - self.tr("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.") - ), nwAlert.ERROR) + self.theParent.makeAlert( + self.tr( + "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." + ), nwAlert.ERROR + ) return False archName = self.tr("Backup from {0}").format(formatTimeStamp(time(), fileSafe=True)) @@ -838,9 +859,11 @@ class NWProject(): logger.info("Backup written to: %s" % archName) if doNotify: self.theParent.makeAlert( - self.tr("Backup archive file written to: {0}").format( - f"{os.path.join(cleanName, archName)}.zip"), - nwAlert.INFO + self.tr( + "Backup archive file written to: {0}" + ).format( + f"{os.path.join(cleanName, archName)}.zip" + ), nwAlert.INFO ) except Exception as e: @@ -865,9 +888,8 @@ class NWProject(): logger.error("No project path set for the example project") return False - srcSample = os.path.abspath(os.path.join(self.mainConf.appRoot, - self.tr("sample"))) - pkgSample = os.path.join(self.mainConf.assetPath, "%s.zip" % self.tr("sample")) + srcSample = os.path.abspath(os.path.join(self.mainConf.appRoot, "sample")) + pkgSample = os.path.join(self.mainConf.assetPath, "sample.zip") isSuccess = False if os.path.isfile(pkgSample): @@ -889,8 +911,8 @@ class NWProject(): dstProj = os.path.join(projPath, nwFiles.PROJ_FILE) shutil.copyfile(srcProj, dstProj) - srcContent = os.path.join(srcSample, self.tr("content")) - dstContent = os.path.join(projPath, self.tr("content")) + srcContent = os.path.join(srcSample, "content") + dstContent = os.path.join(projPath, "content") for srcFile in os.listdir(srcContent): srcDoc = os.path.join(srcContent, srcFile) dstDoc = os.path.join(dstContent, srcFile) @@ -904,10 +926,12 @@ class NWProject(): ) else: - self.makeAlert(( - self.tr("Failed to create a new example project. Could not find the " - "necessary files. They seem to be missing from this installation.") - ), nwAlert.ERROR) + self.makeAlert( + self.tr( + "Failed to create a new example project. Could not find the " + "necessary files. They seem to be missing from this installation." + ), nwAlert.ERROR + ) if isSuccess: self.clearProject() @@ -944,10 +968,12 @@ class NWProject(): if os.path.isdir(projPath): if os.listdir(self.projPath): - self.theParent.makeAlert(( - self.tr("New project folder is not empty. " - "Each project requires a dedicated project folder.") - ), nwAlert.ERROR) + self.theParent.makeAlert( + self.tr( + "New project folder is not empty. " + "Each project requires a dedicated project folder." + ), nwAlert.ERROR + ) return False self.ensureFolderStructure() @@ -994,17 +1020,21 @@ class NWProject(): self.doBackup = doBackup if doBackup: if not os.path.isdir(self.mainConf.backupPath): - self.theParent.makeAlert(( - self.tr("You must set a valid backup path in preferences to use " - "the automatic project backup feature.") - ), nwAlert.WARN) + self.theParent.makeAlert( + self.tr( + "You must set a valid backup path in preferences to use " + "the automatic project backup feature." + ), nwAlert.WARN + ) return False if self.projName == "": - self.theParent.makeAlert(( - self.tr("You must set a valid project name in project settings to " - "use the automatic project backup feature.") - ), nwAlert.WARN) + self.theParent.makeAlert( + self.tr( + "You must set a valid project name in project settings to " + "use the automatic project backup feature." + ), nwAlert.WARN + ) return False return True @@ -1354,6 +1384,7 @@ class NWProject(): aDoc = NWDoc(self, self.theParent) nOrph = 0 noWhere = False + oPrefix = self.tr("Recovered") for oHandle in orphanFiles: # Look for meta data @@ -1365,9 +1396,9 @@ class NWProject(): oName, oParent, oClass, oLayout = aDoc.getMeta() if oName: - oName = self.tr("{0}: {1}").format( - self.tr("Recovered"), - oName.lstrip(self.tr("{0}: ").format(self.tr("Recovered")))) + oName = self.tr("[{0}] {1}").format( + oPrefix, oName.strip("[%s]" % oPrefix).strip() + ) else: nOrph += 1 oName = self.tr("Recovered File {0}").format(nOrph) @@ -1397,10 +1428,12 @@ class NWProject(): self.projTree.append(oHandle, oParent, orphItem) if noWhere: - self.makeAlert(( - self.tr("One or more orphaned files could not be added back into the " - "project. Make sure at least a Novel root folder exists.") - ), nwAlert.WARN) + self.makeAlert( + self.tr( + "One or more orphaned files could not be added back into the " + "project. Make sure at least a Novel root folder exists." + ), nwAlert.WARN + ) return True @@ -1418,13 +1451,9 @@ class NWProject(): if not isFile: # It's a new file, so add a header if self.lastWCount > 0: - outFile.write("# %s\n" % self.tr("Offset {0}").format(self.lastWCount)) + outFile.write("# Offset %d\n" % self.lastWCount) outFile.write("# %-17s %-19s %8s %8s %8s\n" % ( - self.tr("Start Time"), - self.tr("End Time"), - self.tr("Novel"), - self.tr("Notes"), - self.tr("Idle"), + "Start Time", "End Time", "Novel", "Notes", "Idle" )) outFile.write("%-19s %-19s %8d %8d %8d\n" % ( @@ -1471,10 +1500,12 @@ class NWProject(): newPath = os.path.join(self.projContent, tHandle+".nwd") try: os.rename(theFile, newPath) - logger.info(self.tr("{0}: {1}").format(self.tr("Moved file"), theFile)) - logger.info(self.tr("{0}: {1}").format(self.tr("New location"), newPath)) + logger.info("Moved file: %s" % theFile) + logger.info("New location: %s" % newPath) except Exception: - errList.append(self.tr("{0}: {1}").format(self.tr("Could not move"), theFile)) + errList.append( + self.tr("{0}: {1}").format(self.tr("Could not move"), theFile) + ) logger.error("Could not move: %s" % theFile) nw.logException() @@ -1483,8 +1514,9 @@ class NWProject(): os.unlink(theFile) logger.info("Deleted file: %s" % theFile) except Exception: - errList.append(self.tr("{0}: {1}").format( - self.tr("Could not delete"), theFile)) + errList.append( + self.tr("{0}: {1}").format(self.tr("Could not delete"), theFile) + ) logger.error("Could not delete: %s" % theFile) nw.logException() @@ -1499,7 +1531,9 @@ class NWProject(): os.rmdir(theData) logger.info("Removed folder: %s" % theFolder) except Exception: - errList.append(self.tr("{0}: {1}").format(self.tr("Failed to remove"), theFolder)) + errList.append( + self.tr("{0}: {1}").format(self.tr("Failed to remove"), theFolder) + ) logger.error("Failed to remove: %s" % theFolder) nw.logException() @@ -1509,7 +1543,7 @@ class NWProject(): """Move an item that doesn't belong in the project folder to a junk folder. """ - theJunk = os.path.join(self.projPath, self.tr("junk")) + theJunk = os.path.join(self.projPath, "junk") if not self._checkFolder(theJunk): return self.tr("{0}: {1}").format(self.tr("Could not make folder"), theJunk) @@ -1522,7 +1556,7 @@ class NWProject(): except Exception: logger.error("Could not move item %s to junk." % theSrc) nw.logException() - return self.tr("Could not move item {0} to junk.").format(theSrc) + return self.tr("Could not move item {0} to {1}.").format(theSrc, theJunk) return "" diff --git a/nw/core/tohtml.py b/nw/core/tohtml.py index e8500168..04882855 100644 --- a/nw/core/tohtml.py +++ b/nw/core/tohtml.py @@ -407,13 +407,11 @@ class ToHtml(Tokenizer): """ if self.genMode == self.M_PREVIEW: return "

%s: %s

\n" % ( - self.tr("Synopsis"), - tText + self._trSynopsis, tText ) else: return "

%s: %s

\n" % ( - self.tr("Synopsis"), - tText + self._trSynopsis, tText ) def _formatComments(self, tText): @@ -422,7 +420,9 @@ class ToHtml(Tokenizer): if self.genMode == self.M_PREVIEW: return "

%s

\n" % tText else: - return "

%s: %s

\n" % (self.tr("Comment"), tText) + return "

%s: %s

\n" % ( + self._trComment, tText + ) def _formatKeywords(self, tText): """Apply HTML formatting to keywords. diff --git a/nw/core/tokenizer.py b/nw/core/tokenizer.py index 27136879..d5f4edfc 100644 --- a/nw/core/tokenizer.py +++ b/nw/core/tokenizer.py @@ -24,11 +24,12 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ -from functools import partial import logging import re from operator import itemgetter +from functools import partial + from PyQt5.QtCore import QCoreApplication, QRegularExpression from nw.core.document import NWDoc @@ -142,7 +143,13 @@ class Tokenizer(): # Error Handling self.errData = [] - self.tr = partial(QCoreApplication.translate, self.__class__.__name__) + # Internal Mappings + self.tr = partial(QCoreApplication.translate, "Tokenizer") + + # Localisation + self._trSynopsis = self.tr("Synopsis") + self._trComment = self.tr("Comment") + self._trNotes = self.tr("Notes") return @@ -252,7 +259,7 @@ class Tokenizer(): if theItem.itemType != nwItemType.ROOT: return False - theTitle = self.tr("{0}: {1}").format(self.tr("Notes"), theItem.itemName) + theTitle = self.tr("{0}: {1}").format(self._trNotes, theItem.itemName) self.theTokens = [] self.theTokens.append(( self.T_TITLE, 0, theTitle, None, self.A_PBB | self.A_CENTRE diff --git a/nw/core/tree.py b/nw/core/tree.py index d614b4b9..82981969 100644 --- a/nw/core/tree.py +++ b/nw/core/tree.py @@ -24,14 +24,14 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ -from functools import partial import nw import logging import os +from time import time from lxml import etree from hashlib import sha256 -from time import time +from functools import partial from PyQt5.QtCore import QCoreApplication @@ -82,7 +82,8 @@ class NWTree(): self._handleSeed = None # Used for generating handles for testing - self.tr = partial(QCoreApplication.translate, self.__class__.__name__) + # Internal Mappings + self.tr = partial(QCoreApplication.translate, "NWTree") return diff --git a/nw/gui/about.py b/nw/gui/about.py index 33517fb3..f163f934 100644 --- a/nw/gui/about.py +++ b/nw/gui/about.py @@ -62,7 +62,7 @@ class GuiAbout(QDialog): nPx = self.mainConf.pxInt(96) self.nwIcon = QLabel() self.nwIcon.setPixmap(self.theParent.theTheme.getPixmap("novelwriter", (nPx, nPx))) - self.lblName = QLabel("%s" % self.tr("novelWriter")) + self.lblName = QLabel("novelWriter") self.lblVers = QLabel("v%s" % nw.__version__) self.lblDate = QLabel(datetime.strptime(nw.__date__, "%Y-%m-%d").strftime("%x")) @@ -136,24 +136,31 @@ class GuiAbout(QDialog): aboutMsg = "".join([ "

%s

" % self.tr("About novelWriter"), "

{copyright:s}.

", - "

%s

" % (self.tr("{0}: {1}").format( - self.tr("Website"), - "{domain:s}" - )), - "

%s

" % self.tr("novelWriter is a markdown-like text editor designed for " - "organising and writing novels. It is written in Python 3 with " - "a Qt5 GUI, using PyQt5."), - "

%s

" % self.tr("novelWriter is free software: you can redistribute it and/or " - "modify it under the terms of the GNU General Public License as " - "published by the Free Software Foundation, either version 3 of " - "the License, or (at your option) any later version."), - "

%s

" % self.tr("novelWriter is distributed in the hope that it will be " - "useful, but WITHOUT ANY WARRANTY; without even the implied " - "warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR " - "PURPOSE."), - "

%s

" % (self.tr("See the License tab for the full license text, or visit the " - "GNU website at {0} for more details.").format( - "GPL v3.0")), + "

%s

" % self.tr("{0}: {1}").format( + self.tr("Website"), "{domain:s}" + ), + "

%s

" % self.tr( + "novelWriter is a markdown-like text editor designed for organising and " + "writing novels. It is written in Python 3 with a Qt5 GUI, using PyQt5." + ), + "

%s

" % self.tr( + "novelWriter is free software: you can redistribute it and/or modify it " + "under the terms of the GNU General Public License as published by the " + "Free Software Foundation, either version 3 of the License, or (at your " + "option) any later version." + ), + "

%s

" % self.tr( + "novelWriter is distributed in the hope that it will be useful, but " + "WITHOUT ANY WARRANTY; without even the implied warranty of " + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + ), + "

%s

" % ( + self.tr( + "See the License tab for the full license text, or visit the " + "GNU website at {0} for more details.").format( + "GPL v3.0" + ) + ), "

%s

" % self.tr("Credits"), "

{credits:s}

", ]).format( @@ -167,49 +174,61 @@ class GuiAbout(QDialog): theIcons = self.theParent.theTheme.theIcons if theTheme.themeName: aboutMsg += "".join([ - ("

%s

" % self.tr("{0}: {1}").format(self.tr("Theme"), theTheme.themeName)), + "

%s

" % self.tr("{0}: {1}").format( + self.tr("Theme"), theTheme.themeName + ), "

", - ("%s
" % self.tr("{0}: {1}").format( - self.tr("Author"), theTheme.themeAuthor)), - ("%s
" % self.tr("{0}: {1}").format( - self.tr("Credit"), theTheme.themeCredit)), - (self.tr("{0}: {1}").format( - self.tr("License"), - "{1}".format( - theTheme.themeLicenseUrl, theTheme.themeLicense) - )), + "%s
" % self.tr("{0}: {1}").format( + self.tr("Author"), theTheme.themeAuthor + ), + "%s
" % self.tr("{0}: {1}").format( + self.tr("Credit"), theTheme.themeCredit + ), + self.tr("{0}: {1}").format( + self.tr("License"), "{1}".format( + theTheme.themeLicenseUrl, theTheme.themeLicense + ) + ), "

" ]) + if theIcons.themeName: aboutMsg += "".join([ - ("

%s

" % self.tr("{0}: {1}").format(self.tr("Icons"), theIcons.themeName)), + "

%s

" % self.tr("{0}: {1}").format( + self.tr("Icons"), theIcons.themeName + ), "

", - ("%s
" % self.tr("{0}: {1}").format( - self.tr("Author"), theIcons.themeAuthor)), - ("%s
" % self.tr("{0}: {1}").format( - self.tr("Credit"), theIcons.themeCredit)), - (self.tr("{0}: {1}").format( - self.tr("License"), - "{1}".format( - theIcons.themeLicenseUrl, theIcons.themeLicense) - )), + "%s
" % self.tr("{0}: {1}").format( + self.tr("Author"), theIcons.themeAuthor + ), + "%s
" % self.tr("{0}: {1}").format( + self.tr("Credit"), theIcons.themeCredit + ), + self.tr("{0}: {1}").format( + self.tr("License"), "{1}".format( + theIcons.themeLicenseUrl, theIcons.themeLicense + ) + ), "

" ]) + if theTheme.syntaxName: aboutMsg += "".join([ - ("

%s

" % self.tr("{0}: {1}").format( - self.tr("Syntax"), - theTheme.syntaxName)), + "

%s

" % self.tr("{0}: {1}").format( + self.tr("Syntax"), theTheme.syntaxName + ), "

", - ("%s
" % self.tr("{0}: {1}").format( - self.tr("Author"), theTheme.syntaxAuthor)), - ("%s
" % self.tr("{0} {1}").format( - self.tr("Credit"), theTheme.syntaxCredit)), - (self.tr("{0}: {1}").format( - self.tr("License"), - "{1}".format( - theTheme.syntaxLicenseUrl, theTheme.syntaxLicense) - )), + "%s
" % self.tr("{0}: {1}").format( + self.tr("Author"), theTheme.syntaxAuthor + ), + "%s
" % self.tr("{0} {1}").format( + self.tr("Credit"), theTheme.syntaxCredit + ), + self.tr("{0}: {1}").format( + self.tr("License"), "{1}".format( + theTheme.syntaxLicenseUrl, theTheme.syntaxLicense + ) + ), "

" ]) diff --git a/nw/gui/build.py b/nw/gui/build.py index 33ba23d6..1ddf7b3d 100644 --- a/nw/gui/build.py +++ b/nw/gui/build.py @@ -105,7 +105,7 @@ class GuiBuildNovel(QDialog): self.titleForm = QGridLayout(self) self.titleGroup.setLayout(self.titleForm) - fmtHelp = "
".join( + fmtHelp = "
".join([ "%s" % self.tr("{0}:").format("Formatting Codes"), self.tr("{0} for the title as set in the document").format(r"%title%"), self.tr("{0} for chapter number (1, 2, 3)").format(r"%ch%"), @@ -114,14 +114,13 @@ class GuiBuildNovel(QDialog): self.tr("{0} for chapter number in lower case Roman").format(r"%chi%"), self.tr("{0} for scene number within chapter").format(r"%sc%"), self.tr("{0} for scene number within novel").format(r"%sca%"), - ) - fmtScHelp = ( - "

%s" % - self.tr("Leave blank to skip this heading, or set to a static text, like " - "for instance '{0}', to make a separator. The separator will " - "be centred automatically and only appear between sections of " - "the same type.").format("* * *") - ) + ]) + fmtScHelp = "

%s" % self.tr( + "Leave blank to skip this heading, or set to a static text, like " + "for instance '{0}', to make a separator. The separator will " + "be centred automatically and only appear between sections of " + "the same type." + ).format("* * *") xFmt = self.mainConf.pxInt(100) self.fmtTitle = QLineEdit() @@ -332,10 +331,10 @@ class GuiBuildNovel(QDialog): self.fileGroup.setLayout(self.fileForm) self.novelFiles = QSwitch(width=wS, height=hS) - self.novelFiles.setToolTip( - self.tr("Include files with layouts 'Book', 'Page', 'Partition', " - "'Chapter', 'Unnumbered', and 'Scene'.") - ) + self.novelFiles.setToolTip(self.tr( + "Include files with layouts 'Book', 'Page', 'Partition', " + "'Chapter', 'Unnumbered', and 'Scene'." + )) self.novelFiles.setChecked( self.optState.getBool("GuiBuildNovel", "addNovel", True) ) @@ -347,10 +346,10 @@ class GuiBuildNovel(QDialog): ) self.ignoreFlag = QSwitch(width=wS, height=hS) - self.ignoreFlag.setToolTip( - self.tr("Ignore the 'Include when building project' setting and include " - "all files in the output.") - ) + self.ignoreFlag.setToolTip(self.tr( + "Ignore the 'Include when building project' setting and include " + "all files in the output." + )) self.ignoreFlag.setChecked( self.optState.getBool("GuiBuildNovel", "ignoreFlag", False) ) @@ -430,42 +429,51 @@ class GuiBuildNovel(QDialog): self.btnSave = QPushButton("Save As") self.btnSave.setMenu(self.saveMenu) - self.saveODT = QAction(self.tr("{0} ({1})").format(self.tr("Open Document"), ".odt"), self) + self.saveODT = QAction( + self.tr("{0} ({1})").format(self.tr("Open Document"), ".odt"), self + ) self.saveODT.triggered.connect(lambda: self._saveDocument(self.FMT_ODT)) self.saveMenu.addAction(self.saveODT) self.saveFODT = QAction( - self.tr("{0} ({1})").format(self.tr("Flat Open Document"), ".fodt"), self) + self.tr("{0} ({1})").format(self.tr("Flat Open Document"), ".fodt"), self + ) self.saveFODT.triggered.connect(lambda: self._saveDocument(self.FMT_FODT)) self.saveMenu.addAction(self.saveFODT) - self.saveHTM = QAction(self.tr("{0} ({1})").format( - self.tr("novelWriter HTML"), ".htm"), self) + self.saveHTM = QAction( + self.tr("{0} ({1})").format(self.tr("novelWriter HTML"), ".htm"), self + ) self.saveHTM.triggered.connect(lambda: self._saveDocument(self.FMT_HTM)) self.saveMenu.addAction(self.saveHTM) - self.saveNWD = QAction(self.tr("{0} ({1})").format( - self.tr("novelWriter Markdown"), ".nwd"), self) + self.saveNWD = QAction( + self.tr("{0} ({1})").format(self.tr("novelWriter Markdown"), ".nwd"), self + ) self.saveNWD.triggered.connect(lambda: self._saveDocument(self.FMT_NWD)) self.saveMenu.addAction(self.saveNWD) self.saveMD = QAction( - self.tr("{0} ({1})").format(self.tr("Standard Markdown"), ".md"), self) + self.tr("{0} ({1})").format(self.tr("Standard Markdown"), ".md"), self + ) self.saveMD.triggered.connect(lambda: self._saveDocument(self.FMT_MD)) self.saveMenu.addAction(self.saveMD) self.saveGH = QAction( - self.tr("{0} ({1})").format(self.tr("GitHub Markdown"), ".md"), self) + self.tr("{0} ({1})").format(self.tr("GitHub Markdown"), ".md"), self + ) self.saveGH.triggered.connect(lambda: self._saveDocument(self.FMT_GH)) self.saveMenu.addAction(self.saveGH) - self.saveJsonH = QAction(self.tr("{0} ({1})").format( - self.tr("JSON + novelWriter HTML"), ".json"), self) + self.saveJsonH = QAction( + self.tr("{0} ({1})").format(self.tr("JSON + novelWriter HTML"), ".json"), self + ) self.saveJsonH.triggered.connect(lambda: self._saveDocument(self.FMT_JSON_H)) self.saveMenu.addAction(self.saveJsonH) - self.saveJsonM = QAction(self.tr("{0} ({1})").format( - self.tr("JSON + novelWriters Markdown"), ".json"), self) + self.saveJsonM = QAction( + self.tr("{0} ({1})").format(self.tr("JSON + novelWriters Markdown"), ".json"), self + ) self.saveJsonM.triggered.connect(lambda: self._saveDocument(self.FMT_JSON_M)) self.saveMenu.addAction(self.saveJsonM) @@ -745,7 +753,8 @@ class GuiBuildNovel(QDialog): if bldObj.errData: self.theParent.makeAlert("%s:
- %s" % ( self.tr("There were problems when building the project"), - "
- ".join(bldObj.errData)), nwAlert.ERROR) + "
- ".join(bldObj.errData)), nwAlert.ERROR + ) return @@ -997,14 +1006,14 @@ class GuiBuildNovel(QDialog): if wSuccess: self.theParent.makeAlert( "%s
%s" % ( - self.tr("{0} file successfully written to:").format(textFmt), - savePath - ), nwAlert.INFO + self.tr("{0} file successfully written to:").format(textFmt), savePath + ), + nwAlert.INFO ) else: self.theParent.makeAlert( - self.tr("Failed to write {0} file. {1}").format( - textFmt, errMsg), nwAlert.ERROR + self.tr("Failed to write {0} file. {1}").format(textFmt, errMsg), + nwAlert.ERROR ) return wSuccess @@ -1200,9 +1209,11 @@ class GuiBuildNovelDocView(QTextBrowser): self.qDocument = self.document() self.qDocument.setDocumentMargin(self.mainConf.getTextMargin()) self.setPlaceholderText( - self.tr("This area will show the content of the document to be " - "exported or printed. Press the \"Build Preview\" button " - "to generate content.") + self.tr( + "This area will show the content of the document to be " + "exported or printed. Press the \"Build Preview\" button " + "to generate content." + ) ) theFont = QFont() @@ -1234,7 +1245,8 @@ class GuiBuildNovelDocView(QTextBrowser): fPx = int(1.1*self.theTheme.fontPixelSize) self.theTitle = QLabel(self.tr("{0}: {1}".format( - self.tr("Build Time"), self.tr("Unknown"))), self) + self.tr("Build Time"), self.tr("Unknown"))), self + ) self.theTitle.setIndent(0) self.theTitle.setAutoFillBackground(True) self.theTitle.setAlignment(Qt.AlignCenter) @@ -1349,8 +1361,12 @@ class GuiBuildNovelDocView(QTextBrowser): ) else: strBuildTime = self.tr("Unknown") + self.theTitle.setText(self.tr("{0}: {1}").format( - self.tr("Build Time"), strBuildTime)) + self.tr("Build Time"), strBuildTime) + ) + + return def _updateDocMargins(self): """Automatically adjust the header to fill the top of the diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index f9db730a..7adccb4a 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -36,7 +36,7 @@ import logging from time import time from PyQt5.QtCore import ( - QCoreApplication, Qt, QSize, QTimer, pyqtSlot, pyqtSignal, QRegExp, QRegularExpression, + Qt, QSize, QTimer, pyqtSlot, pyqtSignal, QRegExp, QRegularExpression, QPointF, QObject, QRunnable, QPropertyAnimation ) from PyQt5.QtGui import ( @@ -53,8 +53,8 @@ from nw.core import NWDoc, NWSpellSimple, countWords from nw.gui.dochighlight import GuiDocHighlighter from nw.common import transferCase from nw.constants import ( - nwConst, nwAlert, nwUnicode, nwDocAction, nwDocInsert, nwItemClass, - nwKeyWords, nwLabels + trConst, nwConst, nwAlert, nwUnicode, nwDocAction, nwDocInsert, + nwItemClass, nwKeyWords, nwLabels ) logger = logging.getLogger(__name__) @@ -293,15 +293,17 @@ class GuiDocEditor(QTextEdit): docSize = len(theDoc) if docSize > nwConst.MAX_DOCSIZE: - self.theParent.makeAlert(( - self.tr("The document you are trying to open is too big. " - "The document size is {doc_size}. " - "The maximum size allowed is {max_size}."). - format( + self.theParent.makeAlert( + self.tr( + "The document you are trying to open is too big. " + "The document size is {doc_size}. " + "The maximum size allowed is {max_size}." + ).format( doc_size=self.tr("{0}\u202fMB").format(f"{docSize/1.0e6:.2f}"), max_size=self.tr("{0}\u202fMB").format(f"{nwConst.MAX_DOCSIZE/1.0e6:.2f}") - ) - ), nwAlert.ERROR) + ), + nwAlert.ERROR + ) self.clearEditor() return False @@ -384,15 +386,17 @@ class GuiDocEditor(QTextEdit): """ docSize = len(theText) if docSize > nwConst.MAX_DOCSIZE: - self.theParent.makeAlert(( - self.tr("The text you are trying to add is too big. " - "The text size is {text_size}. " - "The maximum size allowed is {max_size}."). - format( + self.theParent.makeAlert( + self.tr( + "The text you are trying to add is too big. " + "The text size is {text_size}. " + "The maximum size allowed is {max_size}." + ).format( text_size=self.tr("{0}\u202fMB").format(f"{docSize/1.0e6:.2f}"), max_size=self.tr("{0}\u202fMB").format(f"{nwConst.MAX_DOCSIZE/1.0e6:.2f}") - ) - ), nwAlert.ERROR) + ), + nwAlert.ERROR + ) return False qApp.setOverrideCursor(QCursor(Qt.WaitCursor)) @@ -747,15 +751,14 @@ class GuiDocEditor(QTextEdit): return False msgBox = QMessageBox() - msgBox.information(self, self.tr("File Location"), "".join([ - (self.tr("{0}
").format(self.tr("File details for the currently open file"))), - (self.tr("{0}
").format( - self.tr("{0}: {1}").format(self.tr("Handle"), "{handle:s}"))), - (self.tr("{0}: {1}").format(self.tr("Location"), "{fileLoc:s}")) - ]).format( - handle = self.theHandle, - fileLoc = str(self.nwDocument.getFileLocation()) - )) + msgBox.information( + self, + self.tr("File Location"), + "%s
%s" % ( + self.tr("The currently open file is saved in:"), + self.nwDocument.getFileLocation() + ), + ) return @@ -938,11 +941,15 @@ class GuiDocEditor(QTextEdit): self.lastFind = None if self.qDocument.characterCount() > nwConst.MAX_DOCSIZE: - self.theParent.makeAlert(( - self.tr("The document has grown too big and you cannot add more text to it. " - "The maximum size of a single novelWriter document is {max_size}."). - format(max_size=self.tr("{0}\u202fMB").format(f"{nwConst.MAX_DOCSIZE/1.0e6:.2f}")) - ), nwAlert.ERROR) + self.theParent.makeAlert( + self.tr( + "The document has grown too big and you cannot add more text to it. " + "The maximum size of a single novelWriter document is {max_size}." + ).format( + max_size=self.tr("{0}\u202fMB").format(f"{nwConst.MAX_DOCSIZE/1.0e6:.2f}") + ), + nwAlert.ERROR + ) self.undo() return @@ -1860,8 +1867,7 @@ class GuiDocEditSearch(QFrame): self.searchOpt.addAction(self.toggleWord) self.toggleRegEx = QAction(self.tr("RegEx Mode"), self) - self.toggleRegEx.setToolTip(self.tr("Use regular expressions (requires Qt {0})").format( - "5.3")) + self.toggleRegEx.setToolTip(self.tr("Search using regular expressions")) self.toggleRegEx.setIcon(self.theTheme.getIcon("search_regex")) self.toggleRegEx.setCheckable(True) self.toggleRegEx.setChecked(self.isRegEx) @@ -2541,10 +2547,8 @@ class GuiDocEditFooter(QWidget): theIcon = self.theParent.importIcons[iStatus] sIcon = theIcon.pixmap(self.sPx, self.sPx) - sClass = QCoreApplication.translate( - "Constant", nwLabels.CLASS_NAME[self.theItem.itemClass]) - sLayout = QCoreApplication.translate( - "Constant", nwLabels.LAYOUT_NAME[self.theItem.itemLayout]) + sClass = trConst(nwLabels.CLASS_NAME[self.theItem.itemClass]) + sLayout = trConst(nwLabels.LAYOUT_NAME[self.theItem.itemLayout]) sText = f"{self.theItem.itemStatus} / {sClass} / {sLayout}" self.statusIcon.setPixmap(sIcon) @@ -2565,7 +2569,9 @@ class GuiDocEditFooter(QWidget): self.linesText.setText( self.tr("{0}: {1} ({2}\u202f%%)".format( - self.tr("Line"), f"{iLine:n}", f"{iDist:.0f}"))) + self.tr("Line"), f"{iLine:n}", f"{iDist:.0f}") + ) + ) return @@ -2581,11 +2587,14 @@ class GuiDocEditFooter(QWidget): self.wordsText.setText( self.tr("{0}: {1} ({2})".format( - self.tr("Words"), f"{wCount:n}", f"{wDiff:+n}"))) + self.tr("Words"), f"{wCount:n}", f"{wDiff:+n}") + ) + ) byteSize = self.docEditor.qDocument.characterCount() self.wordsText.setToolTip( - (self.tr("Document size is {0} bytes").format(f"{byteSize:n}"))) + self.tr("Document size is {0} bytes").format(f"{byteSize:n}") + ) return diff --git a/nw/gui/docmerge.py b/nw/gui/docmerge.py index c62bd5d1..2d2c8887 100644 --- a/nw/gui/docmerge.py +++ b/nw/gui/docmerge.py @@ -104,9 +104,9 @@ class GuiDocMerge(QDialog): finalOrder.append(self.listBox.item(i).data(Qt.UserRole)) if len(finalOrder) == 0: - self.theParent.makeAlert(( - self.tr("No source documents found. Nothing to do.") - ), nwAlert.ERROR) + self.theParent.makeAlert( + self.tr("No source documents found. Nothing to do."), nwAlert.ERROR + ) return theDoc = NWDoc(self.theProject, self.theParent) @@ -116,16 +116,16 @@ class GuiDocMerge(QDialog): theText += "\n\n" if self.sourceItem is None: - self.theParent.makeAlert(( - self.tr("No source document selected. Nothing to do.") - ), nwAlert.ERROR) + self.theParent.makeAlert( + self.tr("No source document selected. Nothing to do."), nwAlert.ERROR + ) return srcItem = self.theProject.projTree[self.sourceItem] if srcItem is None: - self.theParent.makeAlert(( - self.tr("Could not parse source document.") - ), nwAlert.ERROR) + self.theParent.makeAlert( + self.tr("Could not parse source document."), nwAlert.ERROR + ) return nHandle = self.theProject.newFile(srcItem.itemName, srcItem.itemClass, srcItem.itemParent) @@ -166,9 +166,9 @@ class GuiDocMerge(QDialog): if nwItem is None: return if nwItem.itemType is not nwItemType.FOLDER: - self.theParent.makeAlert(( - self.tr("Element selected in the project tree must be a folder.") - ), nwAlert.ERROR) + self.theParent.makeAlert( + self.tr("Element selected in the project tree must be a folder."), nwAlert.ERROR + ) return for sHandle in self.theParent.treeView.getTreeFromHandle(tHandle): diff --git a/nw/gui/docsplit.py b/nw/gui/docsplit.py index ae611b4b..b72f9b3c 100644 --- a/nw/gui/docsplit.py +++ b/nw/gui/docsplit.py @@ -118,16 +118,16 @@ class GuiDocSplit(QDialog): logger.verbose("GuiDocSplit split button clicked") if self.sourceItem is None: - self.theParent.makeAlert(( - self.tr("No source document selected. Nothing to do.") - ), nwAlert.ERROR) + self.theParent.makeAlert( + self.tr("No source document selected. Nothing to do."), nwAlert.ERROR + ) return srcItem = self.theProject.projTree[self.sourceItem] if srcItem is None: - self.theParent.makeAlert(( - self.tr("Could not parse source document.") - ), nwAlert.ERROR) + self.theParent.makeAlert( + self.tr("Could not parse source document."), nwAlert.ERROR + ) return theDoc = NWDoc(self.theProject, self.theParent) @@ -150,26 +150,34 @@ class GuiDocSplit(QDialog): nFiles = len(finalOrder) if nFiles == 0: - self.theParent.makeAlert(( - self.tr("No headers found. Nothing to do.") - ), nwAlert.ERROR) + self.theParent.makeAlert( + self.tr("No headers found. Nothing to do."), nwAlert.ERROR + ) return # Check that another folder can be created parTree = self.theProject.projTree.getItemPath(srcItem.itemParent) if len(parTree) >= nwConst.MAX_DEPTH - 1: - self.theParent.makeAlert(( - self.tr("Cannot add new folder for the document split. " - "Maximum folder depth has been reached. " - "Please move the file to another level in the project tree.") - ), nwAlert.ERROR) + self.theParent.makeAlert( + self.tr( + "Cannot add new folder for the document split. " + "Maximum folder depth has been reached. " + "Please move the file to another level in the project tree." + ), nwAlert.ERROR + ) return - msgYes = self.theParent.askQuestion(self.tr("Split Document"), "%s

%s" % ( - self.tr("The document will be split into {0} file(s) in a new folder. " - "The original document will remain intact.", n=nFiles).format(nFiles), - self.tr("Continue with the splitting process?") - )) + msgYes = self.theParent.askQuestion( + self.tr("Split Document"), + "%s

%s" % ( + self.tr( + "The document will be split into {0} file(s) in a new folder. " + "The original document will remain intact.").format(nFiles), + self.tr( + "Continue with the splitting process?" + ) + ) + ) if not msgYes: return @@ -245,9 +253,9 @@ class GuiDocSplit(QDialog): if nwItem is None: return if nwItem.itemType is not nwItemType.FILE: - self.theParent.makeAlert(( - self.tr("Element selected in the project tree must be a file.") - ), nwAlert.ERROR) + self.theParent.makeAlert( + self.tr("Element selected in the project tree must be a file."), nwAlert.ERROR + ) return self.listBox.clear() diff --git a/nw/gui/docviewer.py b/nw/gui/docviewer.py index 0b34ebeb..33c5a6da 100644 --- a/nw/gui/docviewer.py +++ b/nw/gui/docviewer.py @@ -244,10 +244,13 @@ class GuiDocViewer(QTextBrowser): tHandle, _, sTitle = self.theParent.theIndex.getTagSource(theTag) if tHandle is None: self.theParent.makeAlert( - self.tr("Could not find the reference for tag '{0}'. It either doesn't " - "exist, or the index is out of date. The index can be updated " - "from the Tools menu, or by pressing {1}."). - format(theTag, "F9"), nwAlert.ERROR) + self.tr( + "Could not find the reference for tag '{0}'. It either doesn't " + "exist, or the index is out of date. The index can be updated " + "from the Tools menu, or by pressing {1}." + ).format(theTag, "F9"), + nwAlert.ERROR + ) return False else: # Let the parent handle the opening as it also ensures that @@ -956,8 +959,9 @@ class GuiDocViewFooter(QWidget): self.stickyRefs.setFixedSize(QSize(fPx, fPx)) self.stickyRefs.toggled.connect(self._doToggleSticky) self.stickyRefs.setToolTip( - self.tr("Activate to freeze the content of the references panel when " - "changing document") + self.tr( + "Activate to freeze the content of the references panel when changing document" + ) ) # Show Comments diff --git a/nw/gui/itemdetails.py b/nw/gui/itemdetails.py index ec77b2c9..608aa6ba 100644 --- a/nw/gui/itemdetails.py +++ b/nw/gui/itemdetails.py @@ -27,12 +27,12 @@ along with this program. If not, see . import nw import logging -from PyQt5.QtCore import QCoreApplication, Qt +from PyQt5.QtCore import Qt from PyQt5.QtGui import QFont, QPixmap from PyQt5.QtWidgets import QWidget, QGridLayout, QLabel from nw.constants import ( - nwLabels, nwItemClass, nwItemType, nwItemLayout + trConst, nwLabels, nwItemClass, nwItemType, nwItemLayout ) logger = logging.getLogger(__name__) @@ -118,7 +118,7 @@ class GuiItemDetails(QWidget): self.layoutData.setAlignment(Qt.AlignLeft) # Character Count - self.cCountName = QLabel(self.tr(" Characters")) + self.cCountName = QLabel(" "+self.tr("Characters")) self.cCountName.setFont(self.fntLabel) self.cCountName.setAlignment(Qt.AlignRight) @@ -127,7 +127,7 @@ class GuiItemDetails(QWidget): self.cCountData.setAlignment(Qt.AlignRight) # Word Count - self.wCountName = QLabel(self.tr(" Words")) + self.wCountName = QLabel(" "+self.tr("Words")) self.wCountName.setFont(self.fntLabel) self.wCountName.setAlignment(Qt.AlignRight) @@ -136,7 +136,7 @@ class GuiItemDetails(QWidget): self.wCountData.setAlignment(Qt.AlignRight) # Paragraph Count - self.pCountName = QLabel(self.tr(" Paragraphs")) + self.pCountName = QLabel(" "+self.tr("Paragraphs")) self.pCountName.setFont(self.fntLabel) self.pCountName.setAlignment(Qt.AlignRight) @@ -268,10 +268,8 @@ class GuiItemDetails(QWidget): self.labelData.setText(theLabel) self.statusData.setText(nwItem.itemStatus) - self.classData.setText(QCoreApplication.translate( - "Constant", nwLabels.CLASS_NAME[nwItem.itemClass])) - self.layoutData.setText(QCoreApplication.translate( - "Constant", nwLabels.LAYOUT_NAME[nwItem.itemLayout])) + self.classData.setText(trConst(nwLabels.CLASS_NAME[nwItem.itemClass])) + self.layoutData.setText(trConst(nwLabels.LAYOUT_NAME[nwItem.itemLayout])) if nwItem.itemType == nwItemType.FILE: self.cCountData.setText(f"{nwItem.charCount:n}") diff --git a/nw/gui/itemeditor.py b/nw/gui/itemeditor.py index 37154d86..107172f0 100644 --- a/nw/gui/itemeditor.py +++ b/nw/gui/itemeditor.py @@ -27,14 +27,14 @@ along with this program. If not, see . import nw import logging -from PyQt5.QtCore import QCoreApplication, pyqtSlot +from PyQt5.QtCore import pyqtSlot from PyQt5.QtWidgets import ( QDialog, QVBoxLayout, QGridLayout, QLineEdit, QComboBox, QLabel, QDialogButtonBox ) from nw.gui.custom import QSwitch -from nw.constants import nwLabels, nwItemLayout, nwItemType, nwLists +from nw.constants import trConst, nwLabels, nwItemLayout, nwItemType, nwLists logger = logging.getLogger(__name__) @@ -103,8 +103,7 @@ class GuiItemEditor(QDialog): for itemLayout in nwItemLayout: if itemLayout in validLayouts: - self.editLayout.addItem(QCoreApplication.translate( - "Constant", nwLabels.LAYOUT_NAME[itemLayout]), itemLayout) + self.editLayout.addItem(trConst(nwLabels.LAYOUT_NAME[itemLayout]), itemLayout) # Export Switch self.textExport = QLabel(self.tr("Include when building project")) @@ -139,17 +138,21 @@ class GuiItemEditor(QDialog): # Assemble ## + nameLabel = QLabel(self.tr("Label")) + statusLabel = QLabel(self.tr("Status")) + layoutLabel = QLabel(self.tr("Layout")) + self.mainForm = QGridLayout() self.mainForm.setVerticalSpacing(vSp) self.mainForm.setHorizontalSpacing(mSp) - self.mainForm.addWidget(QLabel(self.tr("Label")), 0, 0, 1, 1) - self.mainForm.addWidget(self.editName, 0, 1, 1, 2) - self.mainForm.addWidget(QLabel(self.tr("Status")), 1, 0, 1, 1) - self.mainForm.addWidget(self.editStatus, 1, 1, 1, 2) - self.mainForm.addWidget(QLabel(self.tr("Layout")), 2, 0, 1, 1) - self.mainForm.addWidget(self.editLayout, 2, 1, 1, 2) - self.mainForm.addWidget(self.textExport, 3, 0, 1, 2) - self.mainForm.addWidget(self.editExport, 3, 2, 1, 1) + self.mainForm.addWidget(nameLabel, 0, 0, 1, 1) + self.mainForm.addWidget(self.editName, 0, 1, 1, 2) + self.mainForm.addWidget(statusLabel, 1, 0, 1, 1) + self.mainForm.addWidget(self.editStatus, 1, 1, 1, 2) + self.mainForm.addWidget(layoutLabel, 2, 0, 1, 1) + self.mainForm.addWidget(self.editLayout, 2, 1, 1, 2) + self.mainForm.addWidget(self.textExport, 3, 0, 1, 2) + self.mainForm.addWidget(self.editExport, 3, 2, 1, 1) self.mainForm.setColumnStretch(0, 0) self.mainForm.setColumnStretch(1, 1) self.mainForm.setColumnStretch(2, 0) diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py index dd2da1bd..184289f8 100644 --- a/nw/gui/mainmenu.py +++ b/nw/gui/mainmenu.py @@ -27,13 +27,13 @@ along with this program. If not, see . import nw import logging -from PyQt5.QtCore import QCoreApplication, QUrl, QProcess +from PyQt5.QtCore import QUrl, QProcess from PyQt5.QtGui import QDesktopServices from PyQt5.QtWidgets import QMenuBar, QAction from nw.constants import ( - nwItemType, nwItemClass, nwDocAction, nwDocInsert, nwKeyWords, nwLabels, - nwUnicode + trConst, nwItemType, nwItemClass, nwDocAction, nwDocInsert, nwKeyWords, + nwLabels, nwUnicode ) logger = logging.getLogger(__name__) @@ -566,7 +566,8 @@ class GuiMainMenu(QMenuBar): # Insert > Figure Dash self.aInsFigDash = QAction(self.tr("Figure Dash"), self) self.aInsFigDash.setStatusTip( - self.tr("Insert figure dash (same width as a number character)")) + self.tr("Insert figure dash (same width as a number character)") + ) self.aInsFigDash.setShortcut("Ctrl+K, ~") self.aInsFigDash.triggered.connect(lambda: self._docInsert(nwUnicode.U_FGDASH)) self.mInsDashes.addAction(self.aInsFigDash) @@ -740,8 +741,7 @@ class GuiMainMenu(QMenuBar): self.mInsKWItems[nwKeyWords.ENTITY_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, E") self.mInsKWItems[nwKeyWords.CUSTOM_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, X") for n, keyWord in enumerate(self.mInsKWItems): - self.mInsKWItems[keyWord][0].setText( - QCoreApplication.translate("Constant", nwLabels.KEY_NAME[keyWord])) + self.mInsKWItems[keyWord][0].setText(trConst(nwLabels.KEY_NAME[keyWord])) self.mInsKWItems[keyWord][0].setShortcut(self.mInsKWItems[keyWord][1]) self.mInsKWItems[keyWord][0].triggered.connect( lambda n, keyWord=keyWord: self._insertKeyWord(keyWord) @@ -796,7 +796,8 @@ class GuiMainMenu(QMenuBar): # Search > Replace Next self.aReplaceNext = QAction(self.tr("Replace Next"), self) self.aReplaceNext.setStatusTip( - self.tr("Find and replace next occurrence text in document")) + self.tr("Find and replace next occurrence text in document") + ) self.aReplaceNext.setShortcut("Ctrl+Shift+1") self.aReplaceNext.triggered.connect(lambda: self._docAction(nwDocAction.REPL_NEXT)) self.srcMenu.addAction(self.aReplaceNext) @@ -898,14 +899,16 @@ class GuiMainMenu(QMenuBar): # Format > Replace Single Quotes self.aFmtReplSng = QAction(self.tr("Replace Single Quotes"), self) self.aFmtReplSng.setStatusTip( - self.tr("Replace all straight single quotes in selected text")) + self.tr("Replace all straight single quotes in selected text") + ) self.aFmtReplSng.triggered.connect(lambda: self._docAction(nwDocAction.REPL_SNG)) self.fmtMenu.addAction(self.aFmtReplSng) # Format > Replace Double Quotes self.aFmtReplDbl = QAction(self.tr("Replace Double Quotes"), self) self.aFmtReplDbl.setStatusTip( - self.tr("Replace all straight double quotes in selected text")) + self.tr("Replace all straight double quotes in selected text") + ) self.aFmtReplDbl.triggered.connect(lambda: self._docAction(nwDocAction.REPL_DBL)) self.fmtMenu.addAction(self.aFmtReplDbl) @@ -1031,7 +1034,8 @@ class GuiMainMenu(QMenuBar): self.aHelpWeb = QAction(self.tr("Documentation (Online)"), self) self.aHelpWeb.setStatusTip( - self.tr("View online documentation at {0}").format(nw.__docurl__)) + self.tr("View online documentation at {0}").format(nw.__docurl__) + ) self.aHelpWeb.triggered.connect(lambda: self._openWebsite(nw.__docurl__)) if self.mainConf.hasHelp and self.mainConf.hasAssistant: self.aHelpWeb.setShortcut("Shift+F1") @@ -1045,28 +1049,32 @@ class GuiMainMenu(QMenuBar): # Document > Report an Issue self.aIssue = QAction(self.tr("Report an Issue (GitHub)"), self) self.aIssue.setStatusTip( - self.tr("Report a bug or issue on GitHub at {0}").format(nw.__issuesurl__)) + self.tr("Report a bug or issue on GitHub at {0}").format(nw.__issuesurl__) + ) self.aIssue.triggered.connect(lambda: self._openWebsite(nw.__issuesurl__)) self.helpMenu.addAction(self.aIssue) # Document > Ask a Question self.aQuestion = QAction(self.tr("Ask a Question (GitHub)"), self) self.aQuestion.setStatusTip( - self.tr("Ask a question on GitHub at {0}").format(nw.__helpurl__)) + self.tr("Ask a question on GitHub at {0}").format(nw.__helpurl__) + ) self.aQuestion.triggered.connect(lambda: self._openWebsite(nw.__helpurl__)) self.helpMenu.addAction(self.aQuestion) # Document > Latest Release self.aRelease = QAction(self.tr("Latest Release (GitHub)"), self) self.aRelease.setStatusTip( - self.tr("Open the Releases page on GitHub at {0}").format(nw.__releaseurl__)) + self.tr("Open the Releases page on GitHub at {0}").format(nw.__releaseurl__) + ) self.aRelease.triggered.connect(lambda: self._openWebsite(nw.__releaseurl__)) self.helpMenu.addAction(self.aRelease) # Document > Main Website self.aWebsite = QAction(self.tr("The novelWriter Website"), self) self.aWebsite.setStatusTip( - self.tr("Open the novelWriter website at {0}").format(nw.__url__)) + self.tr("Open the novelWriter website at {0}").format(nw.__url__) + ) self.aWebsite.triggered.connect(lambda: self._openWebsite(nw.__url__)) self.helpMenu.addAction(self.aWebsite) diff --git a/nw/gui/outline.py b/nw/gui/outline.py index fac7de53..7a54c6be 100644 --- a/nw/gui/outline.py +++ b/nw/gui/outline.py @@ -29,12 +29,12 @@ import logging from time import time -from PyQt5.QtCore import QCoreApplication, Qt, QSize +from PyQt5.QtCore import Qt, QSize from PyQt5.QtWidgets import ( QTreeWidget, QTreeWidgetItem, QMenu, QAction, QAbstractItemView ) -from nw.constants import nwKeyWords, nwLabels, nwOutline +from nw.constants import trConst, nwKeyWords, nwLabels, nwOutline logger = logging.getLogger(__name__) @@ -149,8 +149,7 @@ class GuiOutline(QTreeWidget): """ self.clear() self.setColumnCount(1) - self.setHeaderLabel( - QCoreApplication.translate("Constant", nwLabels.OUTLINE_COLS[nwOutline.TITLE])) + self.setHeaderLabel(trConst(nwLabels.OUTLINE_COLS[nwOutline.TITLE])) self.treeOrder = [] self.colWidth = {} @@ -356,8 +355,7 @@ class GuiOutline(QTreeWidget): if self.firstView: theLabels = [] for i, hItem in enumerate(self.treeOrder): - theLabels.append( - QCoreApplication.translate("Constant", nwLabels.OUTLINE_COLS[hItem])) + theLabels.append(trConst(nwLabels.OUTLINE_COLS[hItem])) self.colIndex[hItem] = i self.setHeaderLabels(theLabels) @@ -484,8 +482,7 @@ class GuiOutlineHeaderMenu(QMenu): for hItem in nwOutline: if hItem == nwOutline.TITLE: continue - self.actionMap[hItem] = QAction( - QCoreApplication.translate("Constant", nwLabels.OUTLINE_COLS[hItem]), self) + self.actionMap[hItem] = QAction(trConst(nwLabels.OUTLINE_COLS[hItem]), self) self.actionMap[hItem].setCheckable(True) self.actionMap[hItem].toggled.connect( lambda isChecked, tItem=hItem : self._columnToggled(isChecked, tItem) diff --git a/nw/gui/outlinedetails.py b/nw/gui/outlinedetails.py index ab83ccee..d5ba010a 100644 --- a/nw/gui/outlinedetails.py +++ b/nw/gui/outlinedetails.py @@ -27,12 +27,13 @@ along with this program. If not, see . import nw import logging -from PyQt5.QtCore import QCoreApplication, QT_TRANSLATE_NOOP, Qt +from PyQt5.QtCore import Qt +from PyQt5.QtCore import QT_TRANSLATE_NOOP as QT_TRN from PyQt5.QtWidgets import ( QScrollArea, QWidget, QGridLayout, QHBoxLayout, QGroupBox, QLabel ) -from nw.constants import nwLabels, nwKeyWords +from nw.constants import trConst, nwLabels, nwKeyWords from nw.common import checkInt logger = logging.getLogger(__name__) @@ -40,10 +41,10 @@ logger = logging.getLogger(__name__) class GuiOutlineDetails(QScrollArea): LVL_MAP = { - "H1" : QT_TRANSLATE_NOOP("GuiOutlineDetails", "Title"), - "H2" : QT_TRANSLATE_NOOP("GuiOutlineDetails", "Chapter"), - "H3" : QT_TRANSLATE_NOOP("GuiOutlineDetails", "Scene"), - "H4" : QT_TRANSLATE_NOOP("GuiOutlineDetails", "Section"), + "H1" : QT_TRN("GuiOutlineDetails", "Title"), + "H2" : QT_TRN("GuiOutlineDetails", "Chapter"), + "H3" : QT_TRN("GuiOutlineDetails", "Scene"), + "H4" : QT_TRN("GuiOutlineDetails", "Section"), } def __init__(self, theParent): @@ -104,24 +105,15 @@ class GuiOutlineDetails(QScrollArea): self.synopLWrap.addWidget(self.synopValue, 1) # Tags - self.povKeyLabel = QLabel("%s" % QCoreApplication.translate( - "Constant", nwLabels.KEY_NAME[nwKeyWords.POV_KEY])) - self.focKeyLabel = QLabel("%s" % QCoreApplication.translate( - "Constant", nwLabels.KEY_NAME[nwKeyWords.FOCUS_KEY])) - self.chrKeyLabel = QLabel("%s" % QCoreApplication.translate( - "Constant", nwLabels.KEY_NAME[nwKeyWords.CHAR_KEY])) - self.pltKeyLabel = QLabel("%s" % QCoreApplication.translate( - "Constant", nwLabels.KEY_NAME[nwKeyWords.PLOT_KEY])) - self.timKeyLabel = QLabel("%s" % QCoreApplication.translate( - "Constant", nwLabels.KEY_NAME[nwKeyWords.TIME_KEY])) - self.wldKeyLabel = QLabel("%s" % QCoreApplication.translate( - "Constant", nwLabels.KEY_NAME[nwKeyWords.WORLD_KEY])) - self.objKeyLabel = QLabel("%s" % QCoreApplication.translate( - "Constant", nwLabels.KEY_NAME[nwKeyWords.OBJECT_KEY])) - self.entKeyLabel = QLabel("%s" % QCoreApplication.translate( - "Constant", nwLabels.KEY_NAME[nwKeyWords.ENTITY_KEY])) - self.cstKeyLabel = QLabel("%s" % QCoreApplication.translate( - "Constant", nwLabels.KEY_NAME[nwKeyWords.CUSTOM_KEY])) + self.povKeyLabel = QLabel("%s" % trConst(nwLabels.KEY_NAME[nwKeyWords.POV_KEY])) + self.focKeyLabel = QLabel("%s" % trConst(nwLabels.KEY_NAME[nwKeyWords.FOCUS_KEY])) + self.chrKeyLabel = QLabel("%s" % trConst(nwLabels.KEY_NAME[nwKeyWords.CHAR_KEY])) + self.pltKeyLabel = QLabel("%s" % trConst(nwLabels.KEY_NAME[nwKeyWords.PLOT_KEY])) + self.timKeyLabel = QLabel("%s" % trConst(nwLabels.KEY_NAME[nwKeyWords.TIME_KEY])) + self.wldKeyLabel = QLabel("%s" % trConst(nwLabels.KEY_NAME[nwKeyWords.WORLD_KEY])) + self.objKeyLabel = QLabel("%s" % trConst(nwLabels.KEY_NAME[nwKeyWords.OBJECT_KEY])) + self.entKeyLabel = QLabel("%s" % trConst(nwLabels.KEY_NAME[nwKeyWords.ENTITY_KEY])) + self.cstKeyLabel = QLabel("%s" % trConst(nwLabels.KEY_NAME[nwKeyWords.CUSTOM_KEY])) self.povKeyLWrap = QHBoxLayout() self.focKeyLWrap = QHBoxLayout() diff --git a/nw/gui/projdetails.py b/nw/gui/projdetails.py index 9e7e1029..cccdc6c9 100644 --- a/nw/gui/projdetails.py +++ b/nw/gui/projdetails.py @@ -155,8 +155,11 @@ class GuiProjectDetailsMain(QWidget): self.bookTitle.setAlignment(Qt.AlignHCenter) self.bookTitle.setWordWrap(True) - self.projName = QLabel(self.tr("{0}: {1}").format( - self.tr("Working Title"), self.theProject.projName)) + self.projName = QLabel( + self.tr("{0}: {1}").format( + self.tr("Working Title"), self.theProject.projName + ) + ) workFont = self.projName.font() workFont.setPointSizeF(0.8*fPt) workFont.setItalic(True) @@ -273,13 +276,14 @@ class GuiProjectDetailsContents(QWidget): self.tocTree.setIndentation(0) self.tocTree.setColumnCount(6) self.tocTree.setSelectionMode(QAbstractItemView.NoSelection) - self.tocTree.setHeaderLabels( - [self.tr("Title"), - self.tr("Words"), - self.tr("Pages"), - self.tr("Page"), - self.tr("Progress"), - ""]) + self.tocTree.setHeaderLabels([ + self.tr("Title"), + self.tr("Words"), + self.tr("Pages"), + self.tr("Page"), + self.tr("Progress"), + "" + ]) treeHeadItem = self.tocTree.headerItem() treeHeadItem.setTextAlignment(self.C_WORDS, Qt.AlignRight) diff --git a/nw/gui/projload.py b/nw/gui/projload.py index 8579d194..118a1ba8 100644 --- a/nw/gui/projload.py +++ b/nw/gui/projload.py @@ -192,7 +192,8 @@ class GuiProjectLoad(QDialog): self, self.tr("Open novelWriter Project"), "", ";;".join([ self.tr("{0} ({1})").format( - self.tr("novelWriter Project File"), nwFiles.PROJ_FILE), + self.tr("novelWriter Project File"), nwFiles.PROJ_FILE + ), self.tr("{0} ({1})").format(self.tr("All Files"), "*") ]), options=dlgOpt @@ -233,8 +234,10 @@ class GuiProjectLoad(QDialog): projName = selList[0].text(self.C_NAME) msgYes = self.theParent.askQuestion( self.tr("Remove Entry"), - self.tr("Remove '{0}' from the recent projects list? " - "The project files will not be deleted.").format(projName) + self.tr( + "Remove '{0}' from the recent projects list? " + "The project files will not be deleted." + ).format(projName) ) if msgYes: self.mainConf.removeFromRecentCache( diff --git a/nw/gui/projsettings.py b/nw/gui/projsettings.py index 71cc7043..4a3a6898 100644 --- a/nw/gui/projsettings.py +++ b/nw/gui/projsettings.py @@ -378,7 +378,8 @@ class GuiProjectEditStatus(QWidget): self.colData[selIdx][4] ) selItem.setText(self.tr("{0} [{1}]").format( - self.colData[selIdx][0], self.colCounts[selIdx])) + self.colData[selIdx][0], self.colCounts[selIdx]) + ) selItem.setIcon(self.colButton.icon()) self.editName.setEnabled(False) self.colChanged = True @@ -496,7 +497,8 @@ class GuiProjectEditReplace(QWidget): self.bottomBox.addWidget(self.delButton) self.outerBox.addWidget( - QLabel("%s" % self.tr("Text Replace List for Preview and Export"))) + QLabel("%s" % self.tr("Text Replace List for Preview and Export")) + ) self.outerBox.addWidget(self.listBox) self.outerBox.addLayout(self.bottomBox) self.setLayout(self.outerBox) @@ -548,7 +550,7 @@ class GuiProjectEditReplace(QWidget): saveKey = self._stripNotAllowed(newKey) if len(saveKey) > 0 and len(newVal) > 0: - selItem.setText(0, self.tr("<{0}>").format(saveKey)) + selItem.setText(0, "<%s>" % saveKey) selItem.setText(1, newVal) self.editKey.clear() self.editValue.clear() diff --git a/nw/gui/projtree.py b/nw/gui/projtree.py index 02b8228c..a9d08ed1 100644 --- a/nw/gui/projtree.py +++ b/nw/gui/projtree.py @@ -30,7 +30,7 @@ import logging from time import time -from PyQt5.QtCore import QCoreApplication, Qt, QSize, pyqtSignal +from PyQt5.QtCore import Qt, QSize, pyqtSignal from PyQt5.QtGui import QIcon from PyQt5.QtWidgets import ( QTreeWidget, QTreeWidgetItem, QAbstractItemView, QMenu, QAction @@ -38,7 +38,8 @@ from PyQt5.QtWidgets import ( from nw.core import NWDoc from nw.constants import ( - nwLabels, nwItemType, nwItemClass, nwItemLayout, nwAlert, nwConst, nwLists + trConst, nwLabels, nwItemType, nwItemClass, nwItemLayout, nwAlert, + nwConst, nwLists ) logger = logging.getLogger(__name__) @@ -214,8 +215,7 @@ class GuiProjectTree(QTreeWidget): ) if itemType == nwItemType.ROOT: - tHandle = self.theProject.newRoot( - QCoreApplication.translate("Constant", nwLabels.CLASS_NAME[itemClass]), itemClass) + tHandle = self.theProject.newRoot(trConst(nwLabels.CLASS_NAME[itemClass]), itemClass) if tHandle is None: logger.error("No root item added") return False @@ -250,8 +250,7 @@ class GuiProjectTree(QTreeWidget): if self.theProject.projTree.isTrashRoot(pHandle): self.makeAlert( self.tr("Cannot add new files or folders to the {0} folder.").format( - QCoreApplication.translate( - "Constant", nwLabels.CLASS_NAME[nwItemClass.TRASH]) + trConst(nwLabels.CLASS_NAME[nwItemClass.TRASH]) ), nwAlert.ERROR ) return False @@ -573,10 +572,10 @@ class GuiProjectTree(QTreeWidget): self._deleteTreeItem(tHandle) self._setTreeChanged(True) else: - self.makeAlert(( - self.tr("Cannot delete folder. It is not empty."), - self.tr("Recursive deletion is not supported."), - self.tr("Please delete the content first."), + self.makeAlert(self.tr( + "Cannot delete folder. It is not empty. " + "Recursive deletion is not supported. " + "Please delete the content first." ), nwAlert.ERROR) return False @@ -589,10 +588,10 @@ class GuiProjectTree(QTreeWidget): self.theParent.mainMenu.setAvailableRoot() self._setTreeChanged(True) else: - self.makeAlert(( - self.tr("Cannot delete root folder. It is not empty."), - self.tr("Recursive deletion is not supported."), - self.tr("Please delete the content first."), + self.makeAlert(self.tr( + "Cannot delete root folder. It is not empty. " + "Recursive deletion is not supported. " + "Please delete the content first." ), nwAlert.ERROR) return False @@ -974,9 +973,9 @@ class GuiProjectTree(QTreeWidget): self.addTopLevelItem(newItem) else: self.makeAlert( - self.tr("There is nowhere to add item with name '{0}'").format( - nwItem.itemName), - nwAlert.ERROR + self.tr( + "There is nowhere to add item with name '{0}'").format(nwItem.itemName + ), nwAlert.ERROR ) del self._treeMap[tHandle] return None diff --git a/nw/gui/projwizard.py b/nw/gui/projwizard.py index fc854f16..e3efc484 100644 --- a/nw/gui/projwizard.py +++ b/nw/gui/projwizard.py @@ -28,7 +28,7 @@ import nw import logging import os -from PyQt5.QtCore import QCoreApplication, Qt +from PyQt5.QtCore import Qt from PyQt5.QtWidgets import ( QWizard, QWizardPage, QLabel, QVBoxLayout, QLineEdit, QPlainTextEdit, QPushButton, QFileDialog, QHBoxLayout, QRadioButton, QFormLayout, @@ -36,7 +36,7 @@ from PyQt5.QtWidgets import ( ) from nw.common import makeFileNameSafe -from nw.constants import nwLabels, nwItemClass +from nw.constants import trConst, nwLabels, nwItemClass from nw.gui.custom import QSwitch logger = logging.getLogger(__name__) @@ -96,10 +96,12 @@ class ProjWizardIntroPage(QWizardPage): self.setTitle(self.tr("Create New Project")) self.theText = QLabel( - self.tr("Provide at least a working title. The working title should not " - "be change beyond this point as it is used by the application for " - "generating file names for for instance backups. The other fields " - "are optional and can be changed at any time in Project Settings.") + self.tr( + "Provide at least a working title. The working title should not " + "be change beyond this point as it is used by the application for " + "generating file names for for instance backups. The other fields " + "are optional and can be changed at any time in Project Settings." + ) ) self.theText.setWordWrap(True) @@ -166,8 +168,10 @@ class ProjWizardFolderPage(QWizardPage): self.setTitle(self.tr("Select Project Folder")) self.theText = QLabel( - self.tr("Select a location to store the project. A new project folder " - "will be created in the selected location.") + self.tr( + "Select a location to store the project. A new project folder " + "will be created in the selected location." + ) ) self.theText.setWordWrap(True) @@ -240,9 +244,11 @@ class ProjWizardPopulatePage(QWizardPage): self.setTitle(self.tr("Populate Project")) self.theText = QLabel( - self.tr("Choose how to pre-fill the project. Either with a minimal set of " - "starter items, an example project explaining and showing many of " - "the features, or show further custom options on the next page.") + self.tr( + "Choose how to pre-fill the project. Either with a minimal set of " + "starter items, an example project explaining and showing many of " + "the features, or show further custom options on the next page." + ) ) self.theText.setWordWrap(True) @@ -295,9 +301,11 @@ class ProjWizardCustomPage(QWizardPage): self.setTitle(self.tr("Custom Project Options")) self.theText = QLabel( - self.tr("Select which additional root folders to make, and how to populate " - "the Novel folder. If you don't want to add chapters or scenes, set " - "the values to 0. You can add scenes without chapters.") + self.tr( + "Select which additional root folders to make, and how to populate " + "the Novel folder. If you don't want to add chapters or scenes, set " + "the values to 0. You can add scenes without chapters." + ) ) self.theText.setWordWrap(True) @@ -308,18 +316,24 @@ class ProjWizardCustomPage(QWizardPage): self.rootForm = QGridLayout() self.rootGroup.setLayout(self.rootForm) - self.lblPlot = QLabel(self.tr("{0} folder").format( - QCoreApplication.translate("Constant", nwLabels.CLASS_NAME[nwItemClass.PLOT]))) - self.lblChar = QLabel(self.tr("{0} folder").format( - QCoreApplication.translate("Constant", nwLabels.CLASS_NAME[nwItemClass.CHARACTER]))) - self.lblWorld = QLabel(self.tr("{0} folder").format( - QCoreApplication.translate("Constant", nwLabels.CLASS_NAME[nwItemClass.WORLD]))) - self.lblTime = QLabel(self.tr("{0} folder").format( - QCoreApplication.translate("Constant", nwLabels.CLASS_NAME[nwItemClass.TIMELINE]))) + self.lblPlot = QLabel(self.tr("{0} folder").format( + trConst(nwLabels.CLASS_NAME[nwItemClass.PLOT])) + ) + self.lblChar = QLabel(self.tr("{0} folder").format( + trConst(nwLabels.CLASS_NAME[nwItemClass.CHARACTER])) + ) + self.lblWorld = QLabel(self.tr("{0} folder").format( + trConst(nwLabels.CLASS_NAME[nwItemClass.WORLD])) + ) + self.lblTime = QLabel(self.tr("{0} folder").format( + trConst(nwLabels.CLASS_NAME[nwItemClass.TIMELINE])) + ) self.lblObject = QLabel(self.tr("{0} folder").format( - QCoreApplication.translate("Constant", nwLabels.CLASS_NAME[nwItemClass.OBJECT]))) + trConst(nwLabels.CLASS_NAME[nwItemClass.OBJECT])) + ) self.lblEntity = QLabel(self.tr("{0} folder").format( - QCoreApplication.translate("Constant", nwLabels.CLASS_NAME[nwItemClass.ENTITY]))) + trConst(nwLabels.CLASS_NAME[nwItemClass.ENTITY])) + ) self.addPlot = QSwitch() self.addChar = QSwitch() @@ -407,9 +421,10 @@ class ProjWizardFinalPage(QWizardPage): self.setTitle(self.tr("Finished")) self.theText = QLabel("".join([ - ("

%s

" % self.tr("All done.")), - ("

%s

" % self.tr("Press '{0}' to create the new project.").format( - self.tr("Done") if self.mainConf.osDarwin else self.tr("Finish"))) + "

%s

" % self.tr("All done."), + "

%s

" % self.tr("Press '{0}' to create the new project.").format( + self.tr("Done") if self.mainConf.osDarwin else self.tr("Finish") + ) ])) self.theText.setWordWrap(True) diff --git a/nw/gui/theme.py b/nw/gui/theme.py index fc30a839..946a6435 100644 --- a/nw/gui/theme.py +++ b/nw/gui/theme.py @@ -31,8 +31,9 @@ import configparser import os from math import ceil +from functools import partial -from PyQt5.QtCore import Qt +from PyQt5.QtCore import QCoreApplication, Qt from PyQt5.QtWidgets import QStyle, qApp from PyQt5.QtGui import ( QPalette, QColor, QIcon, QFont, QFontMetrics, QFontDatabase, QPixmap @@ -157,6 +158,10 @@ class GuiTheme: logger.verbose("Text 'N' Height: %d" % self.textNHeight) logger.verbose("Text 'N' Width: %d" % self.textNWidth) + # Internal Mapping + self.makeAlert = self.theParent.makeAlert + self.tr = partial(QCoreApplication.translate, "GuiTheme") + return ## @@ -392,7 +397,7 @@ class GuiTheme: with open(themeConf, mode="r", encoding="utf8") as inFile: confParser.read_file(inFile) except Exception as e: - self.theParent.makeAlert( + self.makeAlert( [self.tr("Could not load theme config file."), str(e)], nwAlert.ERROR ) continue @@ -425,7 +430,7 @@ class GuiTheme: with open(syntaxPath, mode="r", encoding="utf8") as inFile: confParser.read_file(inFile) except Exception as e: - self.theParent.makeAlert( + self.makeAlert( [self.tr("Could not load syntax file."), str(e)], nwAlert.ERROR ) return [] @@ -740,7 +745,7 @@ class GuiIcons: with open(themeConf, mode="r", encoding="utf8") as inFile: confParser.read_file(inFile) except Exception as e: - self.theParent.makeAlert( + self.makeAlert( [self.tr("Could not load theme config file."), str(e)], nwAlert.ERROR ) continue diff --git a/nw/gui/writingstats.py b/nw/gui/writingstats.py index 44fd38e5..2a48213a 100644 --- a/nw/gui/writingstats.py +++ b/nw/gui/writingstats.py @@ -159,18 +159,27 @@ class GuiWritingStats(QDialog): self.totalWords.setFont(self.theTheme.guiFontFixed) self.totalWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight) - self.infoForm.addWidget(QLabel(self.tr("{0}:").format(self.tr("Total Time"))), 0, 0) - self.infoForm.addWidget(QLabel(self.tr("{0}:").format(self.tr("Idle Time"))), 1, 0) - self.infoForm.addWidget(QLabel(self.tr("{0}:").format(self.tr("Filtered Time"))), 2, 0) - self.infoForm.addWidget(QLabel(self.tr("{0}:").format(self.tr("Novel Word Count"))), 3, 0) - self.infoForm.addWidget(QLabel(self.tr("{0}:").format(self.tr("Notes Word Count"))), 4, 0) - self.infoForm.addWidget(QLabel(self.tr("{0}:").format(self.tr("Total Word Count"))), 5, 0) + lblTTime = QLabel(self.tr("{0}:").format(self.tr("Total Time"))) + lblITime = QLabel(self.tr("{0}:").format(self.tr("Idle Time"))) + lblFTime = QLabel(self.tr("{0}:").format(self.tr("Filtered Time"))) + lblNvCount = QLabel(self.tr("{0}:").format(self.tr("Novel Word Count"))) + lblNtCount = QLabel(self.tr("{0}:").format(self.tr("Notes Word Count"))) + lblTtCount = QLabel(self.tr("{0}:").format(self.tr("Total Word Count"))) + + self.infoForm.addWidget(lblTTime, 0, 0) + self.infoForm.addWidget(lblITime, 1, 0) + self.infoForm.addWidget(lblFTime, 2, 0) + self.infoForm.addWidget(lblNvCount, 3, 0) + self.infoForm.addWidget(lblNtCount, 4, 0) + self.infoForm.addWidget(lblTtCount, 5, 0) + self.infoForm.addWidget(self.labelTotal, 0, 1) self.infoForm.addWidget(self.labelIdleT, 1, 1) self.infoForm.addWidget(self.labelFilter, 2, 1) self.infoForm.addWidget(self.novelWords, 3, 1) self.infoForm.addWidget(self.notesWords, 4, 1) self.infoForm.addWidget(self.totalWords, 5, 1) + self.infoForm.setRowStretch(6, 1) # Filter Options diff --git a/nw/guimain.py b/nw/guimain.py index 2af04c74..618a95e5 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -358,9 +358,10 @@ class GuiMain(QMainWindow): if os.path.isfile(os.path.join(projPath, self.theProject.projFile)): self.makeAlert( - self.tr("A project already exists in that location. " - "Please choose another folder."), - nwAlert.ERROR + self.tr( + "A project already exists in that location. " + "Please choose another folder." + ), nwAlert.ERROR ) return False @@ -394,8 +395,10 @@ class GuiMain(QMainWindow): if not isYes: msgYes = self.askQuestion( self.tr("Close Project"), - "%s
%s" % (self.tr("Close the current project?"), - self.tr("Changes are saved automatically.")) + "%s
%s" % ( + self.tr("Close the current project?"), + self.tr("Changes are saved automatically.") + ) ) if not msgYes: return False @@ -461,9 +464,11 @@ class GuiMain(QMainWindow): try: lockDetails = ( - "
%s" % self.tr("The project was locked by the computer " - "'{computer_name}' ({os_name} {os_version}), " - "last active on {time}") + "
%s" % self.tr( + "The project was locked by the computer " + "'{computer_name}' ({os_name} {os_version}), " + "last active on {time}" + ) ).format( computer_name = self.theProject.lockedBy[0], os_name = self.theProject.lockedBy[1], @@ -479,12 +484,16 @@ class GuiMain(QMainWindow): msgRes = msgBox.warning( self, self.tr("Project Locked"), "%s

%s
%s" % ( - self.tr("The project is already open by another instance of novelWriter, and " - "is therefore locked. Override lock and continue anyway?"), - self.tr("Note: If the program or the computer previously crashed, the lock " - "can safely be overridden. If, however, another instance of " - "novelWriter has the project open, overriding the lock may corrupt " - "the project, and is not recommended."), + self.tr( + "The project is already open by another instance of novelWriter, and " + "is therefore locked. Override lock and continue anyway?" + ), + self.tr( + "Note: If the program or the computer previously crashed, the lock " + "can safely be overridden. If, however, another instance of " + "novelWriter has the project open, overriding the lock may corrupt " + "the project, and is not recommended." + ), lockDetails ), QMessageBox.Yes | QMessageBox.No, QMessageBox.No @@ -732,10 +741,13 @@ class GuiMain(QMainWindow): return False if not self.docEditor.isEmpty(): - msgYes = self.askQuestion(self.tr("Import Document"), ( - self.tr("Importing the file will overwrite the current content of the document. " - "Do you want to proceed?") - )) + msgYes = self.askQuestion( + self.tr("Import Document"), + self.tr( + "Importing the file will overwrite the current content of the document. " + "Do you want to proceed?" + ) + ) if not msgYes: return False @@ -874,9 +886,12 @@ class GuiMain(QMainWindow): if tItem is not None: self.setStatus(self.tr("{0}: '{1}'").format(self.tr("Indexing"), tItem.itemName)) else: - self.setStatus(self.tr("{0}: {1}").format( - self.tr("Indexing"), - self.tr("Unknown item"))) + self.setStatus( + self.tr("{0}: {1}").format( + self.tr("Indexing"), + self.tr("Unknown item") + ) + ) if tItem is not None and tItem.itemType == nwItemType.FILE: logger.verbose("Scanning: %s" % tItem.itemName) @@ -894,14 +909,16 @@ class GuiMain(QMainWindow): self.treeView.projectWordCount() tEnd = time() - self.setStatus(self.tr("Indexing completed in {0} ms"). - format(f"{(tEnd - tStart)*1000.0:.1f}")) + self.setStatus( + self.tr("Indexing completed in {0} ms").format(f"{(tEnd - tStart)*1000.0:.1f}") + ) self.docEditor.updateTagHighLighting() qApp.restoreOverrideCursor() if not beQuiet: - self.makeAlert(self.tr("The project index has been successfully rebuilt."), - nwAlert.INFO) + self.makeAlert( + self.tr("The project index has been successfully rebuilt."), nwAlert.INFO + ) return True @@ -1153,8 +1170,10 @@ class GuiMain(QMainWindow): if self.hasProject: msgYes = self.askQuestion( self.tr("Exit"), - "%s
%s" % (self.tr("Do you want to exit novelWriter?"), - self.tr("Changes are saved automatically.")) + "%s
%s" % ( + self.tr("Do you want to exit novelWriter?"), + self.tr("Changes are saved automatically.") + ) ) if not msgYes: return False diff --git a/sample/nwProject.nwx b/sample/nwProject.nwx index 3e1756e3..94a7dcc9 100644 --- a/sample/nwProject.nwx +++ b/sample/nwProject.nwx @@ -1,13 +1,13 @@ - + Sample Project Sample Project Jane Smith Jay Doh - 936 + 1019 161 - 46791 + 48272 False @@ -120,7 +120,7 @@ 1810 318 8 - 3 + 1112 Another Scene