tag
@@ -451,7 +459,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()
@@ -470,24 +478,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?
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
%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
@@ -496,14 +512,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
@@ -602,7 +619,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()
@@ -623,7 +640,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
@@ -702,7 +719,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,
@@ -721,7 +738,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
@@ -773,27 +790,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)
@@ -804,20 +827,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:
@@ -827,18 +852,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
@@ -865,7 +893,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):
@@ -887,14 +915,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()
@@ -925,16 +955,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()
@@ -981,17 +1013,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
@@ -1120,8 +1156,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
@@ -1330,7 +1366,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:
@@ -1341,6 +1377,7 @@ class NWProject():
aDoc = NWDoc(self, self.theParent)
nOrph = 0
noWhere = False
+ oPrefix = self.tr("Recovered")
for oHandle in orphanFiles:
# Look for meta data
@@ -1352,10 +1389,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:
@@ -1382,10 +1421,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
@@ -1432,7 +1473,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)
@@ -1455,7 +1496,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()
@@ -1464,7 +1505,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()
@@ -1477,10 +1518,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
@@ -1491,7 +1532,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)
@@ -1502,7 +1543,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 ""
diff --git a/nw/core/spellcheck.py b/nw/core/spellcheck.py
index 9d22afcd..f03fc034 100644
--- a/nw/core/spellcheck.py
+++ b/nw/core/spellcheck.py
@@ -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
diff --git a/nw/core/tohtml.py b/nw/core/tohtml.py
index 67ebcc0f..04882855 100644
--- a/nw/core/tohtml.py
+++ b/nw/core/tohtml.py
@@ -406,9 +406,13 @@ class ToHtml(Tokenizer):
"""Apply HTML formatting to synopsis.
"""
if self.genMode == self.M_PREVIEW:
- return "\n" % tText
+ return "\n" % (
+ self._trSynopsis, tText
+ )
else:
- return "Synopsis: %s
\n" % tText
+ return "%s: %s
\n" % (
+ self._trSynopsis, tText
+ )
def _formatComments(self, tText):
"""Apply HTML formatting to comments.
@@ -416,7 +420,9 @@ class ToHtml(Tokenizer):
if self.genMode == self.M_PREVIEW:
return "\n" % tText
else:
- return "\n" % tText
+ return "\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 3e0474ae..a830ca9d 100644
--- a/nw/core/tokenizer.py
+++ b/nw/core/tokenizer.py
@@ -28,7 +28,9 @@ import logging
import re
from operator import itemgetter
-from PyQt5.QtCore import QRegularExpression
+from functools import partial
+
+from PyQt5.QtCore import QCoreApplication, QRegularExpression
from nw.core.document import NWDoc
from nw.core.tools import numberToWord, numberToRoman
@@ -141,6 +143,18 @@ class Tokenizer():
# Error Handling
self.errData = []
+ # Internal Mappings
+ self.tr = partial(QCoreApplication.translate, "Tokenizer")
+
+ # Localisation
+ self._trSynopsis = self.tr("Synopsis")
+ self._trComment = self.tr("Comment")
+ self._trNotes = self.tr("Notes")
+
+ self._spellLang = self.theProject.projLang
+ if self._spellLang is None:
+ self._spellLang = self.theParent.mainConf.spellLanguage
+
return
##
@@ -249,7 +263,7 @@ class Tokenizer():
if theItem.itemType != nwItemType.ROOT:
return False
- theTitle = "Notes: %s" % 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
@@ -278,10 +292,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
@@ -653,7 +667,7 @@ class Tokenizer():
theTitle = theTitle.replace(r"%sc%", str(self.numChScene))
theTitle = theTitle.replace(r"%sca%", str(self.numAbsScene))
if r"%chw%" in theTitle:
- theTitle = theTitle.replace(r"%chw%", numberToWord(self.numChapter, "en"))
+ theTitle = theTitle.replace(r"%chw%", numberToWord(self.numChapter, self._spellLang))
if r"%chi%" in theTitle:
theTitle = theTitle.replace(r"%chi%", numberToRoman(self.numChapter, True))
if r"%chI%" in theTitle:
diff --git a/nw/core/tools.py b/nw/core/tools.py
index 44b4c865..7e538d6d 100644
--- a/nw/core/tools.py
+++ b/nw/core/tools.py
@@ -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()
diff --git a/nw/error.py b/nw/error.py
index 998b7347..c29e3eb6 100644
--- a/nw/error.py
+++ b/nw/error.py
@@ -68,6 +68,7 @@ class NWErrorMessage(QDialog):
self.msgBody.setReadOnly(True)
self.btnBox = QDialogButtonBox(QDialogButtonBox.Close)
+ self.btnBox.button(QDialogButtonBox.Close).setText(self.tr("Close"))
self.btnBox.rejected.connect(self._doClose)
# Assemble
diff --git a/nw/gui/about.py b/nw/gui/about.py
index d584cb25..685621d3 100644
--- a/nw/gui/about.py
+++ b/nw/gui/about.py
@@ -55,7 +55,7 @@ class GuiAbout(QDialog):
self.innerBox = QHBoxLayout()
self.innerBox.setSpacing(self.mainConf.pxInt(16))
- self.setWindowTitle("About novelWriter")
+ self.setWindowTitle(self.tr("About novelWriter"))
self.setMinimumWidth(self.mainConf.pxInt(650))
self.setMinimumHeight(self.mainConf.pxInt(600))
@@ -90,13 +90,14 @@ class GuiAbout(QDialog):
# Main Tab Area
self.tabBox = QTabWidget()
- self.tabBox.addTab(self.pageAbout, "About")
- self.tabBox.addTab(self.pageNotes, "Release")
- self.tabBox.addTab(self.pageLicense, "License")
+ self.tabBox.addTab(self.pageAbout, self.tr("About"))
+ self.tabBox.addTab(self.pageNotes, self.tr("Release"))
+ self.tabBox.addTab(self.pageLicense, self.tr("License"))
self.innerBox.addWidget(self.tabBox)
# OK Button
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok)
+ self.buttonBox.button(QDialogButtonBox.Ok).setText(self.tr("OK"))
self.buttonBox.accepted.connect(self._doClose)
self.outerBox.addLayout(self.innerBox)
@@ -132,80 +133,76 @@ class GuiAbout(QDialog):
"""Generate the content for the About page.
"""
listPrefix = " • "
- aboutMsg = (
- "About novelWriter
"
- "{copyright:s}.
"
- "Website: {domain:s}
"
- "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.
"
- "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.
"
- "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.
"
- "See the License tab for the full license text, or visit the "
- "GNU website at "
- "GPL v3.0 "
- "for more details.
"
- "Credits
"
- "{credits:s}
"
+ webLink = f"{nw.__domain__:s}"
+ aboutMsg = (
+ "{title1}
"
+ "{copyright}.
"
+ "{website}
"
+ "{intro}
"
+ "{license1}
"
+ "{license2}
"
+ "{license3}
"
+ "{title2}
"
+ "{credits}
"
).format(
+ title1 = self.tr("About novelWriter"),
copyright = nw.__copyright__,
- website = nw.__url__,
- domain = nw.__domain__,
+ website = self.tr("Website: {0}").format(webLink),
+ title2 = self.tr("Credits"),
credits = "
".join(["%s%s" % (listPrefix, x) for x in nw.__credits__]),
+ intro = 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."
+ ),
+ license1 = 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."
+ ),
+ license2 = 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."
+ ),
+ license3 = 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"
+ ),
)
theTheme = self.theParent.theTheme
theIcons = self.theParent.theTheme.theIcons
if theTheme.themeName:
- aboutMsg += (
- "Theme: {name:s}
"
- ""
- "Author: {author:s}
"
- "Credit: {credit:s}
"
- "License: {license:s}"
- "
"
- ).format(
- name = theTheme.themeName,
- author = theTheme.themeAuthor,
- credit = theTheme.themeCredit,
- license = theTheme.themeLicense,
- lic_url = theTheme.themeLicenseUrl,
+ aboutMsg += "%s
%s
%s
%s
" % (
+ self.tr("Theme: {0}").format(theTheme.themeName),
+ self.tr("Author: {0}").format(theTheme.themeAuthor),
+ self.tr("Credit: {0}").format(theTheme.themeCredit),
+ self.tr("License: {0}").format(
+ f"{theTheme.themeLicense}"
+ )
)
+
if theIcons.themeName:
- aboutMsg += (
- "Icons: {name:s}
"
- ""
- "Author: {author:s}
"
- "Credit: {credit:s}
"
- "License: {license:s}"
- "
"
- ).format(
- name = theIcons.themeName,
- author = theIcons.themeAuthor,
- credit = theIcons.themeCredit,
- license = theIcons.themeLicense,
- lic_url = theIcons.themeLicenseUrl,
+ aboutMsg += "%s
%s
%s
%s
" % (
+ self.tr("Icons: {0}").format(theIcons.themeName),
+ self.tr("Author: {0}").format(theIcons.themeAuthor),
+ self.tr("Credit: {0}").format(theIcons.themeCredit),
+ self.tr("License: {0}").format(
+ f"{theIcons.themeLicense}"
+ )
)
+
if theTheme.syntaxName:
- aboutMsg += (
- "Syntax: {name:s}
"
- ""
- "Author: {author:s}
"
- "Credit: {credit:s}
"
- "License: {license:s}"
- "
"
- ).format(
- name = theTheme.syntaxName,
- author = theTheme.syntaxAuthor,
- credit = theTheme.syntaxCredit,
- license = theTheme.syntaxLicense,
- lic_url = theTheme.syntaxLicenseUrl,
+ aboutMsg += "%s
%s
%s
%s
" % (
+ self.tr("Syntax: {0}").format(theTheme.syntaxName),
+ self.tr("Author: {0}").format(theTheme.syntaxAuthor),
+ self.tr("Credit: {0}").format(theTheme.syntaxCredit),
+ self.tr("License: {0}").format(
+ f"{theTheme.syntaxLicense}"
+ )
)
self.pageAbout.setHtml(aboutMsg)
diff --git a/nw/gui/build.py b/nw/gui/build.py
index 3b76248f..95bd2c40 100644
--- a/nw/gui/build.py
+++ b/nw/gui/build.py
@@ -84,7 +84,7 @@ class GuiBuildNovel(QDialog):
self.htmlSize = 0 # Size of the html document
self.buildTime = 0 # The timestamp of the last build
- self.setWindowTitle("Build Novel Project")
+ self.setWindowTitle(self.tr("Build Novel Project"))
self.setMinimumWidth(self.mainConf.pxInt(700))
self.setMinimumHeight(self.mainConf.pxInt(600))
@@ -101,27 +101,26 @@ class GuiBuildNovel(QDialog):
# Title Formats
# =============
- self.titleGroup = QGroupBox("Title Formats for Novel Files", self)
+ self.titleGroup = QGroupBox(self.tr("Title Formats for Novel Files"), self)
self.titleForm = QGridLayout(self)
self.titleGroup.setLayout(self.titleForm)
- fmtHelp = (
- r"Formatting Codes:
"
- r"%title% for the title as set in the document
"
- r"%ch% for chapter number (1, 2, 3)
"
- r"%chw% for chapter number as a word (one, two)
"
- r"%chI% for chapter number in upper case Roman
"
- r"%chi% for chapter number in lower case Roman
"
- r"%sc% for scene number within chapter
"
- r"%sca% for scene number within novel"
- )
- fmtScHelp = (
- r"
"
- r"Leave blank to skip this heading, or set to a static text, like "
- r"for instance '* * *', to make a separator. The separator will "
- r"be centred automatically and only appear between sections of "
- r"the same type."
- )
+ fmtHelp = "
".join([
+ "%s" % self.tr("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%"),
+ self.tr("{0} for chapter number as a word (one, two)").format(r"%chw%"),
+ self.tr("{0} for chapter number in upper case Roman").format(r"%chI%"),
+ 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("* * *")
xFmt = self.mainConf.pxInt(100)
self.fmtTitle = QLineEdit()
@@ -176,11 +175,11 @@ class GuiBuildNovel(QDialog):
self.boxSection = QHBoxLayout()
self.boxSection.addWidget(self.fmtSection)
- titleLabel = QLabel("Title")
- chapterLabel = QLabel("Chapter")
- unnumbLabel = QLabel("Unnumbered")
- sceneLabel = QLabel("Scene")
- sectionLabel = QLabel("Section")
+ titleLabel = QLabel(self.tr("Title"))
+ chapterLabel = QLabel(self.tr("Chapter"))
+ unnumbLabel = QLabel(self.tr("Unnumbered"))
+ sceneLabel = QLabel(self.tr("Scene"))
+ sectionLabel = QLabel(self.tr("Section"))
self.titleForm.addWidget(titleLabel, 0, 0, 1, 1, Qt.AlignLeft)
self.titleForm.addLayout(self.boxTitle, 0, 1, 1, 1, Qt.AlignRight)
@@ -199,7 +198,7 @@ class GuiBuildNovel(QDialog):
# Font Options
# ============
- self.fontGroup = QGroupBox("Font Options", self)
+ self.fontGroup = QGroupBox(self.tr("Font Options"), self)
self.fontForm = QGridLayout(self)
self.fontGroup.setLayout(self.fontForm)
@@ -237,11 +236,11 @@ class GuiBuildNovel(QDialog):
self.boxFont = QHBoxLayout()
self.boxFont.addWidget(self.textFont)
- fontFamilyLabel = QLabel("Font family")
- fontSizeLabel = QLabel("Font size")
- lineHeightLabel = QLabel("Line height")
- justifyLabel = QLabel("Justify text")
- stylingLabel = QLabel("Disable styling")
+ fontFamilyLabel = QLabel(self.tr("Font family"))
+ fontSizeLabel = QLabel(self.tr("Font size"))
+ lineHeightLabel = QLabel(self.tr("Line height"))
+ justifyLabel = QLabel(self.tr("Justify text"))
+ stylingLabel = QLabel(self.tr("Disable styling"))
self.fontForm.addWidget(fontFamilyLabel, 0, 0, 1, 1, Qt.AlignLeft)
self.fontForm.addLayout(self.boxFont, 0, 1, 1, 1, Qt.AlignRight)
@@ -283,7 +282,7 @@ class GuiBuildNovel(QDialog):
# Include Options
# ===============
- self.textGroup = QGroupBox("Include Options", self)
+ self.textGroup = QGroupBox(self.tr("Include Options"), self)
self.textForm = QGridLayout(self)
self.textGroup.setLayout(self.textForm)
@@ -307,10 +306,10 @@ class GuiBuildNovel(QDialog):
self.optState.getBool("GuiBuildNovel", "incBodyText", True)
)
- synopsisLabel = QLabel("Include synopsis")
- commentsLabel = QLabel("Include comments")
- keywordsLabel = QLabel("Include keywords")
- bodyLabel = QLabel("Include body text")
+ synopsisLabel = QLabel(self.tr("Include synopsis"))
+ commentsLabel = QLabel(self.tr("Include comments"))
+ keywordsLabel = QLabel(self.tr("Include keywords"))
+ bodyLabel = QLabel(self.tr("Include body text"))
self.textForm.addWidget(synopsisLabel, 0, 0, 1, 1, Qt.AlignLeft)
self.textForm.addWidget(self.includeSynopsis, 0, 1, 1, 1, Qt.AlignRight)
@@ -327,37 +326,34 @@ class GuiBuildNovel(QDialog):
# File Filter Options
# ===================
- self.fileGroup = QGroupBox("File Filter Options", self)
+ self.fileGroup = QGroupBox(self.tr("File Filter Options"), self)
self.fileForm = QGridLayout(self)
self.fileGroup.setLayout(self.fileForm)
self.novelFiles = QSwitch(width=wS, height=hS)
- self.novelFiles.setToolTip(
- "Include files with layouts 'Book', 'Page', 'Partition', "
- "'Chapter', 'Unnumbered', and 'Scene'."
- )
+ self.novelFiles.setToolTip(self.tr("Include files with layouts other than 'Note'."))
self.novelFiles.setChecked(
self.optState.getBool("GuiBuildNovel", "addNovel", True)
)
self.noteFiles = QSwitch(width=wS, height=hS)
- self.noteFiles.setToolTip("Include files with layout 'Note'.")
+ self.noteFiles.setToolTip(self.tr("Include files with layout 'Note'."))
self.noteFiles.setChecked(
self.optState.getBool("GuiBuildNovel", "addNotes", False)
)
self.ignoreFlag = QSwitch(width=wS, height=hS)
- self.ignoreFlag.setToolTip(
+ 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)
)
- novelLabel = QLabel("Include novel files")
- notesLabel = QLabel("Include note files")
- exportLabel = QLabel("Ignore export flag")
+ novelLabel = QLabel(self.tr("Include novel files"))
+ notesLabel = QLabel(self.tr("Include note files"))
+ exportLabel = QLabel(self.tr("Ignore export flag"))
self.fileForm.addWidget(novelLabel, 0, 0, 1, 1, Qt.AlignLeft)
self.fileForm.addWidget(self.novelFiles, 0, 1, 1, 1, Qt.AlignRight)
@@ -372,7 +368,7 @@ class GuiBuildNovel(QDialog):
# Export Options
# ==============
- self.exportGroup = QGroupBox("Export Options", self)
+ self.exportGroup = QGroupBox(self.tr("Export Options"), self)
self.exportForm = QGridLayout(self)
self.exportGroup.setLayout(self.exportForm)
@@ -386,8 +382,8 @@ class GuiBuildNovel(QDialog):
self.optState.getBool("GuiBuildNovel", "replaceUCode", False)
)
- tabsLabel = QLabel("Replace tabs with spaces")
- uCodeLabel = QLabel("Replace Unicode in HTML")
+ tabsLabel = QLabel(self.tr("Replace tabs with spaces"))
+ uCodeLabel = QLabel(self.tr("Replace Unicode in HTML"))
self.exportForm.addWidget(tabsLabel, 0, 0, 1, 1, Qt.AlignLeft)
self.exportForm.addWidget(self.replaceTabs, 0, 1, 1, 1, Qt.AlignRight)
@@ -402,7 +398,7 @@ class GuiBuildNovel(QDialog):
self.buildProgress = QProgressBar()
- self.buildNovel = QPushButton("Build Preview")
+ self.buildNovel = QPushButton(self.tr("Build Preview"))
self.buildNovel.clicked.connect(self._buildPreview)
# Action Buttons
@@ -413,56 +409,56 @@ class GuiBuildNovel(QDialog):
# Printing
self.printMenu = QMenu(self)
- self.btnPrint = QPushButton("Print")
+ self.btnPrint = QPushButton(self.tr("Print"))
self.btnPrint.setMenu(self.printMenu)
- self.printSend = QAction("Print Preview", self)
+ self.printSend = QAction(self.tr("Print Preview"), self)
self.printSend.triggered.connect(self._printDocument)
self.printMenu.addAction(self.printSend)
- self.printFile = QAction("Print to PDF", self)
+ self.printFile = QAction(self.tr("Print to PDF"), self)
self.printFile.triggered.connect(lambda: self._saveDocument(self.FMT_PDF))
self.printMenu.addAction(self.printFile)
# Saving to File
self.saveMenu = QMenu(self)
- self.btnSave = QPushButton("Save As")
+ self.btnSave = QPushButton(self.tr("Save As"))
self.btnSave.setMenu(self.saveMenu)
- self.saveODT = QAction("Open Document (.odt)", self)
+ self.saveODT = QAction(self.tr("Open Document (.odt)"), self)
self.saveODT.triggered.connect(lambda: self._saveDocument(self.FMT_ODT))
self.saveMenu.addAction(self.saveODT)
- self.saveFODT = QAction("Flat Open Document (.fodt)", self)
+ self.saveFODT = QAction(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("novelWriter HTML (.htm)", self)
+ self.saveHTM = QAction(self.tr("novelWriter HTML (.htm)"), self)
self.saveHTM.triggered.connect(lambda: self._saveDocument(self.FMT_HTM))
self.saveMenu.addAction(self.saveHTM)
- self.saveNWD = QAction("novelWriter Markdown (.nwd)", self)
+ self.saveNWD = QAction(self.tr("novelWriter Markdown (.nwd)"), self)
self.saveNWD.triggered.connect(lambda: self._saveDocument(self.FMT_NWD))
self.saveMenu.addAction(self.saveNWD)
- self.saveMD = QAction("Standard Markdown (.md)", self)
+ self.saveMD = QAction(self.tr("Standard Markdown (.md)"), self)
self.saveMD.triggered.connect(lambda: self._saveDocument(self.FMT_MD))
self.saveMenu.addAction(self.saveMD)
- self.saveGH = QAction("GitHub Markdown (.md)", self)
+ self.saveGH = QAction(self.tr("GitHub Markdown (.md)"), self)
self.saveGH.triggered.connect(lambda: self._saveDocument(self.FMT_GH))
self.saveMenu.addAction(self.saveGH)
- self.saveJsonH = QAction("JSON + novelWriter HTML (.json)", self)
+ self.saveJsonH = QAction(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("JSON + novelWriters Markdown (.json)", self)
+ self.saveJsonM = QAction(self.tr("JSON + novelWriters Markdown (.json)"), self)
self.saveJsonM.triggered.connect(lambda: self._saveDocument(self.FMT_JSON_M))
self.saveMenu.addAction(self.saveJsonM)
- self.btnClose = QPushButton("Close")
+ self.btnClose = QPushButton(self.tr("Close"))
self.btnClose.clicked.connect(self._doClose)
self.buttonBox.addWidget(self.btnSave)
@@ -567,7 +563,7 @@ class GuiBuildNovel(QDialog):
self.docView.setContent(self.htmlText, self.buildTime)
else:
self.docView.setText(
- "Failed to generate preview. The result is too big."
+ self.tr("Failed to generate preview. The result is too big.")
)
else:
@@ -736,10 +732,10 @@ class GuiBuildNovel(QDialog):
logger.debug("Built project in %.3f ms" % (1000*(tEnd - tStart)))
if bldObj.errData:
- self.theParent.makeAlert((
- "There were problems when building the project:"
- "
- %s"
- ) % "
- ".join(bldObj.errData), nwAlert.ERROR)
+ self.theParent.makeAlert("%s:
- %s" % (
+ self.tr("There were problems when building the project"),
+ "
- ".join(bldObj.errData)), nwAlert.ERROR
+ )
return
@@ -796,39 +792,39 @@ class GuiBuildNovel(QDialog):
if theFmt == self.FMT_ODT:
fileExt = "odt"
- textFmt = "Open Document"
+ textFmt = self.tr("Open Document")
elif theFmt == self.FMT_FODT:
fileExt = "fodt"
- textFmt = "Flat Open Document"
+ textFmt = self.tr("Flat Open Document")
elif theFmt == self.FMT_HTM:
fileExt = "htm"
- textFmt = "Plain HTML"
+ textFmt = self.tr("Plain HTML")
elif theFmt == self.FMT_NWD:
fileExt = "nwd"
- textFmt = "novelWriter Markdown"
+ textFmt = self.tr("novelWriter Markdown")
elif theFmt == self.FMT_MD:
fileExt = "md"
- textFmt = "Standard Markdown"
+ textFmt = self.tr("Standard Markdown")
elif theFmt == self.FMT_GH:
fileExt = "md"
- textFmt = "GitHub Markdown"
+ textFmt = self.tr("GitHub Markdown")
elif theFmt == self.FMT_JSON_H:
fileExt = "json"
- textFmt = "JSON + novelWriter HTML"
+ textFmt = self.tr("JSON + novelWriter HTML")
elif theFmt == self.FMT_JSON_M:
fileExt = "json"
- textFmt = "JSON + novelWriter Markdown"
+ textFmt = self.tr("JSON + novelWriter Markdown")
elif theFmt == self.FMT_PDF:
fileExt = "pdf"
- textFmt = "PDF"
+ textFmt = self.tr("PDF")
else:
return False
@@ -848,7 +844,7 @@ class GuiBuildNovel(QDialog):
dlgOpt = QFileDialog.Options()
dlgOpt |= QFileDialog.DontUseNativeDialog
savePath, _ = QFileDialog.getSaveFileName(
- self, "Save Document As", savePath, options=dlgOpt
+ self, self.tr("Save Document As"), savePath, options=dlgOpt
)
if not savePath:
return False
@@ -985,20 +981,20 @@ class GuiBuildNovel(QDialog):
errMsg - str(e)
else:
- errMsg = "Unknown format"
+ errMsg = self.tr("Unknown format")
# Report to user
if wSuccess:
self.theParent.makeAlert(
- "%s file successfully written to:
%s" % (
- textFmt, savePath
- ), nwAlert.INFO
+ "%s
%s" % (
+ self.tr("{0} file successfully written to:").format(textFmt), savePath
+ ),
+ nwAlert.INFO
)
else:
self.theParent.makeAlert(
- "Failed to write %s file. %s" % (
- textFmt, errMsg
- ), nwAlert.ERROR
+ self.tr("Failed to write {0} file. {1}").format(textFmt, errMsg),
+ nwAlert.ERROR
)
return wSuccess
@@ -1193,11 +1189,11 @@ class GuiBuildNovelDocView(QTextBrowser):
self.qDocument = self.document()
self.qDocument.setDocumentMargin(self.mainConf.getTextMargin())
- self.setPlaceholderText(
+ 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."
- )
+ ))
theFont = QFont()
if self.mainConf.textFont is None:
@@ -1227,7 +1223,7 @@ class GuiBuildNovelDocView(QTextBrowser):
fPx = int(1.1*self.theTheme.fontPixelSize)
- self.theTitle = QLabel("Build Time: Unknown", self)
+ self.theTitle = QLabel("", self)
self.theTitle.setIndent(0)
self.theTitle.setAutoFillBackground(True)
self.theTitle.setAlignment(Qt.AlignCenter)
@@ -1236,6 +1232,7 @@ class GuiBuildNovelDocView(QTextBrowser):
self.theTitle.setFont(lblFont)
self._updateDocMargins()
+ self._updateBuildAge()
self.setStyleSheet()
# Age Timer
@@ -1341,8 +1338,11 @@ class GuiBuildNovelDocView(QTextBrowser):
fuzzyTime(time() - self.buildTime)
)
else:
- strBuildTime = "Unknown"
- self.theTitle.setText("Build Time: %s" % strBuildTime)
+ strBuildTime = self.tr("Unknown")
+
+ self.theTitle.setText(self.tr("Build Time: {0}").format(strBuildTime))
+
+ return
def _updateDocMargins(self):
"""Automatically adjust the header to fill the top of the
diff --git a/nw/gui/custom.py b/nw/gui/custom.py
index 303a5ae1..4b187f21 100644
--- a/nw/gui/custom.py
+++ b/nw/gui/custom.py
@@ -500,6 +500,8 @@ class QuotesDialog(QDialog):
# Buttons
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+ self.buttonBox.button(QDialogButtonBox.Ok).setText(self.tr("Ok"))
+ self.buttonBox.button(QDialogButtonBox.Cancel).setText(self.tr("Cancel"))
self.buttonBox.accepted.connect(self._doAccept)
self.buttonBox.rejected.connect(self._doReject)
diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py
index abafc6fb..7dc80520 100644
--- a/nw/gui/doceditor.py
+++ b/nw/gui/doceditor.py
@@ -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,11 +293,17 @@ class GuiDocEditor(QTextEdit):
docSize = len(theDoc)
if docSize > nwConst.MAX_DOCSIZE:
- self.theParent.makeAlert((
- "The document you are trying to open is too big. "
- "The document size is %.2f\u202fMB. "
- "The maximum size allowed is %.2f\u202fMB."
- ) % (docSize/1.0e6, nwConst.MAX_DOCSIZE/1.0e6), nwAlert.ERROR)
+ self.theParent.makeAlert(
+ self.tr(
+ "The document you are trying to open is too big. "
+ "The document size is {0} MB. "
+ "The maximum size allowed is {1} MB."
+ ).format(
+ f"{docSize/1.0e6:.2f}",
+ f"{nwConst.MAX_DOCSIZE/1.0e6:.2f}"
+ ),
+ nwAlert.ERROR
+ )
self.clearEditor()
return False
@@ -380,11 +386,17 @@ class GuiDocEditor(QTextEdit):
"""
docSize = len(theText)
if docSize > nwConst.MAX_DOCSIZE:
- self.theParent.makeAlert((
- "The text you are trying to add is too big. "
- "The text size is %.2f\u202fMB. "
- "The maximum size allowed is %.2f\u202fMB."
- ) % (docSize/1.0e6, nwConst.MAX_DOCSIZE/1.0e6), nwAlert.ERROR)
+ self.theParent.makeAlert(
+ self.tr(
+ "The text you are trying to add is too big. "
+ "The text size is {0} MB. "
+ "The maximum size allowed is {1} MB."
+ ).format(
+ f"{docSize/1.0e6:.2f}",
+ f"{nwConst.MAX_DOCSIZE/1.0e6:.2f}"
+ ),
+ nwAlert.ERROR
+ )
return False
qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
@@ -568,12 +580,14 @@ class GuiDocEditor(QTextEdit):
"""
if not isinstance(theLine, int):
return False
+
if theLine >= 0:
theBlock = self.qDocument.findBlockByLineNumber(theLine)
if theBlock:
self.setCursorPosition(theBlock.position())
self.docFooter.updateLineCount()
logger.verbose("Cursor moved to line %d" % theLine)
+
return True
##
@@ -591,11 +605,9 @@ class GuiDocEditor(QTextEdit):
theLang = self.theProject.projLang
self.theDict.setLanguage(theLang, self.theProject.projDict)
+ theTag, theProvider = self.theDict.describeDict()
- aLang, aName = self.theDict.describeDict()
- self.theParent.statusBar.setLanguage(
- aLang, "%s [%s]" % (self.mainConf.spellTool.title(), aName.title())
- )
+ self.theParent.statusBar.setLanguage(theLang, theProvider)
if not self.bigDoc:
self.spellCheckDocument()
@@ -641,10 +653,8 @@ class GuiDocEditor(QTextEdit):
self.hLight.rehighlight()
qApp.restoreOverrideCursor()
afTime = time()
- logger.debug(
- "Document highlighted in %.3f ms" % (1000*(afTime-bfTime))
- )
- self.theParent.statusBar.showMessage("Spell check complete")
+ logger.debug("Document highlighted in %.3f ms" % (1000*(afTime-bfTime)))
+ self.theParent.statusBar.showMessage(self.tr("Spell check complete"))
return True
@@ -743,14 +753,14 @@ class GuiDocEditor(QTextEdit):
return False
msgBox = QMessageBox()
- msgBox.information(self, "File Location", (
- "File details for the currently open file
"
- "Handle: {handle:s}
"
- "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
@@ -799,7 +809,7 @@ class GuiDocEditor(QTextEdit):
theCursor = self.textCursor()
theBlock = theCursor.block()
if not theBlock.isValid():
- logger.error("Filed to insert keyword '%s'" % keyWord)
+ logger.error("Failed to insert keyword '%s'" % keyWord)
return False
theCursor.beginEditBlock()
@@ -933,10 +943,15 @@ class GuiDocEditor(QTextEdit):
self.lastFind = None
if self.qDocument.characterCount() > nwConst.MAX_DOCSIZE:
- self.theParent.makeAlert((
- "The document has grown too big and you cannot add more text to it. "
- "The maximum size of a single novelWriter document is %.2f\u202fMB."
- ) % (nwConst.MAX_DOCSIZE/1.0e6), 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 {0} MB."
+ ).format(
+ f"{nwConst.MAX_DOCSIZE/1.0e6:.2f}"
+ ),
+ nwAlert.ERROR
+ )
self.undo()
return
@@ -966,21 +981,21 @@ class GuiDocEditor(QTextEdit):
# ===========================
if self._followTag(theCursor=posCursor, loadTag=False):
- mnuTag = QAction("Follow Tag", mnuContext)
+ mnuTag = QAction(self.tr("Follow Tag"), mnuContext)
mnuTag.triggered.connect(lambda: self._followTag(theCursor=posCursor))
mnuContext.addAction(mnuTag)
mnuContext.addSeparator()
if userSelection:
- mnuCut = QAction("Cut", mnuContext)
+ mnuCut = QAction(self.tr("Cut"), mnuContext)
mnuCut.triggered.connect(lambda: self.docAction(nwDocAction.CUT))
mnuContext.addAction(mnuCut)
- mnuCopy = QAction("Copy", mnuContext)
+ mnuCopy = QAction(self.tr("Copy"), mnuContext)
mnuCopy.triggered.connect(lambda: self.docAction(nwDocAction.COPY))
mnuContext.addAction(mnuCopy)
- mnuPaste = QAction("Paste", mnuContext)
+ mnuPaste = QAction(self.tr("Paste"), mnuContext)
mnuPaste.triggered.connect(lambda: self.docAction(nwDocAction.PASTE))
mnuContext.addAction(mnuPaste)
@@ -989,17 +1004,17 @@ class GuiDocEditor(QTextEdit):
# Selections
# ==========
- mnuSelAll = QAction("Select All", mnuContext)
+ mnuSelAll = QAction(self.tr("Select All"), mnuContext)
mnuSelAll.triggered.connect(lambda: self.docAction(nwDocAction.SEL_ALL))
mnuContext.addAction(mnuSelAll)
- mnuSelWord = QAction("Select Word", mnuContext)
+ mnuSelWord = QAction(self.tr("Select Word"), mnuContext)
mnuSelWord.triggered.connect(
lambda: self._makePosSelection(QTextCursor.WordUnderCursor, thePos)
)
mnuContext.addAction(mnuSelWord)
- mnuSelPara = QAction("Select Paragraph", mnuContext)
+ mnuSelPara = QAction(self.tr("Select Paragraph"), mnuContext)
mnuSelPara.triggered.connect(
lambda: self._makePosSelection(QTextCursor.BlockUnderCursor, thePos)
)
@@ -1025,7 +1040,7 @@ class GuiDocEditor(QTextEdit):
if spellCheck:
mnuContext.addSeparator()
- mnuHead = QAction("Spelling Suggestion(s)", mnuContext)
+ mnuHead = QAction(self.tr("Spelling Suggestion(s)"), mnuContext)
mnuContext.addAction(mnuHead)
theSuggest = self.theDict.suggestWords(theWord)[:15]
@@ -1037,11 +1052,13 @@ class GuiDocEditor(QTextEdit):
)
mnuContext.addAction(mnuWord)
else:
- mnuHead = QAction("%s No Suggestions" % nwUnicode.U_ENDASH, mnuContext)
+ mnuHead = QAction(
+ "%s %s" % (nwUnicode.U_ENDASH, self.tr("No Suggestions")), mnuContext
+ )
mnuContext.addAction(mnuHead)
mnuContext.addSeparator()
- mnuAdd = QAction("Add Word to Dictionary", mnuContext)
+ mnuAdd = QAction(self.tr("Add Word to Dictionary"), mnuContext)
mnuAdd.triggered.connect(lambda thePos: self._addWord(posCursor))
mnuContext.addAction(mnuAdd)
@@ -1131,15 +1148,11 @@ class GuiDocEditor(QTextEdit):
QPointF(theSize.width(), theSize.height()), Qt.FuzzyHit
)
if self.queuePos <= thePos:
- logger.verbose(
- "Allowed cursor move to %d <= %d" % (self.queuePos, thePos)
- )
+ logger.verbose("Allowed cursor move to %d <= %d" % (self.queuePos, thePos))
self.setCursorPosition(self.queuePos)
self.queuePos = None
else:
- logger.verbose(
- "Denied cursor move to %d > %d" % (self.queuePos, thePos)
- )
+ logger.verbose("Denied cursor move to %d > %d" % (self.queuePos, thePos))
return
##
@@ -1317,7 +1330,8 @@ class GuiDocEditor(QTextEdit):
else:
self.theParent.makeAlert(
- "Please selection some text before calling replace quotes.", nwAlert.ERROR
+ self.tr("Please selection some text before calling replace quotes."),
+ nwAlert.ERROR
)
return
@@ -1530,9 +1544,7 @@ class GuiDocEditor(QTextEdit):
theText = newText
cOffset -= 0
else:
- logger.error(
- "Unknown or unsupported block format requested: %s" % str(docAction)
- )
+ logger.error("Unknown or unsupported block format requested: %s" % str(docAction))
return False
# Replace the block text
@@ -1817,12 +1829,12 @@ class GuiDocEditSearch(QFrame):
# ==========
self.searchBox = QLineEdit(self)
self.searchBox.setFont(boxFont)
- self.searchBox.setPlaceholderText("Search")
+ self.searchBox.setPlaceholderText(self.tr("Search"))
self.searchBox.returnPressed.connect(self._doSearch)
self.replaceBox = QLineEdit(self)
self.replaceBox.setFont(boxFont)
- self.replaceBox.setPlaceholderText("Replace")
+ self.replaceBox.setPlaceholderText(self.tr("Replace"))
self.replaceBox.returnPressed.connect(self._doReplace)
self.searchOpt = QToolBar(self)
@@ -1831,44 +1843,44 @@ class GuiDocEditSearch(QFrame):
self.searchOpt.setContentsMargins(0, 0, 0, 0)
self.searchOpt.setStyleSheet(r"QToolBar {padding: 0;}")
- self.searchLabel = QLabel("Search")
+ self.searchLabel = QLabel(self.tr("Search"))
self.searchLabel.setFont(boxFont)
self.searchLabel.setIndent(self.mainConf.pxInt(6))
- self.toggleCase = QAction("Case Sensitive", self)
- self.toggleCase.setToolTip("Match case")
+ self.toggleCase = QAction(self.tr("Case Sensitive"), self)
+ self.toggleCase.setToolTip(self.tr("Match case"))
self.toggleCase.setIcon(self.theTheme.getIcon("search_case"))
self.toggleCase.setCheckable(True)
self.toggleCase.setChecked(self.isCaseSense)
self.toggleCase.toggled.connect(self._doToggleCase)
self.searchOpt.addAction(self.toggleCase)
- self.toggleWord = QAction("Whole Words Only", self)
- self.toggleWord.setToolTip("Match whole words")
+ self.toggleWord = QAction(self.tr("Whole Words Only"), self)
+ self.toggleWord.setToolTip(self.tr("Match whole words"))
self.toggleWord.setIcon(self.theTheme.getIcon("search_word"))
self.toggleWord.setCheckable(True)
self.toggleWord.setChecked(self.isWholeWord)
self.toggleWord.toggled.connect(self._doToggleWord)
self.searchOpt.addAction(self.toggleWord)
- self.toggleRegEx = QAction("RegEx Mode", self)
- self.toggleRegEx.setToolTip("Use regular expressions (requires Qt 5.3)")
+ self.toggleRegEx = QAction(self.tr("RegEx Mode"), self)
+ 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)
self.toggleRegEx.toggled.connect(self._doToggleRegEx)
self.searchOpt.addAction(self.toggleRegEx)
- self.toggleLoop = QAction("Loop Search", self)
- self.toggleLoop.setToolTip("Loop the search when reaching the end")
+ self.toggleLoop = QAction(self.tr("Loop Search"), self)
+ self.toggleLoop.setToolTip(self.tr("Loop the search when reaching the end"))
self.toggleLoop.setIcon(self.theTheme.getIcon("search_loop"))
self.toggleLoop.setCheckable(True)
self.toggleLoop.setChecked(self.doLoop)
self.toggleLoop.toggled.connect(self._doToggleLoop)
self.searchOpt.addAction(self.toggleLoop)
- self.toggleProject = QAction("Search Next File", self)
- self.toggleProject.setToolTip("Continue searching in the next file")
+ self.toggleProject = QAction(self.tr("Search Next File"), self)
+ self.toggleProject.setToolTip(self.tr("Continue searching in the next file"))
self.toggleProject.setIcon(self.theTheme.getIcon("search_project"))
self.toggleProject.setCheckable(True)
self.toggleProject.setChecked(self.doNextFile)
@@ -1877,8 +1889,8 @@ class GuiDocEditSearch(QFrame):
self.searchOpt.addSeparator()
- self.toggleMatchCap = QAction("Preserve Case", self)
- self.toggleMatchCap.setToolTip("Preserve case on replace")
+ self.toggleMatchCap = QAction(self.tr("Preserve Case"), self)
+ self.toggleMatchCap.setToolTip(self.tr("Preserve case on replace"))
self.toggleMatchCap.setIcon(self.theTheme.getIcon("search_preserve"))
self.toggleMatchCap.setCheckable(True)
self.toggleMatchCap.setChecked(self.doMatchCap)
@@ -1887,8 +1899,8 @@ class GuiDocEditSearch(QFrame):
self.searchOpt.addSeparator()
- self.cancelSearch = QAction("Close Search", self)
- self.cancelSearch.setToolTip("Close the search box [Esc]")
+ self.cancelSearch = QAction(self.tr("Close Search"), self)
+ self.cancelSearch.setToolTip(self.tr("Close the search box [{0}]").format("Esc"))
self.cancelSearch.setIcon(self.theTheme.getIcon("search_cancel"))
self.cancelSearch.triggered.connect(self._doClose)
self.searchOpt.addAction(self.cancelSearch)
@@ -1900,18 +1912,18 @@ class GuiDocEditSearch(QFrame):
self.showReplace = QToolButton(self)
self.showReplace.setArrowType(Qt.RightArrow)
self.showReplace.setCheckable(True)
- self.showReplace.setToolTip("Show/hide the replace text box")
+ self.showReplace.setToolTip(self.tr("Show/hide the replace text box"))
self.showReplace.setStyleSheet(r"QToolButton {border: none; background: transparent;}")
self.showReplace.toggled.connect(self._doToggleReplace)
self.searchButton = QPushButton(self.theTheme.getIcon("search"), "")
self.searchButton.setFixedSize(QSize(bPx, bPx))
- self.searchButton.setToolTip("Find in current document")
+ self.searchButton.setToolTip(self.tr("Find in current document"))
self.searchButton.clicked.connect(self._doSearch)
self.replaceButton = QPushButton(self.theTheme.getIcon("search-replace"), "")
self.replaceButton.setFixedSize(QSize(bPx, bPx))
- self.replaceButton.setToolTip("Find and replace in current document")
+ self.replaceButton.setToolTip(self.tr("Find and replace in current document"))
self.replaceButton.clicked.connect(self._doReplace)
self.mainBox.addWidget(self.searchLabel, 0, 0, 1, 2, Qt.AlignLeft)
@@ -2204,7 +2216,7 @@ class GuiDocEditHeader(QWidget):
self.editButton.setStyleSheet(buttonStyle)
self.editButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.editButton.setVisible(False)
- self.editButton.setToolTip("Edit document meta")
+ self.editButton.setToolTip(self.tr("Edit document meta"))
self.editButton.clicked.connect(self._editDocument)
self.searchButton = QToolButton(self)
@@ -2215,7 +2227,7 @@ class GuiDocEditHeader(QWidget):
self.searchButton.setStyleSheet(buttonStyle)
self.searchButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.searchButton.setVisible(False)
- self.searchButton.setToolTip("Search document")
+ self.searchButton.setToolTip(self.tr("Search document"))
self.searchButton.clicked.connect(self._searchDocument)
self.minmaxButton = QToolButton(self)
@@ -2226,7 +2238,7 @@ class GuiDocEditHeader(QWidget):
self.minmaxButton.setStyleSheet(buttonStyle)
self.minmaxButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.minmaxButton.setVisible(False)
- self.minmaxButton.setToolTip("Toggle Focus Mode")
+ self.minmaxButton.setToolTip(self.tr("Toggle Focus Mode"))
self.minmaxButton.clicked.connect(self._minmaxDocument)
self.closeButton = QToolButton(self)
@@ -2237,7 +2249,7 @@ class GuiDocEditHeader(QWidget):
self.closeButton.setStyleSheet(buttonStyle)
self.closeButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.closeButton.setVisible(False)
- self.closeButton.setToolTip("Close the document")
+ self.closeButton.setToolTip(self.tr("Close the document"))
self.closeButton.clicked.connect(self._closeDocument)
# Assemble Layout
@@ -2413,7 +2425,7 @@ class GuiDocEditFooter(QWidget):
self.statusIcon.setFixedHeight(self.sPx)
self.statusIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop)
- self.statusText = QLabel("Status")
+ self.statusText = QLabel(self.tr("Status"))
self.statusText.setIndent(0)
self.statusText.setMargin(0)
self.statusText.setContentsMargins(0, 0, 0, 0)
@@ -2429,7 +2441,7 @@ class GuiDocEditFooter(QWidget):
self.linesIcon.setFixedHeight(self.sPx)
self.linesIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop)
- self.linesText = QLabel("Line: 0")
+ self.linesText = QLabel("")
self.linesText.setIndent(0)
self.linesText.setMargin(0)
self.linesText.setContentsMargins(0, 0, 0, 0)
@@ -2445,7 +2457,7 @@ class GuiDocEditFooter(QWidget):
self.wordsIcon.setFixedHeight(self.sPx)
self.wordsIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop)
- self.wordsText = QLabel("Words: 0")
+ self.wordsText = QLabel("")
self.wordsText.setIndent(0)
self.wordsText.setMargin(0)
self.wordsText.setContentsMargins(0, 0, 0, 0)
@@ -2476,6 +2488,8 @@ class GuiDocEditFooter(QWidget):
# Fix the Colours
self.matchColours()
+ self.updateLineCount()
+ self.updateCounts()
logger.debug("GuiDocEditFooter initialisation complete")
@@ -2532,8 +2546,8 @@ class GuiDocEditFooter(QWidget):
theIcon = self.theParent.importIcons[iStatus]
sIcon = theIcon.pixmap(self.sPx, self.sPx)
- sClass = nwLabels.CLASS_NAME[self.theItem.itemClass]
- sLayout = 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)
@@ -2552,7 +2566,9 @@ class GuiDocEditFooter(QWidget):
iLine = theCursor.blockNumber() + 1
iDist = 100*iLine/self.docEditor.qDocument.blockCount()
- self.linesText.setText(f"Line: {iLine:n} ({iDist:.0f}\u202f%)")
+ self.linesText.setText(
+ self.tr("Line: {0} ({1})").format(f"{iLine:n}", f"{iDist:.0f} %")
+ )
return
@@ -2566,10 +2582,14 @@ class GuiDocEditFooter(QWidget):
wCount = self.theItem.wordCount
wDiff = wCount - self.theItem.initCount
- self.wordsText.setText(f"Words: {wCount:n} ({wDiff:+n})")
+ self.wordsText.setText(
+ self.tr("Words: {0} ({1})").format(f"{wCount:n}", f"{wDiff:+n}")
+ )
byteSize = self.docEditor.qDocument.characterCount()
- self.wordsText.setToolTip(f"Document size is {byteSize:n} bytes")
+ self.wordsText.setToolTip(
+ 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 d3004ec1..2d2c8887 100644
--- a/nw/gui/docmerge.py
+++ b/nw/gui/docmerge.py
@@ -53,11 +53,11 @@ class GuiDocMerge(QDialog):
self.sourceItem = None
self.outerBox = QVBoxLayout()
- self.setWindowTitle("Merge Documents")
+ self.setWindowTitle(self.tr("Merge Documents"))
- self.headLabel = QLabel("Documents to Merge")
+ self.headLabel = QLabel("%s" % self.tr("Documents to Merge"))
self.helpLabel = QHelpLabel(
- "Drag and drop items to change the order.", self.theParent.theTheme.helpText
+ self.tr("Drag and drop items to change the order."), self.theParent.theTheme.helpText
)
self.listBox = QListWidget()
@@ -66,6 +66,8 @@ class GuiDocMerge(QDialog):
self.listBox.setMinimumHeight(self.mainConf.pxInt(180))
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+ self.buttonBox.button(QDialogButtonBox.Ok).setText(self.tr("Ok"))
+ self.buttonBox.button(QDialogButtonBox.Cancel).setText(self.tr("Cancel"))
self.buttonBox.accepted.connect(self._doMerge)
self.buttonBox.rejected.connect(self._doClose)
@@ -102,9 +104,9 @@ class GuiDocMerge(QDialog):
finalOrder.append(self.listBox.item(i).data(Qt.UserRole))
if len(finalOrder) == 0:
- self.theParent.makeAlert((
- "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)
@@ -114,16 +116,16 @@ class GuiDocMerge(QDialog):
theText += "\n\n"
if self.sourceItem is None:
- self.theParent.makeAlert((
- "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((
- "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)
@@ -164,9 +166,9 @@ class GuiDocMerge(QDialog):
if nwItem is None:
return
if nwItem.itemType is not nwItemType.FOLDER:
- self.theParent.makeAlert((
- "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 a06aaa67..b72f9b3c 100644
--- a/nw/gui/docsplit.py
+++ b/nw/gui/docsplit.py
@@ -56,11 +56,12 @@ class GuiDocSplit(QDialog):
self.sourceItem = None
self.outerBox = QVBoxLayout()
- self.setWindowTitle("Split Document")
+ self.setWindowTitle(self.tr("Split Document"))
- self.headLabel = QLabel("Document Headers")
+ self.headLabel = QLabel("%s" % self.tr("Document Headers"))
self.helpLabel = QHelpLabel(
- "Select the maximum level to split into files.", self.theParent.theTheme.helpText
+ self.tr("Select the maximum level to split into files."),
+ self.theParent.theTheme.helpText
)
self.listBox = QListWidget()
@@ -69,10 +70,10 @@ class GuiDocSplit(QDialog):
self.listBox.setMinimumHeight(self.mainConf.pxInt(180))
self.splitLevel = QComboBox(self)
- self.splitLevel.addItem("Split on Header Level 1 (Title)", 1)
- self.splitLevel.addItem("Split up to Header Level 2 (Chapter)", 2)
- self.splitLevel.addItem("Split up to Header Level 3 (Scene)", 3)
- self.splitLevel.addItem("Split up to Header Level 4 (Section)", 4)
+ self.splitLevel.addItem(self.tr("Split on Header Level 1 (Title)"), 1)
+ self.splitLevel.addItem(self.tr("Split up to Header Level 2 (Chapter)"), 2)
+ self.splitLevel.addItem(self.tr("Split up to Header Level 3 (Scene)"), 3)
+ self.splitLevel.addItem(self.tr("Split up to Header Level 4 (Section)"), 4)
spIndex = self.splitLevel.findData(
self.optState.getInt("GuiDocSplit", "spLevel", 3)
)
@@ -81,6 +82,8 @@ class GuiDocSplit(QDialog):
self.splitLevel.currentIndexChanged.connect(self._populateList)
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+ self.buttonBox.button(QDialogButtonBox.Ok).setText(self.tr("Ok"))
+ self.buttonBox.button(QDialogButtonBox.Cancel).setText(self.tr("Cancel"))
self.buttonBox.accepted.connect(self._doSplit)
self.buttonBox.rejected.connect(self._doClose)
@@ -115,16 +118,16 @@ class GuiDocSplit(QDialog):
logger.verbose("GuiDocSplit split button clicked")
if self.sourceItem is None:
- self.theParent.makeAlert((
- "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((
- "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)
@@ -147,26 +150,34 @@ class GuiDocSplit(QDialog):
nFiles = len(finalOrder)
if nFiles == 0:
- self.theParent.makeAlert((
- "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((
- "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("Split Document", (
- "The document will be split into %d file(s) in a new folder. "
- "The original document will remain intact.
"
- "Continue with the splitting process?"
- ) % nFiles)
+ 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
@@ -242,9 +253,9 @@ class GuiDocSplit(QDialog):
if nwItem is None:
return
if nwItem.itemType is not nwItemType.FILE:
- self.theParent.makeAlert((
- "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 ddc0d876..638622f4 100644
--- a/nw/gui/docviewer.py
+++ b/nw/gui/docviewer.py
@@ -183,7 +183,7 @@ class GuiDocViewer(QTextBrowser):
except Exception:
logger.error("Failed to generate preview for document with handle '%s'" % tHandle)
nw.logException()
- self.setText("An error occurred while generating the preview.")
+ self.setText(self.tr("An error occurred while generating the preview."))
return False
# Refresh the tab stops
@@ -243,11 +243,14 @@ class GuiDocViewer(QTextBrowser):
logger.debug("Loading document from tag '%s'" % theTag)
tHandle, _, sTitle = self.theParent.theIndex.getTagSource(theTag)
if tHandle is None:
- self.theParent.makeAlert((
- "Could not find the reference for tag '%s'. It either doesn't "
- "exist, or the index is out of date. The index can be updated "
- "from the Tools menu, or by pressing F9."
- ) % theTag, nwAlert.ERROR)
+ 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
+ )
return False
else:
# Let the parent handle the opening as it also ensures that
@@ -415,7 +418,7 @@ class GuiDocViewer(QTextBrowser):
# ===================
if userSelection:
- mnuCopy = QAction("Copy", mnuContext)
+ mnuCopy = QAction(self.tr("Copy"), mnuContext)
mnuCopy.triggered.connect(lambda: self.docAction(nwDocAction.COPY))
mnuContext.addAction(mnuCopy)
@@ -424,17 +427,17 @@ class GuiDocViewer(QTextBrowser):
# Selections
# ==========
- mnuSelAll = QAction("Select All", mnuContext)
+ mnuSelAll = QAction(self.tr("Select All"), mnuContext)
mnuSelAll.triggered.connect(lambda: self.docAction(nwDocAction.SEL_ALL))
mnuContext.addAction(mnuSelAll)
- mnuSelWord = QAction("Select Word", mnuContext)
+ mnuSelWord = QAction(self.tr("Select Word"), mnuContext)
mnuSelWord.triggered.connect(
lambda: self._makePosSelection(QTextCursor.WordUnderCursor, thePos)
)
mnuContext.addAction(mnuSelWord)
- mnuSelPara = QAction("Select Paragraph", mnuContext)
+ mnuSelPara = QAction(self.tr("Select Paragraph"), mnuContext)
mnuSelPara.triggered.connect(
lambda: self._makePosSelection(QTextCursor.BlockUnderCursor, thePos)
)
@@ -740,7 +743,7 @@ class GuiDocViewHeader(QWidget):
self.backButton.setStyleSheet(buttonStyle)
self.backButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.backButton.setVisible(False)
- self.backButton.setToolTip("Go backward")
+ self.backButton.setToolTip(self.tr("Go backward"))
self.backButton.clicked.connect(self.docViewer.navBackward)
self.forwardButton = QToolButton(self)
@@ -751,7 +754,7 @@ class GuiDocViewHeader(QWidget):
self.forwardButton.setStyleSheet(buttonStyle)
self.forwardButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.forwardButton.setVisible(False)
- self.forwardButton.setToolTip("Go forward")
+ self.forwardButton.setToolTip(self.tr("Go forward"))
self.forwardButton.clicked.connect(self.docViewer.navForward)
self.refreshButton = QToolButton(self)
@@ -762,7 +765,7 @@ class GuiDocViewHeader(QWidget):
self.refreshButton.setStyleSheet(buttonStyle)
self.refreshButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.refreshButton.setVisible(False)
- self.refreshButton.setToolTip("Reload the document")
+ self.refreshButton.setToolTip(self.tr("Reload the document"))
self.refreshButton.clicked.connect(self._refreshDocument)
self.closeButton = QToolButton(self)
@@ -773,7 +776,7 @@ class GuiDocViewHeader(QWidget):
self.closeButton.setStyleSheet(buttonStyle)
self.closeButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.closeButton.setVisible(False)
- self.closeButton.setToolTip("Close the document")
+ self.closeButton.setToolTip(self.tr("Close the document"))
self.closeButton.clicked.connect(self._closeDocument)
# Assemble Layout
@@ -944,7 +947,7 @@ class GuiDocViewFooter(QWidget):
self.showHide.setIconSize(QSize(fPx, fPx))
self.showHide.setFixedSize(QSize(fPx, fPx))
self.showHide.clicked.connect(self._doShowHide)
- self.showHide.setToolTip("Show/hide the references panel")
+ self.showHide.setToolTip(self.tr("Show/hide the references panel"))
# Sticky Button
self.stickyRefs = QToolButton(self)
@@ -955,9 +958,9 @@ class GuiDocViewFooter(QWidget):
self.stickyRefs.setIconSize(QSize(fPx, fPx))
self.stickyRefs.setFixedSize(QSize(fPx, fPx))
self.stickyRefs.toggled.connect(self._doToggleSticky)
- self.stickyRefs.setToolTip(
+ self.stickyRefs.setToolTip(self.tr(
"Activate to freeze the content of the references panel when changing document"
- )
+ ))
# Show Comments
self.showComments = QToolButton(self)
@@ -969,7 +972,7 @@ class GuiDocViewFooter(QWidget):
self.showComments.setIconSize(QSize(fPx, fPx))
self.showComments.setFixedSize(QSize(fPx, fPx))
self.showComments.toggled.connect(self._doToggleComments)
- self.showComments.setToolTip("Show comments")
+ self.showComments.setToolTip(self.tr("Show comments"))
# Show Synopsis
self.showSynopsis = QToolButton(self)
@@ -981,10 +984,10 @@ class GuiDocViewFooter(QWidget):
self.showSynopsis.setIconSize(QSize(fPx, fPx))
self.showSynopsis.setFixedSize(QSize(fPx, fPx))
self.showSynopsis.toggled.connect(self._doToggleSynopsis)
- self.showSynopsis.setToolTip("Show synopsis comments")
+ self.showSynopsis.setToolTip(self.tr("Show synopsis comments"))
# Labels
- self.lblRefs = QLabel("References")
+ self.lblRefs = QLabel(self.tr("References"))
self.lblRefs.setBuddy(self.showHide)
self.lblRefs.setIndent(0)
self.lblRefs.setMargin(0)
@@ -993,7 +996,7 @@ class GuiDocViewFooter(QWidget):
self.lblRefs.setFixedHeight(fPx)
self.lblRefs.setAlignment(Qt.AlignLeft | Qt.AlignTop)
- self.lblSticky = QLabel("Sticky")
+ self.lblSticky = QLabel(self.tr("Sticky"))
self.lblSticky.setBuddy(self.stickyRefs)
self.lblSticky.setIndent(0)
self.lblSticky.setMargin(0)
@@ -1002,7 +1005,7 @@ class GuiDocViewFooter(QWidget):
self.lblSticky.setFixedHeight(fPx)
self.lblSticky.setAlignment(Qt.AlignLeft | Qt.AlignTop)
- self.lblComments = QLabel("Comments")
+ self.lblComments = QLabel(self.tr("Comments"))
self.lblComments.setBuddy(self.showComments)
self.lblComments.setIndent(0)
self.lblComments.setMargin(0)
@@ -1011,7 +1014,7 @@ class GuiDocViewFooter(QWidget):
self.lblComments.setFixedHeight(fPx)
self.lblComments.setAlignment(Qt.AlignLeft | Qt.AlignTop)
- self.lblSynopsis = QLabel("Synopsis")
+ self.lblSynopsis = QLabel(self.tr("Synopsis"))
self.lblSynopsis.setBuddy(self.showSynopsis)
self.lblSynopsis.setIndent(0)
self.lblSynopsis.setMargin(0)
diff --git a/nw/gui/itemdetails.py b/nw/gui/itemdetails.py
index 52584d40..608aa6ba 100644
--- a/nw/gui/itemdetails.py
+++ b/nw/gui/itemdetails.py
@@ -32,7 +32,7 @@ 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__)
@@ -67,7 +67,7 @@ class GuiItemDetails(QWidget):
self.fntValue.setPointSizeF(0.9*fPt)
# Label
- self.labelName = QLabel("Label")
+ self.labelName = QLabel(self.tr("Label"))
self.labelName.setFont(self.fntLabel)
self.labelName.setAlignment(Qt.AlignLeft | Qt.AlignBaseline)
@@ -80,7 +80,7 @@ class GuiItemDetails(QWidget):
self.labelData.setWordWrap(True)
# Status
- self.statusName = QLabel("Status")
+ self.statusName = QLabel(self.tr("Status"))
self.statusName.setFont(self.fntLabel)
self.statusName.setAlignment(Qt.AlignLeft)
@@ -92,7 +92,7 @@ class GuiItemDetails(QWidget):
self.statusData.setAlignment(Qt.AlignLeft)
# Class
- self.className = QLabel("Class")
+ self.className = QLabel(self.tr("Class"))
self.className.setFont(self.fntLabel)
self.className.setAlignment(Qt.AlignLeft)
@@ -105,7 +105,7 @@ class GuiItemDetails(QWidget):
self.classData.setAlignment(Qt.AlignLeft)
# Layout
- self.layoutName = QLabel("Layout")
+ self.layoutName = QLabel(self.tr("Layout"))
self.layoutName.setFont(self.fntLabel)
self.layoutName.setAlignment(Qt.AlignLeft)
@@ -118,7 +118,7 @@ class GuiItemDetails(QWidget):
self.layoutData.setAlignment(Qt.AlignLeft)
# Character Count
- self.cCountName = QLabel(" 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(" 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(" Paragraphs")
+ self.pCountName = QLabel(" "+self.tr("Paragraphs"))
self.pCountName.setFont(self.fntLabel)
self.pCountName.setAlignment(Qt.AlignRight)
@@ -259,17 +259,17 @@ class GuiItemDetails(QWidget):
iPx = int(round(0.8*self.theTheme.baseIconSize))
self.statusFlag.setPixmap(flagIcon.pixmap(iPx, iPx))
- self.classFlag.setText(nwLabels.CLASS_FLAG[nwItem.itemClass])
+ self.classFlag.setText(nwLabels.CLASS_FLAG[nwItem.itemClass]) # NO-I18N
if nwItem.itemLayout == nwItemLayout.NO_LAYOUT:
self.layoutFlag.setText("-")
else:
- self.layoutFlag.setText(nwLabels.LAYOUT_FLAG[nwItem.itemLayout])
+ self.layoutFlag.setText(nwLabels.LAYOUT_FLAG[nwItem.itemLayout]) # NO-I18N
self.labelData.setText(theLabel)
self.statusData.setText(nwItem.itemStatus)
- self.classData.setText(nwLabels.CLASS_NAME[nwItem.itemClass])
- self.layoutData.setText(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 63f67021..107172f0 100644
--- a/nw/gui/itemeditor.py
+++ b/nw/gui/itemeditor.py
@@ -34,7 +34,7 @@ from PyQt5.QtWidgets import (
)
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__)
@@ -58,7 +58,7 @@ class GuiItemEditor(QDialog):
if self.theItem is None:
self._doClose()
- self.setWindowTitle("Item Settings")
+ self.setWindowTitle(self.tr("Item Settings"))
mVd = self.mainConf.pxInt(220)
mSp = self.mainConf.pxInt(16)
@@ -103,10 +103,10 @@ class GuiItemEditor(QDialog):
for itemLayout in nwItemLayout:
if itemLayout in validLayouts:
- self.editLayout.addItem(nwLabels.LAYOUT_NAME[itemLayout], itemLayout)
+ self.editLayout.addItem(trConst(nwLabels.LAYOUT_NAME[itemLayout]), itemLayout)
# Export Switch
- self.textExport = QLabel("Include when building project")
+ self.textExport = QLabel(self.tr("Include when building project"))
self.editExport = QSwitch()
if self.theItem.itemType == nwItemType.FILE:
self.editExport.setEnabled(True)
@@ -117,6 +117,8 @@ class GuiItemEditor(QDialog):
# Buttons
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+ self.buttonBox.button(QDialogButtonBox.Ok).setText(self.tr("Ok"))
+ self.buttonBox.button(QDialogButtonBox.Cancel).setText(self.tr("Cancel"))
self.buttonBox.accepted.connect(self._doSave)
self.buttonBox.rejected.connect(self._doClose)
@@ -136,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("Label"), 0, 0, 1, 1)
- self.mainForm.addWidget(self.editName, 0, 1, 1, 2)
- self.mainForm.addWidget(QLabel("Status"), 1, 0, 1, 1)
- self.mainForm.addWidget(self.editStatus, 1, 1, 1, 2)
- self.mainForm.addWidget(QLabel("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 6d6267a9..dfba6ce1 100644
--- a/nw/gui/mainmenu.py
+++ b/nw/gui/mainmenu.py
@@ -32,8 +32,8 @@ 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__)
@@ -180,31 +180,31 @@ class GuiMainMenu(QMenuBar):
"""Assemble the Project menu.
"""
# Project
- self.projMenu = self.addMenu("&Project")
+ self.projMenu = self.addMenu(self.tr("&Project"))
# Project > New Project
- self.aNewProject = QAction("New Project", self)
- self.aNewProject.setStatusTip("Create new project")
+ self.aNewProject = QAction(self.tr("New Project"), self)
+ self.aNewProject.setStatusTip(self.tr("Create new project"))
self.aNewProject.triggered.connect(lambda: self.theParent.newProject(None))
self.projMenu.addAction(self.aNewProject)
# Project > Open Project
- self.aOpenProject = QAction("Open Project", self)
- self.aOpenProject.setStatusTip("Open project")
+ self.aOpenProject = QAction(self.tr("Open Project"), self)
+ self.aOpenProject.setStatusTip(self.tr("Open project"))
self.aOpenProject.setShortcut("Ctrl+Shift+O")
self.aOpenProject.triggered.connect(lambda: self.theParent.showProjectLoadDialog())
self.projMenu.addAction(self.aOpenProject)
# Project > Save Project
- self.aSaveProject = QAction("Save Project", self)
- self.aSaveProject.setStatusTip("Save project")
+ self.aSaveProject = QAction(self.tr("Save Project"), self)
+ self.aSaveProject.setStatusTip(self.tr("Save project"))
self.aSaveProject.setShortcut("Ctrl+Shift+S")
self.aSaveProject.triggered.connect(lambda: self.theParent.saveProject())
self.projMenu.addAction(self.aSaveProject)
# Project > Close Project
- self.aCloseProject = QAction("Close Project", self)
- self.aCloseProject.setStatusTip("Close project")
+ self.aCloseProject = QAction(self.tr("Close Project"), self)
+ self.aCloseProject.setStatusTip(self.tr("Close project"))
self.aCloseProject.setShortcut("Ctrl+Shift+W")
self.aCloseProject.triggered.connect(lambda: self.theParent.closeProject(False))
self.projMenu.addAction(self.aCloseProject)
@@ -213,15 +213,15 @@ class GuiMainMenu(QMenuBar):
self.projMenu.addSeparator()
# Project > Project Settings
- self.aProjectSettings = QAction("Project Settings", self)
- self.aProjectSettings.setStatusTip("Project settings")
+ self.aProjectSettings = QAction(self.tr("Project Settings"), self)
+ self.aProjectSettings.setStatusTip(self.tr("Project settings"))
self.aProjectSettings.setShortcut("Ctrl+Shift+,")
self.aProjectSettings.triggered.connect(lambda: self.theParent.showProjectSettingsDialog())
self.projMenu.addAction(self.aProjectSettings)
# Project > Project Details
- self.aProjectDetails = QAction("Project Details", self)
- self.aProjectDetails.setStatusTip("Project details")
+ self.aProjectDetails = QAction(self.tr("Project Details"), self)
+ self.aProjectDetails.setStatusTip(self.tr("Project details"))
self.aProjectDetails.setShortcut("Shift+F6")
self.aProjectDetails.triggered.connect(lambda: self.theParent.showProjectDetailsDialog())
self.projMenu.addAction(self.aProjectDetails)
@@ -230,17 +230,17 @@ class GuiMainMenu(QMenuBar):
self.projMenu.addSeparator()
# Project > New Root
- self.rootMenu = self.projMenu.addMenu("Create Root Folder")
+ self.rootMenu = self.projMenu.addMenu(self.tr("Create Root Folder"))
self.rootItems = {}
- self.rootItems[nwItemClass.NOVEL] = QAction("Novel Root", self.rootMenu)
- self.rootItems[nwItemClass.PLOT] = QAction("Plot Root", self.rootMenu)
- self.rootItems[nwItemClass.CHARACTER] = QAction("Character Root", self.rootMenu)
- self.rootItems[nwItemClass.WORLD] = QAction("Location Root", self.rootMenu)
- self.rootItems[nwItemClass.TIMELINE] = QAction("Timeline Root", self.rootMenu)
- self.rootItems[nwItemClass.OBJECT] = QAction("Object Root", self.rootMenu)
- self.rootItems[nwItemClass.ENTITY] = QAction("Entity Root", self.rootMenu)
- self.rootItems[nwItemClass.CUSTOM] = QAction("Custom Root", self.rootMenu)
- self.rootItems[nwItemClass.ARCHIVE] = QAction("Outtakes Root", self.rootMenu)
+ self.rootItems[nwItemClass.NOVEL] = QAction(self.tr("Novel Root"), self.rootMenu)
+ self.rootItems[nwItemClass.PLOT] = QAction(self.tr("Plot Root"), self.rootMenu)
+ self.rootItems[nwItemClass.CHARACTER] = QAction(self.tr("Character Root"), self.rootMenu)
+ self.rootItems[nwItemClass.WORLD] = QAction(self.tr("Location Root"), self.rootMenu)
+ self.rootItems[nwItemClass.TIMELINE] = QAction(self.tr("Timeline Root"), self.rootMenu)
+ self.rootItems[nwItemClass.OBJECT] = QAction(self.tr("Object Root"), self.rootMenu)
+ self.rootItems[nwItemClass.ENTITY] = QAction(self.tr("Entity Root"), self.rootMenu)
+ self.rootItems[nwItemClass.CUSTOM] = QAction(self.tr("Custom Root"), self.rootMenu)
+ self.rootItems[nwItemClass.ARCHIVE] = QAction(self.tr("Outtakes Root"), self.rootMenu)
nCount = 0
for itemClass in self.rootItems.keys():
nCount += 1 # This forces the lambdas to be unique
@@ -250,8 +250,8 @@ class GuiMainMenu(QMenuBar):
self.rootMenu.addAction(self.rootItems[itemClass])
# Project > New Folder
- self.aCreateFolder = QAction("Create Folder", self)
- self.aCreateFolder.setStatusTip("Create folder")
+ self.aCreateFolder = QAction(self.tr("Create Folder"), self)
+ self.aCreateFolder.setStatusTip(self.tr("Create folder"))
self.aCreateFolder.setShortcut("Ctrl+Shift+N")
self.aCreateFolder.triggered.connect(lambda: self._newTreeItem(nwItemType.FOLDER, None))
self.projMenu.addAction(self.aCreateFolder)
@@ -260,43 +260,43 @@ class GuiMainMenu(QMenuBar):
self.projMenu.addSeparator()
# Project > Edit
- self.aEditItem = QAction("Edit Item", self)
- self.aEditItem.setStatusTip("Change project item settings")
+ self.aEditItem = QAction(self.tr("Edit Item"), self)
+ self.aEditItem.setStatusTip(self.tr("Change project item settings"))
self.aEditItem.setShortcuts(["Ctrl+E", "F2"])
self.aEditItem.triggered.connect(lambda: self.theParent.editItem(None))
self.projMenu.addAction(self.aEditItem)
# Project > Delete
- self.aDeleteItem = QAction("Delete Item", self)
- self.aDeleteItem.setStatusTip("Delete selected project item")
+ self.aDeleteItem = QAction(self.tr("Delete Item"), self)
+ self.aDeleteItem.setStatusTip(self.tr("Delete selected project item"))
self.aDeleteItem.setShortcut("Ctrl+Shift+Del")
self.aDeleteItem.triggered.connect(lambda: self.theParent.treeView.deleteItem(None))
self.projMenu.addAction(self.aDeleteItem)
# Project > Move Up
- self.aMoveUp = QAction("Move Item Up", self)
- self.aMoveUp.setStatusTip("Move project item up")
+ self.aMoveUp = QAction(self.tr("Move Item Up"), self)
+ self.aMoveUp.setStatusTip(self.tr("Move project item up"))
self.aMoveUp.setShortcut("Ctrl+Up")
self.aMoveUp.triggered.connect(lambda: self._moveTreeItem(-1))
self.projMenu.addAction(self.aMoveUp)
# Project > Move Down
- self.aMoveDown = QAction("Move Item Down", self)
- self.aMoveDown.setStatusTip("Move project item down")
+ self.aMoveDown = QAction(self.tr("Move Item Down"), self)
+ self.aMoveDown.setStatusTip(self.tr("Move project item down"))
self.aMoveDown.setShortcut("Ctrl+Down")
self.aMoveDown.triggered.connect(lambda: self._moveTreeItem(1))
self.projMenu.addAction(self.aMoveDown)
# Project > Undo Last Action
- self.aMoveUndo = QAction("Undo Last Move", self)
- self.aMoveUndo.setStatusTip("Undo last item move")
+ self.aMoveUndo = QAction(self.tr("Undo Last Move"), self)
+ self.aMoveUndo.setStatusTip(self.tr("Undo last item move"))
self.aMoveUndo.setShortcut("Ctrl+Shift+Z")
self.aMoveUndo.triggered.connect(lambda: self.theParent.treeView.undoLastMove())
self.projMenu.addAction(self.aMoveUndo)
# Project > Empty Trash
- self.aEmptyTrash = QAction("Empty Trash", self)
- self.aEmptyTrash.setStatusTip("Permanently delete all files in the Trash folder")
+ self.aEmptyTrash = QAction(self.tr("Empty Trash"), self)
+ self.aEmptyTrash.setStatusTip(self.tr("Permanently delete all files in the Trash folder"))
self.aEmptyTrash.triggered.connect(lambda: self.theParent.treeView.emptyTrash())
self.projMenu.addAction(self.aEmptyTrash)
@@ -304,8 +304,8 @@ class GuiMainMenu(QMenuBar):
self.projMenu.addSeparator()
# Project > Exit
- self.aExitNW = QAction("Exit", self)
- self.aExitNW.setStatusTip("Exit novelWriter")
+ self.aExitNW = QAction(self.tr("Exit"), self)
+ self.aExitNW.setStatusTip(self.tr("Exit novelWriter"))
self.aExitNW.setShortcut("Ctrl+Q")
self.aExitNW.setMenuRole(QAction.QuitRole)
self.aExitNW.triggered.connect(lambda: self.theParent.closeMain())
@@ -317,32 +317,32 @@ class GuiMainMenu(QMenuBar):
"""Assemble the Document menu.
"""
# Document
- self.docuMenu = self.addMenu("&Document")
+ self.docuMenu = self.addMenu(self.tr("&Document"))
# Document > New
- self.aNewDoc = QAction("New Document", self)
- self.aNewDoc.setStatusTip("Create new document")
+ self.aNewDoc = QAction(self.tr("New Document"), self)
+ self.aNewDoc.setStatusTip(self.tr("Create new document"))
self.aNewDoc.setShortcut("Ctrl+N")
self.aNewDoc.triggered.connect(lambda: self._newTreeItem(nwItemType.FILE, None))
self.docuMenu.addAction(self.aNewDoc)
# Document > Open
- self.aOpenDoc = QAction("Open Document", self)
- self.aOpenDoc.setStatusTip("Open selected document")
+ self.aOpenDoc = QAction(self.tr("Open Document"), self)
+ self.aOpenDoc.setStatusTip(self.tr("Open selected document"))
self.aOpenDoc.setShortcut("Ctrl+O")
self.aOpenDoc.triggered.connect(lambda: self.theParent.openSelectedItem())
self.docuMenu.addAction(self.aOpenDoc)
# Document > Save
- self.aSaveDoc = QAction("Save Document", self)
- self.aSaveDoc.setStatusTip("Save current document")
+ self.aSaveDoc = QAction(self.tr("Save Document"), self)
+ self.aSaveDoc.setStatusTip(self.tr("Save current document"))
self.aSaveDoc.setShortcut("Ctrl+S")
self.aSaveDoc.triggered.connect(lambda: self.theParent.saveDocument())
self.docuMenu.addAction(self.aSaveDoc)
# Document > Close
- self.aCloseDoc = QAction("Close Document", self)
- self.aCloseDoc.setStatusTip("Close current document")
+ self.aCloseDoc = QAction(self.tr("Close Document"), self)
+ self.aCloseDoc.setStatusTip(self.tr("Close current document"))
self.aCloseDoc.setShortcut("Ctrl+W")
self.aCloseDoc.triggered.connect(lambda: self.theParent.closeDocEditor())
self.docuMenu.addAction(self.aCloseDoc)
@@ -351,15 +351,15 @@ class GuiMainMenu(QMenuBar):
self.docuMenu.addSeparator()
# Document > Preview
- self.aViewDoc = QAction("View Document", self)
- self.aViewDoc.setStatusTip("View document as HTML")
+ self.aViewDoc = QAction(self.tr("View Document"), self)
+ self.aViewDoc.setStatusTip(self.tr("View document as HTML"))
self.aViewDoc.setShortcut("Ctrl+R")
self.aViewDoc.triggered.connect(lambda: self.theParent.viewDocument(None))
self.docuMenu.addAction(self.aViewDoc)
# Document > Close Preview
- self.aCloseView = QAction("Close Document View", self)
- self.aCloseView.setStatusTip("Close document view pane")
+ self.aCloseView = QAction(self.tr("Close Document View"), self)
+ self.aCloseView.setStatusTip(self.tr("Close document view pane"))
self.aCloseView.setShortcut("Ctrl+Shift+R")
self.aCloseView.triggered.connect(lambda: self.theParent.closeDocViewer())
self.docuMenu.addAction(self.aCloseView)
@@ -368,29 +368,31 @@ class GuiMainMenu(QMenuBar):
self.docuMenu.addSeparator()
# Document > Show File Details
- self.aFileDetails = QAction("Show File Details", self)
+ self.aFileDetails = QAction(self.tr("Show File Details"), self)
self.aFileDetails.setStatusTip(
- "Shows a message box with the document location in the project folder"
+ self.tr("Shows a message box with the document location in the project folder")
)
self.aFileDetails.triggered.connect(lambda: self.theParent.docEditor.revealLocation())
self.docuMenu.addAction(self.aFileDetails)
# Document > Import From File
- self.aImportFile = QAction("Import from File", self)
- self.aImportFile.setStatusTip("Import document from a text or markdown file")
+ self.aImportFile = QAction(self.tr("Import from File"), self)
+ self.aImportFile.setStatusTip(self.tr("Import document from a text or markdown file"))
self.aImportFile.setShortcut("Ctrl+Shift+I")
self.aImportFile.triggered.connect(lambda: self.theParent.importDocument())
self.docuMenu.addAction(self.aImportFile)
# Document > Merge Documents
- self.aMergeDocs = QAction("Merge Folder to Document", self)
- self.aMergeDocs.setStatusTip("Merge a folder of documents to a single document")
+ self.aMergeDocs = QAction(self.tr("Merge Folder to Document"), self)
+ self.aMergeDocs.setStatusTip(self.tr("Merge a folder of documents to a single document"))
self.aMergeDocs.triggered.connect(lambda: self.theParent.mergeDocuments())
self.docuMenu.addAction(self.aMergeDocs)
# Document > Split Document
- self.aSplitDoc = QAction("Split Document to Folder", self)
- self.aSplitDoc.setStatusTip("Split a document into a folder of multiple documents")
+ self.aSplitDoc = QAction(self.tr("Split Document to Folder"), self)
+ self.aSplitDoc.setStatusTip(
+ self.tr("Split a document into a folder of multiple documents")
+ )
self.aSplitDoc.triggered.connect(lambda: self.theParent.splitDocument())
self.docuMenu.addAction(self.aSplitDoc)
@@ -400,18 +402,18 @@ class GuiMainMenu(QMenuBar):
"""Assemble the Edit menu.
"""
# Edit
- self.editMenu = self.addMenu("&Edit")
+ self.editMenu = self.addMenu(self.tr("&Edit"))
# Edit > Undo
- self.aEditUndo = QAction("Undo", self)
- self.aEditUndo.setStatusTip("Undo last change")
+ self.aEditUndo = QAction(self.tr("Undo"), self)
+ self.aEditUndo.setStatusTip(self.tr("Undo last change"))
self.aEditUndo.setShortcut("Ctrl+Z")
self.aEditUndo.triggered.connect(lambda: self._docAction(nwDocAction.UNDO))
self.editMenu.addAction(self.aEditUndo)
# Edit > Redo
- self.aEditRedo = QAction("Redo", self)
- self.aEditRedo.setStatusTip("Redo last change")
+ self.aEditRedo = QAction(self.tr("Redo"), self)
+ self.aEditRedo.setStatusTip(self.tr("Redo last change"))
self.aEditRedo.setShortcut("Ctrl+Y")
self.aEditRedo.triggered.connect(lambda: self._docAction(nwDocAction.REDO))
self.editMenu.addAction(self.aEditRedo)
@@ -420,22 +422,22 @@ class GuiMainMenu(QMenuBar):
self.editMenu.addSeparator()
# Edit > Cut
- self.aEditCut = QAction("Cut", self)
- self.aEditCut.setStatusTip("Cut selected text")
+ self.aEditCut = QAction(self.tr("Cut"), self)
+ self.aEditCut.setStatusTip(self.tr("Cut selected text"))
self.aEditCut.setShortcut("Ctrl+X")
self.aEditCut.triggered.connect(lambda: self._docAction(nwDocAction.CUT))
self.editMenu.addAction(self.aEditCut)
# Edit > Copy
- self.aEditCopy = QAction("Copy", self)
- self.aEditCopy.setStatusTip("Copy selected text")
+ self.aEditCopy = QAction(self.tr("Copy"), self)
+ self.aEditCopy.setStatusTip(self.tr("Copy selected text"))
self.aEditCopy.setShortcut("Ctrl+C")
self.aEditCopy.triggered.connect(lambda: self._docAction(nwDocAction.COPY))
self.editMenu.addAction(self.aEditCopy)
# Edit > Paste
- self.aEditPaste = QAction("Paste", self)
- self.aEditPaste.setStatusTip("Paste text from clipboard")
+ self.aEditPaste = QAction(self.tr("Paste"), self)
+ self.aEditPaste.setStatusTip(self.tr("Paste text from clipboard"))
self.aEditPaste.setShortcut("Ctrl+V")
self.aEditPaste.triggered.connect(lambda: self._docAction(nwDocAction.PASTE))
self.editMenu.addAction(self.aEditPaste)
@@ -444,15 +446,15 @@ class GuiMainMenu(QMenuBar):
self.editMenu.addSeparator()
# Edit > Select All
- self.aSelectAll = QAction("Select All", self)
- self.aSelectAll.setStatusTip("Select all text in document")
+ self.aSelectAll = QAction(self.tr("Select All"), self)
+ self.aSelectAll.setStatusTip(self.tr("Select all text in document"))
self.aSelectAll.setShortcut("Ctrl+A")
self.aSelectAll.triggered.connect(lambda: self._docAction(nwDocAction.SEL_ALL))
self.editMenu.addAction(self.aSelectAll)
# Edit > Select Paragraph
- self.aSelectPar = QAction("Select Paragraph", self)
- self.aSelectPar.setStatusTip("Select all text in paragraph")
+ self.aSelectPar = QAction(self.tr("Select Paragraph"), self)
+ self.aSelectPar.setStatusTip(self.tr("Select all text in paragraph"))
self.aSelectPar.setShortcut("Ctrl+Shift+A")
self.aSelectPar.triggered.connect(lambda: self._docAction(nwDocAction.SEL_PARA))
self.editMenu.addAction(self.aSelectPar)
@@ -463,32 +465,32 @@ class GuiMainMenu(QMenuBar):
"""Assemble the View menu.
"""
# View
- self.viewMenu = self.addMenu("&View")
+ self.viewMenu = self.addMenu(self.tr("&View"))
# View > TreeView
- self.aFocusTree = QAction("Focus Project Tree", self)
- self.aFocusTree.setStatusTip("Move focus to project tree")
+ self.aFocusTree = QAction(self.tr("Focus Project Tree"), self)
+ self.aFocusTree.setStatusTip(self.tr("Move focus to project tree"))
self.aFocusTree.setShortcut("Alt+1")
self.aFocusTree.triggered.connect(lambda: self.theParent.setFocus(1))
self.viewMenu.addAction(self.aFocusTree)
# View > Document Pane 1
- self.aFocusEditor = QAction("Focus Document Editor", self)
- self.aFocusEditor.setStatusTip("Move focus to left document pane")
+ self.aFocusEditor = QAction(self.tr("Focus Document Editor"), self)
+ self.aFocusEditor.setStatusTip(self.tr("Move focus to left document pane"))
self.aFocusEditor.setShortcut("Alt+2")
self.aFocusEditor.triggered.connect(lambda: self.theParent.setFocus(2))
self.viewMenu.addAction(self.aFocusEditor)
# View > Document Pane 2
- self.aFocusView = QAction("Focus Document Viewer", self)
- self.aFocusView.setStatusTip("Move focus to right document pane")
+ self.aFocusView = QAction(self.tr("Focus Document Viewer"), self)
+ self.aFocusView.setStatusTip(self.tr("Move focus to right document pane"))
self.aFocusView.setShortcut("Alt+3")
self.aFocusView.triggered.connect(lambda: self.theParent.setFocus(3))
self.viewMenu.addAction(self.aFocusView)
# View > Outline
- self.aFocusOutline = QAction("Focus Outline", self)
- self.aFocusOutline.setStatusTip("Move focus to outline")
+ self.aFocusOutline = QAction(self.tr("Focus Outline"), self)
+ self.aFocusOutline.setStatusTip(self.tr("Move focus to outline"))
self.aFocusOutline.setShortcut("Alt+4")
self.aFocusOutline.triggered.connect(lambda: self.theParent.setFocus(4))
self.viewMenu.addAction(self.aFocusOutline)
@@ -497,15 +499,15 @@ class GuiMainMenu(QMenuBar):
self.viewMenu.addSeparator()
# View > Go Backward
- self.aViewPrev = QAction("Go Backward", self)
- self.aViewPrev.setStatusTip("Move backward in the view history of the right pane")
+ self.aViewPrev = QAction(self.tr("Go Backward"), self)
+ self.aViewPrev.setStatusTip(self.tr("Move backward in the view history of the right pane"))
self.aViewPrev.setShortcut("Alt+Left")
self.aViewPrev.triggered.connect(lambda: self.theParent.docViewer.navBackward())
self.viewMenu.addAction(self.aViewPrev)
# View > Go Forward
- self.aViewNext = QAction("Go Forward", self)
- self.aViewNext.setStatusTip("Move forward in the view history of the right pane")
+ self.aViewNext = QAction(self.tr("Go Forward"), self)
+ self.aViewNext.setStatusTip(self.tr("Move forward in the view history of the right pane"))
self.aViewNext.setShortcut("Alt+Right")
self.aViewNext.triggered.connect(lambda: self.theParent.docViewer.navForward())
self.viewMenu.addAction(self.aViewNext)
@@ -514,8 +516,10 @@ class GuiMainMenu(QMenuBar):
self.viewMenu.addSeparator()
# View > Focus Mode
- self.aFocusMode = QAction("Focus Mode", self)
- self.aFocusMode.setStatusTip("Toggles a distraction free mode, only showing text editor")
+ self.aFocusMode = QAction(self.tr("Focus Mode"), self)
+ self.aFocusMode.setStatusTip(
+ self.tr("Toggles a distraction free mode, only showing text editor")
+ )
self.aFocusMode.setShortcut("F8")
self.aFocusMode.setCheckable(True)
self.aFocusMode.setChecked(self.theParent.isFocusMode)
@@ -523,8 +527,8 @@ class GuiMainMenu(QMenuBar):
self.viewMenu.addAction(self.aFocusMode)
# View > Toggle Full Screen
- self.aFullScreen = QAction("Full Screen Mode", self)
- self.aFullScreen.setStatusTip("Maximises the main window")
+ self.aFullScreen = QAction(self.tr("Full Screen Mode"), self)
+ self.aFullScreen.setStatusTip(self.tr("Maximises the main window"))
self.aFullScreen.setShortcut("F11")
self.aFullScreen.triggered.connect(lambda: self.theParent.toggleFullScreenMode())
self.viewMenu.addAction(self.aFullScreen)
@@ -535,187 +539,189 @@ class GuiMainMenu(QMenuBar):
"""Assemble the Insert menu.
"""
# Insert
- self.insertMenu = self.addMenu("&Insert")
+ self.insertMenu = self.addMenu(self.tr("&Insert"))
- # Insert > Dashes
- self.mInsDashes = self.insertMenu.addMenu("Dashes")
+ # Insert > Dashes and Dots
+ self.mInsDashes = self.insertMenu.addMenu(self.tr("Dashes"))
# Insert > Short Dash
- self.aInsENDash = QAction("Short Dash", self)
- self.aInsENDash.setStatusTip("Insert short dash (en dash)")
+ self.aInsENDash = QAction(self.tr("Short Dash"), self)
+ self.aInsENDash.setStatusTip(self.tr("Insert short dash (en dash)"))
self.aInsENDash.setShortcut("Ctrl+K, -")
self.aInsENDash.triggered.connect(lambda: self._docInsert(nwUnicode.U_ENDASH))
self.mInsDashes.addAction(self.aInsENDash)
# Insert > Long Dash
- self.aInsEMDash = QAction("Long Dash", self)
- self.aInsEMDash.setStatusTip("Insert long dash (em dash)")
+ self.aInsEMDash = QAction(self.tr("Long Dash"), self)
+ self.aInsEMDash.setStatusTip(self.tr("Insert long dash (em dash)"))
self.aInsEMDash.setShortcut("Ctrl+K, _")
self.aInsEMDash.triggered.connect(lambda: self._docInsert(nwUnicode.U_EMDASH))
self.mInsDashes.addAction(self.aInsEMDash)
# Insert > Long Dash
- self.aInsHorBar = QAction("Horizontal Bar", self)
- self.aInsHorBar.setStatusTip("Insert a horizontal bar (quotation dash)")
+ self.aInsHorBar = QAction(self.tr("Horizontal Bar"), self)
+ self.aInsHorBar.setStatusTip(self.tr("Insert a horizontal bar (quotation dash)"))
self.aInsHorBar.setShortcut("Ctrl+K, Ctrl+_")
self.aInsHorBar.triggered.connect(lambda: self._docInsert(nwUnicode.U_HBAR))
self.mInsDashes.addAction(self.aInsHorBar)
# Insert > Figure Dash
- self.aInsFigDash = QAction("Figure Dash", self)
- self.aInsFigDash.setStatusTip("Insert figure dash (same width as a number character)")
+ self.aInsFigDash = QAction(self.tr("Figure Dash"), self)
+ self.aInsFigDash.setStatusTip(
+ 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)
# Insert > Quote Marks
- self.mInsQuotes = self.insertMenu.addMenu("Quote Marks")
+ self.mInsQuotes = self.insertMenu.addMenu(self.tr("Quote Marks"))
# Insert > Left Single Quote
- self.aInsQuoteLS = QAction("Left Single Quote", self)
- self.aInsQuoteLS.setStatusTip("Insert left single quote")
+ self.aInsQuoteLS = QAction(self.tr("Left Single Quote"), self)
+ self.aInsQuoteLS.setStatusTip(self.tr("Insert left single quote"))
self.aInsQuoteLS.setShortcut("Ctrl+K, 1")
self.aInsQuoteLS.triggered.connect(lambda: self._docInsert(nwDocInsert.QUOTE_LS))
self.mInsQuotes.addAction(self.aInsQuoteLS)
# Insert > Right Single Quote
- self.aInsQuoteRS = QAction("Right Single Quote", self)
- self.aInsQuoteRS.setStatusTip("Insert right single quote")
+ self.aInsQuoteRS = QAction(self.tr("Right Single Quote"), self)
+ self.aInsQuoteRS.setStatusTip(self.tr("Insert right single quote"))
self.aInsQuoteRS.setShortcut("Ctrl+K, 2")
self.aInsQuoteRS.triggered.connect(lambda: self._docInsert(nwDocInsert.QUOTE_RS))
self.mInsQuotes.addAction(self.aInsQuoteRS)
# Insert > Left Double Quote
- self.aInsQuoteLD = QAction("Left Double Quote", self)
- self.aInsQuoteLD.setStatusTip("Insert left double quote")
+ self.aInsQuoteLD = QAction(self.tr("Left Double Quote"), self)
+ self.aInsQuoteLD.setStatusTip(self.tr("Insert left double quote"))
self.aInsQuoteLD.setShortcut("Ctrl+K, 3")
self.aInsQuoteLD.triggered.connect(lambda: self._docInsert(nwDocInsert.QUOTE_LD))
self.mInsQuotes.addAction(self.aInsQuoteLD)
# Insert > Right Double Quote
- self.aInsQuoteRD = QAction("Right Double Quote", self)
- self.aInsQuoteRD.setStatusTip("Insert right double quote")
+ self.aInsQuoteRD = QAction(self.tr("Right Double Quote"), self)
+ self.aInsQuoteRD.setStatusTip(self.tr("Insert right double quote"))
self.aInsQuoteRD.setShortcut("Ctrl+K, 4")
self.aInsQuoteRD.triggered.connect(lambda: self._docInsert(nwDocInsert.QUOTE_RD))
self.mInsQuotes.addAction(self.aInsQuoteRD)
# Insert > Alternative Apostrophe
- self.aInsMSApos = QAction("Alternative Apostrophe", self)
- self.aInsMSApos.setStatusTip("Insert modifier letter single apostrophe")
+ self.aInsMSApos = QAction(self.tr("Alternative Apostrophe"), self)
+ self.aInsMSApos.setStatusTip(self.tr("Insert modifier letter single apostrophe"))
self.aInsMSApos.setShortcut("Ctrl+K, '")
self.aInsMSApos.triggered.connect(lambda: self._docInsert(nwUnicode.U_MAPOSS))
self.mInsQuotes.addAction(self.aInsMSApos)
# Insert > Symbols
- self.mInsPunct = self.insertMenu.addMenu("General Punctuation")
+ self.mInsPunct = self.insertMenu.addMenu(self.tr("General Punctuation"))
# Insert > Ellipsis
- self.aInsEllipsis = QAction("Ellipsis", self)
- self.aInsEllipsis.setStatusTip("Insert ellipsis")
+ self.aInsEllipsis = QAction(self.tr("Ellipsis"), self)
+ self.aInsEllipsis.setStatusTip(self.tr("Insert ellipsis"))
self.aInsEllipsis.setShortcut("Ctrl+K, .")
self.aInsEllipsis.triggered.connect(lambda: self._docInsert(nwUnicode.U_HELLIP))
self.mInsPunct.addAction(self.aInsEllipsis)
# Insert > Prime
- self.aInsPrime = QAction("Prime", self)
- self.aInsPrime.setStatusTip("Insert a prime symbol")
+ self.aInsPrime = QAction(self.tr("Prime"), self)
+ self.aInsPrime.setStatusTip(self.tr("Insert a prime symbol"))
self.aInsPrime.setShortcut("Ctrl+K, Ctrl+'")
self.aInsPrime.triggered.connect(lambda: self._docInsert(nwUnicode.U_PRIME))
self.mInsPunct.addAction(self.aInsPrime)
# Insert > Double Prime
- self.aInsDPrime = QAction("Double Prime", self)
- self.aInsDPrime.setStatusTip("Insert a double prime symbol")
+ self.aInsDPrime = QAction(self.tr("Double Prime"), self)
+ self.aInsDPrime.setStatusTip(self.tr("Insert a double prime symbol"))
self.aInsDPrime.setShortcut("Ctrl+K, Ctrl+\"")
self.aInsDPrime.triggered.connect(lambda: self._docInsert(nwUnicode.U_DPRIME))
self.mInsPunct.addAction(self.aInsDPrime)
# Insert > Breaks and Spaces
- self.mInsBreaks = self.insertMenu.addMenu("Breaks and Spaces")
+ self.mInsBreaks = self.insertMenu.addMenu(self.tr("Breaks and Spaces"))
# Insert > Hard Line Break
- self.aInsHardBreak = QAction("Hard Line Break", self)
- self.aInsHardBreak.setStatusTip("Insert a hard line break")
+ self.aInsHardBreak = QAction(self.tr("Hard Line Break"), self)
+ self.aInsHardBreak.setStatusTip(self.tr("Insert a hard line break"))
self.aInsHardBreak.setShortcut("Ctrl+K, Return")
self.aInsHardBreak.triggered.connect(lambda: self._docInsert(nwDocInsert.HARD_BREAK))
self.mInsBreaks.addAction(self.aInsHardBreak)
# Insert > Non-Breaking Space
- self.aInsNBSpace = QAction("Non-Breaking Space", self)
- self.aInsNBSpace.setStatusTip("Insert a non-breaking space")
+ self.aInsNBSpace = QAction(self.tr("Non-Breaking Space"), self)
+ self.aInsNBSpace.setStatusTip(self.tr("Insert a non-breaking space"))
self.aInsNBSpace.setShortcut("Ctrl+K, Space")
self.aInsNBSpace.triggered.connect(lambda: self._docInsert(nwUnicode.U_NBSP))
self.mInsBreaks.addAction(self.aInsNBSpace)
# Insert > Thin Space
- self.aInsThinSpace = QAction("Thin Space", self)
- self.aInsThinSpace.setStatusTip("Insert a thin space")
+ self.aInsThinSpace = QAction(self.tr("Thin Space"), self)
+ self.aInsThinSpace.setStatusTip(self.tr("Insert a thin space"))
self.aInsThinSpace.setShortcut("Ctrl+K, Shift+Space")
self.aInsThinSpace.triggered.connect(lambda: self._docInsert(nwUnicode.U_THSP))
self.mInsBreaks.addAction(self.aInsThinSpace)
# Insert > Thin Non-Breaking Space
- self.aInsThinNBSpace = QAction("Thin Non-Breaking Space", self)
- self.aInsThinNBSpace.setStatusTip("Insert a thin non-breaking space")
+ self.aInsThinNBSpace = QAction(self.tr("Thin Non-Breaking Space"), self)
+ self.aInsThinNBSpace.setStatusTip(self.tr("Insert a thin non-breaking space"))
self.aInsThinNBSpace.setShortcut("Ctrl+K, Ctrl+Space")
self.aInsThinNBSpace.triggered.connect(lambda: self._docInsert(nwUnicode.U_THNBSP))
self.mInsBreaks.addAction(self.aInsThinNBSpace)
# Insert > Symbols
- self.mInsSymbol = self.insertMenu.addMenu("Other Symbols")
+ self.mInsSymbol = self.insertMenu.addMenu(self.tr("Other Symbols"))
# Insert > List Bullet
- self.aInsBullet = QAction("List Bullet", self)
- self.aInsBullet.setStatusTip("Insert a list bullet")
+ self.aInsBullet = QAction(self.tr("List Bullet"), self)
+ self.aInsBullet.setStatusTip(self.tr("Insert a list bullet"))
self.aInsBullet.setShortcut("Ctrl+K, *")
self.aInsBullet.triggered.connect(lambda: self._docInsert(nwUnicode.U_BULL))
self.mInsSymbol.addAction(self.aInsBullet)
# Insert > Hyphen Bullet
- self.aInsHyBull = QAction("Hyphen Bullet", self)
- self.aInsHyBull.setStatusTip("Insert a hyphen bullet (alternative bullet)")
+ self.aInsHyBull = QAction(self.tr("Hyphen Bullet"), self)
+ self.aInsHyBull.setStatusTip(self.tr("Insert a hyphen bullet (alternative bullet)"))
self.aInsHyBull.setShortcut("Ctrl+K, Ctrl+-")
self.aInsHyBull.triggered.connect(lambda: self._docInsert(nwUnicode.U_HYBULL))
self.mInsSymbol.addAction(self.aInsHyBull)
# Insert > Flower Mark
- self.aInsFlower = QAction("Flower Mark", self)
- self.aInsFlower.setStatusTip("Insert a flower mark (alternative bullet)")
+ self.aInsFlower = QAction(self.tr("Flower Mark"), self)
+ self.aInsFlower.setStatusTip(self.tr("Insert a flower mark (alternative bullet)"))
self.aInsFlower.setShortcut("Ctrl+K, Ctrl+*")
self.aInsFlower.triggered.connect(lambda: self._docInsert(nwUnicode.U_FLOWER))
self.mInsSymbol.addAction(self.aInsFlower)
# Insert > Per Mille
- self.aInsPerMille = QAction("Per Mille", self)
- self.aInsPerMille.setStatusTip("Insert a per mille symbol")
+ self.aInsPerMille = QAction(self.tr("Per Mille"), self)
+ self.aInsPerMille.setStatusTip(self.tr("Insert a per mille symbol"))
self.aInsPerMille.setShortcut("Ctrl+K, %")
self.aInsPerMille.triggered.connect(lambda: self._docInsert(nwUnicode.U_PERMIL))
self.mInsSymbol.addAction(self.aInsPerMille)
# Insert > Degree Symbol
- self.aInsDegree = QAction("Degree Symbol", self)
- self.aInsDegree.setStatusTip("Insert a degree symbol")
+ self.aInsDegree = QAction(self.tr("Degree Symbol"), self)
+ self.aInsDegree.setStatusTip(self.tr("Insert a degree symbol"))
self.aInsDegree.setShortcut("Ctrl+K, Ctrl+O")
self.aInsDegree.triggered.connect(lambda: self._docInsert(nwUnicode.U_DEGREE))
self.mInsSymbol.addAction(self.aInsDegree)
# Insert > Minus Sign
- self.aInsMinus = QAction("Minus Sign", self)
- self.aInsMinus.setStatusTip("Insert a minus sign (not a hypen or dash)")
+ self.aInsMinus = QAction(self.tr("Minus Sign"), self)
+ self.aInsMinus.setStatusTip(self.tr("Insert a minus sign (not a hypen or dash)"))
self.aInsMinus.setShortcut("Ctrl+K, Ctrl+M")
self.aInsMinus.triggered.connect(lambda: self._docInsert(nwUnicode.U_MINUS))
self.mInsSymbol.addAction(self.aInsMinus)
# Insert > Times Sign
- self.aInsTimes = QAction("Times Sign", self)
- self.aInsTimes.setStatusTip("Insert a times sign (multiplication cross)")
+ self.aInsTimes = QAction(self.tr("Times Sign"), self)
+ self.aInsTimes.setStatusTip(self.tr("Insert a times sign (multiplication cross)"))
self.aInsTimes.setShortcut("Ctrl+K, Ctrl+X")
self.aInsTimes.triggered.connect(lambda: self._docInsert(nwUnicode.U_TIMES))
self.mInsSymbol.addAction(self.aInsTimes)
# Insert > Division
- self.aInsDivide = QAction("Division Sign", self)
- self.aInsDivide.setStatusTip("Insert a division sign")
+ self.aInsDivide = QAction(self.tr("Division Sign"), self)
+ self.aInsDivide.setStatusTip(self.tr("Insert a division sign"))
self.aInsDivide.setShortcut("Ctrl+K, Ctrl+D")
self.aInsDivide.triggered.connect(lambda: self._docInsert(nwUnicode.U_DIVIDE))
self.mInsSymbol.addAction(self.aInsDivide)
@@ -724,7 +730,7 @@ class GuiMainMenu(QMenuBar):
self.insertMenu.addSeparator()
# Insert > Tags and References
- self.mInsKeywords = self.insertMenu.addMenu("Tags and References")
+ self.mInsKeywords = self.insertMenu.addMenu(self.tr("Tags and References"))
self.mInsKWItems = {}
self.mInsKWItems[nwKeyWords.TAG_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, G")
self.mInsKWItems[nwKeyWords.POV_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, V")
@@ -737,7 +743,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(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)
@@ -750,18 +756,18 @@ class GuiMainMenu(QMenuBar):
"""Assemble the Search menu.
"""
# Search
- self.srcMenu = self.addMenu("&Search")
+ self.srcMenu = self.addMenu(self.tr("&Search"))
# Search > Find
- self.aFind = QAction("Find", self)
- self.aFind.setStatusTip("Find text in document")
+ self.aFind = QAction(self.tr("Find"), self)
+ self.aFind.setStatusTip(self.tr("Find text in document"))
self.aFind.setShortcut("Ctrl+F")
self.aFind.triggered.connect(lambda: self._docAction(nwDocAction.FIND))
self.srcMenu.addAction(self.aFind)
# Search > Replace
- self.aReplace = QAction("Replace", self)
- self.aReplace.setStatusTip("Replace text in document")
+ self.aReplace = QAction(self.tr("Replace"), self)
+ self.aReplace.setStatusTip(self.tr("Replace text in document"))
if self.mainConf.osDarwin:
self.aReplace.setShortcut("Ctrl+=")
else:
@@ -770,8 +776,8 @@ class GuiMainMenu(QMenuBar):
self.srcMenu.addAction(self.aReplace)
# Search > Find Next
- self.aFindNext = QAction("Find Next", self)
- self.aFindNext.setStatusTip("Find next occurrence text in document")
+ self.aFindNext = QAction(self.tr("Find Next"), self)
+ self.aFindNext.setStatusTip(self.tr("Find next occurrence of text in document"))
if self.mainConf.osDarwin:
self.aFindNext.setShortcuts(["Ctrl+G", "F3"])
else:
@@ -780,8 +786,8 @@ class GuiMainMenu(QMenuBar):
self.srcMenu.addAction(self.aFindNext)
# Search > Find Prev
- self.aFindPrev = QAction("Find Previous", self)
- self.aFindPrev.setStatusTip("Find previous occurrence text in document")
+ self.aFindPrev = QAction(self.tr("Find Previous"), self)
+ self.aFindPrev.setStatusTip(self.tr("Find previous occurrence of text in document"))
if self.mainConf.osDarwin:
self.aFindPrev.setShortcuts(["Ctrl+Shift+G", "Shift+F3"])
else:
@@ -790,8 +796,10 @@ class GuiMainMenu(QMenuBar):
self.srcMenu.addAction(self.aFindPrev)
# Search > Replace Next
- self.aReplaceNext = QAction("Replace Next", self)
- self.aReplaceNext.setStatusTip("Find and replace next occurrence text in document")
+ self.aReplaceNext = QAction(self.tr("Replace Next"), self)
+ self.aReplaceNext.setStatusTip(
+ self.tr("Find and replace next occurrence of text in document")
+ )
self.aReplaceNext.setShortcut("Ctrl+Shift+1")
self.aReplaceNext.triggered.connect(lambda: self._docAction(nwDocAction.REPL_NEXT))
self.srcMenu.addAction(self.aReplaceNext)
@@ -802,25 +810,25 @@ class GuiMainMenu(QMenuBar):
"""Assemble the Format menu.
"""
# Format
- self.fmtMenu = self.addMenu("&Format")
+ self.fmtMenu = self.addMenu(self.tr("&Format"))
# Format > Emphasis
- self.aFmtEmph = QAction("Emphasis", self)
- self.aFmtEmph.setStatusTip("Add emphasis to selected text (italic)")
+ self.aFmtEmph = QAction(self.tr("Emphasis"), self)
+ self.aFmtEmph.setStatusTip(self.tr("Add emphasis to selected text (italic)"))
self.aFmtEmph.setShortcut("Ctrl+I")
self.aFmtEmph.triggered.connect(lambda: self._docAction(nwDocAction.EMPH))
self.fmtMenu.addAction(self.aFmtEmph)
# Format > Strong Emphasis
- self.aFmtStrong = QAction("Strong Emphasis", self)
- self.aFmtStrong.setStatusTip("Add strong emphasis to selected text (bold)")
+ self.aFmtStrong = QAction(self.tr("Strong Emphasis"), self)
+ self.aFmtStrong.setStatusTip(self.tr("Add strong emphasis to selected text (bold)"))
self.aFmtStrong.setShortcut("Ctrl+B")
self.aFmtStrong.triggered.connect(lambda: self._docAction(nwDocAction.STRONG))
self.fmtMenu.addAction(self.aFmtStrong)
# Format > Strikethrough
- self.aFmtStrike = QAction("Strikethrough", self)
- self.aFmtStrike.setStatusTip("Add strikethrough to selected text")
+ self.aFmtStrike = QAction(self.tr("Strikethrough"), self)
+ self.aFmtStrike.setStatusTip(self.tr("Add strikethrough to selected text"))
self.aFmtStrike.setShortcut("Ctrl+D")
self.aFmtStrike.triggered.connect(lambda: self._docAction(nwDocAction.STRIKE))
self.fmtMenu.addAction(self.aFmtStrike)
@@ -829,15 +837,15 @@ class GuiMainMenu(QMenuBar):
self.fmtMenu.addSeparator()
# Format > Double Quotes
- self.aFmtDQuote = QAction("Wrap Double Quotes", self)
- self.aFmtDQuote.setStatusTip("Wrap selected text in double quotes")
+ self.aFmtDQuote = QAction(self.tr("Wrap Double Quotes"), self)
+ self.aFmtDQuote.setStatusTip(self.tr("Wrap selected text in double quotes"))
self.aFmtDQuote.setShortcut("Ctrl+\"")
self.aFmtDQuote.triggered.connect(lambda: self._docAction(nwDocAction.D_QUOTE))
self.fmtMenu.addAction(self.aFmtDQuote)
# Format > Single Quotes
- self.aFmtSQuote = QAction("Wrap Single Quotes", self)
- self.aFmtSQuote.setStatusTip("Wrap selected text in single quotes")
+ self.aFmtSQuote = QAction(self.tr("Wrap Single Quotes"), self)
+ self.aFmtSQuote.setStatusTip(self.tr("Wrap selected text in single quotes"))
self.aFmtSQuote.setShortcut("Ctrl+'")
self.aFmtSQuote.triggered.connect(lambda: self._docAction(nwDocAction.S_QUOTE))
self.fmtMenu.addAction(self.aFmtSQuote)
@@ -846,43 +854,43 @@ class GuiMainMenu(QMenuBar):
self.fmtMenu.addSeparator()
# Format > Header 1
- self.aFmtHead1 = QAction("Header 1", self)
- self.aFmtHead1.setStatusTip("Change the block format to Header 1")
+ self.aFmtHead1 = QAction(self.tr("Header 1"), self)
+ self.aFmtHead1.setStatusTip(self.tr("Change the block format to Header 1"))
self.aFmtHead1.setShortcut("Ctrl+1")
self.aFmtHead1.triggered.connect(lambda: self._docAction(nwDocAction.BLOCK_H1))
self.fmtMenu.addAction(self.aFmtHead1)
# Format > Header 2
- self.aFmtHead2 = QAction("Header 2", self)
- self.aFmtHead2.setStatusTip("Change the block format to Header 2")
+ self.aFmtHead2 = QAction(self.tr("Header 2"), self)
+ self.aFmtHead2.setStatusTip(self.tr("Change the block format to Header 2"))
self.aFmtHead2.setShortcut("Ctrl+2")
self.aFmtHead2.triggered.connect(lambda: self._docAction(nwDocAction.BLOCK_H2))
self.fmtMenu.addAction(self.aFmtHead2)
# Format > Header 3
- self.aFmtHead3 = QAction("Header 3", self)
- self.aFmtHead3.setStatusTip("Change the block format to Header 3")
+ self.aFmtHead3 = QAction(self.tr("Header 3"), self)
+ self.aFmtHead3.setStatusTip(self.tr("Change the block format to Header 3"))
self.aFmtHead3.setShortcut("Ctrl+3")
self.aFmtHead3.triggered.connect(lambda: self._docAction(nwDocAction.BLOCK_H3))
self.fmtMenu.addAction(self.aFmtHead3)
# Format > Header 4
- self.aFmtHead4 = QAction("Header 4", self)
- self.aFmtHead4.setStatusTip("Change the block format to Header 4")
+ self.aFmtHead4 = QAction(self.tr("Header 4"), self)
+ self.aFmtHead4.setStatusTip(self.tr("Change the block format to Header 4"))
self.aFmtHead4.setShortcut("Ctrl+4")
self.aFmtHead4.triggered.connect(lambda: self._docAction(nwDocAction.BLOCK_H4))
self.fmtMenu.addAction(self.aFmtHead4)
# Format > Comment
- self.aFmtComment = QAction("Comment", self)
- self.aFmtComment.setStatusTip("Change the block format to comment")
+ self.aFmtComment = QAction(self.tr("Comment"), self)
+ self.aFmtComment.setStatusTip(self.tr("Change the block format to comment"))
self.aFmtComment.setShortcut("Ctrl+/")
self.aFmtComment.triggered.connect(lambda: self._docAction(nwDocAction.BLOCK_COM))
self.fmtMenu.addAction(self.aFmtComment)
# Format > Remove Block Format
- self.aFmtNoFormat = QAction("Remove Block Format", self)
- self.aFmtNoFormat.setStatusTip("Strips block format")
+ self.aFmtNoFormat = QAction(self.tr("Remove Block Format"), self)
+ self.aFmtNoFormat.setStatusTip(self.tr("Strips block format"))
self.aFmtNoFormat.setShortcuts(["Ctrl+0", "Ctrl+Shift+/"])
self.aFmtNoFormat.triggered.connect(lambda: self._docAction(nwDocAction.BLOCK_TXT))
self.fmtMenu.addAction(self.aFmtNoFormat)
@@ -891,14 +899,18 @@ class GuiMainMenu(QMenuBar):
self.fmtMenu.addSeparator()
# Format > Replace Single Quotes
- self.aFmtReplSng = QAction("Replace Single Quotes", self)
- self.aFmtReplSng.setStatusTip("Replace all straight single quotes in selected text")
+ self.aFmtReplSng = QAction(self.tr("Replace Single Quotes"), self)
+ self.aFmtReplSng.setStatusTip(
+ 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("Replace Double Quotes", self)
- self.aFmtReplDbl.setStatusTip("Replace all straight double quotes in selected text")
+ self.aFmtReplDbl = QAction(self.tr("Replace Double Quotes"), self)
+ self.aFmtReplDbl.setStatusTip(
+ 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)
@@ -908,11 +920,11 @@ class GuiMainMenu(QMenuBar):
"""Assemble the Tools menu.
"""
# Tools
- self.toolsMenu = self.addMenu("&Tools")
+ self.toolsMenu = self.addMenu(self.tr("&Tools"))
# Tools > Check Spelling
- self.aSpellCheck = QAction("Check Spelling", self)
- self.aSpellCheck.setStatusTip("Toggle check spelling")
+ self.aSpellCheck = QAction(self.tr("Check Spelling"), self)
+ self.aSpellCheck.setStatusTip(self.tr("Toggle check spelling"))
self.aSpellCheck.setCheckable(True)
self.aSpellCheck.setChecked(self.theProject.spellCheck)
self.aSpellCheck.triggered.connect(self._toggleSpellCheck) # triggered, not toggled!
@@ -920,15 +932,15 @@ class GuiMainMenu(QMenuBar):
self.toolsMenu.addAction(self.aSpellCheck)
# Tools > Re-Run Spell Check
- self.aReRunSpell = QAction("Re-Run Spell Check", self)
- self.aReRunSpell.setStatusTip("Run the spell checker on current document")
+ self.aReRunSpell = QAction(self.tr("Re-Run Spell Check"), self)
+ self.aReRunSpell.setStatusTip(self.tr("Run the spell checker on current document"))
self.aReRunSpell.setShortcut("F7")
self.aReRunSpell.triggered.connect(lambda: self.theParent.docEditor.spellCheckDocument())
self.toolsMenu.addAction(self.aReRunSpell)
# Tools > Project Word List
- self.aEditWordList = QAction("Project Word List", self)
- self.aEditWordList.setStatusTip("Edit the project's word list")
+ self.aEditWordList = QAction(self.tr("Project Word List"), self)
+ self.aEditWordList.setStatusTip(self.tr("Edit the project's word list"))
self.aEditWordList.triggered.connect(lambda: self.theParent.showProjectWordListDialog())
self.toolsMenu.addAction(self.aEditWordList)
@@ -936,22 +948,24 @@ class GuiMainMenu(QMenuBar):
self.toolsMenu.addSeparator()
# Tools > Rebuild Indices
- self.aRebuildIndex = QAction("Rebuild Index", self)
- self.aRebuildIndex.setStatusTip("Rebuild the tag indices and word counts")
+ self.aRebuildIndex = QAction(self.tr("Rebuild Index"), self)
+ self.aRebuildIndex.setStatusTip(self.tr("Rebuild the tag indices and word counts"))
self.aRebuildIndex.setShortcut("F9")
self.aRebuildIndex.triggered.connect(lambda: self.theParent.rebuildIndex())
self.toolsMenu.addAction(self.aRebuildIndex)
# Tools > Rebuild Outline
- self.aRebuildOutline = QAction("Rebuild Outline", self)
- self.aRebuildOutline.setStatusTip("Rebuild the novel outline tree")
+ self.aRebuildOutline = QAction(self.tr("Rebuild Outline"), self)
+ self.aRebuildOutline.setStatusTip(self.tr("Rebuild the novel outline tree"))
self.aRebuildOutline.setShortcut("F10")
self.aRebuildOutline.triggered.connect(lambda: self.theParent.rebuildOutline())
self.toolsMenu.addAction(self.aRebuildOutline)
# Tools > Toggle Auto Build Outline
- self.aAutoOutline = QAction("Auto-Update Outline", self)
- self.aAutoOutline.setStatusTip("Update project outline when a novel file is changed")
+ self.aAutoOutline = QAction(self.tr("Auto-Update Outline"), self)
+ self.aAutoOutline.setStatusTip(
+ self.tr("Update project outline when a novel file is changed")
+ )
self.aAutoOutline.setCheckable(True)
self.aAutoOutline.toggled.connect(self._toggleAutoOutline)
self.aAutoOutline.setShortcut("Ctrl+F10")
@@ -961,28 +975,28 @@ class GuiMainMenu(QMenuBar):
self.toolsMenu.addSeparator()
# Tools > Backup
- self.aBackupProject = QAction("Backup Project Folder", self)
- self.aBackupProject.setStatusTip("Backup Project")
+ self.aBackupProject = QAction(self.tr("Backup Project Folder"), self)
+ self.aBackupProject.setStatusTip(self.tr("Backup Project"))
self.aBackupProject.triggered.connect(lambda: self.theProject.zipIt(True))
self.toolsMenu.addAction(self.aBackupProject)
# Tools > Export Project
- self.aBuildProject = QAction("Build Novel Project", self)
- self.aBuildProject.setStatusTip("Launch the Build novel project tool")
+ self.aBuildProject = QAction(self.tr("Build Novel Project"), self)
+ self.aBuildProject.setStatusTip(self.tr("Launch the Build novel project tool"))
self.aBuildProject.setShortcut("F5")
self.aBuildProject.triggered.connect(lambda: self.theParent.showBuildProjectDialog())
self.toolsMenu.addAction(self.aBuildProject)
# Tools > Writing Stats
- self.aWritingStats = QAction("Writing Statistics", self)
- self.aWritingStats.setStatusTip("Show the writing statistics dialog")
+ self.aWritingStats = QAction(self.tr("Writing Statistics"), self)
+ self.aWritingStats.setStatusTip(self.tr("Show the writing statistics dialog"))
self.aWritingStats.setShortcut("F6")
self.aWritingStats.triggered.connect(lambda: self.theParent.showWritingStatsDialog())
self.toolsMenu.addAction(self.aWritingStats)
# Tools > Settings
- self.aPreferences = QAction("Preferences", self)
- self.aPreferences.setStatusTip("Preferences")
+ self.aPreferences = QAction(self.tr("Preferences"), self)
+ self.aPreferences.setStatusTip(self.tr("Preferences"))
self.aPreferences.setShortcut("Ctrl+,")
self.aPreferences.setMenuRole(QAction.PreferencesRole)
self.aPreferences.triggered.connect(lambda: self.theParent.showPreferencesDialog())
@@ -994,18 +1008,18 @@ class GuiMainMenu(QMenuBar):
"""Assemble the Help menu.
"""
# Help
- self.helpMenu = self.addMenu("&Help")
+ self.helpMenu = self.addMenu(self.tr("&Help"))
# Help > About
- self.aAboutNW = QAction("About novelWriter", self)
- self.aAboutNW.setStatusTip("About novelWriter")
+ self.aAboutNW = QAction(self.tr("About novelWriter"), self)
+ self.aAboutNW.setStatusTip(self.tr("About novelWriter"))
self.aAboutNW.setMenuRole(QAction.AboutRole)
self.aAboutNW.triggered.connect(lambda: self.theParent.showAboutNWDialog())
self.helpMenu.addAction(self.aAboutNW)
# Help > About Qt5
- self.aAboutQt = QAction("About Qt5", self)
- self.aAboutQt.setStatusTip("About Qt5")
+ self.aAboutQt = QAction(self.tr("About Qt5"), self)
+ self.aAboutQt.setStatusTip(self.tr("About Qt5"))
self.aAboutQt.setMenuRole(QAction.AboutQtRole)
self.aAboutQt.triggered.connect(lambda: self.theParent.showAboutQtDialog())
self.helpMenu.addAction(self.aAboutQt)
@@ -1015,14 +1029,16 @@ class GuiMainMenu(QMenuBar):
# Document > Documentation
if self.mainConf.hasHelp and self.mainConf.hasAssistant:
- self.aHelpLoc = QAction("Documentation (Local)", self)
- self.aHelpLoc.setStatusTip("View local documentation with Qt Assistant")
+ self.aHelpLoc = QAction(self.tr("Documentation (Local)"), self)
+ self.aHelpLoc.setStatusTip(self.tr("View local documentation with Qt Assistant"))
self.aHelpLoc.triggered.connect(self._openAssistant)
self.aHelpLoc.setShortcut("F1")
self.helpMenu.addAction(self.aHelpLoc)
- self.aHelpWeb = QAction("Documentation (Online)", self)
- self.aHelpWeb.setStatusTip("View online documentation at %s" % nw.__docurl__)
+ self.aHelpWeb = QAction(self.tr("Documentation (Online)"), self)
+ self.aHelpWeb.setStatusTip(
+ 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")
@@ -1034,26 +1050,34 @@ class GuiMainMenu(QMenuBar):
self.helpMenu.addSeparator()
# Document > Report an Issue
- self.aIssue = QAction("Report an Issue (GitHub)", self)
- self.aIssue.setStatusTip("Report a bug or issue on GitHub at %s" % nw.__issuesurl__)
+ 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.aIssue.triggered.connect(lambda: self._openWebsite(nw.__issuesurl__))
self.helpMenu.addAction(self.aIssue)
# Document > Ask a Question
- self.aQuestion = QAction("Ask a Question (GitHub)", self)
- self.aQuestion.setStatusTip("Ask a question on GitHub at %s" % nw.__helpurl__)
+ 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.aQuestion.triggered.connect(lambda: self._openWebsite(nw.__helpurl__))
self.helpMenu.addAction(self.aQuestion)
# Document > Latest Release
- self.aRelease = QAction("Latest Release (GitHub)", self)
- self.aRelease.setStatusTip("Open the Releases page on GitHub at %s" % nw.__releaseurl__)
+ 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.aRelease.triggered.connect(lambda: self._openWebsite(nw.__releaseurl__))
self.helpMenu.addAction(self.aRelease)
# Document > Main Website
- self.aWebsite = QAction("The novelWriter Website", self)
- self.aWebsite.setStatusTip("Open the novelWriter website at %s" % nw.__url__)
+ self.aWebsite = QAction(self.tr("The novelWriter Website"), self)
+ self.aWebsite.setStatusTip(
+ 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/noveltree.py b/nw/gui/noveltree.py
index 0ceaf9ec..7dde6aeb 100644
--- a/nw/gui/noveltree.py
+++ b/nw/gui/noveltree.py
@@ -63,7 +63,11 @@ class GuiNovelTree(QTreeWidget):
self.setIconSize(QSize(iPx, iPx))
self.setIndentation(iPx)
self.setColumnCount(3)
- self.setHeaderLabels(["Title", "Words", "POV"])
+ self.setHeaderLabels([
+ self.tr("Title"),
+ self.tr("Words"),
+ self.tr("POV")
+ ])
self.itemDoubleClicked.connect(self._treeDoubleClick)
self.itemSelectionChanged.connect(self._itemSelected)
self.setSelectionBehavior(QAbstractItemView.SelectRows)
@@ -73,9 +77,9 @@ class GuiNovelTree(QTreeWidget):
treeHeadItem = self.headerItem()
treeHeadItem.setTextAlignment(self.C_WORDS, Qt.AlignRight)
- treeHeadItem.setToolTip(self.C_TITLE, "Section title")
- treeHeadItem.setToolTip(self.C_WORDS, "Word count")
- treeHeadItem.setToolTip(self.C_POV, "Point-of-view character")
+ treeHeadItem.setToolTip(self.C_TITLE, self.tr("Section title"))
+ treeHeadItem.setToolTip(self.C_WORDS, self.tr("Word count"))
+ treeHeadItem.setToolTip(self.C_POV, self.tr("Point-of-view character"))
treeHeader = self.header()
treeHeader.setStretchLastSection(True)
diff --git a/nw/gui/outline.py b/nw/gui/outline.py
index 666c8026..7a54c6be 100644
--- a/nw/gui/outline.py
+++ b/nw/gui/outline.py
@@ -34,7 +34,7 @@ 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,7 +149,7 @@ class GuiOutline(QTreeWidget):
"""
self.clear()
self.setColumnCount(1)
- self.setHeaderLabel(nwLabels.OUTLINE_COLS[nwOutline.TITLE])
+ self.setHeaderLabel(trConst(nwLabels.OUTLINE_COLS[nwOutline.TITLE]))
self.treeOrder = []
self.colWidth = {}
@@ -355,7 +355,7 @@ class GuiOutline(QTreeWidget):
if self.firstView:
theLabels = []
for i, hItem in enumerate(self.treeOrder):
- theLabels.append(nwLabels.OUTLINE_COLS[hItem])
+ theLabels.append(trConst(nwLabels.OUTLINE_COLS[hItem]))
self.colIndex[hItem] = i
self.setHeaderLabels(theLabels)
@@ -474,7 +474,7 @@ class GuiOutlineHeaderMenu(QMenu):
self.theParent = theParent
self.acceptToggle = True
- mnuHead = QAction("Select Columns", self)
+ mnuHead = QAction(self.tr("Select Columns"), self)
self.addAction(mnuHead)
self.addSeparator()
@@ -482,7 +482,7 @@ class GuiOutlineHeaderMenu(QMenu):
for hItem in nwOutline:
if hItem == nwOutline.TITLE:
continue
- self.actionMap[hItem] = QAction(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 15785ff8..8a1bc101 100644
--- a/nw/gui/outlinedetails.py
+++ b/nw/gui/outlinedetails.py
@@ -27,12 +27,12 @@ along with this program. If not, see .
import nw
import logging
-from PyQt5.QtCore import Qt
+from PyQt5.QtCore import Qt, QT_TRANSLATE_NOOP
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 +40,10 @@ logger = logging.getLogger(__name__)
class GuiOutlineDetails(QScrollArea):
LVL_MAP = {
- "H1" : "Title",
- "H2" : "Chapter",
- "H3" : "Scene",
- "H4" : "Section"
+ "H1" : QT_TRANSLATE_NOOP("GuiOutlineDetails", "Title"),
+ "H2" : QT_TRANSLATE_NOOP("GuiOutlineDetails", "Chapter"),
+ "H3" : QT_TRANSLATE_NOOP("GuiOutlineDetails", "Scene"),
+ "H4" : QT_TRANSLATE_NOOP("GuiOutlineDetails", "Section"),
}
def __init__(self, theParent):
@@ -66,9 +66,9 @@ class GuiOutlineDetails(QScrollArea):
vSpace = int(self.mainConf.pxInt(4))
# Details Area
- self.titleLabel = QLabel("Title")
- self.fileLabel = QLabel("Document")
- self.itemLabel = QLabel("Status")
+ self.titleLabel = QLabel("%s" % self.tr("Title"))
+ self.fileLabel = QLabel("%s" % self.tr("Document"))
+ self.itemLabel = QLabel("%s" % self.tr("Status"))
self.titleValue = QLabel("")
self.fileValue = QLabel("")
self.itemValue = QLabel("")
@@ -81,9 +81,9 @@ class GuiOutlineDetails(QScrollArea):
self.itemValue.setMaximumWidth(maxTitle)
# Stats Area
- self.cCLabel = QLabel("Characters")
- self.wCLabel = QLabel("Words")
- self.pCLabel = QLabel("Paragraphs")
+ self.cCLabel = QLabel("%s" % self.tr("Characters"))
+ self.wCLabel = QLabel("%s" % self.tr("Words"))
+ self.pCLabel = QLabel("%s" % self.tr("Paragraphs"))
self.cCValue = QLabel("")
self.wCValue = QLabel("")
self.pCValue = QLabel("")
@@ -96,7 +96,7 @@ class GuiOutlineDetails(QScrollArea):
self.pCValue.setAlignment(Qt.AlignRight)
# Synopsis
- self.synopLabel = QLabel("Synopsis")
+ self.synopLabel = QLabel("%s" % self.tr("Synopsis"))
self.synopValue = QLabel("")
self.synopLWrap = QHBoxLayout()
self.synopValue.setWordWrap(True)
@@ -104,15 +104,15 @@ class GuiOutlineDetails(QScrollArea):
self.synopLWrap.addWidget(self.synopValue, 1)
# Tags
- self.povKeyLabel = QLabel("%s" % nwLabels.KEY_NAME[nwKeyWords.POV_KEY])
- self.focKeyLabel = QLabel("%s" % nwLabels.KEY_NAME[nwKeyWords.FOCUS_KEY])
- self.chrKeyLabel = QLabel("%s" % nwLabels.KEY_NAME[nwKeyWords.CHAR_KEY])
- self.pltKeyLabel = QLabel("%s" % nwLabels.KEY_NAME[nwKeyWords.PLOT_KEY])
- self.timKeyLabel = QLabel("%s" % nwLabels.KEY_NAME[nwKeyWords.TIME_KEY])
- self.wldKeyLabel = QLabel("%s" % nwLabels.KEY_NAME[nwKeyWords.WORLD_KEY])
- self.objKeyLabel = QLabel("%s" % nwLabels.KEY_NAME[nwKeyWords.OBJECT_KEY])
- self.entKeyLabel = QLabel("%s" % nwLabels.KEY_NAME[nwKeyWords.ENTITY_KEY])
- self.cstKeyLabel = QLabel("%s" % 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()
@@ -165,7 +165,7 @@ class GuiOutlineDetails(QScrollArea):
self.cstKeyLWrap.addWidget(self.cstKeyValue, 1)
# Selected Item Details
- self.mainGroup = QGroupBox("Title Details", self)
+ self.mainGroup = QGroupBox(self.tr("Title Details"), self)
self.mainForm = QGridLayout()
self.mainGroup.setLayout(self.mainForm)
@@ -190,7 +190,7 @@ class GuiOutlineDetails(QScrollArea):
self.mainForm.setVerticalSpacing(vSpace)
# Selected Item Tags
- self.tagsGroup = QGroupBox("Reference Tags", self)
+ self.tagsGroup = QGroupBox(self.tr("Reference Tags"), self)
self.tagsForm = QGridLayout()
self.tagsGroup.setLayout(self.tagsForm)
@@ -256,7 +256,7 @@ class GuiOutlineDetails(QScrollArea):
def clearDetails(self):
"""Clear all the data labels.
"""
- self.titleLabel.setText("Title")
+ self.titleLabel.setText("%s" % self.tr("Title"))
self.titleValue.setText("")
self.fileValue.setText("")
self.itemValue.setText("")
@@ -286,9 +286,9 @@ class GuiOutlineDetails(QScrollArea):
return False
if novIdx["level"] in self.LVL_MAP:
- self.titleLabel.setText("%s" % self.LVL_MAP[novIdx["level"]])
+ self.titleLabel.setText("%s" % self.tr(self.LVL_MAP[novIdx["level"]]))
else:
- self.titleLabel.setText("Title")
+ self.titleLabel.setText("%s" % self.tr("Title"))
self.titleValue.setText(novIdx["title"])
self.fileValue.setText(nwItem.itemName)
diff --git a/nw/gui/preferences.py b/nw/gui/preferences.py
index 4913be25..74a2af90 100644
--- a/nw/gui/preferences.py
+++ b/nw/gui/preferences.py
@@ -28,7 +28,7 @@ import nw
import logging
import os
-from PyQt5.QtCore import Qt
+from PyQt5.QtCore import Qt, QLocale
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import (
QDialog, QWidget, QComboBox, QSpinBox, QPushButton, QDialogButtonBox,
@@ -53,7 +53,7 @@ class GuiPreferences(PagedDialog):
self.theParent = theParent
self.theProject = theProject
- self.setWindowTitle("Preferences")
+ self.setWindowTitle(self.tr("Preferences"))
self.tabGeneral = GuiPreferencesGeneral(self.theParent)
self.tabProjects = GuiPreferencesProjects(self.theParent)
@@ -62,14 +62,16 @@ class GuiPreferences(PagedDialog):
self.tabSyntax = GuiPreferencesSyntax(self.theParent)
self.tabAuto = GuiPreferencesAutomation(self.theParent)
- self.addTab(self.tabGeneral, "General")
- self.addTab(self.tabProjects, "Projects")
- self.addTab(self.tabDocs, "Documents")
- self.addTab(self.tabEditor, "Editor")
- self.addTab(self.tabSyntax, "Highlighting")
- self.addTab(self.tabAuto, "Automation")
+ self.addTab(self.tabGeneral, self.tr("General"))
+ self.addTab(self.tabProjects, self.tr("Projects"))
+ self.addTab(self.tabDocs, self.tr("Documents"))
+ self.addTab(self.tabEditor, self.tr("Editor"))
+ self.addTab(self.tabSyntax, self.tr("Highlighting"))
+ self.addTab(self.tabAuto, self.tr("Automation"))
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+ self.buttonBox.button(QDialogButtonBox.Ok).setText(self.tr("Ok"))
+ self.buttonBox.button(QDialogButtonBox.Cancel).setText(self.tr("Cancel"))
self.buttonBox.accepted.connect(self._doSave)
self.buttonBox.rejected.connect(self._doClose)
self.addControls(self.buttonBox)
@@ -98,7 +100,7 @@ class GuiPreferences(PagedDialog):
if needsRestart:
self.theParent.makeAlert(
- "Some changes will not be applied until novelWriter has been restarted.",
+ self.tr("Some changes will not be applied until novelWriter has been restarted."),
nwAlert.INFO
)
@@ -130,7 +132,7 @@ class GuiPreferencesGeneral(QWidget):
# Look and Feel
# =============
- self.mainForm.addGroupLabel("Look and Feel")
+ self.mainForm.addGroupLabel(self.tr("Look and Feel"))
## Select Theme
self.guiTheme = QComboBox()
@@ -143,9 +145,9 @@ class GuiPreferencesGeneral(QWidget):
self.guiTheme.setCurrentIndex(themeIdx)
self.mainForm.addRow(
- "Main GUI theme",
+ self.tr("Main GUI theme"),
self.guiTheme,
- "Changing this requires restarting novelWriter."
+ self.tr("Changing this requires restarting novelWriter.")
)
## Select Icon Theme
@@ -159,18 +161,18 @@ class GuiPreferencesGeneral(QWidget):
self.guiIcons.setCurrentIndex(iconIdx)
self.mainForm.addRow(
- "Main icon theme",
+ self.tr("Main icon theme"),
self.guiIcons,
- "Changing this requires restarting novelWriter."
+ self.tr("Changing this requires restarting novelWriter.")
)
## Dark Icons
self.guiDark = QSwitch()
self.guiDark.setChecked(self.mainConf.guiDark)
self.mainForm.addRow(
- "Prefer icons for dark backgrounds",
+ self.tr("Prefer icons for dark backgrounds"),
self.guiDark,
- "May improve the look of icons on dark themes."
+ self.tr("May improve the look of icons on dark themes.")
)
## Font Family
@@ -182,9 +184,9 @@ class GuiPreferencesGeneral(QWidget):
self.fontButton.setMaximumWidth(int(2.5*self.theTheme.getTextWidth("...")))
self.fontButton.clicked.connect(self._selectFont)
self.mainForm.addRow(
- "Font family",
+ self.tr("Font family"),
self.guiFont,
- "Changing this requires restarting novelWriter.",
+ self.tr("Changing this requires restarting novelWriter."),
theButton = self.fontButton
)
@@ -195,38 +197,38 @@ class GuiPreferencesGeneral(QWidget):
self.guiFontSize.setSingleStep(1)
self.guiFontSize.setValue(self.mainConf.guiFontSize)
self.mainForm.addRow(
- "Font size",
+ self.tr("Font size"),
self.guiFontSize,
- "Changing this requires restarting novelWriter.",
- theUnit = "pt"
+ self.tr("Changing this requires restarting novelWriter."),
+ theUnit = self.tr("pt")
)
# GUI Settings
# ============
- self.mainForm.addGroupLabel("GUI Settings")
+ self.mainForm.addGroupLabel(self.tr("GUI Settings"))
self.showFullPath = QSwitch()
self.showFullPath.setChecked(self.mainConf.showFullPath)
self.mainForm.addRow(
- "Show full path in document header",
+ self.tr("Show full path in document header"),
self.showFullPath,
- "Add the parent folder names to the header."
+ self.tr("Add the parent folder names to the header.")
)
self.hideVScroll = QSwitch()
self.hideVScroll.setChecked(self.mainConf.hideVScroll)
self.mainForm.addRow(
- "Hide vertical scroll bars in main windows",
+ self.tr("Hide vertical scroll bars in main windows"),
self.hideVScroll,
- "Scrolling available with mouse wheel and keys only."
+ self.tr("Scrolling available with mouse wheel and keys only.")
)
self.hideHScroll = QSwitch()
self.hideHScroll.setChecked(self.mainConf.hideHScroll)
self.mainForm.addRow(
- "Hide horizontal scroll bars in main windows",
+ self.tr("Hide horizontal scroll bars in main windows"),
self.hideHScroll,
- "Scrolling available with mouse wheel and keys only."
+ self.tr("Scrolling available with mouse wheel and keys only.")
)
return
@@ -295,7 +297,7 @@ class GuiPreferencesProjects(QWidget):
# Automatic Save
# ==============
- self.mainForm.addGroupLabel("Automatic Save")
+ self.mainForm.addGroupLabel(self.tr("Automatic Save"))
## Document Save Timer
self.autoSaveDoc = QSpinBox(self)
@@ -304,10 +306,10 @@ class GuiPreferencesProjects(QWidget):
self.autoSaveDoc.setSingleStep(1)
self.autoSaveDoc.setValue(self.mainConf.autoSaveDoc)
self.mainForm.addRow(
- "Save document interval",
+ self.tr("Save document interval"),
self.autoSaveDoc,
- "How often the open document is automatically saved.",
- theUnit="seconds"
+ self.tr("How often the open document is automatically saved."),
+ theUnit=self.tr("seconds")
)
## Project Save Timer
@@ -317,24 +319,24 @@ class GuiPreferencesProjects(QWidget):
self.autoSaveProj.setSingleStep(1)
self.autoSaveProj.setValue(self.mainConf.autoSaveProj)
self.mainForm.addRow(
- "Save project interval",
+ self.tr("Save project interval"),
self.autoSaveProj,
- "How often the open project is automatically saved.",
- theUnit="seconds"
+ self.tr("How often the open project is automatically saved."),
+ theUnit=self.tr("seconds")
)
# Project Backup
# ==============
- self.mainForm.addGroupLabel("Project Backup")
+ self.mainForm.addGroupLabel(self.tr("Project Backup"))
## Backup Path
self.backupPath = self.mainConf.backupPath
- self.backupGetPath = QPushButton("Browse")
+ self.backupGetPath = QPushButton(self.tr("Browse"))
self.backupGetPath.clicked.connect(self._backupFolder)
self.backupPathRow = self.mainForm.addRow(
- "Backup storage location",
+ self.tr("Backup storage location"),
self.backupGetPath,
- "Path: %s" % self.backupPath
+ self.tr("Path: {0}").format(self.backupPath)
)
## Run when closing
@@ -342,9 +344,9 @@ class GuiPreferencesProjects(QWidget):
self.backupOnClose.setChecked(self.mainConf.backupOnClose)
self.backupOnClose.toggled.connect(self._toggledBackupOnClose)
self.mainForm.addRow(
- "Run backup when the project is closed",
+ self.tr("Run backup when the project is closed"),
self.backupOnClose,
- "Can be overridden for individual projects in project settings."
+ self.tr("Can be overridden for individual projects in project settings.")
)
## Ask before backup
@@ -353,22 +355,22 @@ class GuiPreferencesProjects(QWidget):
self.askBeforeBackup.setChecked(self.mainConf.askBeforeBackup)
self.askBeforeBackup.setEnabled(self.mainConf.backupOnClose)
self.mainForm.addRow(
- "Ask before running backup",
+ self.tr("Ask before running backup"),
self.askBeforeBackup,
- "If off, backups will run in the background."
+ self.tr("If off, backups will run in the background.")
)
# Session Timer
# =============
- self.mainForm.addGroupLabel("Session Timer")
+ self.mainForm.addGroupLabel(self.tr("Session Timer"))
## Pause when idle
self.stopWhenIdle = QSwitch()
self.stopWhenIdle.setChecked(self.mainConf.stopWhenIdle)
self.mainForm.addRow(
- "Pause the session timer when not writing",
+ self.tr("Pause the session timer when not writing"),
self.stopWhenIdle,
- "Also pauses when the application window does not have focus."
+ self.tr("Also pauses when the application window does not have focus.")
)
## Inactive time for idle
@@ -379,10 +381,10 @@ class GuiPreferencesProjects(QWidget):
self.userIdleTime.setDecimals(1)
self.userIdleTime.setValue(self.mainConf.userIdleTime/60.0)
self.mainForm.addRow(
- "Editor inactive time before pausing timer",
+ self.tr("Editor inactive time before pausing timer"),
self.userIdleTime,
- "User activity includes typing and changing the content.",
- theUnit="minutes"
+ self.tr("User activity includes typing and changing the content."),
+ theUnit=self.tr("minutes")
)
return
@@ -422,11 +424,13 @@ class GuiPreferencesProjects(QWidget):
dlgOpt |= QFileDialog.ShowDirsOnly
dlgOpt |= QFileDialog.DontUseNativeDialog
newDir = QFileDialog.getExistingDirectory(
- self, "Backup Directory", currDir, options=dlgOpt
+ self, self.tr("Backup Directory"), currDir, options=dlgOpt
)
if newDir:
self.backupPath = newDir
- self.mainForm.setHelpText(self.backupPathRow, "Path: %s" % self.backupPath)
+ self.mainForm.setHelpText(
+ self.backupPathRow, self.tr("Path: {0}").format(self.backupPath)
+ )
return True
return False
@@ -456,7 +460,7 @@ class GuiPreferencesDocuments(QWidget):
# Text Style
# ==========
- self.mainForm.addGroupLabel("Text Style")
+ self.mainForm.addGroupLabel(self.tr("Text Style"))
## Font Family
self.textFont = QLineEdit()
@@ -467,9 +471,9 @@ class GuiPreferencesDocuments(QWidget):
self.fontButton.setMaximumWidth(int(2.5*self.theTheme.getTextWidth("...")))
self.fontButton.clicked.connect(self._selectFont)
self.mainForm.addRow(
- "Font family",
+ self.tr("Font family"),
self.textFont,
- "Font for the document editor and viewer.",
+ self.tr("Font for the document editor and viewer."),
theButton = self.fontButton
)
@@ -480,15 +484,15 @@ class GuiPreferencesDocuments(QWidget):
self.textSize.setSingleStep(1)
self.textSize.setValue(self.mainConf.textSize)
self.mainForm.addRow(
- "Font size",
+ self.tr("Font size"),
self.textSize,
- "Font size for the document editor and viewer.",
- theUnit = "pt"
+ self.tr("Font size for the document editor and viewer."),
+ theUnit = self.tr("pt")
)
# Text Flow
# =========
- self.mainForm.addGroupLabel("Text Flow")
+ self.mainForm.addGroupLabel(self.tr("Text Flow"))
## Max Text Width in Normal Mode
self.textWidth = QSpinBox(self)
@@ -497,10 +501,10 @@ class GuiPreferencesDocuments(QWidget):
self.textWidth.setSingleStep(10)
self.textWidth.setValue(self.mainConf.textWidth)
self.mainForm.addRow(
- "Maximum text width in \"Normal Mode\"",
+ self.tr("Maximum text width in \"Normal Mode\""),
self.textWidth,
- "Horizontal margins are scaled automatically.",
- theUnit="px"
+ self.tr("Horizontal margins are scaled automatically."),
+ theUnit=self.tr("px")
)
## Max Text Width in Focus Mode
@@ -510,37 +514,37 @@ class GuiPreferencesDocuments(QWidget):
self.focusWidth.setSingleStep(10)
self.focusWidth.setValue(self.mainConf.focusWidth)
self.mainForm.addRow(
- "Maximum text width in \"Focus Mode\"",
+ self.tr("Maximum text width in \"Focus Mode\""),
self.focusWidth,
- "Horizontal margins are scaled automatically.",
- theUnit="px"
+ self.tr("Horizontal margins are scaled automatically."),
+ theUnit=self.tr("px")
)
## Document Fixed Width
self.textFixedW = QSwitch()
self.textFixedW.setChecked(not self.mainConf.textFixedW)
self.mainForm.addRow(
- "Disable maximum text width in \"Normal Mode\"",
+ self.tr("Disable maximum text width in \"Normal Mode\""),
self.textFixedW,
- "Text width is defined by the margins only."
+ self.tr("Text width is defined by the margins only.")
)
## Focus Mode Footer
self.hideFocusFooter = QSwitch()
self.hideFocusFooter.setChecked(self.mainConf.hideFocusFooter)
self.mainForm.addRow(
- "Hide document footer in \"Focus Mode\"",
+ self.tr("Hide document footer in \"Focus Mode\""),
self.hideFocusFooter,
- "Hide the information bar at the bottom of the document."
+ self.tr("Hide the information bar at the bottom of the document.")
)
## Justify Text
self.doJustify = QSwitch()
self.doJustify.setChecked(self.mainConf.doJustify)
self.mainForm.addRow(
- "Justify the text margins in editor and viewer",
+ self.tr("Justify the text margins in editor and viewer"),
self.doJustify,
- "Lay out text with straight edges in the editor and viewer."
+ self.tr("Lay out text with straight edges in the editor and viewer.")
)
## Document Margins
@@ -550,10 +554,10 @@ class GuiPreferencesDocuments(QWidget):
self.textMargin.setSingleStep(1)
self.textMargin.setValue(self.mainConf.textMargin)
self.mainForm.addRow(
- "Text margin",
+ self.tr("Text margin"),
self.textMargin,
- "If maximum width is set, this becomes the minimum margin.",
- theUnit="px"
+ self.tr("If maximum width is set, this becomes the minimum margin."),
+ theUnit=self.tr("px")
)
## Tab Width
@@ -563,10 +567,10 @@ class GuiPreferencesDocuments(QWidget):
self.tabWidth.setSingleStep(1)
self.tabWidth.setValue(self.mainConf.tabWidth)
self.mainForm.addRow(
- "Tab width",
+ self.tr("Tab width"),
self.tabWidth,
- "The width of a tab key press in the editor and viewer.",
- theUnit="px"
+ self.tr("The width of a tab key press in the editor and viewer."),
+ theUnit=self.tr("px")
)
return
@@ -624,14 +628,19 @@ class GuiPreferencesEditor(QWidget):
self.mainForm.setHelpTextStyle(self.theTheme.helpText)
self.setLayout(self.mainForm)
+ mW = self.mainConf.pxInt(250)
+
# Spell Checking
# ==============
- self.mainForm.addGroupLabel("Spell Checking")
+ self.mainForm.addGroupLabel(self.tr("Spell Checking"))
## Spell Check Provider and Language
self.spellLangList = QComboBox(self)
+ self.spellLangList.setMaximumWidth(mW)
+
self.spellToolList = QComboBox(self)
- self.spellToolList.addItem("Internal (difflib)", nwConst.SP_INTERNAL)
+ self.spellToolList.setMaximumWidth(mW)
+ self.spellToolList.addItem("%s (difflib)" % self.tr("Internal"), nwConst.SP_INTERNAL)
self.spellToolList.addItem("Spell Enchant (pyenchant)", nwConst.SP_ENCHANT)
theModel = self.spellToolList.model()
@@ -645,14 +654,14 @@ class GuiPreferencesEditor(QWidget):
self._doUpdateSpellTool(0)
self.mainForm.addRow(
- "Spell check provider",
+ self.tr("Spell check provider"),
self.spellToolList,
- "Note that the internal spell check tool is quite slow."
+ self.tr("Note that the internal spell check tool is quite slow.")
)
self.mainForm.addRow(
- "Spell check language",
+ self.tr("Spell check language"),
self.spellLangList,
- "Available languages are determined by your system."
+ self.tr("Available languages are determined by your system.")
)
## Big Document Size Limit
@@ -662,15 +671,15 @@ class GuiPreferencesEditor(QWidget):
self.bigDocLimit.setSingleStep(10)
self.bigDocLimit.setValue(self.mainConf.bigDocLimit)
self.mainForm.addRow(
- "Big document limit",
+ self.tr("Big document limit"),
self.bigDocLimit,
- "Full spell checking is disabled above this limit.",
- theUnit="kB"
+ self.tr("Full spell checking is disabled above this limit."),
+ theUnit=self.tr("kB")
)
# Word Count
# ==========
- self.mainForm.addGroupLabel("Word Count")
+ self.mainForm.addGroupLabel(self.tr("Word Count"))
## Word Count Timer
self.wordCountTimer = QDoubleSpinBox(self)
@@ -680,54 +689,54 @@ class GuiPreferencesEditor(QWidget):
self.wordCountTimer.setSingleStep(0.1)
self.wordCountTimer.setValue(self.mainConf.wordCountTimer)
self.mainForm.addRow(
- "Word count interval",
+ self.tr("Word count interval"),
self.wordCountTimer,
- "How often the word count is updated.",
- theUnit="seconds"
+ self.tr("How often the word count is updated."),
+ theUnit=self.tr("seconds")
)
# Writing Guides
# ==============
- self.mainForm.addGroupLabel("Writing Guides")
+ self.mainForm.addGroupLabel(self.tr("Writing Guides"))
## Show Tabs and Spaces
self.showTabsNSpaces = QSwitch()
self.showTabsNSpaces.setChecked(self.mainConf.showTabsNSpaces)
self.mainForm.addRow(
- "Show tabs and spaces",
+ self.tr("Show tabs and spaces"),
self.showTabsNSpaces,
- "Add symbols to indicate tabs and spaces in the editor."
+ self.tr("Add symbols to indicate tabs and spaces in the editor.")
)
## Show Line Endings
self.showLineEndings = QSwitch()
self.showLineEndings.setChecked(self.mainConf.showLineEndings)
self.mainForm.addRow(
- "Show line endings",
+ self.tr("Show line endings"),
self.showLineEndings,
- "Add a symbol to indicate line endings in the editor."
+ self.tr("Add a symbol to indicate line endings in the editor.")
)
# Scroll Behaviour
# ================
- self.mainForm.addGroupLabel("Scroll Behaviour")
+ self.mainForm.addGroupLabel(self.tr("Scroll Behaviour"))
## Scroll Past End
self.scrollPastEnd = QSwitch()
self.scrollPastEnd.setChecked(self.mainConf.scrollPastEnd)
self.mainForm.addRow(
- "Scroll past end of the document",
+ self.tr("Scroll past end of the document"),
self.scrollPastEnd,
- "Also improves trypewriter scrolling for short documents."
+ self.tr("Also improves trypewriter scrolling for short documents.")
)
## Typewriter Scrolling
self.autoScroll = QSwitch()
self.autoScroll.setChecked(self.mainConf.autoScroll)
self.mainForm.addRow(
- "Typewriter style scrolling when you type",
+ self.tr("Typewriter style scrolling when you type"),
self.autoScroll,
- "Try to keep the cursor at a fixed vertical position."
+ self.tr("Try to keep the cursor at a fixed vertical position.")
)
## Typewriter Position
@@ -737,9 +746,9 @@ class GuiPreferencesEditor(QWidget):
self.autoScrollPos.setSingleStep(1)
self.autoScrollPos.setValue(int(self.mainConf.autoScrollPos))
self.mainForm.addRow(
- "Minimum position for Typewriter scrolling",
+ self.tr("Minimum position for Typewriter scrolling"),
self.autoScrollPos,
- "Percentage of the editor height from the top.",
+ self.tr("Percentage of the editor height from the top."),
theUnit = "%"
)
@@ -792,8 +801,11 @@ class GuiPreferencesEditor(QWidget):
theDict = NWSpellSimple()
self.spellLangList.clear()
- for spTag, spName in theDict.listDictionaries():
- self.spellLangList.addItem(spName, spTag)
+ for spTag, spProv in theDict.listDictionaries():
+ qLocal = QLocale(spTag)
+ spLang = qLocal.nativeLanguageName().title()
+ spName = qLocal.bcp47Name()
+ self.spellLangList.addItem("%s (%s) [%s]" % (spLang, spName, spProv), spTag)
spellIdx = self.spellLangList.findData(self.mainConf.spellLanguage)
if spellIdx != -1:
@@ -819,7 +831,7 @@ class GuiPreferencesSyntax(QWidget):
# Highlighting Theme
# ==================
- self.mainForm.addGroupLabel("Highlighting Theme")
+ self.mainForm.addGroupLabel(self.tr("Highlighting Theme"))
self.guiSyntax = QComboBox()
self.guiSyntax.setMinimumWidth(self.mainConf.pxInt(200))
@@ -831,50 +843,50 @@ class GuiPreferencesSyntax(QWidget):
self.guiSyntax.setCurrentIndex(syntaxIdx)
self.mainForm.addRow(
- "Highlighting theme",
+ self.tr("Highlighting theme"),
self.guiSyntax,
- "Colour theme to apply to the editor and viewer."
+ self.tr("Colour theme to apply to the editor and viewer.")
)
# Quotes & Dialogue
# =================
- self.mainForm.addGroupLabel("Quotes & Dialogue")
+ self.mainForm.addGroupLabel(self.tr("Quotes & Dialogue"))
self.highlightQuotes = QSwitch()
self.highlightQuotes.setChecked(self.mainConf.highlightQuotes)
self.highlightQuotes.toggled.connect(self._toggleHighlightQuotes)
self.mainForm.addRow(
- "Highlight text wrapped in quotes",
+ self.tr("Highlight text wrapped in quotes"),
self.highlightQuotes,
- "Applies to single, double and straight quotes."
+ self.tr("Applies to single, double and straight quotes.")
)
self.allowOpenSQuote = QSwitch()
self.allowOpenSQuote.setChecked(self.mainConf.allowOpenSQuote)
self.mainForm.addRow(
- "Allow open-ended single quotes",
+ self.tr("Allow open-ended single quotes"),
self.allowOpenSQuote,
- "Highlight single-quoted line with no closing quote."
+ self.tr("Highlight single-quoted line with no closing quote.")
)
self.allowOpenDQuote = QSwitch()
self.allowOpenDQuote.setChecked(self.mainConf.allowOpenDQuote)
self.mainForm.addRow(
- "Allow open-ended double quotes",
+ self.tr("Allow open-ended double quotes"),
self.allowOpenDQuote,
- "Highlight double-quoted line with no closing quote."
+ self.tr("Highlight double-quoted line with no closing quote.")
)
# Text Emphasis
# =============
- self.mainForm.addGroupLabel("Text Emphasis")
+ self.mainForm.addGroupLabel(self.tr("Text Emphasis"))
self.highlightEmph = QSwitch()
self.highlightEmph.setChecked(self.mainConf.highlightEmph)
self.mainForm.addRow(
- "Add highlight colour to emphasised text",
+ self.tr("Add highlight colour to emphasised text"),
self.highlightEmph,
- "Applies to emphasis (italic) and strong (bold)."
+ self.tr("Applies to emphasis (italic) and strong (bold).")
)
return
@@ -927,15 +939,15 @@ class GuiPreferencesAutomation(QWidget):
# Automatic Features
# ==================
- self.mainForm.addGroupLabel("Automatic Features")
+ self.mainForm.addGroupLabel(self.tr("Automatic Features"))
## Auto-Select Word Under Cursor
self.autoSelect = QSwitch()
self.autoSelect.setChecked(self.mainConf.autoSelect)
self.mainForm.addRow(
- "Auto-select word under cursor",
+ self.tr("Auto-select word under cursor"),
self.autoSelect,
- "Apply formatting to word under cursor if no selection is made."
+ self.tr("Apply formatting to word under cursor if no selection is made.")
)
## Auto-Replace as You Type Main Switch
@@ -943,23 +955,23 @@ class GuiPreferencesAutomation(QWidget):
self.doReplace.setChecked(self.mainConf.doReplace)
self.doReplace.toggled.connect(self._toggleAutoReplaceMain)
self.mainForm.addRow(
- "Auto-replace text as you type",
+ self.tr("Auto-replace text as you type"),
self.doReplace,
- "Allow the editor to replace symbols as you type."
+ self.tr("Allow the editor to replace symbols as you type.")
)
# Replace as You Type
# ===================
- self.mainForm.addGroupLabel("Replace as You Type")
+ self.mainForm.addGroupLabel(self.tr("Replace as You Type"))
## Auto-Replace Single Quotes
self.doReplaceSQuote = QSwitch()
self.doReplaceSQuote.setChecked(self.mainConf.doReplaceSQuote)
self.doReplaceSQuote.setEnabled(self.mainConf.doReplace)
self.mainForm.addRow(
- "Auto-replace single quotes",
+ self.tr("Auto-replace single quotes"),
self.doReplaceSQuote,
- "Try to guess which is an opening or a closing single quote."
+ self.tr("Try to guess which is an opening or a closing single quote.")
)
## Auto-Replace Double Quotes
@@ -967,9 +979,9 @@ class GuiPreferencesAutomation(QWidget):
self.doReplaceDQuote.setChecked(self.mainConf.doReplaceDQuote)
self.doReplaceDQuote.setEnabled(self.mainConf.doReplace)
self.mainForm.addRow(
- "Auto-replace double quotes",
+ self.tr("Auto-replace double quotes"),
self.doReplaceDQuote,
- "Try to guess which is an opening or a closing double quote."
+ self.tr("Try to guess which is an opening or a closing double quote.")
)
## Auto-Replace Hyphens
@@ -977,9 +989,9 @@ class GuiPreferencesAutomation(QWidget):
self.doReplaceDash.setChecked(self.mainConf.doReplaceDash)
self.doReplaceDash.setEnabled(self.mainConf.doReplace)
self.mainForm.addRow(
- "Auto-replace dashes",
+ self.tr("Auto-replace dashes"),
self.doReplaceDash,
- "Double and triple hyphens become short and long dashes."
+ self.tr("Double and triple hyphens become short and long dashes.")
)
## Auto-Replace Dots
@@ -987,14 +999,14 @@ class GuiPreferencesAutomation(QWidget):
self.doReplaceDots.setChecked(self.mainConf.doReplaceDots)
self.doReplaceDots.setEnabled(self.mainConf.doReplace)
self.mainForm.addRow(
- "Auto-replace dots",
+ self.tr("Auto-replace dots"),
self.doReplaceDots,
- "Three consecutive dots become ellipsis."
+ self.tr("Three consecutive dots become ellipsis.")
)
# Quotation Style
# ===============
- self.mainForm.addGroupLabel("Quotation Style")
+ self.mainForm.addGroupLabel(self.tr("Quotation Style"))
qWidth = self.mainConf.pxInt(40)
bWidth = int(2.5*self.theTheme.getTextWidth("..."))
@@ -1011,9 +1023,9 @@ class GuiPreferencesAutomation(QWidget):
self.btnSingleStyleO.setMaximumWidth(bWidth)
self.btnSingleStyleO.clicked.connect(lambda: self._getQuote("SO"))
self.mainForm.addRow(
- "Single quote open style",
+ self.tr("Single quote open style"),
self.quoteSym["SO"],
- "The symbol to use for a leading single quote.",
+ self.tr("The symbol to use for a leading single quote."),
theButton=self.btnSingleStyleO
)
@@ -1027,9 +1039,9 @@ class GuiPreferencesAutomation(QWidget):
self.btnSingleStyleC.setMaximumWidth(bWidth)
self.btnSingleStyleC.clicked.connect(lambda: self._getQuote("SC"))
self.mainForm.addRow(
- "Single quote close style",
+ self.tr("Single quote close style"),
self.quoteSym["SC"],
- "The symbol to use for a trailing single quote.",
+ self.tr("The symbol to use for a trailing single quote."),
theButton=self.btnSingleStyleC
)
@@ -1044,9 +1056,9 @@ class GuiPreferencesAutomation(QWidget):
self.btnDoubleStyleO.setMaximumWidth(bWidth)
self.btnDoubleStyleO.clicked.connect(lambda: self._getQuote("DO"))
self.mainForm.addRow(
- "Double quote open style",
+ self.tr("Double quote open style"),
self.quoteSym["DO"],
- "The symbol to use for a leading double quote.",
+ self.tr("The symbol to use for a leading double quote."),
theButton=self.btnDoubleStyleO
)
@@ -1060,9 +1072,9 @@ class GuiPreferencesAutomation(QWidget):
self.btnDoubleStyleC.setMaximumWidth(bWidth)
self.btnDoubleStyleC.clicked.connect(lambda: self._getQuote("DC"))
self.mainForm.addRow(
- "Double quote close style",
+ self.tr("Double quote close style"),
self.quoteSym["DC"],
- "The symbol to use for a trailing double quote.",
+ self.tr("The symbol to use for a trailing double quote."),
theButton=self.btnDoubleStyleC
)
diff --git a/nw/gui/projdetails.py b/nw/gui/projdetails.py
index d37cebee..1ee40f6f 100644
--- a/nw/gui/projdetails.py
+++ b/nw/gui/projdetails.py
@@ -54,7 +54,7 @@ class GuiProjectDetails(PagedDialog):
self.theProject = theProject
self.optState = theProject.optState
- self.setWindowTitle("Project Details")
+ self.setWindowTitle(self.tr("Project Details"))
wW = self.mainConf.pxInt(600)
wH = self.mainConf.pxInt(400)
@@ -69,10 +69,11 @@ class GuiProjectDetails(PagedDialog):
self.tabMain = GuiProjectDetailsMain(self.theParent, self.theProject)
self.tabContents = GuiProjectDetailsContents(self.theParent, self.theProject)
- self.addTab(self.tabMain, "Overview")
- self.addTab(self.tabContents, "Contents")
+ self.addTab(self.tabMain, self.tr("Overview"))
+ self.addTab(self.tabContents, self.tr("Contents"))
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Close)
+ self.buttonBox.button(QDialogButtonBox.Close).setText(self.tr("Close"))
self.buttonBox.rejected.connect(self._doClose)
self.addControls(self.buttonBox)
@@ -154,7 +155,9 @@ class GuiProjectDetailsMain(QWidget):
self.bookTitle.setAlignment(Qt.AlignHCenter)
self.bookTitle.setWordWrap(True)
- self.projName = QLabel("Working Title: %s" % self.theProject.projName)
+ self.projName = QLabel(
+ self.tr("Working Title: {0}").format(self.theProject.projName)
+ )
workFont = self.projName.font()
workFont.setPointSizeF(0.8*fPt)
workFont.setItalic(True)
@@ -162,7 +165,7 @@ class GuiProjectDetailsMain(QWidget):
self.projName.setAlignment(Qt.AlignHCenter)
self.projName.setWordWrap(True)
- self.bookAuthors = QLabel("By %s" % self.theProject.getAuthors())
+ self.bookAuthors = QLabel(self.tr("By {0}").format(self.theProject.getAuthors()))
authFont = self.bookAuthors.font()
authFont.setPointSizeF(1.2*fPt)
self.bookAuthors.setFont(authFont)
@@ -175,20 +178,20 @@ class GuiProjectDetailsMain(QWidget):
hCounts = self.theIndex.getNovelTitleCounts()
nwCount = self.theIndex.getNovelWordCount()
- self.wordCountLbl = QLabel("Words:")
+ self.wordCountLbl = QLabel("%s:" % self.tr("Words"))
self.wordCountVal = QLabel(f"{nwCount:n}")
- self.chapCountLbl = QLabel("Chapters:")
+ self.chapCountLbl = QLabel("%s:" % self.tr("Chapters"))
self.chapCountVal = QLabel(f"{hCounts[2]:n}")
- self.sceneCountLbl = QLabel("Scenes:")
+ self.sceneCountLbl = QLabel("%s:" % self.tr("Scenes"))
self.sceneCountVal = QLabel(f"{hCounts[3]:n}")
- self.revCountLbl = QLabel("Revisions:")
+ self.revCountLbl = QLabel("%s:" % self.tr("Revisions"))
self.revCountVal = QLabel(f"{self.theProject.saveCount:n}")
edTime = self.theProject.getCurrentEditTime()
- self.editTimeLbl = QLabel("Editing Time:")
+ self.editTimeLbl = QLabel("%s:" % self.tr("Editing Time"))
self.editTimeVal = QLabel(f"{edTime//3600:02d}:{edTime%3600//60:02d}")
self.statsGrid = QGridLayout()
@@ -208,7 +211,7 @@ class GuiProjectDetailsMain(QWidget):
# Meta
# ====
- self.projPathLbl = QLabel("Path:")
+ self.projPathLbl = QLabel("%s:" % self.tr("Path"))
self.projPathVal = QLineEdit()
self.projPathVal.setText(self.theProject.projPath)
self.projPathVal.setReadOnly(True)
@@ -271,7 +274,14 @@ class GuiProjectDetailsContents(QWidget):
self.tocTree.setIndentation(0)
self.tocTree.setColumnCount(6)
self.tocTree.setSelectionMode(QAbstractItemView.NoSelection)
- self.tocTree.setHeaderLabels(["Title", "Words", "Pages", "Page", "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)
@@ -304,16 +314,16 @@ class GuiProjectDetailsContents(QWidget):
clearDouble = self.optState.getInt("GuiProjectDetails", "clearDouble", True)
wordsHelp = (
- "Typical word count for a 5 by 8 inch book page with 11 pt font is 350."
+ self.tr("Typical word count for a 5 by 8 inch book page with 11 pt font is 350.")
)
offsetHelp = (
- "Start counting page numbers from this page."
+ self.tr("Start counting page numbers from this page.")
)
dblHelp = (
- "Assume a new chapter or partition always start on an odd numbered page."
+ self.tr("Assume a new chapter or partition always start on an odd numbered page.")
)
- self.wpLabel = QLabel("Words per page")
+ self.wpLabel = QLabel(self.tr("Words per page"))
self.wpLabel.setToolTip(wordsHelp)
self.wpValue = QSpinBox()
@@ -324,7 +334,7 @@ class GuiProjectDetailsContents(QWidget):
self.wpValue.setToolTip(wordsHelp)
self.wpValue.valueChanged.connect(self._populateTree)
- self.poLabel = QLabel("Count pages from")
+ self.poLabel = QLabel(self.tr("Count pages from"))
self.poLabel.setToolTip(offsetHelp)
self.poValue = QSpinBox()
@@ -335,7 +345,7 @@ class GuiProjectDetailsContents(QWidget):
self.poValue.setToolTip(offsetHelp)
self.poValue.valueChanged.connect(self._populateTree)
- self.dblLabel = QLabel("Clear double pages")
+ self.dblLabel = QLabel(self.tr("Clear double pages"))
self.dblLabel.setToolTip(dblHelp)
self.dblValue = QSwitch(self, 2*iPx, iPx)
@@ -358,7 +368,7 @@ class GuiProjectDetailsContents(QWidget):
# ========
self.outerBox = QVBoxLayout()
- self.outerBox.addWidget(QLabel("Table of Contents"))
+ self.outerBox.addWidget(QLabel("%s" % self.tr("Table of Contents")))
self.outerBox.addWidget(self.tocTree)
self.outerBox.addLayout(self.optionsBox)
@@ -390,7 +400,7 @@ class GuiProjectDetailsContents(QWidget):
"""
self._theToC = []
self._theToC = self.theIndex.getTableOfContents(2)
- self._theToC.append(("", 0, "END", 0))
+ self._theToC.append(("", 0, self.tr("END"), 0))
return
##
diff --git a/nw/gui/projload.py b/nw/gui/projload.py
index 29401de3..24c40ca2 100644
--- a/nw/gui/projload.py
+++ b/nw/gui/projload.py
@@ -74,7 +74,7 @@ class GuiProjectLoad(QDialog):
self.outerBox.setSpacing(sPx)
self.innerBox.setSpacing(sPx)
- self.setWindowTitle("Open Project")
+ self.setWindowTitle(self.tr("Open Project"))
self.setMinimumWidth(self.mainConf.pxInt(650))
self.setMinimumHeight(self.mainConf.pxInt(400))
self.setModal(True)
@@ -90,7 +90,11 @@ class GuiProjectLoad(QDialog):
self.listBox.setSelectionMode(QAbstractItemView.SingleSelection)
self.listBox.setDragDropMode(QAbstractItemView.NoDragDrop)
self.listBox.setColumnCount(3)
- self.listBox.setHeaderLabels(["Working Title", "Words", "Last Opened"])
+ self.listBox.setHeaderLabels([
+ self.tr("Working Title"),
+ self.tr("Words"),
+ self.tr("Last Opened"),
+ ])
self.listBox.setRootIsDecorated(False)
self.listBox.itemSelectionChanged.connect(self._doSelectRecent)
self.listBox.itemDoubleClicked.connect(self._doOpenRecent)
@@ -100,8 +104,8 @@ class GuiProjectLoad(QDialog):
treeHead.setTextAlignment(self.C_COUNT, Qt.AlignRight)
treeHead.setTextAlignment(self.C_TIME, Qt.AlignRight)
- self.lblRecent = QLabel("Recently Opened Projects")
- self.lblPath = QLabel("Path")
+ self.lblRecent = QLabel("%s" % self.tr("Recently Opened Projects"))
+ self.lblPath = QLabel("%s" % self.tr("Path"))
self.selPath = QLineEdit("")
self.selPath.setReadOnly(True)
@@ -123,13 +127,15 @@ class GuiProjectLoad(QDialog):
self.innerBox.addLayout(self.projectForm)
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Open | QDialogButtonBox.Cancel)
+ self.buttonBox.button(QDialogButtonBox.Open).setText(self.tr("Open"))
+ self.buttonBox.button(QDialogButtonBox.Cancel).setText(self.tr("Cancel"))
self.buttonBox.accepted.connect(self._doOpenRecent)
self.buttonBox.rejected.connect(self._doCancel)
- self.newButton = self.buttonBox.addButton("New", QDialogButtonBox.ActionRole)
+ self.newButton = self.buttonBox.addButton(self.tr("New"), QDialogButtonBox.ActionRole)
self.newButton.clicked.connect(self._doNewProject)
- self.delButton = self.buttonBox.addButton("Remove", QDialogButtonBox.ActionRole)
+ self.delButton = self.buttonBox.addButton(self.tr("Remove"), QDialogButtonBox.ActionRole)
self.delButton.clicked.connect(self._doDeleteRecent)
self.outerBox.addLayout(self.innerBox)
@@ -180,12 +186,15 @@ class GuiProjectLoad(QDialog):
"""Browse for a folder path.
"""
logger.verbose("GuiProjectLoad browse button clicked")
+ extFilter = [
+ self.tr("novelWriter Project File ({0})").format(nwFiles.PROJ_FILE),
+ self.tr("All files ({0})").format("*.*"),
+ ]
dlgOpt = QFileDialog.Options()
dlgOpt |= QFileDialog.DontUseNativeDialog
projFile, _ = QFileDialog.getOpenFileName(
- self, "Open novelWriter Project", "",
- "novelWriter Project File (%s);;All Files (*)" % nwFiles.PROJ_FILE,
- options=dlgOpt
+ self, self.tr("Open novelWriter Project"), "",
+ filter=";;".join(extFilter), options=dlgOpt
)
if projFile:
thePath = os.path.abspath(os.path.dirname(projFile))
@@ -221,10 +230,13 @@ class GuiProjectLoad(QDialog):
selList = self.listBox.selectedItems()
if selList:
projName = selList[0].text(self.C_NAME)
- msgYes = self.theParent.askQuestion("Remove Entry", (
- "Remove '%s' from the recent projects list? "
- "The project files will not be deleted."
- ) % projName)
+ 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)
+ )
if msgYes:
self.mainConf.removeFromRecentCache(
selList[0].data(self.C_NAME, Qt.UserRole)
diff --git a/nw/gui/projsettings.py b/nw/gui/projsettings.py
index e6a4e485..39cfe930 100644
--- a/nw/gui/projsettings.py
+++ b/nw/gui/projsettings.py
@@ -54,7 +54,7 @@ class GuiProjectSettings(PagedDialog):
self.optState = theProject.optState
self.theProject.countStatus()
- self.setWindowTitle("Project Settings")
+ self.setWindowTitle(self.tr("Project Settings"))
wW = self.mainConf.pxInt(570)
wH = self.mainConf.pxInt(375)
@@ -71,12 +71,14 @@ class GuiProjectSettings(PagedDialog):
self.tabImport = GuiProjectEditStatus(self.theParent, self.theProject, False)
self.tabReplace = GuiProjectEditReplace(self.theParent, self.theProject)
- self.addTab(self.tabMain, "Settings")
- self.addTab(self.tabStatus, "Status")
- self.addTab(self.tabImport, "Importance")
- self.addTab(self.tabReplace, "Auto-Replace")
+ self.addTab(self.tabMain, self.tr("Settings"))
+ self.addTab(self.tabStatus, self.tr("Status"))
+ self.addTab(self.tabImport, self.tr("Importance"))
+ self.addTab(self.tabReplace, self.tr("Auto-Replace"))
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+ self.buttonBox.button(QDialogButtonBox.Ok).setText(self.tr("Ok"))
+ self.buttonBox.button(QDialogButtonBox.Cancel).setText(self.tr("Cancel"))
self.buttonBox.accepted.connect(self._doSave)
self.buttonBox.rejected.connect(self._doClose)
self.addControls(self.buttonBox)
@@ -166,7 +168,7 @@ class GuiProjectEditMain(QWidget):
self.mainForm.setHelpTextStyle(self.theParent.theTheme.helpText)
self.setLayout(self.mainForm)
- self.mainForm.addGroupLabel("Project Settings")
+ self.mainForm.addGroupLabel(self.tr("Project Settings"))
xW = self.mainConf.pxInt(250)
xH = self.mainConf.pxInt(100)
@@ -176,9 +178,9 @@ class GuiProjectEditMain(QWidget):
self.editName.setFixedWidth(xW)
self.editName.setText(self.theProject.projName)
self.mainForm.addRow(
- "Working title",
+ self.tr("Working title"),
self.editName,
- "Should be set only once."
+ self.tr("Should be set only once.")
)
self.editTitle = QLineEdit()
@@ -186,9 +188,9 @@ class GuiProjectEditMain(QWidget):
self.editTitle.setFixedWidth(xW)
self.editTitle.setText(self.theProject.bookTitle)
self.mainForm.addRow(
- "Novel title",
+ self.tr("Novel title"),
self.editTitle,
- "Change whenever you want!"
+ self.tr("Change whenever you want!")
)
self.editAuthors = QPlainTextEdit()
@@ -199,22 +201,22 @@ class GuiProjectEditMain(QWidget):
self.editAuthors.setFixedHeight(xH)
self.editAuthors.setFixedWidth(xW)
self.mainForm.addRow(
- "Author(s)",
+ self.tr("Author(s)"),
self.editAuthors,
- "One name per line."
+ self.tr("One name per line.")
)
self.spellLang = QComboBox(self)
theDict = self.theParent.docEditor.theDict
- self.spellLang.addItem("Default", "None")
+ self.spellLang.addItem(self.tr("Default"), "None")
if theDict is not None:
for spTag, spName in theDict.listDictionaries():
self.spellLang.addItem(spName, spTag)
self.mainForm.addRow(
- "Spell check language",
+ self.tr("Spell check language"),
self.spellLang,
- "Overrides main preferences."
+ self.tr("Overrides main preferences.")
)
spellIdx = 0
@@ -226,9 +228,9 @@ class GuiProjectEditMain(QWidget):
self.doBackup = QSwitch(self)
self.doBackup.setChecked(not self.theProject.doBackup)
self.mainForm.addRow(
- "No backup on close",
+ self.tr("No backup on close"),
self.doBackup,
- "Overrides main preferences."
+ self.tr("Overrides main preferences.")
)
return
@@ -271,12 +273,12 @@ class GuiProjectEditStatus(QWidget):
self.editName = QLineEdit()
self.editName.setMaxLength(40)
self.editName.setEnabled(False)
- self.newButton = QPushButton("New")
- self.delButton = QPushButton("Delete")
- self.saveButton = QPushButton("Save")
+ self.newButton = QPushButton(self.tr("New"))
+ self.delButton = QPushButton(self.tr("Delete"))
+ self.saveButton = QPushButton(self.tr("Save"))
self.colPixmap = QPixmap(self.iPx, self.iPx)
self.colPixmap.fill(QColor(120, 120, 120))
- self.colButton = QPushButton(QIcon(self.colPixmap), "Colour")
+ self.colButton = QPushButton(QIcon(self.colPixmap), self.tr("Colour"))
self.colButton.setIconSize(self.colPixmap.rect().size())
self.newButton.clicked.connect(self._newItem)
@@ -287,7 +289,7 @@ class GuiProjectEditStatus(QWidget):
self.mainForm.addWidget(self.newButton)
self.mainForm.addWidget(self.delButton)
self.mainForm.addStretch(1)
- self.mainForm.addWidget(QLabel("Name"))
+ self.mainForm.addWidget(QLabel("%s" % self.tr("Name")))
self.mainForm.addWidget(self.editName)
self.mainForm.addWidget(self.colButton)
self.mainForm.addStretch(1)
@@ -297,9 +299,9 @@ class GuiProjectEditStatus(QWidget):
self.mainBox.addLayout(self.mainForm)
if isStatus:
- self.outerBox.addWidget(QLabel("Novel File Status Levels"))
+ self.outerBox.addWidget(QLabel("%s" % self.tr("Novel File Status Levels")))
else:
- self.outerBox.addWidget(QLabel("Note File Importance Levels"))
+ self.outerBox.addWidget(QLabel("%s" % self.tr("Note File Importance Levels")))
self.outerBox.addLayout(self.mainBox)
self.setLayout(self.outerBox)
@@ -325,7 +327,8 @@ class GuiProjectEditStatus(QWidget):
"""
if self.selColour is not None:
newCol = QColorDialog.getColor(
- self.selColour, self, "Select Colour", QColorDialog.DontUseNativeDialog
+ self.selColour, self, self.tr("Select Colour"),
+ QColorDialog.DontUseNativeDialog
)
if newCol.isValid():
self.selColour = newCol
@@ -338,7 +341,7 @@ class GuiProjectEditStatus(QWidget):
def _newItem(self):
"""Create a new status item.
"""
- newItem = self._addItem("New Item", (0, 0, 0), None, 0)
+ newItem = self._addItem(self.tr("New Item"), (0, 0, 0), None, 0)
newItem.setBackground(QBrush(QColor(0, 255, 0, 80)))
self.colChanged = True
return
@@ -355,7 +358,7 @@ class GuiProjectEditStatus(QWidget):
self.colChanged = True
else:
self.theParent.makeAlert(
- "Cannot delete status item that is in use.", nwAlert.ERROR
+ self.tr("Cannot delete status item that is in use."), nwAlert.ERROR
)
return
@@ -372,7 +375,9 @@ class GuiProjectEditStatus(QWidget):
self.selColour.blue(),
self.colData[selIdx][4]
)
- selItem.setText("%s [%d]" % (self.colData[selIdx][0], self.colCounts[selIdx]))
+ selItem.setText(self.tr("{0} [{1}]").format(
+ self.colData[selIdx][0], self.colCounts[selIdx])
+ )
selItem.setIcon(self.colButton.icon())
self.editName.setEnabled(False)
self.colChanged = True
@@ -384,7 +389,7 @@ class GuiProjectEditStatus(QWidget):
newIcon = QPixmap(self.iPx, self.iPx)
newIcon.fill(QColor(*iCol))
newItem = QListWidgetItem()
- newItem.setText("%s [%d]" % (iName, nUse))
+ newItem.setText(self.tr("{0} [{1}]").format(iName, nUse))
newItem.setIcon(QIcon(newIcon))
newItem.setData(Qt.UserRole, len(self.colData))
self.listBox.addItem(newItem)
@@ -450,7 +455,10 @@ class GuiProjectEditReplace(QWidget):
self.optState.getInt("GuiProjectSettings", "replaceColW", 100)
)
self.listBox = QTreeWidget()
- self.listBox.setHeaderLabels(["Keyword", "Replace With"])
+ self.listBox.setHeaderLabels([
+ self.tr("Keyword"),
+ self.tr("Replace With"),
+ ])
self.listBox.itemSelectionChanged.connect(self._selectedItem)
self.listBox.setColumnWidth(0, wCol0)
self.listBox.setIndentation(0)
@@ -467,9 +475,9 @@ class GuiProjectEditReplace(QWidget):
self.saveButton = QPushButton(self.theTheme.getIcon("done"), "")
self.addButton = QPushButton(self.theTheme.getIcon("add"), "")
self.delButton = QPushButton(self.theTheme.getIcon("remove"), "")
- self.saveButton.setToolTip("Save entry")
- self.addButton.setToolTip("Add new entry")
- self.delButton.setToolTip("Delete selected entry")
+ self.saveButton.setToolTip(self.tr("Save entry"))
+ self.addButton.setToolTip(self.tr("Add new entry"))
+ self.delButton.setToolTip(self.tr("Delete selected entry"))
self.editKey.setEnabled(False)
self.editKey.setMaxLength(40)
@@ -486,7 +494,9 @@ class GuiProjectEditReplace(QWidget):
self.bottomBox.addWidget(self.addButton)
self.bottomBox.addWidget(self.delButton)
- self.outerBox.addWidget(QLabel("Text Replace List for Preview and Export"))
+ self.outerBox.addWidget(
+ 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)
diff --git a/nw/gui/projtree.py b/nw/gui/projtree.py
index eff1ae7c..90126ab8 100644
--- a/nw/gui/projtree.py
+++ b/nw/gui/projtree.py
@@ -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__)
@@ -85,14 +86,19 @@ class GuiProjectTree(QTreeWidget):
self.setExpandsOnDoubleClick(True)
self.setIndentation(iPx)
self.setColumnCount(4)
- self.setHeaderLabels(["Label", "Words", "Inc", "Flags"])
+ self.setHeaderLabels([
+ self.tr("Label"),
+ self.tr("Words"),
+ self.tr("Inc"),
+ self.tr("Flags")
+ ])
treeHeadItem = self.headerItem()
treeHeadItem.setTextAlignment(self.C_COUNT, Qt.AlignRight)
- treeHeadItem.setToolTip(self.C_NAME, "Item label")
- treeHeadItem.setToolTip(self.C_COUNT, "Word count")
- treeHeadItem.setToolTip(self.C_EXPORT, "Include in build")
- treeHeadItem.setToolTip(self.C_FLAGS, "Status, class, and layout flags")
+ treeHeadItem.setToolTip(self.C_NAME, self.tr("Item label"))
+ treeHeadItem.setToolTip(self.C_COUNT, self.tr("Word count"))
+ treeHeadItem.setToolTip(self.C_EXPORT, self.tr("Include in build"))
+ treeHeadItem.setToolTip(self.C_FLAGS, self.tr("Status, class, and layout flags"))
# Let the last column stretch, and set the minimum size to the
# size of the icon as the default Qt font metrics approach fails
@@ -193,12 +199,12 @@ class GuiProjectTree(QTreeWidget):
if itemClass is None:
if itemType == nwItemType.FILE:
self.makeAlert(
- "Please select a valid location in the tree to add the document.",
+ self.tr("Please select a valid location in the tree to add the document."),
nwAlert.ERROR
)
else:
self.makeAlert(
- "Please select a valid location in the tree to add the folder.",
+ self.tr("Please select a valid location in the tree to add the folder."),
nwAlert.ERROR
)
return False
@@ -209,7 +215,7 @@ class GuiProjectTree(QTreeWidget):
)
if itemType == nwItemType.ROOT:
- tHandle = self.theProject.newRoot(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
@@ -223,7 +229,7 @@ class GuiProjectTree(QTreeWidget):
# If still nothing, give up
if pHandle is None:
self.makeAlert(
- "Did not find anywhere to add the file or folder!", nwAlert.ERROR
+ self.tr("Did not find anywhere to add the file or folder!"), nwAlert.ERROR
)
return False
@@ -237,14 +243,14 @@ class GuiProjectTree(QTreeWidget):
# If we again have no home, give up
if pHandle is None:
self.makeAlert(
- "Did not find anywhere to add the file or folder!", nwAlert.ERROR
+ self.tr("Did not find anywhere to add the file or folder!"), nwAlert.ERROR
)
return False
if self.theProject.projTree.isTrashRoot(pHandle):
self.makeAlert(
- "Cannot add new files or folders to the %s folder." % (
- nwLabels.CLASS_NAME[nwItemClass.TRASH]
+ self.tr("Cannot add new files or folders to the {0} folder.").format(
+ trConst(nwLabels.CLASS_NAME[nwItemClass.TRASH])
), nwAlert.ERROR
)
return False
@@ -253,18 +259,18 @@ class GuiProjectTree(QTreeWidget):
# If we're still here, add the file or folder
if itemType == nwItemType.FILE:
- tHandle = self.theProject.newFile("New File", itemClass, pHandle)
+ tHandle = self.theProject.newFile(self.tr("New File"), itemClass, pHandle)
elif itemType == nwItemType.FOLDER:
if len(parTree) >= nwConst.MAX_DEPTH - 1:
# Folders cannot be deeper than MAX_DEPTH - 1, leaving room
# for one more level of files.
self.makeAlert((
- "Cannot add new folder to this item. "
- "Maximum folder depth has been reached."
+ self.tr("Cannot add new folder to this item."),
+ self.tr("Maximum folder depth has been reached.")
), nwAlert.ERROR)
return False
- tHandle = self.theProject.newFolder("New Folder", itemClass, pHandle)
+ tHandle = self.theProject.newFolder(self.tr("New Folder"), itemClass, pHandle)
else:
logger.error("Failed to add new item")
@@ -428,7 +434,7 @@ class GuiProjectTree(QTreeWidget):
logger.debug("Emptying Trash folder")
if trashHandle is None:
self.makeAlert(
- "There is currently no Trash folder in this project.", nwAlert.INFO
+ self.tr("There is currently no Trash folder in this project."), nwAlert.INFO
)
return False
@@ -438,11 +444,12 @@ class GuiProjectTree(QTreeWidget):
nTrash = len(theTrash)
if nTrash == 0:
- self.makeAlert("The Trash folder is already empty.", nwAlert.INFO)
+ self.makeAlert(self.tr("The Trash folder is already empty."), nwAlert.INFO)
return False
msgYes = self.askQuestion(
- "Empty Trash", "Permanently delete %d file(s) from Trash?" % nTrash
+ self.tr("Empty Trash"),
+ self.tr("Permanently delete {0} file(s) from Trash?").format(nTrash)
)
if not msgYes:
return False
@@ -500,7 +507,8 @@ class GuiProjectTree(QTreeWidget):
doPermanent = False
if not alreadyAsked:
msgYes = self.askQuestion(
- "Delete File", "Permanently delete file '%s'?" % nwItemS.itemName
+ self.tr("Delete File"),
+ self.tr("Permanently delete file '{0}'?").format(nwItemS.itemName)
)
if msgYes:
doPermanent = True
@@ -529,7 +537,8 @@ class GuiProjectTree(QTreeWidget):
doTrash = False
if askForTrash:
msgYes = self.askQuestion(
- "Delete File", "Move file '%s' to Trash?" % nwItemS.itemName
+ self.tr("Delete File"),
+ self.tr("Move file '{0}' to Trash?").format(nwItemS.itemName),
)
if msgYes:
doTrash = True
@@ -563,7 +572,7 @@ class GuiProjectTree(QTreeWidget):
self._deleteTreeItem(tHandle)
self._setTreeChanged(True)
else:
- self.makeAlert((
+ self.makeAlert(self.tr(
"Cannot delete folder. It is not empty. "
"Recursive deletion is not supported. "
"Please delete the content first."
@@ -579,7 +588,7 @@ class GuiProjectTree(QTreeWidget):
self.theParent.mainMenu.setAvailableRoot()
self._setTreeChanged(True)
else:
- self.makeAlert((
+ self.makeAlert(self.tr(
"Cannot delete root folder. It is not empty. "
"Recursive deletion is not supported. "
"Please delete the content first."
@@ -835,7 +844,7 @@ class GuiProjectTree(QTreeWidget):
snItem = self.theProject.projTree[sHandle]
dnItem = self.theProject.projTree[dHandle]
if dnItem is None:
- self.makeAlert("The item cannot be moved to that location.", nwAlert.ERROR)
+ self.makeAlert(self.tr("The item cannot be moved to that location."), nwAlert.ERROR)
return
pItem = sItem.parent()
@@ -866,7 +875,7 @@ class GuiProjectTree(QTreeWidget):
else:
theEvent.ignore()
logger.debug("Drag'n'drop of item %s not accepted" % sHandle)
- self.makeAlert("The item cannot be moved to that location.", nwAlert.ERROR)
+ self.makeAlert(self.tr("The item cannot be moved to that location."), nwAlert.ERROR)
return
@@ -964,7 +973,9 @@ class GuiProjectTree(QTreeWidget):
self.addTopLevelItem(newItem)
else:
self.makeAlert(
- "There is nowhere to add item with name '%s'" % 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
@@ -1083,43 +1094,43 @@ class GuiProjectTreeMenu(QMenu):
self.theTree = theTree
self.theItem = None
- self.editItem = QAction("Edit Project Item", self)
+ self.editItem = QAction(self.tr("Edit Project Item"), self)
self.editItem.triggered.connect(self._doEditItem)
self.addAction(self.editItem)
- self.openItem = QAction("Open Document", self)
+ self.openItem = QAction(self.tr("Open Document"), self)
self.openItem.triggered.connect(self._doOpenItem)
self.addAction(self.openItem)
- self.viewItem = QAction("View Document", self)
+ self.viewItem = QAction(self.tr("View Document"), self)
self.viewItem.triggered.connect(self._doViewItem)
self.addAction(self.viewItem)
- self.toggleExp = QAction("Toggle Included Flag", self)
+ self.toggleExp = QAction(self.tr("Toggle Included Flag"), self)
self.toggleExp.triggered.connect(self._doToggleExported)
self.addAction(self.toggleExp)
- self.newFile = QAction("New File", self)
+ self.newFile = QAction(self.tr("New File"), self)
self.newFile.triggered.connect(self._doMakeFile)
self.addAction(self.newFile)
- self.newFolder = QAction("New Folder", self)
+ self.newFolder = QAction(self.tr("New Folder"), self)
self.newFolder.triggered.connect(self._doMakeFolder)
self.addAction(self.newFolder)
- self.deleteItem = QAction("Delete Item", self)
+ self.deleteItem = QAction(self.tr("Delete Item"), self)
self.deleteItem.triggered.connect(self._doDeleteItem)
self.addAction(self.deleteItem)
- self.emptyTrash = QAction("Empty Trash", self)
+ self.emptyTrash = QAction(self.tr("Empty Trash"), self)
self.emptyTrash.triggered.connect(self._doEmptyTrash)
self.addAction(self.emptyTrash)
- self.moveUp = QAction("Move Item Up", self)
+ self.moveUp = QAction(self.tr("Move Item Up"), self)
self.moveUp.triggered.connect(self._doMoveUp)
self.addAction(self.moveUp)
- self.moveDown = QAction("Move Item Down", self)
+ self.moveDown = QAction(self.tr("Move Item Down"), self)
self.moveDown.triggered.connect(self._doMoveDown)
self.addAction(self.moveDown)
diff --git a/nw/gui/projwizard.py b/nw/gui/projwizard.py
index b1d72520..590a48ab 100644
--- a/nw/gui/projwizard.py
+++ b/nw/gui/projwizard.py
@@ -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__)
@@ -94,16 +94,18 @@ class ProjWizardIntroPage(QWizardPage):
self.theWizard = theWizard
self.theTheme = theWizard.theTheme
- self.setTitle("Create New Project")
- self.theText = QLabel(
+ 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.theText.setWordWrap(True)
- self.imgCredit = QLabel("Side image by Peter Mitterhofer, CC BY-SA 4.0")
+ self.imgCredit = QLabel(self.tr("Side image by {0}, {1}").format(
+ "Peter Mitterhofer", "CC BY-SA 4.0"
+ ))
lblFont = self.imgCredit.font()
lblFont.setPointSizeF(0.6*self.theTheme.fontPointSize)
self.imgCredit.setFont(lblFont)
@@ -117,22 +119,22 @@ class ProjWizardIntroPage(QWizardPage):
self.projName = QLineEdit()
self.projName.setMaxLength(200)
self.projName.setFixedWidth(xW)
- self.projName.setPlaceholderText("Required")
+ self.projName.setPlaceholderText(self.tr("Required"))
self.projTitle = QLineEdit()
self.projTitle.setMaxLength(200)
self.projTitle.setFixedWidth(xW)
- self.projTitle.setPlaceholderText("Optional")
+ self.projTitle.setPlaceholderText(self.tr("Optional"))
self.projAuthors = QPlainTextEdit()
self.projAuthors.setFixedHeight(xH)
self.projAuthors.setFixedWidth(xW)
- self.projAuthors.setPlaceholderText("Optional. One name per line.")
+ self.projAuthors.setPlaceholderText(self.tr("Optional. One name per line."))
self.mainForm = QFormLayout()
- self.mainForm.addRow("Working Title", self.projName)
- self.mainForm.addRow("Novel Title", self.projTitle)
- self.mainForm.addRow("Author(s)", self.projAuthors)
+ self.mainForm.addRow(self.tr("Working Title"), self.projName)
+ self.mainForm.addRow(self.tr("Novel Title"), self.projTitle)
+ self.mainForm.addRow(self.tr("Author(s)"), self.projAuthors)
self.mainForm.setVerticalSpacing(fS)
self.registerField("projName*", self.projName)
@@ -161,11 +163,11 @@ class ProjWizardFolderPage(QWizardPage):
self.theWizard = theWizard
self.theTheme = theWizard.theTheme
- self.setTitle("Select Project Folder")
- self.theText = QLabel(
+ 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.theText.setWordWrap(True)
xW = self.mainConf.pxInt(300)
@@ -174,14 +176,14 @@ class ProjWizardFolderPage(QWizardPage):
self.projPath = QLineEdit("")
self.projPath.setFixedWidth(xW)
- self.projPath.setPlaceholderText("Required")
+ self.projPath.setPlaceholderText(self.tr("Required"))
self.browseButton = QPushButton("...")
self.browseButton.setMaximumWidth(int(2.5*self.theTheme.getTextWidth("...")))
self.browseButton.clicked.connect(self._doBrowse)
self.mainForm = QHBoxLayout()
- self.mainForm.addWidget(QLabel("Project Path"), 0)
+ self.mainForm.addWidget(QLabel(self.tr("Project Path")), 0)
self.mainForm.addWidget(self.projPath, 1)
self.mainForm.addWidget(self.browseButton, 0)
self.mainForm.setSpacing(fS)
@@ -213,7 +215,7 @@ class ProjWizardFolderPage(QWizardPage):
dlgOpt |= QFileDialog.ShowDirsOnly
dlgOpt |= QFileDialog.DontUseNativeDialog
projDir = QFileDialog.getExistingDirectory(
- self, "Select Project Folder", lastPath, options=dlgOpt
+ self, self.tr("Select Project Folder"), lastPath, options=dlgOpt
)
if projDir:
projName = self.field("projName")
@@ -235,20 +237,20 @@ class ProjWizardPopulatePage(QWizardPage):
self.mainConf = nw.CONFIG
self.theWizard = theWizard
- self.setTitle("Populate Project")
- self.theText = QLabel(
+ 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.theText.setWordWrap(True)
vS = self.mainConf.pxInt(12)
fS = self.mainConf.pxInt(4)
- self.popMinimal = QRadioButton("Fill the project with a minimal set of items")
- self.popSample = QRadioButton("Fill the project with example files")
- self.popCustom = QRadioButton("Show detailed options for filling the project")
+ self.popMinimal = QRadioButton(self.tr("Fill the project with a minimal set of items"))
+ self.popSample = QRadioButton(self.tr("Fill the project with example files"))
+ self.popCustom = QRadioButton(self.tr("Show detailed options for filling the project"))
self.popMinimal.setChecked(True)
self.popBox = QVBoxLayout()
@@ -290,27 +292,39 @@ class ProjWizardCustomPage(QWizardPage):
self.mainConf = nw.CONFIG
self.theWizard = theWizard
- self.setTitle("Custom Project Options")
- self.theText = QLabel(
+ 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.theText.setWordWrap(True)
vS = self.mainConf.pxInt(12)
# Root Folders
- self.rootGroup = QGroupBox("Additional Root Folders")
+ self.rootGroup = QGroupBox(self.tr("Additional Root Folders"))
self.rootForm = QGridLayout()
self.rootGroup.setLayout(self.rootForm)
- self.lblPlot = QLabel("%s folder" % nwLabels.CLASS_NAME[nwItemClass.PLOT])
- self.lblChar = QLabel("%s folder" % nwLabels.CLASS_NAME[nwItemClass.CHARACTER])
- self.lblWorld = QLabel("%s folder" % nwLabels.CLASS_NAME[nwItemClass.WORLD])
- self.lblTime = QLabel("%s folder" % nwLabels.CLASS_NAME[nwItemClass.TIMELINE])
- self.lblObject = QLabel("%s folder" % nwLabels.CLASS_NAME[nwItemClass.OBJECT])
- self.lblEntity = QLabel("%s folder" % nwLabels.CLASS_NAME[nwItemClass.ENTITY])
+ 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(
+ trConst(nwLabels.CLASS_NAME[nwItemClass.OBJECT]))
+ )
+ self.lblEntity = QLabel(self.tr("{0} folder").format(
+ trConst(nwLabels.CLASS_NAME[nwItemClass.ENTITY]))
+ )
self.addPlot = QSwitch()
self.addChar = QSwitch()
@@ -338,7 +352,7 @@ class ProjWizardCustomPage(QWizardPage):
self.rootForm.setRowStretch(6, 1)
# Novel Options
- self.novelGroup = QGroupBox("Populate Novel Folder")
+ self.novelGroup = QGroupBox(self.tr("Populate Novel Folder"))
self.novelForm = QGridLayout()
self.novelGroup.setLayout(self.novelForm)
@@ -353,9 +367,9 @@ class ProjWizardCustomPage(QWizardPage):
self.chFolders = QSwitch()
self.chFolders.setChecked(True)
- self.novelForm.addWidget(QLabel("Add chapters"), 0, 0)
- self.novelForm.addWidget(QLabel("Scenes (per chapter)"), 1, 0)
- self.novelForm.addWidget(QLabel("Add chapter folders"), 2, 0)
+ self.novelForm.addWidget(QLabel(self.tr("Add chapters")), 0, 0)
+ self.novelForm.addWidget(QLabel(self.tr("Scenes (per chapter)")), 1, 0)
+ self.novelForm.addWidget(QLabel(self.tr("Add chapter folders")), 2, 0)
self.novelForm.addWidget(self.numChapters, 0, 1, 1, 1, Qt.AlignRight)
self.novelForm.addWidget(self.numScenes, 1, 1, 1, 1, Qt.AlignRight)
self.novelForm.addWidget(self.chFolders, 2, 1, 1, 1, Qt.AlignRight)
@@ -396,13 +410,15 @@ class ProjWizardFinalPage(QWizardPage):
self.mainConf = nw.CONFIG
self.theWizard = theWizard
- self.setTitle("Finished")
- self.theText = QLabel((
- "All done.
"
- "Press '{finish}' to create the new project.
"
- ).format(
- finish = "Done" if self.mainConf.osDarwin else "Finish"
- ))
+ self.setTitle(self.tr("Finished"))
+ self.theText = QLabel(
+ "{done}
{help}
".format(
+ done = self.tr("All done."),
+ help = 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)
# Assemble
diff --git a/nw/gui/statusbar.py b/nw/gui/statusbar.py
index 441cacda..4691202c 100644
--- a/nw/gui/statusbar.py
+++ b/nw/gui/statusbar.py
@@ -29,10 +29,10 @@ import logging
from time import time
+from PyQt5.QtCore import QLocale
from PyQt5.QtGui import QColor, QPainter
from PyQt5.QtWidgets import qApp, QStatusBar, QLabel, QAbstractButton
-from nw.core import NWSpellCheck
from nw.common import formatTime
logger = logging.getLogger(__name__)
@@ -63,7 +63,7 @@ class GuiMainStatus(QStatusBar):
## The Spell Checker Language
self.langIcon = QLabel("")
- self.langText = QLabel("None")
+ self.langText = QLabel(self.tr("None"))
self.langIcon.setPixmap(self.theTheme.getPixmap("status_lang", (iPx, iPx)))
self.langIcon.setContentsMargins(0, 0, 0, 0)
self.langText.setContentsMargins(0, 0, xM, 0)
@@ -72,7 +72,7 @@ class GuiMainStatus(QStatusBar):
## The Editor Status
self.docIcon = StatusLED(colNone, colTrue, colFalse, iPx, iPx, self)
- self.docText = QLabel("Editor")
+ self.docText = QLabel(self.tr("Editor"))
self.docIcon.setContentsMargins(0, 0, 0, 0)
self.docText.setContentsMargins(0, 0, xM, 0)
self.addPermanentWidget(self.docIcon)
@@ -80,7 +80,7 @@ class GuiMainStatus(QStatusBar):
## The Project Status
self.projIcon = StatusLED(colNone, colTrue, colFalse, iPx, iPx, self)
- self.projText = QLabel("Project")
+ self.projText = QLabel(self.tr("Project"))
self.projIcon.setContentsMargins(0, 0, 0, 0)
self.projText.setContentsMargins(0, 0, xM, 0)
self.addPermanentWidget(self.projIcon)
@@ -103,7 +103,7 @@ class GuiMainStatus(QStatusBar):
self.timeIcon = QLabel()
self.timeText = QLabel("")
self.timeIcon.setPixmap(self.timePixmap)
- self.timeText.setToolTip("Session Time")
+ self.timeText.setToolTip(self.tr("Session Time"))
self.timeText.setMinimumWidth(self.theTheme.getTextWidth("00:00:00:"))
self.timeIcon.setContentsMargins(0, 0, 0, 0)
self.timeText.setContentsMargins(0, 0, 0, 0)
@@ -151,13 +151,17 @@ class GuiMainStatus(QStatusBar):
"""Set the language code for the spell checker.
"""
if theLanguage is None:
- self.langText.setText("None")
+ self.langText.setText(self.tr("None"))
self.langText.setToolTip("")
else:
- self.langText.setText(NWSpellCheck.expandLanguage(theLanguage))
- self.langText.setToolTip(
- "Provider: %s" % (theProvider if theProvider else "unknown")
- )
+ qLocal = QLocale(theLanguage)
+ spLang = qLocal.nativeLanguageName().title()
+ self.langText.setText(spLang)
+ if theProvider:
+ self.langText.setToolTip("%s (%s)" % (theLanguage, theProvider))
+ else:
+ self.langText.setToolTip(theLanguage)
+
return
def setProjectStatus(self, isChanged):
@@ -175,8 +179,8 @@ class GuiMainStatus(QStatusBar):
def setStats(self, pWC, sWC):
"""Set the current project statistics.
"""
- self.statsText.setText(f"Words: {pWC:n} ({sWC:+n})")
- self.statsText.setToolTip("Project word count (session change)")
+ self.statsText.setText(self.tr("Words: {0} ({1})").format(f"{pWC:n}", f"{sWC:+n}"))
+ self.statsText.setToolTip(self.tr("Project word count (session change)"))
return
def setUserIdle(self, userIdle):
diff --git a/nw/gui/theme.py b/nw/gui/theme.py
index 9ec6018e..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,8 +397,8 @@ class GuiTheme:
with open(themeConf, mode="r", encoding="utf8") as inFile:
confParser.read_file(inFile)
except Exception as e:
- self.theParent.makeAlert(
- ["Could not load theme config file.", str(e)], nwAlert.ERROR
+ self.makeAlert(
+ [self.tr("Could not load theme config file."), str(e)], nwAlert.ERROR
)
continue
themeName = ""
@@ -425,8 +430,8 @@ class GuiTheme:
with open(syntaxPath, mode="r", encoding="utf8") as inFile:
confParser.read_file(inFile)
except Exception as e:
- self.theParent.makeAlert(
- ["Could not load syntax file.", str(e)], nwAlert.ERROR
+ self.makeAlert(
+ [self.tr("Could not load syntax file."), str(e)], nwAlert.ERROR
)
return []
syntaxName = ""
@@ -740,8 +745,8 @@ class GuiIcons:
with open(themeConf, mode="r", encoding="utf8") as inFile:
confParser.read_file(inFile)
except Exception as e:
- self.theParent.makeAlert(
- ["Could not load theme config file.", str(e)], nwAlert.ERROR
+ self.makeAlert(
+ [self.tr("Could not load theme config file."), str(e)], nwAlert.ERROR
)
continue
themeName = ""
diff --git a/nw/gui/wordlist.py b/nw/gui/wordlist.py
index f5b6fe80..0e7f3747 100644
--- a/nw/gui/wordlist.py
+++ b/nw/gui/wordlist.py
@@ -52,7 +52,7 @@ class GuiWordList(QDialog):
self.theProject = theProject
self.optState = theProject.optState
- self.setWindowTitle("Project Word List")
+ self.setWindowTitle(self.tr("Project Word List"))
mS = self.mainConf.pxInt(250)
wW = self.mainConf.pxInt(320)
@@ -68,7 +68,7 @@ class GuiWordList(QDialog):
# Main Widgets
# ============
- self.headLabel = QLabel("Project Word List")
+ self.headLabel = QLabel("%s" % self.tr("Project Word List"))
self.listBox = QListWidget()
self.listBox.setDragDropMode(QAbstractItemView.NoDragDrop)
@@ -77,11 +77,11 @@ class GuiWordList(QDialog):
self.newEntry = QLineEdit()
self.addButton = QPushButton(self.theTheme.getIcon("add"), "")
- self.addButton.setToolTip("Add new entry")
+ self.addButton.setToolTip(self.tr("Add new entry"))
self.addButton.clicked.connect(self._doAdd)
self.delButton = QPushButton(self.theTheme.getIcon("remove"), "")
- self.delButton.setToolTip("Delete selected entry")
+ self.delButton.setToolTip(self.tr("Delete selected entry"))
self.delButton.clicked.connect(self._doDelete)
self.editBox = QHBoxLayout()
@@ -90,6 +90,8 @@ class GuiWordList(QDialog):
self.editBox.addWidget(self.delButton, 0)
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Save | QDialogButtonBox.Close)
+ self.buttonBox.button(QDialogButtonBox.Save).setText(self.tr("Save"))
+ self.buttonBox.button(QDialogButtonBox.Close).setText(self.tr("Close"))
self.buttonBox.accepted.connect(self._doSave)
self.buttonBox.rejected.connect(self._doClose)
@@ -121,12 +123,13 @@ class GuiWordList(QDialog):
"""
newWord = self.newEntry.text().strip()
if newWord == "":
- self.theParent.makeAlert("Cannot add a blank word.", nwAlert.ERROR)
+ self.theParent.makeAlert(self.tr("Cannot add a blank word."), nwAlert.ERROR)
return False
if self.listBox.findItems(newWord, Qt.MatchExactly):
self.theParent.makeAlert(
- "The word '%s' is already in the word list." % newWord, nwAlert.ERROR
+ self.tr("The word '{0}' is already in the word list.").format(newWord),
+ nwAlert.ERROR
)
return False
diff --git a/nw/gui/writingstats.py b/nw/gui/writingstats.py
index dec1fef4..aa8ded71 100644
--- a/nw/gui/writingstats.py
+++ b/nw/gui/writingstats.py
@@ -72,7 +72,7 @@ class GuiWritingStats(QDialog):
self.timeFilter = 0.0
self.wordOffset = 0
- self.setWindowTitle("Writing Statistics")
+ self.setWindowTitle(self.tr("Writing Statistics"))
self.setMinimumWidth(self.mainConf.pxInt(420))
self.setMinimumHeight(self.mainConf.pxInt(400))
self.resize(
@@ -95,7 +95,13 @@ class GuiWritingStats(QDialog):
)
self.listBox = QTreeWidget()
- self.listBox.setHeaderLabels(["Session Start", "Length", "Idle", "Words", "Histogram"])
+ self.listBox.setHeaderLabels([
+ self.tr("Session Start"),
+ self.tr("Length"),
+ self.tr("Idle"),
+ self.tr("Words"),
+ self.tr("Histogram"),
+ ])
self.listBox.setIndentation(0)
self.listBox.setColumnWidth(self.C_TIME, wCol0)
self.listBox.setColumnWidth(self.C_LENGTH, wCol1)
@@ -125,7 +131,7 @@ class GuiWritingStats(QDialog):
self.barImage.fill(self.palette().highlight().color())
# Session Info
- self.infoBox = QGroupBox("Sum Totals", self)
+ self.infoBox = QGroupBox(self.tr("Sum Totals"), self)
self.infoForm = QGridLayout(self)
self.infoBox.setLayout(self.infoForm)
@@ -153,24 +159,33 @@ class GuiWritingStats(QDialog):
self.totalWords.setFont(self.theTheme.guiFontFixed)
self.totalWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
- self.infoForm.addWidget(QLabel("Total Time:"), 0, 0)
- self.infoForm.addWidget(QLabel("Idle Time:"), 1, 0)
- self.infoForm.addWidget(QLabel("Filtered Time:"), 2, 0)
- self.infoForm.addWidget(QLabel("Novel Word Count:"), 3, 0)
- self.infoForm.addWidget(QLabel("Notes Word Count:"), 4, 0)
- self.infoForm.addWidget(QLabel("Total Word Count:"), 5, 0)
+ lblTTime = QLabel(self.tr("Total Time:"))
+ lblITime = QLabel(self.tr("Idle Time:"))
+ lblFTime = QLabel(self.tr("Filtered Time:"))
+ lblNvCount = QLabel(self.tr("Novel Word Count:"))
+ lblNtCount = QLabel(self.tr("Notes Word Count:"))
+ lblTtCount = QLabel(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
sPx = self.theTheme.baseIconSize
- self.filterBox = QGroupBox("Filters", self)
+ self.filterBox = QGroupBox(self.tr("Filters"), self)
self.filterForm = QGridLayout(self)
self.filterBox.setLayout(self.filterForm)
@@ -210,12 +225,12 @@ class GuiWritingStats(QDialog):
)
self.showIdleTime.clicked.connect(self._updateListBox)
- self.filterForm.addWidget(QLabel("Count novel files"), 0, 0)
- self.filterForm.addWidget(QLabel("Count note files"), 1, 0)
- self.filterForm.addWidget(QLabel("Hide zero word count"), 2, 0)
- self.filterForm.addWidget(QLabel("Hide negative word count"), 3, 0)
- self.filterForm.addWidget(QLabel("Group entries by day"), 4, 0)
- self.filterForm.addWidget(QLabel("Show idle time"), 5, 0)
+ self.filterForm.addWidget(QLabel(self.tr("Count novel files")), 0, 0)
+ self.filterForm.addWidget(QLabel(self.tr("Count note files")), 1, 0)
+ self.filterForm.addWidget(QLabel(self.tr("Hide zero word count")), 2, 0)
+ self.filterForm.addWidget(QLabel(self.tr("Hide negative word count")), 3, 0)
+ self.filterForm.addWidget(QLabel(self.tr("Group entries by day")), 4, 0)
+ self.filterForm.addWidget(QLabel(self.tr("Show idle time")), 5, 0)
self.filterForm.addWidget(self.incNovel, 0, 1)
self.filterForm.addWidget(self.incNotes, 1, 1)
self.filterForm.addWidget(self.hideZeros, 2, 1)
@@ -236,7 +251,7 @@ class GuiWritingStats(QDialog):
self.optsBox = QHBoxLayout()
self.optsBox.addStretch(1)
- self.optsBox.addWidget(QLabel("Word count cap for the histogram"), 0)
+ self.optsBox.addWidget(QLabel(self.tr("Word count cap for the histogram")), 0)
self.optsBox.addWidget(self.histMax, 0)
# Buttons
@@ -244,19 +259,20 @@ class GuiWritingStats(QDialog):
self.buttonBox.rejected.connect(self._doClose)
self.btnClose = self.buttonBox.addButton(QDialogButtonBox.Close)
+ self.buttonBox.button(QDialogButtonBox.Close).setText(self.tr("Close"))
self.btnClose.setAutoDefault(False)
- self.btnSave = self.buttonBox.addButton("Save As", QDialogButtonBox.ActionRole)
+ self.btnSave = self.buttonBox.addButton(self.tr("Save As"), QDialogButtonBox.ActionRole)
self.btnSave.setAutoDefault(False)
self.saveMenu = QMenu(self)
self.btnSave.setMenu(self.saveMenu)
- self.saveJSON = QAction("JSON Data File (.json)", self)
+ self.saveJSON = QAction(self.tr("JSON Data File (.json)"), self)
self.saveJSON.triggered.connect(lambda: self._saveData(self.FMT_JSON))
self.saveMenu.addAction(self.saveJSON)
- self.saveCSV = QAction("CSV Data File (.csv)", self)
+ self.saveCSV = QAction(self.tr("CSV Data File (.csv)"), self)
self.saveCSV.triggered.connect(lambda: self._saveData(self.FMT_CSV))
self.saveMenu.addAction(self.saveCSV)
@@ -338,10 +354,10 @@ class GuiWritingStats(QDialog):
if dataFmt == self.FMT_JSON:
fileExt = "json"
- textFmt = "JSON Data File"
+ textFmt = self.tr("JSON Data File")
elif dataFmt == self.FMT_CSV:
fileExt = "csv"
- textFmt = "CSV Data File"
+ textFmt = self.tr("CSV Data File")
else:
return False
@@ -356,7 +372,7 @@ class GuiWritingStats(QDialog):
dlgOpt = QFileDialog.Options()
dlgOpt |= QFileDialog.DontUseNativeDialog
savePath, _ = QFileDialog.getSaveFileName(
- self, "Save Document As", savePath, options=dlgOpt
+ self, self.tr("Save Document As"), savePath, options=dlgOpt
)
if not savePath:
return False
@@ -474,7 +490,7 @@ class GuiWritingStats(QDialog):
except Exception as e:
self.theParent.makeAlert(
- ["Failed to read session log file.", str(e)], nwAlert.ERROR
+ [self.tr("Failed to read session log file."), str(e)], nwAlert.ERROR
)
return False
diff --git a/nw/guimain.py b/nw/guimain.py
index 6cb68cb3..f059ef16 100644
--- a/nw/guimain.py
+++ b/nw/guimain.py
@@ -76,6 +76,7 @@ class GuiMain(QMainWindow):
logger.info("Python Version: %s (0x%x)" % (
self.mainConf.verPyString, self.mainConf.verPyHexVal)
)
+ logger.info("GUI Language: %s" % self.mainConf.guiLang)
# Core Classes
# ============
@@ -122,8 +123,8 @@ class GuiMain(QMainWindow):
self.projTabs = QTabWidget()
self.projTabs.setTabPosition(QTabWidget.South)
self.projTabs.setStyleSheet(r"QTabWidget::pane {border: 0;};")
- self.projTabs.addTab(self.treeView, "Project")
- self.projTabs.addTab(self.novelView, "Novel")
+ self.projTabs.addTab(self.treeView, self.tr("Project"))
+ self.projTabs.addTab(self.novelView, self.tr("Novel"))
self.projTabs.currentChanged.connect(self._projTabsChanged)
tabFont = self.projTabs.tabBar().font()
@@ -139,17 +140,17 @@ class GuiMain(QMainWindow):
self.treeButtons.setStyleSheet(r"QToolBar {padding: 0;}")
self.projTabs.setCornerWidget(self.treeButtons, Qt.BottomRightCorner)
- self.projDetailsBtn = QAction("Project Details")
+ self.projDetailsBtn = QAction(self.tr("Project Details"))
self.projDetailsBtn.setIcon(self.theTheme.getIcon("status_lines"))
self.projDetailsBtn.triggered.connect(lambda: self.showProjectDetailsDialog())
self.treeButtons.addAction(self.projDetailsBtn)
- self.projStatsBtn = QAction("Writing Statistics")
+ self.projStatsBtn = QAction(self.tr("Writing Statistics"))
self.projStatsBtn.setIcon(self.theTheme.getIcon("status_stats"))
self.projStatsBtn.triggered.connect(lambda: self.showWritingStatsDialog())
self.treeButtons.addAction(self.projStatsBtn)
- self.projSettingsBtn = QAction("Project Settings")
+ self.projSettingsBtn = QAction(self.tr("Project Settings"))
self.projSettingsBtn.setIcon(self.theTheme.getIcon("settings"))
self.projSettingsBtn.triggered.connect(lambda: self.showProjectSettingsDialog())
self.treeButtons.addAction(self.projSettingsBtn)
@@ -184,8 +185,8 @@ class GuiMain(QMainWindow):
self.mainTabs = QTabWidget()
self.mainTabs.setTabPosition(QTabWidget.East)
self.mainTabs.setStyleSheet(r"QTabWidget::pane {border: 0;}")
- self.mainTabs.addTab(self.splitDocs, "Editor")
- self.mainTabs.addTab(self.splitOutline, "Outline")
+ self.mainTabs.addTab(self.splitDocs, self.tr("Editor"))
+ self.mainTabs.addTab(self.splitOutline, self.tr("Outline"))
self.mainTabs.currentChanged.connect(self._mainTabChanged)
# Splitter : Project Tree / Main Tabs
@@ -339,7 +340,7 @@ class GuiMain(QMainWindow):
if self.hasProject:
if not self.closeProject():
self.makeAlert(
- "Cannot create new project when another project is open.",
+ self.tr("Cannot create new project when another project is open."),
nwAlert.ERROR
)
return False
@@ -357,8 +358,10 @@ class GuiMain(QMainWindow):
if os.path.isfile(os.path.join(projPath, self.theProject.projFile)):
self.makeAlert(
- "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
@@ -372,7 +375,7 @@ class GuiMain(QMainWindow):
self.statusBar.setRefTime(self.theProject.projOpened)
self.statusBar.setProjectStatus(True)
self.statusBar.setDocumentStatus(None)
- self.statusBar.setStatus("New project created ...")
+ self.statusBar.setStatus(self.tr("New project created ..."))
self._updateWindowTitle(self.theProject.projName)
else:
self.theProject.clearProject()
@@ -391,8 +394,11 @@ class GuiMain(QMainWindow):
if not isYes:
msgYes = self.askQuestion(
- "Close Project",
- "Close the current project?
Changes are saved automatically."
+ self.tr("Close Project"),
+ "%s
%s" % (
+ self.tr("Close the current project?"),
+ self.tr("Changes are saved automatically.")
+ )
)
if not msgYes:
return False
@@ -407,7 +413,8 @@ class GuiMain(QMainWindow):
doBackup = True
if self.mainConf.askBeforeBackup:
msgYes = self.askQuestion(
- "Backup Project", "Backup the current project?"
+ self.tr("Backup Project"),
+ self.tr("Backup the current project?")
)
if not msgYes:
doBackup = False
@@ -457,29 +464,35 @@ class GuiMain(QMainWindow):
try:
lockDetails = (
- "
The project was locked by the computer "
- "'%s' (%s %s), last active on %s"
- ) % (
+ "
%s" % self.tr(
+ "The project was locked by the computer "
+ "'{0}' ({1} {2}), last active on {3}."
+ )
+ ).format(
self.theProject.lockedBy[0],
self.theProject.lockedBy[1],
self.theProject.lockedBy[2],
- datetime.fromtimestamp(
- int(self.theProject.lockedBy[3])
- ).strftime("%x %X")
+ datetime.fromtimestamp(int(self.theProject.lockedBy[3])).strftime("%x %X")
)
except Exception:
lockDetails = ""
msgBox = QMessageBox()
msgRes = msgBox.warning(
- self, "Project Locked", (
- "The project is already open by another instance of novelWriter, and "
- "is therefore locked. Override lock and continue anyway?
"
- "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.%s"
- ) % lockDetails,
+ 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."
+ ),
+ lockDetails
+ ),
QMessageBox.Yes | QMessageBox.No, QMessageBox.No
)
if msgRes == QMessageBox.Yes:
@@ -514,6 +527,10 @@ class GuiMain(QMainWindow):
# Check if we need to rebuild the index
if self.theIndex.indexBroken:
+ self.makeAlert(
+ self.tr("The project index is outdated or broken. Rebuilding index."),
+ nwAlert.WARN
+ )
self.rebuildIndex()
# Make sure the changed status is set to false on all that was
@@ -685,15 +702,16 @@ class GuiMain(QMainWindow):
lastPath = self.mainConf.lastPath
extFilter = [
- "Text files (*.txt)",
- "Markdown files (*.md)",
- "novelWriter files (*.nwd)",
- "All files (*.*)",
+ self.tr("Text files ({0})").format("*.txt"),
+ self.tr("Markdown files ({0})").format("*.md"),
+ self.tr("novelWriter files ({0})").format("*.nwd"),
+ self.tr("All files ({0})").format("*.*"),
]
dlgOpt = QFileDialog.Options()
dlgOpt |= QFileDialog.DontUseNativeDialog
loadFile, _ = QFileDialog.getOpenFileName(
- self, "Import File", lastPath, options=dlgOpt, filter=";;".join(extFilter)
+ self, self.tr("Import File"), lastPath,
+ options=dlgOpt, filter=";;".join(extFilter)
)
if not loadFile:
return False
@@ -707,24 +725,26 @@ class GuiMain(QMainWindow):
theText = inFile.read()
self.mainConf.setLastPath(loadFile)
except Exception as e:
- self.makeAlert(
- ["Could not read file. The file must be an existing text file.", str(e)],
- nwAlert.ERROR
- )
+ self.makeAlert([
+ self.tr("Could not read file. The file must be an existing text file."), str(e)
+ ], nwAlert.ERROR)
return False
if self.docEditor.theHandle is None:
self.makeAlert(
- "Please open a document to import the text file into.",
+ self.tr("Please open a document to import the text file into."),
nwAlert.ERROR
)
return False
if not self.docEditor.isEmpty():
- msgYes = self.askQuestion("Import Document", (
- "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
@@ -861,9 +881,9 @@ class GuiMain(QMainWindow):
for nDone, tItem in enumerate(self.theProject.projTree):
if tItem is not None:
- self.setStatus("Indexing: '%s'" % tItem.itemName)
+ self.setStatus(self.tr("Indexing: '{0}'").format(tItem.itemName))
else:
- self.setStatus("Indexing: Unknown item")
+ self.setStatus(self.tr("Indexing: '{0}'").format(self.tr("Unknown item")))
if tItem is not None and tItem.itemType == nwItemType.FILE:
logger.verbose("Scanning: %s" % tItem.itemName)
@@ -881,12 +901,16 @@ class GuiMain(QMainWindow):
self.treeView.projectWordCount()
tEnd = time()
- self.setStatus("Indexing completed in %.1f ms" % ((tEnd - tStart)*1000.0))
+ 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("The project index has been successfully rebuilt.", nwAlert.INFO)
+ self.makeAlert(
+ self.tr("The project index has been successfully rebuilt."), nwAlert.INFO
+ )
return True
@@ -914,7 +938,7 @@ class GuiMain(QMainWindow):
dlgOpt |= QFileDialog.ShowDirsOnly
dlgOpt |= QFileDialog.DontUseNativeDialog
projPath = QFileDialog.getExistingDirectory(
- self, "Save novelWriter Project", "", options=dlgOpt
+ self, self.tr("Save novelWriter Project"), "", options=dlgOpt
)
if projPath:
return projPath
@@ -1100,14 +1124,14 @@ class GuiMain(QMainWindow):
# Popup
msgBox = QMessageBox()
if theLevel == nwAlert.INFO:
- msgBox.information(self, "Information", popMsg)
+ msgBox.information(self, self.tr("Information"), popMsg)
elif theLevel == nwAlert.WARN:
- msgBox.warning(self, "Warning", popMsg)
+ msgBox.warning(self, self.tr("Warning"), popMsg)
elif theLevel == nwAlert.ERROR:
- msgBox.critical(self, "Error", popMsg)
+ msgBox.critical(self, self.tr("Error"), popMsg)
elif theLevel == nwAlert.BUG:
- popMsg += "
This is a bug!"
- msgBox.critical(self, "Internal Error", popMsg)
+ popMsg += "
%s" % self.tr("This is a bug!")
+ msgBox.critical(self, self.tr("Internal Error"), popMsg)
return
@@ -1115,7 +1139,7 @@ class GuiMain(QMainWindow):
"""Ask the user a Yes/No question.
"""
msgBox = QMessageBox()
- msgRes = msgBox.question(self, theTitle, theQuestion)
+ msgRes = msgBox.question(self, theTitle, theQuestion, QMessageBox.Yes | QMessageBox.No)
return msgRes == QMessageBox.Yes
def reportConfErr(self):
@@ -1137,8 +1161,11 @@ class GuiMain(QMainWindow):
"""
if self.hasProject:
msgYes = self.askQuestion(
- "Exit",
- "Do you want to exit novelWriter?
Changes are saved automatically."
+ self.tr("Exit"),
+ "%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..52fba625 100644
--- a/sample/nwProject.nwx
+++ b/sample/nwProject.nwx
@@ -1,13 +1,13 @@
-
+
Sample Project
Sample Project
Jane Smith
Jay Doh
- 936
+ 1021
161
- 46791
+ 48283
False
@@ -120,7 +120,7 @@
1810
318
8
- 3
+ 1112
-
Another Scene
diff --git a/setup.py b/setup.py
index c9c9d302..f11b5014 100755
--- a/setup.py
+++ b/setup.py
@@ -186,6 +186,32 @@ def buildQtDocs():
return
+##
+# Qt Linguist QM Builder (qtlrelease)
+##
+
+def buildQtI18n():
+ """Build the lang.qm files for Qt Linguist.
+ """
+ try:
+ subprocess.call(["lrelease", "-verbose", "novelWriter.pro"])
+ except Exception as e:
+ print("QtI18n Release Error:")
+ print(str(e))
+
+##
+# Qt Linguist TS Builder (qtlupdate)
+##
+
+def buildQtI18nTS():
+ """Build the lang.ts files for Qt Linguist.
+ """
+ try:
+ subprocess.call(["pylupdate5", "-verbose", "-noobsolete", "novelWriter.pro"])
+ except Exception as e:
+ print("QtI18n Release Error:")
+ print(str(e))
+
##
# Sample Project ZIP File Builder (sample)
##
@@ -849,6 +875,8 @@ if __name__ == "__main__":
"\n"
" qthelp Build the help documentation for use with the Qt Assistant. Run before\n"
" install to have local help enable in the the installed version.\n"
+ " qtlupdate Update the translation files for internationalisation.\n"
+ " qtlrelease Build the language files for internationalisation.\n"
" sample Build the sample project as a zip file. Run before install to enable\n"
" creating sample projects in the in-app New Project Wizard.\n"
"\n"
@@ -898,6 +926,14 @@ if __name__ == "__main__":
sys.argv.remove("qthelp")
buildQtDocs()
+ if "qtlrelease" in sys.argv:
+ sys.argv.remove("qtlrelease")
+ buildQtI18n()
+
+ if "qtlupdate" in sys.argv:
+ sys.argv.remove("qtlupdate")
+ buildQtI18nTS()
+
if "sample" in sys.argv:
sys.argv.remove("sample")
buildSampleZip()
diff --git a/setup/README.md b/setup/README.md
index 66bfb548..c64832a6 100644
--- a/setup/README.md
+++ b/setup/README.md
@@ -25,6 +25,10 @@ To target the command, add one of `--target-linux`, `--target-darwin` or
`qthelp` – Build the help documentation for use with the Qt Assistant. Run
before install to have local help enable in the the installed version
+`qtlupdate` – Update the translation files for internationalisation.
+
+`qtlrelease` – Build the language files for internationalisation.
+
`sample` – Build the sample project as a zip file. Run before install to enable
creating sample projects in the in-app New Project Wizard.
diff --git a/tests/reference/baseConfig_novelwriter.conf b/tests/reference/baseConfig_novelwriter.conf
index 1ab78ea7..4ec14a13 100644
--- a/tests/reference/baseConfig_novelwriter.conf
+++ b/tests/reference/baseConfig_novelwriter.conf
@@ -1,5 +1,5 @@
[Main]
-timestamp = 2021-02-09 21:07:03
+timestamp = 2021-02-15 16:31:24
theme = default
syntax = default_light
icons = typicons_colour_light
@@ -7,6 +7,7 @@ guidark = False
guifont =
guifontsize = 11
lastnotes = 0x0
+guilang = en-GB
[Sizes]
geometry = 1200, 650
diff --git a/tests/reference/guiPreferences_novelwriter.conf b/tests/reference/guiPreferences_novelwriter.conf
index f440e00b..b7cf116c 100644
--- a/tests/reference/guiPreferences_novelwriter.conf
+++ b/tests/reference/guiPreferences_novelwriter.conf
@@ -7,6 +7,7 @@ guidark = True
guifont = Sans
guifontsize = 12
lastnotes = 0x0
+guilang = en-GB
[Sizes]
geometry = 1200, 650
diff --git a/tests/test_base/test_base_config.py b/tests/test_base/test_base_config.py
index 7b0b6f4f..10ca038d 100644
--- a/tests/test_base/test_base_config.py
+++ b/tests/test_base/test_base_config.py
@@ -145,7 +145,7 @@ def testBaseConfig_Init(monkeypatch, tmpDir, fncDir, outDir, refDir):
assert os.path.isfile(confFile)
copyfile(confFile, testFile)
- assert cmpFiles(testFile, compFile, [2, 9])
+ assert cmpFiles(testFile, compFile, [2, 9, 10])
monkeypatch.undo()
# Load and save with OSError
@@ -193,7 +193,7 @@ def testBaseConfig_Init(monkeypatch, tmpDir, fncDir, outDir, refDir):
assert tstConf.saveConfig()
copyfile(confFile, testFile)
- assert cmpFiles(testFile, compFile, [2, 9])
+ assert cmpFiles(testFile, compFile, [2, 9, 10])
# END Test testBaseConfig_Init
@@ -458,7 +458,7 @@ def testBaseConfig_SettersGetters(tmpConf, tmpDir, outDir, refDir):
assert not tmpConf.confChanged
copyfile(confFile, testFile)
- assert cmpFiles(testFile, compFile, [2, 9])
+ assert cmpFiles(testFile, compFile, [2, 9, 10])
# END Test testBaseConfig_SettersGetters
diff --git a/tests/test_core/test_core_project.py b/tests/test_core/test_core_project.py
index 09f6bbb8..07967b86 100644
--- a/tests/test_core/test_core_project.py
+++ b/tests/test_core/test_core_project.py
@@ -928,7 +928,7 @@ def testCoreProject_OrphanedFiles(dummyGUI, nwLipsum):
# First Item with Meta Data
orphPath = os.path.join(nwLipsum, "content", "636b6aa9b697b.nwd")
with open(orphPath, mode="w", encoding="utf8") as outFile:
- outFile.write("%%~name:Mars\n")
+ outFile.write("%%~name:[Recovered] Mars\n")
outFile.write("%%~path:5eaea4e8cdee8/636b6aa9b697b\n")
outFile.write("%%~kind:WORLD/NOTE\n")
outFile.write("%%~invalid\n")
@@ -962,7 +962,7 @@ def testCoreProject_OrphanedFiles(dummyGUI, nwLipsum):
# First Item with Meta Data
oItem = theProject.projTree["636b6aa9b697b"]
assert oItem is not None
- assert oItem.itemName == "Recovered: Mars"
+ assert oItem.itemName == "[Recovered] Mars"
assert oItem.itemHandle == "636b6aa9b697b"
assert oItem.itemParent == "60bdf227455cc"
assert oItem.itemClass == nwItemClass.WORLD
diff --git a/tests/test_core/test_core_spell.py b/tests/test_core/test_core_spell.py
index 46ee1937..113ed343 100644
--- a/tests/test_core/test_core_spell.py
+++ b/tests/test_core/test_core_spell.py
@@ -28,7 +28,6 @@ from dummy import causeOSError
from tools import readFile, writeFile
from nw.core.spellcheck import NWSpellCheck, NWSpellEnchant, NWSpellSimple
-from nw.constants import nwConst
@pytest.mark.core
def testCoreSpell_Super(monkeypatch, tmpDir, tmpConf):
@@ -48,10 +47,6 @@ def testCoreSpell_Super(monkeypatch, tmpDir, tmpConf):
assert spChk.listDictionaries() == []
assert spChk.describeDict() == ("", "")
- # Check language info
- assert NWSpellCheck.expandLanguage("en") == "English"
- assert NWSpellCheck.expandLanguage("en_GB") == "English (GB)"
-
# Add a word to the user's dictionary
assert spChk._readProjectDictionary("dummy") is False
monkeypatch.setattr("builtins.open", causeOSError)
@@ -178,11 +173,11 @@ def testCoreSpell_Simple(monkeypatch, tmpDir, tmpConf):
assert "D_word" in wSuggest
# List dictionaries
- assert spChk.listDictionaries() == [("en", "English [%s]" % nwConst.SP_INTERNAL)]
+ assert spChk.listDictionaries() == [("en", "difflib")]
# Description
aTag, aName = spChk.describeDict()
assert aTag == "en"
- assert aName == nwConst.SP_INTERNAL
+ assert aName == ""
# END Test testCoreSpell_Simple
diff --git a/tests/test_core/test_core_tools.py b/tests/test_core/test_core_tools.py
index d49ebf15..802eb94f 100644
--- a/tests/test_core/test_core_tools.py
+++ b/tests/test_core/test_core_tools.py
@@ -86,6 +86,7 @@ def testCoreTools_RomanNumbers():
def testCoreTools_NumberWords():
"""Test the conversion of integer to English words.
"""
+ # English
assert numberToWord(0, "en") == "Zero"
assert numberToWord(1, "en") == "One"
assert numberToWord(2, "en") == "Two"
@@ -114,14 +115,46 @@ def testCoreTools_NumberWords():
assert numberToWord(142, "en") == "One Hundred Forty-Two"
assert numberToWord(999, "en") == "Nine Hundred Ninety-Nine"
+ # Norwegian
+ assert numberToWord(0, "nb") == "null"
+ assert numberToWord(1, "nb") == "én"
+ assert numberToWord(10, "nb") == "ti"
+ assert numberToWord(20, "nb") == "tjue"
+ assert numberToWord(21, "nb") == "tjueén"
+ assert numberToWord(29, "nb") == "tjueni"
+ assert numberToWord(42, "nb") == "førtito"
+ assert numberToWord(60, "nb") == "seksti"
+ assert numberToWord(100, "nb") == "ett hundre"
+ assert numberToWord(114, "nb") == "ett hundre og fjorten"
+ assert numberToWord(142, "nb") == "ett hundre og førtito"
+ assert numberToWord(999, "nb") == "ni hundre og nittini"
+
+ assert numberToWord(0, "nn") == "null"
+ assert numberToWord(1, "nn") == "ein"
+ assert numberToWord(10, "nn") == "ti"
+ assert numberToWord(20, "nn") == "tjue"
+ assert numberToWord(21, "nn") == "tjueein"
+ assert numberToWord(29, "nn") == "tjueni"
+ assert numberToWord(42, "nn") == "førtito"
+ assert numberToWord(60, "nn") == "seksti"
+ assert numberToWord(100, "nn") == "eitt hundre"
+ assert numberToWord(114, "nn") == "eitt hundre og fjorten"
+ assert numberToWord(142, "nn") == "eitt hundre og førtito"
+ assert numberToWord(999, "nn") == "ni hundre og nittini"
+
+ # Check complex language setting
+ assert numberToWord(1, "en_GB") == "One"
+ assert numberToWord(2, "en_GB") == "Two"
+ assert numberToWord(3, "en_GB") == "Three"
+
# Check a few with a nonsense language setting
- assert numberToWord(1, "foo") == "One"
- assert numberToWord(2, "foo") == "Two"
- assert numberToWord(3, "foo") == "Three"
+ assert numberToWord(1, "foo") == "1"
+ assert numberToWord(2, "foo") == "2"
+ assert numberToWord(3, "foo") == "3"
# Test out of range values
- assert numberToWord(12345, "en") == "[Out of Range]"
- assert numberToWord(-2345, "en") == "[Negative]"
+ assert numberToWord(12345, "en") == "[>999]"
+ assert numberToWord(-2345, "en") == "[<0]"
assert numberToWord("234", "en") == "[NaN]"
# END Test testCoreTools_NumberWords
diff --git a/tests/test_gui/test_gui_mainmenu.py b/tests/test_gui/test_gui_mainmenu.py
index 50305184..f66108d5 100644
--- a/tests/test_gui/test_gui_mainmenu.py
+++ b/tests/test_gui/test_gui_mainmenu.py
@@ -595,10 +595,9 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
nwGUI.mainMenu.aFileDetails.activate(QAction.Trigger)
theBits = theMessage.split("
")
- assert len(theBits) == 3
- assert theBits[0] == "File details for the currently open file"
- assert theBits[1] == "Handle: 0e17daca5f3e1"
- assert theBits[2] == "Location: %s" % os.path.join(fncProj, "content", "0e17daca5f3e1.nwd")
+ assert len(theBits) == 2
+ assert theBits[0] == "The currently open file is saved in:"
+ assert theBits[1] == os.path.join(fncProj, "content", "0e17daca5f3e1.nwd")
# qtbot.stopForInteraction()
diff --git a/tests/test_gui/test_gui_preferences.py b/tests/test_gui/test_gui_preferences.py
index fec382d8..1611499d 100644
--- a/tests/test_gui/test_gui_preferences.py
+++ b/tests/test_gui/test_gui_preferences.py
@@ -245,13 +245,7 @@ def testGuiPreferences_Main(qtbot, monkeypatch, fncDir, outDir, refDir):
testFile = os.path.join(outDir, "guiPreferences_novelwriter.conf")
compFile = os.path.join(refDir, "guiPreferences_novelwriter.conf")
copyfile(projFile, testFile)
- ignoreLines = [
- 2, # Timestamp
- 9, # Release Notes
- 12, 13, 14, 15, # Window sizes
- 16, 17, 18, 19, # Window sizes
- 7, 29, # Fonts (depends on system default)
- ]
+ ignoreLines = [2, 9, 10, 13, 14, 15, 16, 17, 18, 19, 20, 7, 30, 31]
assert cmpFiles(testFile, compFile, ignoreLines)
# Clean up