Merge branch 'dev' into tokenizer_localisation
This commit is contained in:
+16
-5
@@ -27,6 +27,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
import logging
|
||||
import os
|
||||
|
||||
from functools import partial
|
||||
|
||||
from PyQt5.QtCore import QCoreApplication
|
||||
|
||||
from nw.constants import nwAlert
|
||||
from nw.common import isHandle
|
||||
from nw.constants import nwItemLayout, nwItemClass
|
||||
@@ -48,6 +52,7 @@ class NWDoc():
|
||||
|
||||
# Internal Mapping
|
||||
self.makeAlert = self.theParent.makeAlert
|
||||
self.tr = partial(QCoreApplication.translate, "NWDoc")
|
||||
|
||||
return
|
||||
|
||||
@@ -111,7 +116,7 @@ class NWDoc():
|
||||
theText += inFile.read()
|
||||
|
||||
except Exception as e:
|
||||
self.makeAlert(["Failed to open document file.", str(e)], nwAlert.ERROR)
|
||||
self.makeAlert([self.tr("Failed to open document file."), str(e)], nwAlert.ERROR)
|
||||
# Note: Document must be cleared in case of an io error,
|
||||
# or else the auto-save or save will try to overwrite it
|
||||
# with an empty file. Return None to alert the caller.
|
||||
@@ -124,7 +129,9 @@ class NWDoc():
|
||||
return ""
|
||||
|
||||
if showStatus and not isOrphan:
|
||||
self.theParent.setStatus("Opened Document: %s" % self._theItem.itemName)
|
||||
self.theParent.setStatus(
|
||||
self.tr("Opened Document: {0}").format(self._theItem.itemName)
|
||||
)
|
||||
|
||||
return theText
|
||||
|
||||
@@ -158,7 +165,7 @@ class NWDoc():
|
||||
outFile.write(docMeta)
|
||||
outFile.write(docText)
|
||||
except Exception as e:
|
||||
self.makeAlert(["Could not save document.", str(e)], nwAlert.ERROR)
|
||||
self.makeAlert([self.tr("Could not save document."), str(e)], nwAlert.ERROR)
|
||||
return False
|
||||
|
||||
# If we're here, the file was successfully saved, so we can
|
||||
@@ -168,7 +175,9 @@ class NWDoc():
|
||||
os.rename(docTemp, docPath)
|
||||
|
||||
if self._theItem is not None:
|
||||
self.theParent.setStatus("Saved Document: %s" % self._theItem.itemName)
|
||||
self.theParent.setStatus(
|
||||
self.tr("Saved Document: {0}").format(self._theItem.itemName)
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
@@ -191,7 +200,9 @@ class NWDoc():
|
||||
os.unlink(chkFile)
|
||||
logger.debug("Deleted: %s" % chkFile)
|
||||
except Exception as e:
|
||||
self.makeAlert(["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
|
||||
|
||||
@@ -228,10 +228,6 @@ class NWIndex():
|
||||
|
||||
if self.indexBroken:
|
||||
self.clearIndex()
|
||||
self.theParent.makeAlert(
|
||||
"The project index is outdated or broken. Rebuilding index.",
|
||||
nwAlert.WARN
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
|
||||
+164
-123
@@ -32,6 +32,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
|
||||
@@ -43,7 +46,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__)
|
||||
@@ -99,11 +102,12 @@ class NWProject():
|
||||
self.notesWCount = 0 # Total number of words in note files
|
||||
self.doBackup = True # Run project backup on exit
|
||||
|
||||
# Set Defaults
|
||||
self.clearProject()
|
||||
|
||||
# Internal Mapping
|
||||
self.makeAlert = self.theParent.makeAlert
|
||||
self.tr = partial(QCoreApplication.translate, "NWProject")
|
||||
|
||||
# Set Defaults
|
||||
self.clearProject()
|
||||
|
||||
return
|
||||
|
||||
@@ -159,7 +163,7 @@ class NWProject():
|
||||
trashHandle = self.projTree.trashRoot()
|
||||
if trashHandle is None:
|
||||
newItem = NWItem(self)
|
||||
newItem.setName("Trash")
|
||||
newItem.setName(self.tr("Trash"))
|
||||
newItem.setType(nwItemType.TRASH)
|
||||
newItem.setClass(nwItemClass.TRASH)
|
||||
self.projTree.append(None, None, newItem)
|
||||
@@ -199,7 +203,7 @@ class NWProject():
|
||||
self.autoReplace = {}
|
||||
self.titleFormat = {
|
||||
"title" : r"%title%",
|
||||
"chapter" : r"Chapter %ch%: %title%",
|
||||
"chapter" : self.tr(r"Chapter %ch%: %title%"),
|
||||
"unnumbered" : r"%title%",
|
||||
"scene" : r"* * *",
|
||||
"section" : r"",
|
||||
@@ -207,15 +211,15 @@ class NWProject():
|
||||
self.spellCheck = False
|
||||
self.autoOutline = True
|
||||
self.statusItems = NWStatus()
|
||||
self.statusItems.addEntry("New", (100, 100, 100))
|
||||
self.statusItems.addEntry("Note", (200, 50, 0))
|
||||
self.statusItems.addEntry("Draft", (200, 150, 0))
|
||||
self.statusItems.addEntry("Finished", (50, 200, 0))
|
||||
self.statusItems.addEntry(self.tr("New"), (100, 100, 100))
|
||||
self.statusItems.addEntry(self.tr("Note"), (200, 50, 0))
|
||||
self.statusItems.addEntry(self.tr("Draft"), (200, 150, 0))
|
||||
self.statusItems.addEntry(self.tr("Finished"), (50, 200, 0))
|
||||
self.importItems = NWStatus()
|
||||
self.importItems.addEntry("New", (100, 100, 100))
|
||||
self.importItems.addEntry("Minor", (200, 50, 0))
|
||||
self.importItems.addEntry("Major", (200, 150, 0))
|
||||
self.importItems.addEntry("Main", (50, 200, 0))
|
||||
self.importItems.addEntry(self.tr("New"), (100, 100, 100))
|
||||
self.importItems.addEntry(self.tr("Minor"), (200, 50, 0))
|
||||
self.importItems.addEntry(self.tr("Major"), (200, 150, 0))
|
||||
self.importItems.addEntry(self.tr("Main"), (50, 200, 0))
|
||||
self.lastEdited = None
|
||||
self.lastViewed = None
|
||||
self.lastWCount = 0
|
||||
@@ -241,7 +245,7 @@ class NWProject():
|
||||
|
||||
# Project Settings
|
||||
projPath = projData.get("projPath", None)
|
||||
projName = projData.get("projName", "New Project")
|
||||
projName = projData.get("projName", self.tr("New Project"))
|
||||
projTitle = projData.get("projTitle", "")
|
||||
projAuthors = projData.get("projAuthors", "")
|
||||
|
||||
@@ -258,7 +262,7 @@ class NWProject():
|
||||
|
||||
titlePage = "# %s\n\n" % (self.bookTitle if self.bookTitle else self.projName)
|
||||
if self.bookAuthors:
|
||||
titlePage = "%sBy %s\n" % (titlePage, ", ".join(self.bookAuthors))
|
||||
titlePage = "%s%s %s\n" % (titlePage, self.tr("By"), self.getAuthors())
|
||||
|
||||
# Document object for writing files
|
||||
aDoc = NWDoc(self, self.theParent)
|
||||
@@ -267,14 +271,14 @@ class NWProject():
|
||||
# Creating a minimal project with a few root folders and a
|
||||
# single chapter folder with a single file.
|
||||
xHandle = {}
|
||||
xHandle[1] = self.newRoot("Novel", nwItemClass.NOVEL)
|
||||
xHandle[2] = self.newRoot("Plot", nwItemClass.PLOT)
|
||||
xHandle[3] = self.newRoot("Characters", nwItemClass.CHARACTER)
|
||||
xHandle[4] = self.newRoot("World", nwItemClass.WORLD)
|
||||
xHandle[5] = self.newFile("Title Page", nwItemClass.NOVEL, xHandle[1])
|
||||
xHandle[6] = self.newFolder("New Chapter", nwItemClass.NOVEL, xHandle[1])
|
||||
xHandle[7] = self.newFile("New Chapter", nwItemClass.NOVEL, xHandle[6])
|
||||
xHandle[8] = self.newFile("New Scene", nwItemClass.NOVEL, xHandle[6])
|
||||
xHandle[1] = self.newRoot(self.tr("Novel"), nwItemClass.NOVEL)
|
||||
xHandle[2] = self.newRoot(self.tr("Plot"), nwItemClass.PLOT)
|
||||
xHandle[3] = self.newRoot(self.tr("Characters"), nwItemClass.CHARACTER)
|
||||
xHandle[4] = self.newRoot(self.tr("World"), nwItemClass.WORLD)
|
||||
xHandle[5] = self.newFile(self.tr("Title Page"), nwItemClass.NOVEL, xHandle[1])
|
||||
xHandle[6] = self.newFolder(self.tr("New Chapter"), nwItemClass.NOVEL, xHandle[1])
|
||||
xHandle[7] = self.newFile(self.tr("New Chapter"), nwItemClass.NOVEL, xHandle[6])
|
||||
xHandle[8] = self.newFile(self.tr("New Scene"), nwItemClass.NOVEL, xHandle[6])
|
||||
|
||||
self.projTree.setFileItemLayout(xHandle[5], nwItemLayout.TITLE)
|
||||
self.projTree.setFileItemLayout(xHandle[7], nwItemLayout.CHAPTER)
|
||||
@@ -284,11 +288,11 @@ class NWProject():
|
||||
aDoc.clearDocument()
|
||||
|
||||
aDoc.openDocument(xHandle[7], showStatus=False)
|
||||
aDoc.saveDocument("## New Chapter\n\n")
|
||||
aDoc.saveDocument("## %s\n\n" % self.tr("New Chapter"))
|
||||
aDoc.clearDocument()
|
||||
|
||||
aDoc.openDocument(xHandle[8], showStatus=False)
|
||||
aDoc.saveDocument("### New Scene\n\n")
|
||||
aDoc.saveDocument("### %s\n\n" % self.tr("New Scene"))
|
||||
aDoc.clearDocument()
|
||||
|
||||
elif popCustom:
|
||||
@@ -297,13 +301,13 @@ class NWProject():
|
||||
# wizard's custom page.
|
||||
|
||||
# Create root folders
|
||||
nHandle = self.newRoot("Novel", nwItemClass.NOVEL)
|
||||
nHandle = self.newRoot(self.tr("Novel"), nwItemClass.NOVEL)
|
||||
for newRoot in projData.get("addRoots", []):
|
||||
if newRoot in nwItemClass:
|
||||
self.newRoot(nwLabels.CLASS_NAME[newRoot], newRoot)
|
||||
self.newRoot(trConst(nwLabels.CLASS_NAME[newRoot]), newRoot)
|
||||
|
||||
# Create a title page
|
||||
tHandle = self.newFile("Title Page", nwItemClass.NOVEL, nHandle)
|
||||
tHandle = self.newFile(self.tr("Title Page"), nwItemClass.NOVEL, nHandle)
|
||||
self.projTree.setFileItemLayout(tHandle, nwItemLayout.TITLE)
|
||||
|
||||
aDoc.openDocument(tHandle, showStatus=False)
|
||||
@@ -318,7 +322,7 @@ class NWProject():
|
||||
# Create chapters
|
||||
if numChapters > 0:
|
||||
for ch in range(numChapters):
|
||||
chTitle = "Chapter %d" % (ch+1)
|
||||
chTitle = self.tr("Chapter {0}").format(f"{ch+1:d}")
|
||||
pHandle = nHandle
|
||||
if chFolders:
|
||||
pHandle = self.newFolder(chTitle, nwItemClass.NOVEL, nHandle)
|
||||
@@ -333,7 +337,7 @@ class NWProject():
|
||||
# Create chapter scenes
|
||||
if numScenes > 0:
|
||||
for sc in range(numScenes):
|
||||
scTitle = "Scene %d.%d" % (ch+1, sc+1)
|
||||
scTitle = self.tr("Scene {0}").format(f"{ch+1:d}.{sc+1:d}")
|
||||
sHandle = self.newFile(scTitle, nwItemClass.NOVEL, pHandle)
|
||||
|
||||
aDoc.openDocument(sHandle, showStatus=False)
|
||||
@@ -343,7 +347,7 @@ class NWProject():
|
||||
# Create scenes (no chapters)
|
||||
elif numScenes > 0:
|
||||
for sc in range(numScenes):
|
||||
scTitle = "Scene %d" % (sc+1)
|
||||
scTitle = self.tr("Scene {0}").format(f"{sc+1:d}")
|
||||
sHandle = self.newFile(scTitle, nwItemClass.NOVEL, nHandle)
|
||||
|
||||
aDoc.openDocument(sHandle, showStatus=False)
|
||||
@@ -367,7 +371,7 @@ class NWProject():
|
||||
if not os.path.isfile(fileName):
|
||||
fileName = os.path.join(fileName, nwFiles.PROJ_FILE)
|
||||
if not os.path.isfile(fileName):
|
||||
self.makeAlert("File not found: %s" % fileName, nwAlert.ERROR)
|
||||
self.makeAlert(self.tr("File not found: {0}").format(fileName), nwAlert.ERROR)
|
||||
return False
|
||||
|
||||
self.clearProject()
|
||||
@@ -416,16 +420,20 @@ class NWProject():
|
||||
try:
|
||||
nwXML = etree.parse(fileName)
|
||||
except Exception as e:
|
||||
self.makeAlert(["Failed to parse project xml.", str(e)], nwAlert.ERROR)
|
||||
self.makeAlert([self.tr("Failed to parse project xml."), str(e)], nwAlert.ERROR)
|
||||
|
||||
# Trying to open backup file instead
|
||||
backFile = fileName[:-3]+"bak"
|
||||
if os.path.isfile(backFile):
|
||||
self.makeAlert("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(["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:
|
||||
@@ -435,9 +443,9 @@ class NWProject():
|
||||
xRoot = nwXML.getroot()
|
||||
nwxRoot = xRoot.tag
|
||||
|
||||
appVersion = xRoot.attrib.get("appVersion", "Unknown")
|
||||
appVersion = xRoot.attrib.get("appVersion", self.tr("Unknown"))
|
||||
hexVersion = xRoot.attrib.get("hexVersion", "0x0")
|
||||
fileVersion = xRoot.attrib.get("fileVersion", "Unknown")
|
||||
fileVersion = xRoot.attrib.get("fileVersion", self.tr("Unknown"))
|
||||
|
||||
# The following are deprecated and will be removed
|
||||
# The settings have been moved to the <project> tag
|
||||
@@ -453,7 +461,7 @@ class NWProject():
|
||||
|
||||
if not nwxRoot == "novelWriterXML":
|
||||
self.makeAlert(
|
||||
"Project file does not appear to be a novelWriterXML file.",
|
||||
self.tr("Project file does not appear to be a novelWriterXML file."),
|
||||
nwAlert.ERROR
|
||||
)
|
||||
self.clearProject()
|
||||
@@ -472,24 +480,32 @@ class NWProject():
|
||||
# read the file. Introduced in version 0.10.
|
||||
|
||||
if fileVersion == "1.0":
|
||||
msgYes = self.theParent.askQuestion("Old Project Version", (
|
||||
"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?<br><br>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."
|
||||
))
|
||||
msgYes = self.theParent.askQuestion(
|
||||
self.tr("Old Project Version"),
|
||||
"%s<br><br>%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."
|
||||
)
|
||||
)
|
||||
)
|
||||
if not msgYes:
|
||||
self.clearProject()
|
||||
return False
|
||||
|
||||
elif fileVersion != "1.1" and fileVersion != "1.2":
|
||||
self.makeAlert((
|
||||
"Unknown or unsupported novelWriter project file format. "
|
||||
"The project cannot be opened by this version of novelWriter. "
|
||||
"The file was saved with novelWriter version {vers:s}."
|
||||
).format(
|
||||
vers = 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
|
||||
@@ -498,14 +514,15 @@ class NWProject():
|
||||
# =========================
|
||||
|
||||
if hexToInt(hexVersion) > hexToInt(nw.__hexversion__):
|
||||
msgYes = self.theParent.askQuestion("Version Conflict", (
|
||||
"This project was saved by a newer version of novelWriter, version %s. "
|
||||
"This is version %s. 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?"
|
||||
) % (
|
||||
appVersion, nw.__version__
|
||||
))
|
||||
msgYes = self.theParent.askQuestion(
|
||||
self.tr("Version Conflict"),
|
||||
self.tr(
|
||||
"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?"
|
||||
).format(appVersion, nw.__version__)
|
||||
)
|
||||
if not msgYes:
|
||||
self.clearProject()
|
||||
return False
|
||||
@@ -604,7 +621,7 @@ class NWProject():
|
||||
self.mainConf.updateRecentCache(self.projPath, self.projName, self.lastWCount, time())
|
||||
self.mainConf.saveRecentCache()
|
||||
|
||||
self.theParent.setStatus("Opened Project: %s" % self.projName)
|
||||
self.theParent.setStatus(self.tr("Opened Project: {0}").format(self.projName))
|
||||
|
||||
self._scanProjectFolder()
|
||||
self.loadProjectLocalisation(self.projLang)
|
||||
@@ -626,7 +643,7 @@ class NWProject():
|
||||
"""
|
||||
if self.projPath is None:
|
||||
self.makeAlert(
|
||||
"Project path not set, cannot save project.", nwAlert.ERROR
|
||||
self.tr("Project path not set, cannot save project."), nwAlert.ERROR
|
||||
)
|
||||
return False
|
||||
|
||||
@@ -705,7 +722,7 @@ class NWProject():
|
||||
xml_declaration = True
|
||||
))
|
||||
except Exception as e:
|
||||
self.makeAlert(["Failed to save project.", str(e)], nwAlert.ERROR)
|
||||
self.makeAlert([self.tr("Failed to save project."), str(e)], nwAlert.ERROR)
|
||||
return False
|
||||
|
||||
# If we're here, the file was successfully saved,
|
||||
@@ -724,7 +741,7 @@ class NWProject():
|
||||
self.mainConf.saveRecentCache()
|
||||
|
||||
self._writeLockFile()
|
||||
self.theParent.setStatus("Saved Project: %s" % self.projName)
|
||||
self.theParent.setStatus(self.tr("Saved Project: {0}").format(self.projName))
|
||||
self.setProjectChanged(False)
|
||||
|
||||
return True
|
||||
@@ -776,27 +793,33 @@ class NWProject():
|
||||
return False
|
||||
|
||||
logger.info("Backing up project")
|
||||
self.theParent.setStatus("Backing up project ...")
|
||||
self.theParent.setStatus(self.tr("Backing up project ..."))
|
||||
|
||||
if self.mainConf.backupPath is None or self.mainConf.backupPath == "":
|
||||
self.theParent.makeAlert((
|
||||
"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((
|
||||
"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((
|
||||
"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)
|
||||
@@ -807,20 +830,22 @@ class NWProject():
|
||||
logger.debug("Created folder %s" % baseDir)
|
||||
except Exception as e:
|
||||
self.theParent.makeAlert(
|
||||
["Could not create backup folder.", str(e)],
|
||||
[self.tr("Could not create backup folder."), str(e)],
|
||||
nwAlert.ERROR
|
||||
)
|
||||
return False
|
||||
|
||||
if os.path.commonpath([self.projPath, baseDir]) == self.projPath:
|
||||
self.theParent.makeAlert((
|
||||
"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 = "Backup from %s" % formatTimeStamp(time(), fileSafe=True)
|
||||
archName = self.tr("Backup from {0}").format(formatTimeStamp(time(), fileSafe=True))
|
||||
baseName = os.path.join(baseDir, archName)
|
||||
|
||||
try:
|
||||
@@ -830,18 +855,21 @@ class NWProject():
|
||||
logger.info("Backup written to: %s" % archName)
|
||||
if doNotify:
|
||||
self.theParent.makeAlert(
|
||||
"Backup archive file written to: %s.zip" % os.path.join(cleanName, archName),
|
||||
nwAlert.INFO
|
||||
self.tr(
|
||||
"Backup archive file written to: {0}"
|
||||
).format(
|
||||
f"{os.path.join(cleanName, archName)}.zip"
|
||||
), nwAlert.INFO
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
self.theParent.makeAlert(
|
||||
["Could not write backup archive.", str(e)],
|
||||
[self.tr("Could not write backup archive."), str(e)],
|
||||
nwAlert.ERROR
|
||||
)
|
||||
return False
|
||||
|
||||
self.theParent.setStatus("Project backed up to '%s.zip'" % baseName)
|
||||
self.theParent.setStatus(self.tr("Project backed up to '{0}'").format(f"{baseName}.zip"))
|
||||
|
||||
return True
|
||||
|
||||
@@ -868,7 +896,7 @@ class NWProject():
|
||||
isSuccess = True
|
||||
except Exception as e:
|
||||
self.makeAlert(
|
||||
["Failed to create a new example project.", str(e)], nwAlert.ERROR
|
||||
[self.tr("Failed to create a new example project."), str(e)], nwAlert.ERROR
|
||||
)
|
||||
|
||||
elif os.path.isdir(srcSample):
|
||||
@@ -890,14 +918,16 @@ class NWProject():
|
||||
|
||||
except Exception as e:
|
||||
self.makeAlert(
|
||||
["Failed to create a new example project.", str(e)], nwAlert.ERROR
|
||||
[self.tr("Failed to create a new example project."), str(e)], nwAlert.ERROR
|
||||
)
|
||||
|
||||
else:
|
||||
self.makeAlert((
|
||||
"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()
|
||||
@@ -928,16 +958,18 @@ class NWProject():
|
||||
logger.debug("Created folder %s" % projPath)
|
||||
except Exception as e:
|
||||
self.theParent.makeAlert((
|
||||
["Could not create new project folder.", str(e)]
|
||||
[self.tr("Could not create new project folder."), str(e)]
|
||||
), nwAlert.ERROR)
|
||||
return False
|
||||
|
||||
if os.path.isdir(projPath):
|
||||
if os.listdir(self.projPath):
|
||||
self.theParent.makeAlert((
|
||||
"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()
|
||||
@@ -984,17 +1016,21 @@ class NWProject():
|
||||
self.doBackup = doBackup
|
||||
if doBackup:
|
||||
if not os.path.isdir(self.mainConf.backupPath):
|
||||
self.theParent.makeAlert((
|
||||
"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((
|
||||
"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
|
||||
@@ -1124,8 +1160,8 @@ class NWProject():
|
||||
if nAuth == 1:
|
||||
authString = self.bookAuthors[0]
|
||||
elif nAuth > 1:
|
||||
authString = "%s and %s" % (
|
||||
", ".join(self.bookAuthors[0:-1]), self.bookAuthors[-1]
|
||||
authString = "%s %s %s" % (
|
||||
", ".join(self.bookAuthors[0:-1]), self.tr("and"), self.bookAuthors[-1]
|
||||
)
|
||||
|
||||
return authString
|
||||
@@ -1374,7 +1410,7 @@ class NWProject():
|
||||
# Report status
|
||||
if len(orphanFiles) > 0:
|
||||
self.makeAlert(
|
||||
"Found %d orphaned file(s) in project folder." % len(orphanFiles),
|
||||
self.tr("Found {0} orphaned file(s) in project folder.").format(len(orphanFiles)),
|
||||
nwAlert.WARN
|
||||
)
|
||||
else:
|
||||
@@ -1385,6 +1421,7 @@ class NWProject():
|
||||
aDoc = NWDoc(self, self.theParent)
|
||||
nOrph = 0
|
||||
noWhere = False
|
||||
oPrefix = self.tr("Recovered")
|
||||
for oHandle in orphanFiles:
|
||||
|
||||
# Look for meta data
|
||||
@@ -1396,10 +1433,12 @@ class NWProject():
|
||||
oName, oParent, oClass, oLayout = aDoc.getMeta()
|
||||
|
||||
if oName:
|
||||
oName = "Recovered: %s" % oName.lstrip("Recovered: ")
|
||||
oName = self.tr("[{0}] {1}").format(
|
||||
oPrefix, oName.replace("[%s]" % oPrefix, "").strip()
|
||||
)
|
||||
else:
|
||||
nOrph += 1
|
||||
oName = "Recovered File %d" % nOrph
|
||||
oName = self.tr("Recovered File {0}").format(nOrph)
|
||||
|
||||
# Recover file meta data
|
||||
if oClass is None:
|
||||
@@ -1426,10 +1465,12 @@ class NWProject():
|
||||
self.projTree.append(oHandle, oParent, orphItem)
|
||||
|
||||
if noWhere:
|
||||
self.makeAlert((
|
||||
"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
|
||||
|
||||
@@ -1476,7 +1517,7 @@ class NWProject():
|
||||
"""
|
||||
theData = os.path.join(self.projPath, theFolder)
|
||||
if not os.path.isdir(theData):
|
||||
errList.append("Not a folder: %s" % theData)
|
||||
errList.append(self.tr("Not a folder: {0}").format(theData))
|
||||
return errList
|
||||
|
||||
logger.info("Old data folder %s found" % theFolder)
|
||||
@@ -1499,7 +1540,7 @@ class NWProject():
|
||||
logger.info("Moved file: %s" % theFile)
|
||||
logger.info("New location: %s" % newPath)
|
||||
except Exception:
|
||||
errList.append("Could not move: %s" % theFile)
|
||||
errList.append(self.tr("Could not move: {0}").format(theFile))
|
||||
logger.error("Could not move: %s" % theFile)
|
||||
nw.logException()
|
||||
|
||||
@@ -1508,7 +1549,7 @@ class NWProject():
|
||||
os.unlink(theFile)
|
||||
logger.info("Deleted file: %s" % theFile)
|
||||
except Exception:
|
||||
errList.append("Could not delete: %s" % theFile)
|
||||
errList.append(self.tr("Could not delete: {0}").format(theFile))
|
||||
logger.error("Could not delete: %s" % theFile)
|
||||
nw.logException()
|
||||
|
||||
@@ -1521,10 +1562,10 @@ class NWProject():
|
||||
# ==================
|
||||
try:
|
||||
os.rmdir(theData)
|
||||
logger.info("Removed folder: %s" % theFolder)
|
||||
logger.info("Deleted folder: %s" % theFolder)
|
||||
except Exception:
|
||||
errList.append("Failed to remove: %s" % theFolder)
|
||||
logger.error("Failed to remove: %s" % theFolder)
|
||||
errList.append(self.tr("Could not delete: {0}").format(theFolder))
|
||||
logger.error("Could not delete: %s" % theFolder)
|
||||
nw.logException()
|
||||
|
||||
return errList
|
||||
@@ -1535,7 +1576,7 @@ class NWProject():
|
||||
"""
|
||||
theJunk = os.path.join(self.projPath, "junk")
|
||||
if not self._checkFolder(theJunk):
|
||||
return "Could not make folder: %s" % theJunk
|
||||
return self.tr("Could not make folder: {0}").format(theJunk)
|
||||
|
||||
theSrc = os.path.join(theDir, theItem)
|
||||
theDst = os.path.join(theJunk, theItem)
|
||||
@@ -1546,7 +1587,7 @@ class NWProject():
|
||||
except Exception:
|
||||
logger.error("Could not move item %s to junk." % theSrc)
|
||||
nw.logException()
|
||||
return "Could not move item %s to junk." % theSrc
|
||||
return self.tr("Could not move item {0} to {1}.").format(theSrc, theJunk)
|
||||
|
||||
return ""
|
||||
|
||||
|
||||
+4
-17
@@ -29,8 +29,6 @@ import logging
|
||||
import os
|
||||
import difflib
|
||||
|
||||
from nw.constants import nwConst, isoLanguage
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# =============================================================================================== #
|
||||
@@ -89,16 +87,6 @@ class NWSpellCheck():
|
||||
"""
|
||||
return "", ""
|
||||
|
||||
@staticmethod
|
||||
def expandLanguage(spTag):
|
||||
"""Translate a language tag to something more user friendly.
|
||||
"""
|
||||
spBits = spTag.split("_")
|
||||
spLang = isoLanguage.ISO_639_1.get(spBits[0], spBits[0])
|
||||
if len(spBits) > 1:
|
||||
spLang += " (%s)" % spBits[1]
|
||||
return spLang
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
@@ -196,10 +184,10 @@ class NWSpellEnchant(NWSpellCheck):
|
||||
try:
|
||||
import enchant
|
||||
for spTag, spProvider in enchant.list_dicts():
|
||||
spName = "%s [%s]" % (self.expandLanguage(spTag), spProvider.name)
|
||||
retList.append((spTag, spName))
|
||||
retList.append((spTag, spProvider.name))
|
||||
except Exception:
|
||||
logger.error("Failed to list languages for enchant spell checking")
|
||||
|
||||
return retList
|
||||
|
||||
def describeDict(self):
|
||||
@@ -332,8 +320,7 @@ class NWSpellSimple(NWSpellCheck):
|
||||
if fExt != ".dict":
|
||||
continue
|
||||
|
||||
spName = "%s [%s]" % (self.expandLanguage(fRoot), nwConst.SP_INTERNAL)
|
||||
retList.append((fRoot, spName))
|
||||
retList.append((fRoot, "difflib"))
|
||||
|
||||
return retList
|
||||
|
||||
@@ -341,6 +328,6 @@ class NWSpellSimple(NWSpellCheck):
|
||||
"""Return the tag and provider of the currently loaded
|
||||
dictionary.
|
||||
"""
|
||||
return self.theLang, nwConst.SP_INTERNAL
|
||||
return self.theLang, ""
|
||||
|
||||
# END Class NWSpellSimple
|
||||
|
||||
@@ -28,6 +28,7 @@ import logging
|
||||
import re
|
||||
|
||||
from operator import itemgetter
|
||||
|
||||
from PyQt5.QtCore import QRegularExpression
|
||||
|
||||
from nw.core.document import NWDoc
|
||||
@@ -281,10 +282,10 @@ class Tokenizer():
|
||||
|
||||
docSize = len(self.theText)
|
||||
if docSize > nwConst.MAX_DOCSIZE:
|
||||
errVal = "Document '%s' is too big (%.2f MB). Skipping." % (
|
||||
self.theItem.itemName, docSize/1.0e6
|
||||
errVal = self.tr("Document '{0}' is too big ({1} MB). Skipping.").format(
|
||||
self.theItem.itemName, f"{docSize/1.0e6:.2f}"
|
||||
)
|
||||
self.theText = "# ERROR\n\n%s\n\n" % errVal
|
||||
self.theText = "# %s\n\n%s\n\n" % (self.tr("ERROR"), errVal)
|
||||
self.errData.append(errVal)
|
||||
|
||||
self.isNone = self.theItem.itemLayout == nwItemLayout.NO_LAYOUT
|
||||
|
||||
+91
-41
@@ -120,35 +120,40 @@ def numberToRoman(numVal, isLower=False):
|
||||
return romNum.lower() if isLower else romNum
|
||||
|
||||
# =============================================================================================== #
|
||||
# Convert an Integer to a Word Number
|
||||
# Convert an Integer to a Number String
|
||||
# =============================================================================================== #
|
||||
|
||||
def numberToWord(numVal, theLanguage):
|
||||
def numberToWord(numVal, theLang):
|
||||
"""Wrapper for converting numbers to words for chapter headings.
|
||||
"""
|
||||
if not isinstance(numVal, int):
|
||||
return "[NaN]"
|
||||
|
||||
if numVal < 0:
|
||||
return "[<0]"
|
||||
|
||||
if numVal > 999:
|
||||
return "[>999]"
|
||||
|
||||
numWord = ""
|
||||
if theLanguage == "en":
|
||||
numWord = _numberToWordEN(numVal)
|
||||
theCode = theLang.split("_")[0]
|
||||
if theCode == "en":
|
||||
numWord = _numberToWord_EN(numVal)
|
||||
elif theCode in ("nb", "nn"):
|
||||
numWord = _numberToWord_NO(numVal, theCode)
|
||||
else:
|
||||
numWord = _numberToWordEN(numVal)
|
||||
# Default to return the number as a string
|
||||
numWord = str(numVal)
|
||||
|
||||
return numWord
|
||||
|
||||
def _numberToWordEN(numVal):
|
||||
def _numberToWord_EN(numVal):
|
||||
"""Convert numbers to English words.
|
||||
"""
|
||||
oneWord = ""
|
||||
tenWord = ""
|
||||
hunWord = ""
|
||||
|
||||
if not isinstance(numVal, int):
|
||||
return "[NaN]"
|
||||
|
||||
if numVal < 0:
|
||||
return "[Negative]"
|
||||
|
||||
if numVal > 999:
|
||||
return "[Out of Range]"
|
||||
|
||||
if numVal == 0:
|
||||
return "Zero"
|
||||
|
||||
@@ -156,38 +161,83 @@ def _numberToWordEN(numVal):
|
||||
tenVal = (numVal - oneVal) % 100
|
||||
hunVal = (numVal - tenVal - oneVal) % 1000
|
||||
|
||||
theHundreds = {
|
||||
100: "One Hundred", 200: "Two Hundred", 300: "Three Hundred",
|
||||
400: "Four Hundred", 500: "Five Hundred", 600: "Six Hundred",
|
||||
700: "Seven Hundred", 800: "Eight Hundred", 900: "Nine Hundred",
|
||||
}
|
||||
theTens = {
|
||||
20: "Twenty", 30: "Thirty", 40: "Forty", 50: "Fifty",
|
||||
60: "Sixty", 70: "Seventy", 80: "Eighty", 90: "Ninety",
|
||||
}
|
||||
theTeens = {
|
||||
0: "Ten", 1: "Eleven", 2: "Twelve", 3: "Thirteen", 4: "Fourteen",
|
||||
5: "Fifteen", 6: "Sixteen", 7: "Seventeen", 8: "Eighteen", 9: "Nineteen",
|
||||
}
|
||||
theOnes = {
|
||||
0: "", 1: "One", 2: "Two", 3: "Three", 4: "Four",
|
||||
5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine",
|
||||
theWords = {
|
||||
0: "", 1: "One", 2: "Two", 3: "Three", 4: "Four", 5: "Five",
|
||||
6: "Six", 7: "Seven", 8: "Eight", 9: "Nine", 10: "Ten",
|
||||
11: "Eleven", 12: "Twelve", 13: "Thirteen", 14: "Fourteen",
|
||||
15: "Fifteen", 16: "Sixteen", 17: "Seventeen", 18: "Eighteen",
|
||||
19: "Nineteen", 20: "Twenty", 30: "Thirty", 40: "Forty",
|
||||
50: "Fifty", 60: "Sixty", 70: "Seventy", 80: "Eighty",
|
||||
90: "Ninety", 100: "One Hundred", 200: "Two Hundred",
|
||||
300: "Three Hundred", 400: "Four Hundred", 500: "Five Hundred",
|
||||
600: "Six Hundred", 700: "Seven Hundred", 800: "Eight Hundred",
|
||||
900: "Nine Hundred",
|
||||
}
|
||||
|
||||
retVale = ""
|
||||
hunWord = theHundreds.get(hunVal, "")
|
||||
retVal = ""
|
||||
hunWord = theWords.get(hunVal, "")
|
||||
if tenVal == 10:
|
||||
oneWord = theTeens.get(oneVal, "")
|
||||
retVale = f"{hunWord} {oneWord}".strip()
|
||||
oneWord = theWords.get(oneVal+10, "")
|
||||
retVal = f"{hunWord} {oneWord}".strip()
|
||||
else:
|
||||
oneWord = theOnes.get(oneVal, "")
|
||||
oneWord = theWords.get(oneVal, "")
|
||||
if tenVal == 0:
|
||||
retVale = f"{hunWord} {oneWord}".strip()
|
||||
retVal = f"{hunWord} {oneWord}".strip()
|
||||
else:
|
||||
tenWord = theTens.get(tenVal, "")
|
||||
tenWord = theWords.get(tenVal, "")
|
||||
if oneVal == 0:
|
||||
retVale = f"{hunWord} {tenWord}".strip()
|
||||
retVal = f"{hunWord} {tenWord}".strip()
|
||||
else:
|
||||
retVale = f"{hunWord} {tenWord}-{oneWord}".strip()
|
||||
retVal = f"{hunWord} {tenWord}-{oneWord}".strip()
|
||||
|
||||
return retVale
|
||||
return retVal
|
||||
|
||||
def _numberToWord_NO(numVal, theCode):
|
||||
"""Convert numbers to Norwegian words.
|
||||
"""
|
||||
oneWord = ""
|
||||
tenWord = ""
|
||||
hunWord = ""
|
||||
|
||||
if numVal == 0:
|
||||
return "null"
|
||||
|
||||
oneVal = numVal % 10
|
||||
tenVal = (numVal - oneVal) % 100
|
||||
hunVal = (numVal - tenVal - oneVal) % 1000
|
||||
|
||||
theWords = {
|
||||
0: "", 1: "én", 2: "to", 3: "tre", 4: "fire", 5: "fem", 6: "seks",
|
||||
7: "sju", 8: "åtte", 9: "ni", 10: "ti", 11: "elleve", 12: "tolv",
|
||||
13: "tretten", 14: "fjorten", 15: "femten", 16: "seksten", 17: "sytten",
|
||||
18: "atten", 19: "nitten", 20: "tjue", 30: "tretti", 40: "førti",
|
||||
50: "femti", 60: "seksti", 70: "sytti", 80: "åtti", 90: "nitti",
|
||||
100: "ett hundre", 200: "to hundre", 300: "tre hundre", 400: "fire hundre",
|
||||
500: "fem hundre", 600: "seks hundre", 700: "sju hundre",
|
||||
800: "åtte hundre", 900: "ni hundre",
|
||||
}
|
||||
|
||||
if theCode == "nn":
|
||||
theWords[1] = "ein"
|
||||
theWords[100] = "eitt hundre"
|
||||
|
||||
retVal = ""
|
||||
hunWord = theWords.get(hunVal, "")
|
||||
if tenVal == 10:
|
||||
oneWord = theWords.get(oneVal+10, "")
|
||||
sepVal = " og " if hunWord and oneWord else " "
|
||||
retVal = f"{hunWord}{sepVal}{oneWord}"
|
||||
else:
|
||||
oneWord = theWords.get(oneVal, "")
|
||||
if tenVal == 0:
|
||||
sepVal = " og " if hunWord and oneWord else " "
|
||||
retVal = f"{hunWord}{sepVal}{oneWord}".strip()
|
||||
else:
|
||||
tenWord = theWords.get(tenVal, "")
|
||||
sepVal = " og " if hunWord and tenWord else " "
|
||||
if oneVal == 0:
|
||||
retVal = f"{hunWord}{sepVal}{tenWord}".strip()
|
||||
else:
|
||||
retVal = f"{hunWord}{sepVal}{tenWord}{oneWord}".strip()
|
||||
|
||||
return retVal.strip()
|
||||
|
||||
Reference in New Issue
Block a user