Merge branch 'dev' into tokenizer_localisation

This commit is contained in:
Veronica K. B. Olsen
2021-02-16 23:13:36 +01:00
51 changed files with 6860 additions and 1751 deletions
BIN
View File
Binary file not shown.
+4709
View File
File diff suppressed because it is too large Load Diff
+447
View File
@@ -0,0 +1,447 @@
<!DOCTYPE QPH>
<QPH sourcelanguage="en" language="pt">
<phrase>
<source>Point of View</source>
<target>Ponto de Vista</target>
</phrase>
<phrase>
<source>Characters</source>
<target>Personagens</target>
</phrase>
<phrase>
<source>Plot</source>
<target>Enredo</target>
</phrase>
<phrase>
<source>Timeline</source>
<target>Linha do Tempo</target>
</phrase>
<phrase>
<source>Locations</source>
<target>Lugares</target>
</phrase>
<phrase>
<source>Objects</source>
<target>Objetos</target>
</phrase>
<phrase>
<source>Entities</source>
<target>Entidades</target>
</phrase>
<phrase>
<source>None</source>
<target>Nenhum</target>
</phrase>
<phrase>
<source>Novel</source>
<target>Livro</target>
</phrase>
<phrase>
<source>Entity</source>
<target>Entidade</target>
</phrase>
<phrase>
<source>Outtakes</source>
<target>Removidos</target>
</phrase>
<phrase>
<source>Trash</source>
<target>Lixeira</target>
</phrase>
<phrase>
<source>Title Page</source>
<target>Página de Título</target>
</phrase>
<phrase>
<source>Book</source>
<target>Livro</target>
</phrase>
<phrase>
<source>Plain Page</source>
<target>Página</target>
</phrase>
<phrase>
<source>Partition</source>
<target>Partição</target>
</phrase>
<phrase>
<source>Unnumbered</source>
<target>Sem Numeração</target>
</phrase>
<phrase>
<source>Scene</source>
<target>Cena</target>
</phrase>
<phrase>
<source>Note</source>
<target>Nota</target>
</phrase>
<phrase>
<source>Title</source>
<target>Título</target>
</phrase>
<phrase>
<source>Level</source>
<target>Nível</target>
</phrase>
<phrase>
<source>Document</source>
<target>Documento</target>
</phrase>
<phrase>
<source>Line</source>
<target>Linha</target>
</phrase>
<phrase>
<source>Chars</source>
<target>Caracteres</target>
</phrase>
<phrase>
<source>Words</source>
<target>Palavras</target>
</phrase>
<phrase>
<source>Synopsis</source>
<target>Sinopse</target>
</phrase>
<phrase>
<source>About</source>
<target>Sobre</target>
</phrase>
<phrase>
<source>Release</source>
<target>Versões</target>
</phrase>
<phrase>
<source>About novelWriter</source>
<target>Sobre o novelWriter</target>
</phrase>
<phrase>
<source>Credits</source>
<target>Créditos</target>
</phrase>
<phrase>
<source>Author</source>
<target>Autor</target>
</phrase>
<phrase>
<source>Credit</source>
<target>Créditos</target>
</phrase>
<phrase>
<source>License</source>
<target>Licença</target>
</phrase>
<phrase>
<source>Theme</source>
<target>Tema</target>
</phrase>
<phrase>
<source>Icons</source>
<target>Ícones</target>
</phrase>
<phrase>
<source>Syntax</source>
<target>Sintaxe</target>
</phrase>
<phrase>
<source>Website</source>
<target>Website</target>
</phrase>
<phrase>
<source>Chapter</source>
<target>Capítulo</target>
</phrase>
<phrase>
<source>Section</source>
<target>Seção</target>
</phrase>
<phrase>
<source>Font family</source>
<target>Família da fonte</target>
</phrase>
<phrase>
<source>Font size</source>
<target>Tamanho da fonte</target>
</phrase>
<phrase>
<source>Justify text</source>
<target>Texto justificado</target>
</phrase>
<phrase>
<source>Print</source>
<target>Imprimir</target>
</phrase>
<phrase>
<source>Build Project</source>
<target>Construir o Projeto</target>
</phrase>
<phrase>
<source>Save As</source>
<target>Salvar Como</target>
</phrase>
<phrase>
<source>Close</source>
<target>Fechar</target>
</phrase>
<phrase>
<source>Plain Text</source>
<target>Texto Simples</target>
</phrase>
<phrase>
<source>Plain HTML</source>
<target>HTML Simples</target>
</phrase>
<phrase>
<source>Save Document As</source>
<target>Salvar Documento Como</target>
</phrase>
<phrase>
<source>Unknown</source>
<target>Desconhecido</target>
</phrase>
<phrase>
<source>Look and Feel</source>
<target>Aparência</target>
</phrase>
<phrase>
<source>Project Backup</source>
<target>Cópia de Segurança</target>
</phrase>
<phrase>
<source>Path</source>
<target>Caminho</target>
</phrase>
<phrase>
<source>Status</source>
<target>Estado</target>
</phrase>
<phrase>
<source>Replace</source>
<target>Substituir</target>
</phrase>
<phrase>
<source>Search</source>
<target>Pesquisa</target>
</phrase>
<phrase>
<source>Handle</source>
<target>Referência</target>
</phrase>
<phrase>
<source>References</source>
<target>Referências</target>
</phrase>
<phrase>
<source>Label</source>
<target>Rótulo</target>
</phrase>
<phrase>
<source>Class</source>
<target>Classe</target>
</phrase>
<phrase>
<source>Layout</source>
<target>Leiaute</target>
</phrase>
<phrase>
<source> Characters</source>
<target>Caracteres</target>
</phrase>
<phrase>
<source>Editor</source>
<target>Editor</target>
</phrase>
<phrase>
<source>Project</source>
<target>Projeto</target>
</phrase>
<phrase>
<source>Provider</source>
<target>Provedor</target>
</phrase>
<phrase>
<source>unknown</source>
<target>desconhecido</target>
</phrase>
<phrase>
<source>Paragraphs</source>
<target>Parágrafos</target>
</phrase>
<phrase>
<source>Default</source>
<target>Padrão</target>
</phrase>
<phrase>
<source>Working title</source>
<target>Nome do projeto</target>
</phrase>
<phrase>
<source>Project path</source>
<target>Caminho do projeto</target>
</phrase>
<phrase>
<source>Project Stats</source>
<target>Estatíticas do Projeto</target>
</phrase>
<phrase>
<source>Folders</source>
<target>Diretórios</target>
</phrase>
<phrase>
<source>Documents</source>
<target>Documentos</target>
</phrase>
<phrase>
<source>Word count</source>
<target>Contagem de palavras</target>
</phrase>
<phrase>
<source>Keyword</source>
<target>Palavra-chave</target>
</phrase>
<phrase>
<source>New</source>
<target>Novo</target>
</phrase>
<phrase>
<source>Delete</source>
<target>Remover</target>
</phrase>
<phrase>
<source>Save</source>
<target>Salvar</target>
</phrase>
<phrase>
<source>Name</source>
<target>Nome</target>
</phrase>
<phrase>
<source>New Item</source>
<target>Novo Item</target>
</phrase>
<phrase>
<source>Last Opened</source>
<target>Aberto Pela Última Vez</target>
</phrase>
<phrase>
<source>Settings</source>
<target>Configurações</target>
</phrase>
<phrase>
<source>Details</source>
<target>Detalhes</target>
</phrase>
<phrase>
<source>Importance</source>
<target>Importância</target>
</phrase>
<phrase>
<source>Auto-Replace</source>
<target>Substituir automaticamente</target>
</phrase>
<phrase>
<source>Flag</source>
<target>Opção</target>
</phrase>
<phrase>
<source>Flags</source>
<target>Opções</target>
</phrase>
<phrase>
<source>(New Entry)</source>
<target></target>
</phrase>
<phrase>
<source>New File</source>
<target>Novo Arquivo</target>
</phrase>
<phrase>
<source>New Folder</source>
<target>Novo Diretório</target>
</phrase>
<phrase>
<source>Histogram</source>
<target>Histograma</target>
</phrase>
<phrase>
<source>Finished</source>
<target>Finalizado</target>
</phrase>
<phrase>
<source>Done</source>
<target>Pronto</target>
</phrase>
<phrase>
<source>Finish</source>
<target>Terminar</target>
</phrase>
<phrase>
<source>Auto-Replace</source>
<target>Substituição Automática</target>
</phrase>
<phrase>
<source>No Suggestions</source>
<target>Sem Sugestões</target>
</phrase>
<phrase>
<source>Browse</source>
<target>Procurar</target>
</phrase>
<phrase>
<source>Tag</source>
<target>Etiqueta</target>
</phrase>
<phrase>
<source>Draft</source>
<target>Rascunho</target>
</phrase>
<phrase>
<source>Minor</source>
<target>Menor</target>
</phrase>
<phrase>
<source>Major</source>
<target>Maior</target>
</phrase>
<phrase>
<source>Backup</source>
<target>Cópia de Segurança</target>
</phrase>
<phrase>
<source>Undo</source>
<target>Desfazer</target>
</phrase>
<phrase>
<source>Release</source>
<target>Lançamento</target>
</phrase>
<phrase>
<source>Pages</source>
<target>Páginas</target>
</phrase>
<phrase>
<source>Page</source>
<target>Página</target>
</phrase>
<phrase>
<source>Progress</source>
<target>Progresso</target>
</phrase>
<phrase>
<source>Chapters</source>
<target>Capítulos</target>
</phrase>
<phrase>
<source>Scenes</source>
<target>Cenas</target>
</phrase>
<phrase>
<source>Revisions</source>
<target>Revisões</target>
</phrase>
<phrase>
<source>seconds</source>
<target>segundos</target>
</phrase>
</QPH>
+33
View File
@@ -0,0 +1,33 @@
SOURCES += \
nw/constants/constants.py \
nw/core/document.py \
nw/core/project.py \
nw/core/tokenizer.py \
nw/gui/about.py \
nw/gui/build.py \
nw/gui/custom.py \
nw/gui/doceditor.py \
nw/gui/docmerge.py \
nw/gui/docsplit.py \
nw/gui/docviewer.py \
nw/gui/itemdetails.py \
nw/gui/itemeditor.py \
nw/gui/mainmenu.py \
nw/gui/noveltree.py \
nw/gui/outline.py \
nw/gui/outlinedetails.py \
nw/gui/preferences.py \
nw/gui/projdetails.py \
nw/gui/projload.py \
nw/gui/projsettings.py \
nw/gui/projtree.py \
nw/gui/projwizard.py \
nw/gui/statusbar.py \
nw/gui/theme.py \
nw/gui/wordlist.py \
nw/gui/writingstats.py \
nw/error.py \
nw/guimain.py
TRANSLATIONS += \
i18n/nw_pt.ts
+1
View File
@@ -285,6 +285,7 @@ def main(sysArgs=None):
sys.excepthook = exceptionHandler sys.excepthook = exceptionHandler
# Launch main GUI # Launch main GUI
CONFIG.initLocalisation(nwApp)
nwGUI = GuiMain() nwGUI = GuiMain()
if not nwGUI.hasProject: if not nwGUI.hasProject:
nwGUI.showProjectLoadDialog() nwGUI.showProjectLoadDialog()
+44 -3
View File
@@ -34,7 +34,10 @@ import os
from time import time from time import time
from PyQt5.Qt import PYQT_VERSION_STR from PyQt5.Qt import PYQT_VERSION_STR
from PyQt5.QtCore import QT_VERSION_STR, QStandardPaths, QSysInfo from PyQt5.QtCore import (
QT_VERSION_STR, QStandardPaths, QSysInfo, QLocale, QLibraryInfo,
QTranslator
)
from nw.constants import nwConst, nwFiles, nwUnicode from nw.constants import nwConst, nwFiles, nwUnicode
from nw.common import splitVersionNumber, formatTimeStamp from nw.common import splitVersionNumber, formatTimeStamp
@@ -85,12 +88,18 @@ class Config:
self.guiSyntax = "default_light" self.guiSyntax = "default_light"
self.guiIcons = "typicons_colour_light" self.guiIcons = "typicons_colour_light"
self.guiDark = False # Load icons for dark backgrounds, if available self.guiDark = False # Load icons for dark backgrounds, if available
self.guiLang = "en" # Hardcoded for now since the GUI is only in English
self.guiFont = "" # Defaults to system default font self.guiFont = "" # Defaults to system default font
self.guiFontSize = 11 self.guiFontSize = 11 # Is overridden if system default is loaded
self.guiScale = 1.0 # Set automatically by Theme class self.guiScale = 1.0 # Set automatically by Theme class
self.lastNotes = "0x0" # The latest release notes that have been shown self.lastNotes = "0x0" # The latest release notes that have been shown
## Localisation
self.qLocal = QLocale.system()
self.guiLang = self.qLocal.name()
self.qtLangPath = QLibraryInfo.location(QLibraryInfo.TranslationsPath)
self.nwLangPath = None
self.qtTrans = {}
## Sizes ## Sizes
self.winGeometry = [1200, 650] self.winGeometry = [1200, 650]
self.treeColWidth = [200, 50, 30] self.treeColWidth = [200, 50, 30]
@@ -292,6 +301,9 @@ class Config:
self.langPath = os.path.join(self.assetPath, "lang") self.langPath = os.path.join(self.assetPath, "lang")
self.appIcon = os.path.join(self.iconPath, "novelwriter.svg") self.appIcon = os.path.join(self.iconPath, "novelwriter.svg")
# Internationalisation
self.nwLangPath = os.path.join(self.appRoot, "i18n")
logger.verbose("App path: %s" % self.appPath) logger.verbose("App path: %s" % self.appPath)
logger.verbose("Last path: %s" % self.lastPath) logger.verbose("Last path: %s" % self.lastPath)
@@ -356,6 +368,31 @@ class Config:
return True return True
def initLocalisation(self, nwApp):
"""Initialise the localisation of the GUI.
"""
self.qLocal = QLocale(self.guiLang)
QLocale.setDefault(self.qLocal)
self.qtTrans = {}
langList = [
(self.qtLangPath, "qt"), # Qt 4.x
(self.qtLangPath, "qtbase"), # Qt 5.x
(self.nwLangPath, "qtbase"), # Alternative Qt 5.x
(self.nwLangPath, "nw"), # novelWriter
]
for lngPath, lngBase in langList:
for lngCode in self.qLocal.uiLanguages():
qTrans = QTranslator()
lngFile = "%s_%s" % (lngBase, lngCode)
if lngFile not in self.qtTrans:
if qTrans.load(lngFile, lngPath):
logger.debug("Loaded: %s" % qTrans.filePath())
nwApp.installTranslator(qTrans)
self.qtTrans[lngFile] = qTrans
return
def loadConfig(self): def loadConfig(self):
"""Load preferences from file and replace default settings. """Load preferences from file and replace default settings.
""" """
@@ -399,6 +436,9 @@ class Config:
self.lastNotes = self._parseLine( self.lastNotes = self._parseLine(
cnfParse, cnfSec, "lastnotes", self.CNF_STR, self.lastNotes cnfParse, cnfSec, "lastnotes", self.CNF_STR, self.lastNotes
) )
self.guiLang = self._parseLine(
cnfParse, cnfSec, "guilang", self.CNF_STR, self.guiLang
)
## Sizes ## Sizes
cnfSec = "Sizes" cnfSec = "Sizes"
@@ -628,6 +668,7 @@ class Config:
cnfParse.set(cnfSec, "guifont", str(self.guiFont)) cnfParse.set(cnfSec, "guifont", str(self.guiFont))
cnfParse.set(cnfSec, "guifontsize", str(self.guiFontSize)) cnfParse.set(cnfSec, "guifontsize", str(self.guiFontSize))
cnfParse.set(cnfSec, "lastnotes", str(self.lastNotes)) cnfParse.set(cnfSec, "lastnotes", str(self.lastNotes))
cnfParse.set(cnfSec, "guilang", str(self.guiLang))
## Sizes ## Sizes
cnfSec = "Sizes" cnfSec = "Sizes"
+3 -5
View File
@@ -1,8 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from nw.constants.iso import isoLanguage, isoCountry
from nw.constants.constants import ( from nw.constants.constants import (
nwConst, nwLists, nwRegEx, nwFiles, nwKeyWords, nwLabels, nwQuotes, trConst, nwConst, nwLists, nwRegEx, nwFiles, nwKeyWords, nwLabels,
nwUnicode, nwHtmlUnicode nwQuotes, nwUnicode, nwHtmlUnicode
) )
from nw.constants.enum import ( from nw.constants.enum import (
nwAlert, nwDocAction, nwItemClass, nwItemLayout, nwItemType, nwOutline, nwAlert, nwDocAction, nwItemClass, nwItemLayout, nwItemType, nwOutline,
@@ -10,8 +9,7 @@ from nw.constants.enum import (
) )
__all__ = [ __all__ = [
"isoCountry", "trConst",
"isoLanguage",
"nwConst", "nwConst",
"nwLists", "nwLists",
"nwRegEx", "nwRegEx",
+66 -59
View File
@@ -24,10 +24,17 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from PyQt5.QtCore import QCoreApplication, QT_TRANSLATE_NOOP
from nw.constants.enum import ( from nw.constants.enum import (
nwItemClass, nwItemLayout, nwItemType, nwOutline nwItemClass, nwItemLayout, nwItemType, nwOutline
) )
def trConst(tString):
"""Wrapper function for locally translating constants.
"""
return QCoreApplication.translate("Constant", tString)
class nwConst(): class nwConst():
# Date and Time Formats # Date and Time Formats
@@ -119,17 +126,17 @@ class nwKeyWords:
class nwLabels(): class nwLabels():
CLASS_NAME = { CLASS_NAME = {
nwItemClass.NO_CLASS : "None", nwItemClass.NO_CLASS : QT_TRANSLATE_NOOP("Constant", "None"),
nwItemClass.NOVEL : "Novel", nwItemClass.NOVEL : QT_TRANSLATE_NOOP("Constant", "Novel"),
nwItemClass.PLOT : "Plot", nwItemClass.PLOT : QT_TRANSLATE_NOOP("Constant", "Plot"),
nwItemClass.CHARACTER : "Characters", nwItemClass.CHARACTER : QT_TRANSLATE_NOOP("Constant", "Characters"),
nwItemClass.WORLD : "Locations", nwItemClass.WORLD : QT_TRANSLATE_NOOP("Constant", "Locations"),
nwItemClass.TIMELINE : "Timeline", nwItemClass.TIMELINE : QT_TRANSLATE_NOOP("Constant", "Timeline"),
nwItemClass.OBJECT : "Objects", nwItemClass.OBJECT : QT_TRANSLATE_NOOP("Constant", "Objects"),
nwItemClass.ENTITY : "Entity", nwItemClass.ENTITY : QT_TRANSLATE_NOOP("Constant", "Entity"),
nwItemClass.CUSTOM : "Custom", nwItemClass.CUSTOM : QT_TRANSLATE_NOOP("Constant", "Custom"),
nwItemClass.ARCHIVE : "Outtakes", nwItemClass.ARCHIVE : QT_TRANSLATE_NOOP("Constant", "Outtakes"),
nwItemClass.TRASH : "Trash", nwItemClass.TRASH : QT_TRANSLATE_NOOP("Constant", "Trash"),
} }
CLASS_FLAG = { CLASS_FLAG = {
nwItemClass.NO_CLASS : "0", nwItemClass.NO_CLASS : "0",
@@ -158,15 +165,15 @@ class nwLabels():
nwItemClass.TRASH : "cls_trash", nwItemClass.TRASH : "cls_trash",
} }
LAYOUT_NAME = { LAYOUT_NAME = {
nwItemLayout.NO_LAYOUT : "None", nwItemLayout.NO_LAYOUT : QT_TRANSLATE_NOOP("Constant", "None"),
nwItemLayout.TITLE : "Title Page", nwItemLayout.TITLE : QT_TRANSLATE_NOOP("Constant", "Title Page"),
nwItemLayout.BOOK : "Book", nwItemLayout.BOOK : QT_TRANSLATE_NOOP("Constant", "Book"),
nwItemLayout.PAGE : "Plain Page", nwItemLayout.PAGE : QT_TRANSLATE_NOOP("Constant", "Plain Page"),
nwItemLayout.PARTITION : "Partition", nwItemLayout.PARTITION : QT_TRANSLATE_NOOP("Constant", "Partition"),
nwItemLayout.UNNUMBERED : "Unnumbered", nwItemLayout.UNNUMBERED : QT_TRANSLATE_NOOP("Constant", "Unnumbered"),
nwItemLayout.CHAPTER : "Chapter", nwItemLayout.CHAPTER : QT_TRANSLATE_NOOP("Constant", "Chapter"),
nwItemLayout.SCENE : "Scene", nwItemLayout.SCENE : QT_TRANSLATE_NOOP("Constant", "Scene"),
nwItemLayout.NOTE : "Note", nwItemLayout.NOTE : QT_TRANSLATE_NOOP("Constant", "Note"),
} }
LAYOUT_FLAG = { LAYOUT_FLAG = {
nwItemLayout.NO_LAYOUT : "Xo", nwItemLayout.NO_LAYOUT : "Xo",
@@ -180,27 +187,27 @@ class nwLabels():
nwItemLayout.NOTE : "Nt", nwItemLayout.NOTE : "Nt",
} }
KEY_NAME = { KEY_NAME = {
nwKeyWords.TAG_KEY : "Tag", nwKeyWords.TAG_KEY : QT_TRANSLATE_NOOP("Constant", "Tag"),
nwKeyWords.POV_KEY : "Point of View", nwKeyWords.POV_KEY : QT_TRANSLATE_NOOP("Constant", "Point of View"),
nwKeyWords.FOCUS_KEY : "Focus", nwKeyWords.FOCUS_KEY : QT_TRANSLATE_NOOP("Constant", "Focus"),
nwKeyWords.CHAR_KEY : "Characters", nwKeyWords.CHAR_KEY : QT_TRANSLATE_NOOP("Constant", "Characters"),
nwKeyWords.PLOT_KEY : "Plot", nwKeyWords.PLOT_KEY : QT_TRANSLATE_NOOP("Constant", "Plot"),
nwKeyWords.TIME_KEY : "Timeline", nwKeyWords.TIME_KEY : QT_TRANSLATE_NOOP("Constant", "Timeline"),
nwKeyWords.WORLD_KEY : "Locations", nwKeyWords.WORLD_KEY : QT_TRANSLATE_NOOP("Constant", "Locations"),
nwKeyWords.OBJECT_KEY : "Objects", nwKeyWords.OBJECT_KEY : QT_TRANSLATE_NOOP("Constant", "Objects"),
nwKeyWords.ENTITY_KEY : "Entities", nwKeyWords.ENTITY_KEY : QT_TRANSLATE_NOOP("Constant", "Entities"),
nwKeyWords.CUSTOM_KEY : "Custom", nwKeyWords.CUSTOM_KEY : QT_TRANSLATE_NOOP("Constant", "Custom"),
} }
OUTLINE_COLS = { OUTLINE_COLS = {
nwOutline.TITLE : "Title", nwOutline.TITLE : QT_TRANSLATE_NOOP("Constant", "Title"),
nwOutline.LEVEL : "Level", nwOutline.LEVEL : QT_TRANSLATE_NOOP("Constant", "Level"),
nwOutline.LABEL : "Document", nwOutline.LABEL : QT_TRANSLATE_NOOP("Constant", "Document"),
nwOutline.LINE : "Line", nwOutline.LINE : QT_TRANSLATE_NOOP("Constant", "Line"),
nwOutline.CCOUNT : "Chars", nwOutline.CCOUNT : QT_TRANSLATE_NOOP("Constant", "Chars"),
nwOutline.WCOUNT : "Words", nwOutline.WCOUNT : QT_TRANSLATE_NOOP("Constant", "Words"),
nwOutline.PCOUNT : "Pars", nwOutline.PCOUNT : QT_TRANSLATE_NOOP("Constant", "Pars"),
nwOutline.POV : "POV", nwOutline.POV : QT_TRANSLATE_NOOP("Constant", "POV"),
nwOutline.FOCUS : "Focus", nwOutline.FOCUS : QT_TRANSLATE_NOOP("Constant", "Focus"),
nwOutline.CHAR : KEY_NAME[nwKeyWords.CHAR_KEY], nwOutline.CHAR : KEY_NAME[nwKeyWords.CHAR_KEY],
nwOutline.PLOT : KEY_NAME[nwKeyWords.PLOT_KEY], nwOutline.PLOT : KEY_NAME[nwKeyWords.PLOT_KEY],
nwOutline.TIME : KEY_NAME[nwKeyWords.TIME_KEY], nwOutline.TIME : KEY_NAME[nwKeyWords.TIME_KEY],
@@ -208,7 +215,7 @@ class nwLabels():
nwOutline.OBJECT : KEY_NAME[nwKeyWords.OBJECT_KEY], nwOutline.OBJECT : KEY_NAME[nwKeyWords.OBJECT_KEY],
nwOutline.ENTITY : KEY_NAME[nwKeyWords.ENTITY_KEY], nwOutline.ENTITY : KEY_NAME[nwKeyWords.ENTITY_KEY],
nwOutline.CUSTOM : KEY_NAME[nwKeyWords.CUSTOM_KEY], nwOutline.CUSTOM : KEY_NAME[nwKeyWords.CUSTOM_KEY],
nwOutline.SYNOP : "Synopsis", nwOutline.SYNOP : QT_TRANSLATE_NOOP("Constant", "Synopsis"),
} }
# END Class nwLabels # END Class nwLabels
@@ -218,28 +225,28 @@ class nwQuotes():
Source: https://en.wikipedia.org/wiki/Quotation_mark Source: https://en.wikipedia.org/wiki/Quotation_mark
""" """
SYMBOLS = { SYMBOLS = {
"\u0027" : "Straight single quotation mark", "\u0027" : QT_TRANSLATE_NOOP("Constant", "Straight single quotation mark"),
"\u0022" : "Straight double quotation mark", "\u0022" : QT_TRANSLATE_NOOP("Constant", "Straight double quotation mark"),
"\u2018" : "Left single quotation mark", "\u2018" : QT_TRANSLATE_NOOP("Constant", "Left single quotation mark"),
"\u2019" : "Right single quotation mark", "\u2019" : QT_TRANSLATE_NOOP("Constant", "Right single quotation mark"),
"\u201a" : "Single low-9 quotation mark", "\u201a" : QT_TRANSLATE_NOOP("Constant", "Single low-9 quotation mark"),
"\u201b" : "Single high-reversed-9 quotation mark", "\u201b" : QT_TRANSLATE_NOOP("Constant", "Single high-reversed-9 quotation mark"),
"\u201c" : "Left double quotation mark", "\u201c" : QT_TRANSLATE_NOOP("Constant", "Left double quotation mark"),
"\u201d" : "Right double quotation mark", "\u201d" : QT_TRANSLATE_NOOP("Constant", "Right double quotation mark"),
"\u201e" : "Double low-9 quotation mark", "\u201e" : QT_TRANSLATE_NOOP("Constant", "Double low-9 quotation mark"),
"\u201f" : "Double high-reversed-9 quotation mark", "\u201f" : QT_TRANSLATE_NOOP("Constant", "Double high-reversed-9 quotation mark"),
"\u2e42" : "Double low-reversed-9 quotation mark", "\u2e42" : QT_TRANSLATE_NOOP("Constant", "Double low-reversed-9 quotation mark"),
"\u2039" : "Single left-pointing angle quotation mark", "\u2039" : QT_TRANSLATE_NOOP("Constant", "Single left-pointing angle quotation mark"),
"\u203a" : "Single right-pointing angle quotation mark", "\u203a" : QT_TRANSLATE_NOOP("Constant", "Single right-pointing angle quotation mark"),
"\u00ab" : "Left-pointing double angle quotation mark", "\u00ab" : QT_TRANSLATE_NOOP("Constant", "Left-pointing double angle quotation mark"),
"\u00bb" : "Right-pointing double angle quotation mark", "\u00bb" : QT_TRANSLATE_NOOP("Constant", "Right-pointing double angle quotation mark"),
"\u300c" : "Left corner bracket", "\u300c" : QT_TRANSLATE_NOOP("Constant", "Left corner bracket"),
"\u300d" : "Right corner bracket", "\u300d" : QT_TRANSLATE_NOOP("Constant", "Right corner bracket"),
"\u300e" : "Left white corner bracket", "\u300e" : QT_TRANSLATE_NOOP("Constant", "Left white corner bracket"),
"\u300f" : "Right white corner bracket", "\u300f" : QT_TRANSLATE_NOOP("Constant", "Right white corner bracket"),
} }
# END Class nwQuotes # END Class nwQuotes
-472
View File
@@ -1,472 +0,0 @@
# -*- coding: utf-8 -*-
"""
novelWriter ISO Codes
=======================
Handles translating language codes to language names
File History:
Created: 2019-11-05 [0.4.0]
This file is a part of novelWriter
Copyright 20182021, Veronica Berglyd Olsen
This program 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.
This program 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 GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
class isoLanguage():
ISO_639_1 = {
"aa" : "Afar",
"ab" : "Abkhazian",
"ae" : "Avestan",
"af" : "Afrikaans",
"ak" : "Akan",
"am" : "Amharic",
"an" : "Aragonese",
"ar" : "Arabic",
"as" : "Assamese",
"av" : "Avaric",
"ay" : "Aymara",
"az" : "Azerbaijani",
"ba" : "Bashkir",
"be" : "Belarusian",
"bg" : "Bulgarian",
"bh" : "Bihari languages",
"bi" : "Bislama",
"bm" : "Bambara",
"bn" : "Bengali",
"bo" : "Tibetan",
"br" : "Breton",
"bs" : "Bosnian",
"ca" : "Catalan",
"ce" : "Chechen",
"ch" : "Chamorro",
"co" : "Corsican",
"cr" : "Cree",
"cs" : "Czech",
"cu" : "Church Slavic",
"cv" : "Chuvash",
"cy" : "Welsh",
"da" : "Danish",
"de" : "German",
"dv" : "Divehi",
"dz" : "Dzongkha",
"ee" : "Ewe",
"el" : "Modern Greek",
"en" : "English",
"eo" : "Esperanto",
"es" : "Spanish",
"et" : "Estonian",
"eu" : "Basque",
"fa" : "Persian",
"ff" : "Fulah",
"fi" : "Finnish",
"fj" : "Fijian",
"fo" : "Faroese",
"fr" : "French",
"fy" : "Western Frisian",
"ga" : "Irish",
"gd" : "Gaelic",
"gl" : "Galician",
"gn" : "Guarani",
"gu" : "Gujarati",
"gv" : "Manx",
"ha" : "Hausa",
"he" : "Hebrew",
"hi" : "Hindi",
"ho" : "Hiri Motu",
"hr" : "Croatian",
"ht" : "Haitian",
"hu" : "Hungarian",
"hy" : "Armenian",
"hz" : "Herero",
"ia" : "Interlingua",
"id" : "Indonesian",
"ie" : "Interlingue",
"ig" : "Igbo",
"ii" : "Sichuan Yi",
"ik" : "Inupiaq",
"io" : "Ido",
"is" : "Icelandic",
"it" : "Italian",
"iu" : "Inuktitut",
"ja" : "Japanese",
"jv" : "Javanese",
"ka" : "Georgian",
"kg" : "Kongo",
"ki" : "Kikuyu",
"kj" : "Kuanyama",
"kk" : "Kazakh",
"kl" : "Kalaallisut",
"km" : "Central Khmer",
"kn" : "Kannada",
"ko" : "Korean",
"kr" : "Kanuri",
"ks" : "Kashmiri",
"ku" : "Kurdish",
"kv" : "Komi",
"kw" : "Cornish",
"ky" : "Kirghiz",
"la" : "Latin",
"lb" : "Luxembourgish",
"lg" : "Ganda",
"li" : "Limburgan",
"ln" : "Lingala",
"lo" : "Lao",
"lt" : "Lithuanian",
"lu" : "Luba-Katanga",
"lv" : "Latvian",
"mg" : "Malagasy",
"mh" : "Marshallese",
"mi" : "Maori",
"mk" : "Macedonian",
"ml" : "Malayalam",
"mn" : "Mongolian",
"mr" : "Marathi",
"ms" : "Malay",
"mt" : "Maltese",
"my" : "Burmese",
"na" : "Nauru",
"nb" : "Norwegian Bokmål",
"nd" : "North Ndebele",
"ne" : "Nepali",
"ng" : "Ndonga",
"nl" : "Dutch",
"nn" : "Norwegian Nynorsk",
"no" : "Norwegian",
"nr" : "South Ndebele",
"nv" : "Navajo",
"ny" : "Chichewa",
"oc" : "Occitan",
"oj" : "Ojibwa",
"om" : "Oromo",
"or" : "Oriya",
"os" : "Ossetian",
"pa" : "Panjabi",
"pi" : "Pali",
"pl" : "Polish",
"ps" : "Pushto",
"pt" : "Portuguese",
"qu" : "Quechua",
"rm" : "Romansh",
"rn" : "Rundi",
"ro" : "Romanian",
"ru" : "Russian",
"rw" : "Kinyarwanda",
"sa" : "Sanskrit",
"sc" : "Sardinian",
"sd" : "Sindhi",
"se" : "Northern Sami",
"sg" : "Sango",
"si" : "Sinhala",
"sk" : "Slovak",
"sl" : "Slovenian",
"sm" : "Samoan",
"sn" : "Shona",
"so" : "Somali",
"sq" : "Albanian",
"sr" : "Serbian",
"ss" : "Swati",
"st" : "Southern Sotho",
"su" : "Sundanese",
"sv" : "Swedish",
"sw" : "Swahili",
"ta" : "Tamil",
"te" : "Telugu",
"tg" : "Tajik",
"th" : "Thai",
"ti" : "Tigrinya",
"tk" : "Turkmen",
"tl" : "Tagalog",
"tn" : "Tswana",
"to" : "Tonga",
"tr" : "Turkish",
"ts" : "Tsonga",
"tt" : "Tatar",
"tw" : "Twi",
"ty" : "Tahitian",
"ug" : "Uighur",
"uk" : "Ukrainian",
"ur" : "Urdu",
"uz" : "Uzbek",
"ve" : "Venda",
"vi" : "Vietnamese",
"vo" : "Volapük",
"wa" : "Walloon",
"wo" : "Wolof",
"xh" : "Xhosa",
"yi" : "Yiddish",
"yo" : "Yoruba",
"za" : "Zhuang",
"zh" : "Chinese",
"zu" : "Zulu",
}
# END Class isoLanguage
class isoCountry():
ISO_3166_1_alpha_2 = {
"AD" : "Andorra",
"AE" : "United Arab Emirates",
"AF" : "Afghanistan",
"AG" : "Antigua and Barbuda",
"AI" : "Anguilla",
"AL" : "Albania",
"AM" : "Armenia",
"AO" : "Angola",
"AQ" : "Antarctica",
"AR" : "Argentina",
"AS" : "American Samoa",
"AT" : "Austria",
"AU" : "Australia",
"AW" : "Aruba",
"AX" : "Åland Islands",
"AZ" : "Azerbaijan",
"BA" : "Bosnia and Herzegovina",
"BB" : "Barbados",
"BD" : "Bangladesh",
"BE" : "Belgium",
"BF" : "Burkina Faso",
"BG" : "Bulgaria",
"BH" : "Bahrain",
"BI" : "Burundi",
"BJ" : "Benin",
"BL" : "Saint Barthélemy",
"BM" : "Bermuda",
"BN" : "Brunei Darussalam",
"BO" : "Plurinational State of Bolivia",
"BQ" : "Sint Eustatius and Saba Bonaire",
"BR" : "Brazil",
"BS" : "Bahamas",
"BT" : "Bhutan",
"BV" : "Bouvet Island",
"BW" : "Botswana",
"BY" : "Belarus",
"BZ" : "Belize",
"CA" : "Canada",
"CC" : "Cocos (Keeling) Islands",
"CD" : "The Democratic Republic of the Congo",
"CF" : "Central African Republic",
"CG" : "Congo",
"CH" : "Switzerland",
"CI" : "Côte d'Ivoire",
"CK" : "Cook Islands",
"CL" : "Chile",
"CM" : "Cameroon",
"CN" : "China",
"CO" : "Colombia",
"CR" : "Costa Rica",
"CU" : "Cuba",
"CV" : "Cape Verde",
"CW" : "Curaçao",
"CX" : "Christmas Island",
"CY" : "Cyprus",
"CZ" : "Czech Republic",
"DE" : "Germany",
"DJ" : "Djibouti",
"DK" : "Denmark",
"DM" : "Dominica",
"DO" : "Dominican Republic",
"DZ" : "Algeria",
"EC" : "Ecuador",
"EE" : "Estonia",
"EG" : "Egypt",
"EH" : "Western Sahara",
"ER" : "Eritrea",
"ES" : "Spain",
"ET" : "Ethiopia",
"FI" : "Finland",
"FJ" : "Fiji",
"FK" : "Falkland Islands (Malvinas)",
"FM" : "Federated States of Micronesia",
"FO" : "Faroe Islands",
"FR" : "France",
"GA" : "Gabon",
"GB" : "United Kingdom",
"GD" : "Grenada",
"GE" : "Georgia",
"GF" : "French Guiana",
"GG" : "Guernsey",
"GH" : "Ghana",
"GI" : "Gibraltar",
"GL" : "Greenland",
"GM" : "Gambia",
"GN" : "Guinea",
"GP" : "Guadeloupe",
"GQ" : "Equatorial Guinea",
"GR" : "Greece",
"GS" : "South Georgia and the South Sandwich Islands",
"GT" : "Guatemala",
"GU" : "Guam",
"GW" : "Guinea-Bissau",
"GY" : "Guyana",
"HK" : "Hong Kong",
"HM" : "Heard Island and McDonald Islands",
"HN" : "Honduras",
"HR" : "Croatia",
"HT" : "Haiti",
"HU" : "Hungary",
"ID" : "Indonesia",
"IE" : "Ireland",
"IL" : "Israel",
"IM" : "Isle of Man",
"IN" : "India",
"IO" : "British Indian Ocean Territory",
"IQ" : "Iraq",
"IR" : "Islamic Republic of Iran",
"IS" : "Iceland",
"IT" : "Italy",
"JE" : "Jersey",
"JM" : "Jamaica",
"JO" : "Jordan",
"JP" : "Japan",
"KE" : "Kenya",
"KG" : "Kyrgyzstan",
"KH" : "Cambodia",
"KI" : "Kiribati",
"KM" : "Comoros",
"KN" : "Saint Kitts and Nevis",
"KP" : "Democratic People's Republic of Korea",
"KR" : "Republic of Korea",
"KW" : "Kuwait",
"KY" : "Cayman Islands",
"KZ" : "Kazakhstan",
"LA" : "Lao People's Democratic Republic",
"LB" : "Lebanon",
"LC" : "Saint Lucia",
"LI" : "Liechtenstein",
"LK" : "Sri Lanka",
"LR" : "Liberia",
"LS" : "Lesotho",
"LT" : "Lithuania",
"LU" : "Luxembourg",
"LV" : "Latvia",
"LY" : "Libya",
"MA" : "Morocco",
"MC" : "Monaco",
"MD" : "Republic of Moldova",
"ME" : "Montenegro",
"MF" : "Saint Martin (French part)",
"MG" : "Madagascar",
"MH" : "Marshall Islands",
"MK" : "The Former Yugoslav Republic of Macedonia",
"ML" : "Mali",
"MM" : "Myanmar",
"MN" : "Mongolia",
"MO" : "Macao",
"MP" : "Northern Mariana Islands",
"MQ" : "Martinique",
"MR" : "Mauritania",
"MS" : "Montserrat",
"MT" : "Malta",
"MU" : "Mauritius",
"MV" : "Maldives",
"MW" : "Malawi",
"MX" : "Mexico",
"MY" : "Malaysia",
"MZ" : "Mozambique",
"NA" : "Namibia",
"NC" : "New Caledonia",
"NE" : "Niger",
"NF" : "Norfolk Island",
"NG" : "Nigeria",
"NI" : "Nicaragua",
"NL" : "Netherlands",
"NO" : "Norway",
"NP" : "Nepal",
"NR" : "Nauru",
"NU" : "Niue",
"NZ" : "New Zealand",
"OM" : "Oman",
"PA" : "Panama",
"PE" : "Peru",
"PF" : "French Polynesia",
"PG" : "Papua New Guinea",
"PH" : "Philippines",
"PK" : "Pakistan",
"PL" : "Poland",
"PM" : "Saint Pierre and Miquelon",
"PN" : "Pitcairn",
"PR" : "Puerto Rico",
"PS" : "State of Palestine",
"PT" : "Portugal",
"PW" : "Palau",
"PY" : "Paraguay",
"QA" : "Qatar",
"RE" : "Réunion",
"RO" : "Romania",
"RS" : "Serbia",
"RU" : "Russian Federation",
"RW" : "Rwanda",
"SA" : "Saudi Arabia",
"SB" : "Solomon Islands",
"SC" : "Seychelles",
"SD" : "Sudan",
"SE" : "Sweden",
"SG" : "Singapore",
"SH" : "Saint Helena, Ascension and Tristan da Cunha",
"SI" : "Slovenia",
"SJ" : "Svalbard and Jan Mayen",
"SK" : "Slovakia",
"SL" : "Sierra Leone",
"SM" : "San Marino",
"SN" : "Senegal",
"SO" : "Somalia",
"SR" : "Suriname",
"SS" : "South Sudan",
"ST" : "Sao Tome and Principe",
"SV" : "El Salvador",
"SX" : "Sint Maarten",
"SY" : "Syrian Arab Republic",
"SZ" : "Swaziland",
"TC" : "Turks and Caicos Islands",
"TD" : "Chad",
"TF" : "French Southern Territories",
"TG" : "Togo",
"TH" : "Thailand",
"TJ" : "Tajikistan",
"TK" : "Tokelau",
"TL" : "Timor-Leste",
"TM" : "Turkmenistan",
"TN" : "Tunisia",
"TO" : "Tonga",
"TR" : "Turkey",
"TT" : "Trinidad and Tobago",
"TV" : "Tuvalu",
"TW" : "Taiwan, Province of China",
"TZ" : "United Republic of Tanzania",
"UA" : "Ukraine",
"UG" : "Uganda",
"UM" : "United States Minor Outlying Islands",
"US" : "United States",
"UY" : "Uruguay",
"UZ" : "Uzbekistan",
"VA" : "Holy See (Vatican City State)",
"VC" : "Saint Vincent and the Grenadines",
"VE" : "Bolivarian Republic of Venezuela",
"VG" : "British Virgin Islands",
"VI" : "U.S. Virgin Islands",
"VN" : "Viet Nam",
"VU" : "Vanuatu",
"WF" : "Wallis and Futuna",
"WS" : "Samoa",
"YE" : "Yemen",
"YT" : "Mayotte",
"ZA" : "South Africa",
"ZM" : "Zambia",
"ZW" : "Zimbabwe",
}
# END Class isoCountry
+16 -5
View File
@@ -27,6 +27,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
import logging import logging
import os import os
from functools import partial
from PyQt5.QtCore import QCoreApplication
from nw.constants import nwAlert from nw.constants import nwAlert
from nw.common import isHandle from nw.common import isHandle
from nw.constants import nwItemLayout, nwItemClass from nw.constants import nwItemLayout, nwItemClass
@@ -48,6 +52,7 @@ class NWDoc():
# Internal Mapping # Internal Mapping
self.makeAlert = self.theParent.makeAlert self.makeAlert = self.theParent.makeAlert
self.tr = partial(QCoreApplication.translate, "NWDoc")
return return
@@ -111,7 +116,7 @@ class NWDoc():
theText += inFile.read() theText += inFile.read()
except Exception as e: except Exception as e:
self.makeAlert(["Failed to open document file.", str(e)], nwAlert.ERROR) self.makeAlert([self.tr("Failed to open document file."), str(e)], nwAlert.ERROR)
# Note: Document must be cleared in case of an io error, # Note: Document must be cleared in case of an io error,
# or else the auto-save or save will try to overwrite it # or else the auto-save or save will try to overwrite it
# with an empty file. Return None to alert the caller. # with an empty file. Return None to alert the caller.
@@ -124,7 +129,9 @@ class NWDoc():
return "" return ""
if showStatus and not isOrphan: if showStatus and not isOrphan:
self.theParent.setStatus("Opened Document: %s" % self._theItem.itemName) self.theParent.setStatus(
self.tr("Opened Document: {0}").format(self._theItem.itemName)
)
return theText return theText
@@ -158,7 +165,7 @@ class NWDoc():
outFile.write(docMeta) outFile.write(docMeta)
outFile.write(docText) outFile.write(docText)
except Exception as e: except Exception as e:
self.makeAlert(["Could not save document.", str(e)], nwAlert.ERROR) self.makeAlert([self.tr("Could not save document."), str(e)], nwAlert.ERROR)
return False return False
# If we're here, the file was successfully saved, so we can # If we're here, the file was successfully saved, so we can
@@ -168,7 +175,9 @@ class NWDoc():
os.rename(docTemp, docPath) os.rename(docTemp, docPath)
if self._theItem is not None: if self._theItem is not None:
self.theParent.setStatus("Saved Document: %s" % self._theItem.itemName) self.theParent.setStatus(
self.tr("Saved Document: {0}").format(self._theItem.itemName)
)
return True return True
@@ -191,7 +200,9 @@ class NWDoc():
os.unlink(chkFile) os.unlink(chkFile)
logger.debug("Deleted: %s" % chkFile) logger.debug("Deleted: %s" % chkFile)
except Exception as e: except Exception as e:
self.makeAlert(["Could not delete document file.", str(e)], nwAlert.ERROR) self.makeAlert(
[self.tr("Could not delete document file."), str(e)], nwAlert.ERROR
)
return False return False
return True return True
-4
View File
@@ -228,10 +228,6 @@ class NWIndex():
if self.indexBroken: if self.indexBroken:
self.clearIndex() self.clearIndex()
self.theParent.makeAlert(
"The project index is outdated or broken. Rebuilding index.",
nwAlert.WARN
)
return return
+164 -123
View File
@@ -32,6 +32,9 @@ import shutil
from lxml import etree from lxml import etree
from time import time from time import time
from functools import partial
from PyQt5.QtCore import QCoreApplication
from nw.core.tree import NWTree from nw.core.tree import NWTree
from nw.core.item import NWItem from nw.core.item import NWItem
@@ -43,7 +46,7 @@ from nw.common import (
makeFileNameSafe, hexToInt makeFileNameSafe, hexToInt
) )
from nw.constants import ( from nw.constants import (
nwFiles, nwItemType, nwItemClass, nwItemLayout, nwLabels, nwAlert trConst, nwFiles, nwItemType, nwItemClass, nwItemLayout, nwLabels, nwAlert
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -99,11 +102,12 @@ class NWProject():
self.notesWCount = 0 # Total number of words in note files self.notesWCount = 0 # Total number of words in note files
self.doBackup = True # Run project backup on exit self.doBackup = True # Run project backup on exit
# Set Defaults
self.clearProject()
# Internal Mapping # Internal Mapping
self.makeAlert = self.theParent.makeAlert self.makeAlert = self.theParent.makeAlert
self.tr = partial(QCoreApplication.translate, "NWProject")
# Set Defaults
self.clearProject()
return return
@@ -159,7 +163,7 @@ class NWProject():
trashHandle = self.projTree.trashRoot() trashHandle = self.projTree.trashRoot()
if trashHandle is None: if trashHandle is None:
newItem = NWItem(self) newItem = NWItem(self)
newItem.setName("Trash") newItem.setName(self.tr("Trash"))
newItem.setType(nwItemType.TRASH) newItem.setType(nwItemType.TRASH)
newItem.setClass(nwItemClass.TRASH) newItem.setClass(nwItemClass.TRASH)
self.projTree.append(None, None, newItem) self.projTree.append(None, None, newItem)
@@ -199,7 +203,7 @@ class NWProject():
self.autoReplace = {} self.autoReplace = {}
self.titleFormat = { self.titleFormat = {
"title" : r"%title%", "title" : r"%title%",
"chapter" : r"Chapter %ch%: %title%", "chapter" : self.tr(r"Chapter %ch%: %title%"),
"unnumbered" : r"%title%", "unnumbered" : r"%title%",
"scene" : r"* * *", "scene" : r"* * *",
"section" : r"", "section" : r"",
@@ -207,15 +211,15 @@ class NWProject():
self.spellCheck = False self.spellCheck = False
self.autoOutline = True self.autoOutline = True
self.statusItems = NWStatus() self.statusItems = NWStatus()
self.statusItems.addEntry("New", (100, 100, 100)) self.statusItems.addEntry(self.tr("New"), (100, 100, 100))
self.statusItems.addEntry("Note", (200, 50, 0)) self.statusItems.addEntry(self.tr("Note"), (200, 50, 0))
self.statusItems.addEntry("Draft", (200, 150, 0)) self.statusItems.addEntry(self.tr("Draft"), (200, 150, 0))
self.statusItems.addEntry("Finished", (50, 200, 0)) self.statusItems.addEntry(self.tr("Finished"), (50, 200, 0))
self.importItems = NWStatus() self.importItems = NWStatus()
self.importItems.addEntry("New", (100, 100, 100)) self.importItems.addEntry(self.tr("New"), (100, 100, 100))
self.importItems.addEntry("Minor", (200, 50, 0)) self.importItems.addEntry(self.tr("Minor"), (200, 50, 0))
self.importItems.addEntry("Major", (200, 150, 0)) self.importItems.addEntry(self.tr("Major"), (200, 150, 0))
self.importItems.addEntry("Main", (50, 200, 0)) self.importItems.addEntry(self.tr("Main"), (50, 200, 0))
self.lastEdited = None self.lastEdited = None
self.lastViewed = None self.lastViewed = None
self.lastWCount = 0 self.lastWCount = 0
@@ -241,7 +245,7 @@ class NWProject():
# Project Settings # Project Settings
projPath = projData.get("projPath", None) projPath = projData.get("projPath", None)
projName = projData.get("projName", "New Project") projName = projData.get("projName", self.tr("New Project"))
projTitle = projData.get("projTitle", "") projTitle = projData.get("projTitle", "")
projAuthors = projData.get("projAuthors", "") projAuthors = projData.get("projAuthors", "")
@@ -258,7 +262,7 @@ class NWProject():
titlePage = "# %s\n\n" % (self.bookTitle if self.bookTitle else self.projName) titlePage = "# %s\n\n" % (self.bookTitle if self.bookTitle else self.projName)
if self.bookAuthors: if self.bookAuthors:
titlePage = "%sBy %s\n" % (titlePage, ", ".join(self.bookAuthors)) titlePage = "%s%s %s\n" % (titlePage, self.tr("By"), self.getAuthors())
# Document object for writing files # Document object for writing files
aDoc = NWDoc(self, self.theParent) aDoc = NWDoc(self, self.theParent)
@@ -267,14 +271,14 @@ class NWProject():
# Creating a minimal project with a few root folders and a # Creating a minimal project with a few root folders and a
# single chapter folder with a single file. # single chapter folder with a single file.
xHandle = {} xHandle = {}
xHandle[1] = self.newRoot("Novel", nwItemClass.NOVEL) xHandle[1] = self.newRoot(self.tr("Novel"), nwItemClass.NOVEL)
xHandle[2] = self.newRoot("Plot", nwItemClass.PLOT) xHandle[2] = self.newRoot(self.tr("Plot"), nwItemClass.PLOT)
xHandle[3] = self.newRoot("Characters", nwItemClass.CHARACTER) xHandle[3] = self.newRoot(self.tr("Characters"), nwItemClass.CHARACTER)
xHandle[4] = self.newRoot("World", nwItemClass.WORLD) xHandle[4] = self.newRoot(self.tr("World"), nwItemClass.WORLD)
xHandle[5] = self.newFile("Title Page", nwItemClass.NOVEL, xHandle[1]) xHandle[5] = self.newFile(self.tr("Title Page"), nwItemClass.NOVEL, xHandle[1])
xHandle[6] = self.newFolder("New Chapter", nwItemClass.NOVEL, xHandle[1]) xHandle[6] = self.newFolder(self.tr("New Chapter"), nwItemClass.NOVEL, xHandle[1])
xHandle[7] = self.newFile("New Chapter", nwItemClass.NOVEL, xHandle[6]) xHandle[7] = self.newFile(self.tr("New Chapter"), nwItemClass.NOVEL, xHandle[6])
xHandle[8] = self.newFile("New Scene", nwItemClass.NOVEL, xHandle[6]) xHandle[8] = self.newFile(self.tr("New Scene"), nwItemClass.NOVEL, xHandle[6])
self.projTree.setFileItemLayout(xHandle[5], nwItemLayout.TITLE) self.projTree.setFileItemLayout(xHandle[5], nwItemLayout.TITLE)
self.projTree.setFileItemLayout(xHandle[7], nwItemLayout.CHAPTER) self.projTree.setFileItemLayout(xHandle[7], nwItemLayout.CHAPTER)
@@ -284,11 +288,11 @@ class NWProject():
aDoc.clearDocument() aDoc.clearDocument()
aDoc.openDocument(xHandle[7], showStatus=False) aDoc.openDocument(xHandle[7], showStatus=False)
aDoc.saveDocument("## New Chapter\n\n") aDoc.saveDocument("## %s\n\n" % self.tr("New Chapter"))
aDoc.clearDocument() aDoc.clearDocument()
aDoc.openDocument(xHandle[8], showStatus=False) aDoc.openDocument(xHandle[8], showStatus=False)
aDoc.saveDocument("### New Scene\n\n") aDoc.saveDocument("### %s\n\n" % self.tr("New Scene"))
aDoc.clearDocument() aDoc.clearDocument()
elif popCustom: elif popCustom:
@@ -297,13 +301,13 @@ class NWProject():
# wizard's custom page. # wizard's custom page.
# Create root folders # Create root folders
nHandle = self.newRoot("Novel", nwItemClass.NOVEL) nHandle = self.newRoot(self.tr("Novel"), nwItemClass.NOVEL)
for newRoot in projData.get("addRoots", []): for newRoot in projData.get("addRoots", []):
if newRoot in nwItemClass: if newRoot in nwItemClass:
self.newRoot(nwLabels.CLASS_NAME[newRoot], newRoot) self.newRoot(trConst(nwLabels.CLASS_NAME[newRoot]), newRoot)
# Create a title page # Create a title page
tHandle = self.newFile("Title Page", nwItemClass.NOVEL, nHandle) tHandle = self.newFile(self.tr("Title Page"), nwItemClass.NOVEL, nHandle)
self.projTree.setFileItemLayout(tHandle, nwItemLayout.TITLE) self.projTree.setFileItemLayout(tHandle, nwItemLayout.TITLE)
aDoc.openDocument(tHandle, showStatus=False) aDoc.openDocument(tHandle, showStatus=False)
@@ -318,7 +322,7 @@ class NWProject():
# Create chapters # Create chapters
if numChapters > 0: if numChapters > 0:
for ch in range(numChapters): for ch in range(numChapters):
chTitle = "Chapter %d" % (ch+1) chTitle = self.tr("Chapter {0}").format(f"{ch+1:d}")
pHandle = nHandle pHandle = nHandle
if chFolders: if chFolders:
pHandle = self.newFolder(chTitle, nwItemClass.NOVEL, nHandle) pHandle = self.newFolder(chTitle, nwItemClass.NOVEL, nHandle)
@@ -333,7 +337,7 @@ class NWProject():
# Create chapter scenes # Create chapter scenes
if numScenes > 0: if numScenes > 0:
for sc in range(numScenes): for sc in range(numScenes):
scTitle = "Scene %d.%d" % (ch+1, sc+1) scTitle = self.tr("Scene {0}").format(f"{ch+1:d}.{sc+1:d}")
sHandle = self.newFile(scTitle, nwItemClass.NOVEL, pHandle) sHandle = self.newFile(scTitle, nwItemClass.NOVEL, pHandle)
aDoc.openDocument(sHandle, showStatus=False) aDoc.openDocument(sHandle, showStatus=False)
@@ -343,7 +347,7 @@ class NWProject():
# Create scenes (no chapters) # Create scenes (no chapters)
elif numScenes > 0: elif numScenes > 0:
for sc in range(numScenes): for sc in range(numScenes):
scTitle = "Scene %d" % (sc+1) scTitle = self.tr("Scene {0}").format(f"{sc+1:d}")
sHandle = self.newFile(scTitle, nwItemClass.NOVEL, nHandle) sHandle = self.newFile(scTitle, nwItemClass.NOVEL, nHandle)
aDoc.openDocument(sHandle, showStatus=False) aDoc.openDocument(sHandle, showStatus=False)
@@ -367,7 +371,7 @@ class NWProject():
if not os.path.isfile(fileName): if not os.path.isfile(fileName):
fileName = os.path.join(fileName, nwFiles.PROJ_FILE) fileName = os.path.join(fileName, nwFiles.PROJ_FILE)
if not os.path.isfile(fileName): if not os.path.isfile(fileName):
self.makeAlert("File not found: %s" % fileName, nwAlert.ERROR) self.makeAlert(self.tr("File not found: {0}").format(fileName), nwAlert.ERROR)
return False return False
self.clearProject() self.clearProject()
@@ -416,16 +420,20 @@ class NWProject():
try: try:
nwXML = etree.parse(fileName) nwXML = etree.parse(fileName)
except Exception as e: except Exception as e:
self.makeAlert(["Failed to parse project xml.", str(e)], nwAlert.ERROR) self.makeAlert([self.tr("Failed to parse project xml."), str(e)], nwAlert.ERROR)
# Trying to open backup file instead # Trying to open backup file instead
backFile = fileName[:-3]+"bak" backFile = fileName[:-3]+"bak"
if os.path.isfile(backFile): if os.path.isfile(backFile):
self.makeAlert("Attempting to open backup project file instead.", nwAlert.INFO) self.makeAlert(
self.tr("Attempting to open backup project file instead."), nwAlert.INFO
)
try: try:
nwXML = etree.parse(backFile) nwXML = etree.parse(backFile)
except Exception as e: except Exception as e:
self.makeAlert(["Failed to parse project xml.", str(e)], nwAlert.ERROR) self.makeAlert(
[self.tr("Failed to parse project xml."), str(e)], nwAlert.ERROR
)
self.clearProject() self.clearProject()
return False return False
else: else:
@@ -435,9 +443,9 @@ class NWProject():
xRoot = nwXML.getroot() xRoot = nwXML.getroot()
nwxRoot = xRoot.tag nwxRoot = xRoot.tag
appVersion = xRoot.attrib.get("appVersion", "Unknown") appVersion = xRoot.attrib.get("appVersion", self.tr("Unknown"))
hexVersion = xRoot.attrib.get("hexVersion", "0x0") hexVersion = xRoot.attrib.get("hexVersion", "0x0")
fileVersion = xRoot.attrib.get("fileVersion", "Unknown") fileVersion = xRoot.attrib.get("fileVersion", self.tr("Unknown"))
# The following are deprecated and will be removed # The following are deprecated and will be removed
# The settings have been moved to the <project> tag # The settings have been moved to the <project> tag
@@ -453,7 +461,7 @@ class NWProject():
if not nwxRoot == "novelWriterXML": if not nwxRoot == "novelWriterXML":
self.makeAlert( 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 nwAlert.ERROR
) )
self.clearProject() self.clearProject()
@@ -472,24 +480,32 @@ class NWProject():
# read the file. Introduced in version 0.10. # read the file. Introduced in version 0.10.
if fileVersion == "1.0": if fileVersion == "1.0":
msgYes = self.theParent.askQuestion("Old Project Version", ( msgYes = self.theParent.askQuestion(
"The project file and data is created by a novelWriter version " self.tr("Old Project Version"),
"lower than 0.7. Do you want to upgrade the project to the " "%s<br><br>%s" % (
"most recent format?<br><br>Note that after the upgrade, you " self.tr(
"cannot open the project with an older version of novelWriter " "The project file and data is created by a novelWriter version "
"any more, so make sure you have a recent backup." "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: if not msgYes:
self.clearProject() self.clearProject()
return False return False
elif fileVersion != "1.1" and fileVersion != "1.2": elif fileVersion != "1.1" and fileVersion != "1.2":
self.makeAlert(( self.makeAlert((
"Unknown or unsupported novelWriter project file format. " self.tr(
"The project cannot be opened by this version of novelWriter. " "Unknown or unsupported novelWriter project file format. "
"The file was saved with novelWriter version {vers:s}." "The project cannot be opened by this version of novelWriter. "
).format( "The file was saved with novelWriter version {0}."
vers = appVersion, ).format(appVersion)
), nwAlert.ERROR) ), nwAlert.ERROR)
self.clearProject() self.clearProject()
return False return False
@@ -498,14 +514,15 @@ class NWProject():
# ========================= # =========================
if hexToInt(hexVersion) > hexToInt(nw.__hexversion__): if hexToInt(hexVersion) > hexToInt(nw.__hexversion__):
msgYes = self.theParent.askQuestion("Version Conflict", ( msgYes = self.theParent.askQuestion(
"This project was saved by a newer version of novelWriter, version %s. " self.tr("Version Conflict"),
"This is version %s. If you continue to open the project, some attributes " self.tr(
"and settings may not be preserved, but the overall project should be fine. " "This project was saved by a newer version of novelWriter, version "
"Continue opening the project?" "{0}. This is version {1}. If you continue to open the project, "
) % ( "some attributes and settings may not be preserved, but the "
appVersion, nw.__version__ "overall project should be fine. Continue opening the project?"
)) ).format(appVersion, nw.__version__)
)
if not msgYes: if not msgYes:
self.clearProject() self.clearProject()
return False return False
@@ -604,7 +621,7 @@ class NWProject():
self.mainConf.updateRecentCache(self.projPath, self.projName, self.lastWCount, time()) self.mainConf.updateRecentCache(self.projPath, self.projName, self.lastWCount, time())
self.mainConf.saveRecentCache() self.mainConf.saveRecentCache()
self.theParent.setStatus("Opened Project: %s" % self.projName) self.theParent.setStatus(self.tr("Opened Project: {0}").format(self.projName))
self._scanProjectFolder() self._scanProjectFolder()
self.loadProjectLocalisation(self.projLang) self.loadProjectLocalisation(self.projLang)
@@ -626,7 +643,7 @@ class NWProject():
""" """
if self.projPath is None: if self.projPath is None:
self.makeAlert( self.makeAlert(
"Project path not set, cannot save project.", nwAlert.ERROR self.tr("Project path not set, cannot save project."), nwAlert.ERROR
) )
return False return False
@@ -705,7 +722,7 @@ class NWProject():
xml_declaration = True xml_declaration = True
)) ))
except Exception as e: 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 return False
# If we're here, the file was successfully saved, # If we're here, the file was successfully saved,
@@ -724,7 +741,7 @@ class NWProject():
self.mainConf.saveRecentCache() self.mainConf.saveRecentCache()
self._writeLockFile() self._writeLockFile()
self.theParent.setStatus("Saved Project: %s" % self.projName) self.theParent.setStatus(self.tr("Saved Project: {0}").format(self.projName))
self.setProjectChanged(False) self.setProjectChanged(False)
return True return True
@@ -776,27 +793,33 @@ class NWProject():
return False return False
logger.info("Backing up project") 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 == "": if self.mainConf.backupPath is None or self.mainConf.backupPath == "":
self.theParent.makeAlert(( self.theParent.makeAlert(
"Cannot backup project because no backup path is set. " self.tr(
"Please set a valid backup location in Tools > Preferences." "Cannot backup project because no backup path is set. "
), nwAlert.ERROR) "Please set a valid backup location in Tools > Preferences."
), nwAlert.ERROR
)
return False return False
if self.projName is None or self.projName == "": if self.projName is None or self.projName == "":
self.theParent.makeAlert(( self.theParent.makeAlert(
"Cannot backup project because no project name is set. " self.tr(
"Please set a Working Title in Project > Project Settings." "Cannot backup project because no project name is set. "
), nwAlert.ERROR) "Please set a Working Title in Project > Project Settings."
), nwAlert.ERROR
)
return False return False
if not os.path.isdir(self.mainConf.backupPath): if not os.path.isdir(self.mainConf.backupPath):
self.theParent.makeAlert(( self.theParent.makeAlert(
"Cannot backup project because the backup path does not exist. " self.tr(
"Please set a valid backup location in Tools > Preferences." "Cannot backup project because the backup path does not exist. "
), nwAlert.ERROR) "Please set a valid backup location in Tools > Preferences."
), nwAlert.ERROR
)
return False return False
cleanName = makeFileNameSafe(self.projName) cleanName = makeFileNameSafe(self.projName)
@@ -807,20 +830,22 @@ class NWProject():
logger.debug("Created folder %s" % baseDir) logger.debug("Created folder %s" % baseDir)
except Exception as e: except Exception as e:
self.theParent.makeAlert( self.theParent.makeAlert(
["Could not create backup folder.", str(e)], [self.tr("Could not create backup folder."), str(e)],
nwAlert.ERROR nwAlert.ERROR
) )
return False return False
if os.path.commonpath([self.projPath, baseDir]) == self.projPath: if os.path.commonpath([self.projPath, baseDir]) == self.projPath:
self.theParent.makeAlert(( self.theParent.makeAlert(
"Cannot backup project because the backup path is within the " self.tr(
"project folder to be backed up. Please choose a different " "Cannot backup project because the backup path is within the "
"backup path in Tools > Preferences." "project folder to be backed up. Please choose a different "
), nwAlert.ERROR) "backup path in Tools > Preferences."
), nwAlert.ERROR
)
return False 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) baseName = os.path.join(baseDir, archName)
try: try:
@@ -830,18 +855,21 @@ class NWProject():
logger.info("Backup written to: %s" % archName) logger.info("Backup written to: %s" % archName)
if doNotify: if doNotify:
self.theParent.makeAlert( self.theParent.makeAlert(
"Backup archive file written to: %s.zip" % os.path.join(cleanName, archName), self.tr(
nwAlert.INFO "Backup archive file written to: {0}"
).format(
f"{os.path.join(cleanName, archName)}.zip"
), nwAlert.INFO
) )
except Exception as e: except Exception as e:
self.theParent.makeAlert( self.theParent.makeAlert(
["Could not write backup archive.", str(e)], [self.tr("Could not write backup archive."), str(e)],
nwAlert.ERROR nwAlert.ERROR
) )
return False 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 return True
@@ -868,7 +896,7 @@ class NWProject():
isSuccess = True isSuccess = True
except Exception as e: except Exception as e:
self.makeAlert( 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): elif os.path.isdir(srcSample):
@@ -890,14 +918,16 @@ class NWProject():
except Exception as e: except Exception as e:
self.makeAlert( 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: else:
self.makeAlert(( self.makeAlert(
"Failed to create a new example project. Could not find the " self.tr(
"necessary files. They seem to be missing from this installation." "Failed to create a new example project. Could not find the "
), nwAlert.ERROR) "necessary files. They seem to be missing from this installation."
), nwAlert.ERROR
)
if isSuccess: if isSuccess:
self.clearProject() self.clearProject()
@@ -928,16 +958,18 @@ class NWProject():
logger.debug("Created folder %s" % projPath) logger.debug("Created folder %s" % projPath)
except Exception as e: except Exception as e:
self.theParent.makeAlert(( self.theParent.makeAlert((
["Could not create new project folder.", str(e)] [self.tr("Could not create new project folder."), str(e)]
), nwAlert.ERROR) ), nwAlert.ERROR)
return False return False
if os.path.isdir(projPath): if os.path.isdir(projPath):
if os.listdir(self.projPath): if os.listdir(self.projPath):
self.theParent.makeAlert(( self.theParent.makeAlert(
"New project folder is not empty. " self.tr(
"Each project requires a dedicated project folder." "New project folder is not empty. "
), nwAlert.ERROR) "Each project requires a dedicated project folder."
), nwAlert.ERROR
)
return False return False
self.ensureFolderStructure() self.ensureFolderStructure()
@@ -984,17 +1016,21 @@ class NWProject():
self.doBackup = doBackup self.doBackup = doBackup
if doBackup: if doBackup:
if not os.path.isdir(self.mainConf.backupPath): if not os.path.isdir(self.mainConf.backupPath):
self.theParent.makeAlert(( self.theParent.makeAlert(
"You must set a valid backup path in preferences to use " self.tr(
"the automatic project backup feature." "You must set a valid backup path in preferences to use "
), nwAlert.WARN) "the automatic project backup feature."
), nwAlert.WARN
)
return False return False
if self.projName == "": if self.projName == "":
self.theParent.makeAlert(( self.theParent.makeAlert(
"You must set a valid project name in project settings to " self.tr(
"use the automatic project backup feature." "You must set a valid project name in project settings to "
), nwAlert.WARN) "use the automatic project backup feature."
), nwAlert.WARN
)
return False return False
return True return True
@@ -1124,8 +1160,8 @@ class NWProject():
if nAuth == 1: if nAuth == 1:
authString = self.bookAuthors[0] authString = self.bookAuthors[0]
elif nAuth > 1: elif nAuth > 1:
authString = "%s and %s" % ( authString = "%s %s %s" % (
", ".join(self.bookAuthors[0:-1]), self.bookAuthors[-1] ", ".join(self.bookAuthors[0:-1]), self.tr("and"), self.bookAuthors[-1]
) )
return authString return authString
@@ -1374,7 +1410,7 @@ class NWProject():
# Report status # Report status
if len(orphanFiles) > 0: if len(orphanFiles) > 0:
self.makeAlert( 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 nwAlert.WARN
) )
else: else:
@@ -1385,6 +1421,7 @@ class NWProject():
aDoc = NWDoc(self, self.theParent) aDoc = NWDoc(self, self.theParent)
nOrph = 0 nOrph = 0
noWhere = False noWhere = False
oPrefix = self.tr("Recovered")
for oHandle in orphanFiles: for oHandle in orphanFiles:
# Look for meta data # Look for meta data
@@ -1396,10 +1433,12 @@ class NWProject():
oName, oParent, oClass, oLayout = aDoc.getMeta() oName, oParent, oClass, oLayout = aDoc.getMeta()
if oName: if oName:
oName = "Recovered: %s" % oName.lstrip("Recovered: ") oName = self.tr("[{0}] {1}").format(
oPrefix, oName.replace("[%s]" % oPrefix, "").strip()
)
else: else:
nOrph += 1 nOrph += 1
oName = "Recovered File %d" % nOrph oName = self.tr("Recovered File {0}").format(nOrph)
# Recover file meta data # Recover file meta data
if oClass is None: if oClass is None:
@@ -1426,10 +1465,12 @@ class NWProject():
self.projTree.append(oHandle, oParent, orphItem) self.projTree.append(oHandle, oParent, orphItem)
if noWhere: if noWhere:
self.makeAlert(( self.makeAlert(
"One or more orphaned files could not be added back into the " self.tr(
"project. Make sure at least a Novel root folder exists." "One or more orphaned files could not be added back into the "
), nwAlert.WARN) "project. Make sure at least a Novel root folder exists."
), nwAlert.WARN
)
return True return True
@@ -1476,7 +1517,7 @@ class NWProject():
""" """
theData = os.path.join(self.projPath, theFolder) theData = os.path.join(self.projPath, theFolder)
if not os.path.isdir(theData): 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 return errList
logger.info("Old data folder %s found" % theFolder) logger.info("Old data folder %s found" % theFolder)
@@ -1499,7 +1540,7 @@ class NWProject():
logger.info("Moved file: %s" % theFile) logger.info("Moved file: %s" % theFile)
logger.info("New location: %s" % newPath) logger.info("New location: %s" % newPath)
except Exception: 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) logger.error("Could not move: %s" % theFile)
nw.logException() nw.logException()
@@ -1508,7 +1549,7 @@ class NWProject():
os.unlink(theFile) os.unlink(theFile)
logger.info("Deleted file: %s" % theFile) logger.info("Deleted file: %s" % theFile)
except Exception: 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) logger.error("Could not delete: %s" % theFile)
nw.logException() nw.logException()
@@ -1521,10 +1562,10 @@ class NWProject():
# ================== # ==================
try: try:
os.rmdir(theData) os.rmdir(theData)
logger.info("Removed folder: %s" % theFolder) logger.info("Deleted folder: %s" % theFolder)
except Exception: except Exception:
errList.append("Failed to remove: %s" % theFolder) errList.append(self.tr("Could not delete: {0}").format(theFolder))
logger.error("Failed to remove: %s" % theFolder) logger.error("Could not delete: %s" % theFolder)
nw.logException() nw.logException()
return errList return errList
@@ -1535,7 +1576,7 @@ class NWProject():
""" """
theJunk = os.path.join(self.projPath, "junk") theJunk = os.path.join(self.projPath, "junk")
if not self._checkFolder(theJunk): 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) theSrc = os.path.join(theDir, theItem)
theDst = os.path.join(theJunk, theItem) theDst = os.path.join(theJunk, theItem)
@@ -1546,7 +1587,7 @@ class NWProject():
except Exception: except Exception:
logger.error("Could not move item %s to junk." % theSrc) logger.error("Could not move item %s to junk." % theSrc)
nw.logException() 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 "" return ""
+4 -17
View File
@@ -29,8 +29,6 @@ import logging
import os import os
import difflib import difflib
from nw.constants import nwConst, isoLanguage
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# =============================================================================================== # # =============================================================================================== #
@@ -89,16 +87,6 @@ class NWSpellCheck():
""" """
return "", "" 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 # Internal Functions
## ##
@@ -196,10 +184,10 @@ class NWSpellEnchant(NWSpellCheck):
try: try:
import enchant import enchant
for spTag, spProvider in enchant.list_dicts(): for spTag, spProvider in enchant.list_dicts():
spName = "%s [%s]" % (self.expandLanguage(spTag), spProvider.name) retList.append((spTag, spProvider.name))
retList.append((spTag, spName))
except Exception: except Exception:
logger.error("Failed to list languages for enchant spell checking") logger.error("Failed to list languages for enchant spell checking")
return retList return retList
def describeDict(self): def describeDict(self):
@@ -332,8 +320,7 @@ class NWSpellSimple(NWSpellCheck):
if fExt != ".dict": if fExt != ".dict":
continue continue
spName = "%s [%s]" % (self.expandLanguage(fRoot), nwConst.SP_INTERNAL) retList.append((fRoot, "difflib"))
retList.append((fRoot, spName))
return retList return retList
@@ -341,6 +328,6 @@ class NWSpellSimple(NWSpellCheck):
"""Return the tag and provider of the currently loaded """Return the tag and provider of the currently loaded
dictionary. dictionary.
""" """
return self.theLang, nwConst.SP_INTERNAL return self.theLang, ""
# END Class NWSpellSimple # END Class NWSpellSimple
+4 -3
View File
@@ -28,6 +28,7 @@ import logging
import re import re
from operator import itemgetter from operator import itemgetter
from PyQt5.QtCore import QRegularExpression from PyQt5.QtCore import QRegularExpression
from nw.core.document import NWDoc from nw.core.document import NWDoc
@@ -281,10 +282,10 @@ class Tokenizer():
docSize = len(self.theText) docSize = len(self.theText)
if docSize > nwConst.MAX_DOCSIZE: if docSize > nwConst.MAX_DOCSIZE:
errVal = "Document '%s' is too big (%.2f MB). Skipping." % ( errVal = self.tr("Document '{0}' is too big ({1} MB). Skipping.").format(
self.theItem.itemName, docSize/1.0e6 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.errData.append(errVal)
self.isNone = self.theItem.itemLayout == nwItemLayout.NO_LAYOUT self.isNone = self.theItem.itemLayout == nwItemLayout.NO_LAYOUT
+91 -41
View File
@@ -120,35 +120,40 @@ def numberToRoman(numVal, isLower=False):
return romNum.lower() if isLower else romNum 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. """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 = "" numWord = ""
if theLanguage == "en": theCode = theLang.split("_")[0]
numWord = _numberToWordEN(numVal) if theCode == "en":
numWord = _numberToWord_EN(numVal)
elif theCode in ("nb", "nn"):
numWord = _numberToWord_NO(numVal, theCode)
else: else:
numWord = _numberToWordEN(numVal) # Default to return the number as a string
numWord = str(numVal)
return numWord return numWord
def _numberToWordEN(numVal): def _numberToWord_EN(numVal):
"""Convert numbers to English words. """Convert numbers to English words.
""" """
oneWord = "" oneWord = ""
tenWord = "" tenWord = ""
hunWord = "" hunWord = ""
if not isinstance(numVal, int):
return "[NaN]"
if numVal < 0:
return "[Negative]"
if numVal > 999:
return "[Out of Range]"
if numVal == 0: if numVal == 0:
return "Zero" return "Zero"
@@ -156,38 +161,83 @@ def _numberToWordEN(numVal):
tenVal = (numVal - oneVal) % 100 tenVal = (numVal - oneVal) % 100
hunVal = (numVal - tenVal - oneVal) % 1000 hunVal = (numVal - tenVal - oneVal) % 1000
theHundreds = { theWords = {
100: "One Hundred", 200: "Two Hundred", 300: "Three Hundred", 0: "", 1: "One", 2: "Two", 3: "Three", 4: "Four", 5: "Five",
400: "Four Hundred", 500: "Five Hundred", 600: "Six Hundred", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine", 10: "Ten",
700: "Seven Hundred", 800: "Eight Hundred", 900: "Nine Hundred", 11: "Eleven", 12: "Twelve", 13: "Thirteen", 14: "Fourteen",
} 15: "Fifteen", 16: "Sixteen", 17: "Seventeen", 18: "Eighteen",
theTens = { 19: "Nineteen", 20: "Twenty", 30: "Thirty", 40: "Forty",
20: "Twenty", 30: "Thirty", 40: "Forty", 50: "Fifty", 50: "Fifty", 60: "Sixty", 70: "Seventy", 80: "Eighty",
60: "Sixty", 70: "Seventy", 80: "Eighty", 90: "Ninety", 90: "Ninety", 100: "One Hundred", 200: "Two Hundred",
} 300: "Three Hundred", 400: "Four Hundred", 500: "Five Hundred",
theTeens = { 600: "Six Hundred", 700: "Seven Hundred", 800: "Eight Hundred",
0: "Ten", 1: "Eleven", 2: "Twelve", 3: "Thirteen", 4: "Fourteen", 900: "Nine Hundred",
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",
} }
retVale = "" retVal = ""
hunWord = theHundreds.get(hunVal, "") hunWord = theWords.get(hunVal, "")
if tenVal == 10: if tenVal == 10:
oneWord = theTeens.get(oneVal, "") oneWord = theWords.get(oneVal+10, "")
retVale = f"{hunWord} {oneWord}".strip() retVal = f"{hunWord} {oneWord}".strip()
else: else:
oneWord = theOnes.get(oneVal, "") oneWord = theWords.get(oneVal, "")
if tenVal == 0: if tenVal == 0:
retVale = f"{hunWord} {oneWord}".strip() retVal = f"{hunWord} {oneWord}".strip()
else: else:
tenWord = theTens.get(tenVal, "") tenWord = theWords.get(tenVal, "")
if oneVal == 0: if oneVal == 0:
retVale = f"{hunWord} {tenWord}".strip() retVal = f"{hunWord} {tenWord}".strip()
else: 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()
+1
View File
@@ -68,6 +68,7 @@ class NWErrorMessage(QDialog):
self.msgBody.setReadOnly(True) self.msgBody.setReadOnly(True)
self.btnBox = QDialogButtonBox(QDialogButtonBox.Close) self.btnBox = QDialogButtonBox(QDialogButtonBox.Close)
self.btnBox.button(QDialogButtonBox.Close).setText(self.tr("Close"))
self.btnBox.rejected.connect(self._doClose) self.btnBox.rejected.connect(self._doClose)
# Assemble # Assemble
+63 -66
View File
@@ -55,7 +55,7 @@ class GuiAbout(QDialog):
self.innerBox = QHBoxLayout() self.innerBox = QHBoxLayout()
self.innerBox.setSpacing(self.mainConf.pxInt(16)) self.innerBox.setSpacing(self.mainConf.pxInt(16))
self.setWindowTitle("About novelWriter") self.setWindowTitle(self.tr("About novelWriter"))
self.setMinimumWidth(self.mainConf.pxInt(650)) self.setMinimumWidth(self.mainConf.pxInt(650))
self.setMinimumHeight(self.mainConf.pxInt(600)) self.setMinimumHeight(self.mainConf.pxInt(600))
@@ -90,13 +90,14 @@ class GuiAbout(QDialog):
# Main Tab Area # Main Tab Area
self.tabBox = QTabWidget() self.tabBox = QTabWidget()
self.tabBox.addTab(self.pageAbout, "About") self.tabBox.addTab(self.pageAbout, self.tr("About"))
self.tabBox.addTab(self.pageNotes, "Release") self.tabBox.addTab(self.pageNotes, self.tr("Release"))
self.tabBox.addTab(self.pageLicense, "License") self.tabBox.addTab(self.pageLicense, self.tr("License"))
self.innerBox.addWidget(self.tabBox) self.innerBox.addWidget(self.tabBox)
# OK Button # OK Button
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok) self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok)
self.buttonBox.button(QDialogButtonBox.Ok).setText(self.tr("OK"))
self.buttonBox.accepted.connect(self._doClose) self.buttonBox.accepted.connect(self._doClose)
self.outerBox.addLayout(self.innerBox) self.outerBox.addLayout(self.innerBox)
@@ -132,80 +133,76 @@ class GuiAbout(QDialog):
"""Generate the content for the About page. """Generate the content for the About page.
""" """
listPrefix = "&nbsp;&nbsp;&bull;&nbsp;&nbsp;" listPrefix = "&nbsp;&nbsp;&bull;&nbsp;&nbsp;"
aboutMsg = ( webLink = f"<a href='{nw.__url__:s}'>{nw.__domain__:s}</a>"
"<h2>About novelWriter</h2>" aboutMsg = (
"<p>{copyright:s}.</p>" "<h2>{title1}</h2>"
"<p>Website: <a href='{website:s}'>{domain:s}</a></p>" "<p>{copyright}.</p>"
"<p>novelWriter is a markdown-like text editor designed for " "<p>{website}</p>"
"organising and writing novels. It is written in Python 3 with a " "<p>{intro}</p>"
"Qt5 GUI, using PyQt5.</p>" "<p>{license1}</p>"
"<p>novelWriter is free software: you can redistribute it and/or " "<p>{license2}</p>"
"modify it under the terms of the GNU General Public License as " "<p>{license3}</p>"
"published by the Free Software Foundation, either version 3 of " "<h3>{title2}</h3>"
"the License, or (at your option) any later version.</p>" "<p>{credits}</p>"
"<p>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.</p>"
"<p>See the License tab for the full license text, or visit the "
"GNU website at "
"<a href='https://www.gnu.org/licenses/gpl-3.0.html'>GPL v3.0</a> "
"for more details.</p>"
"<h3>Credits</h3>"
"<p>{credits:s}</p>"
).format( ).format(
title1 = self.tr("About novelWriter"),
copyright = nw.__copyright__, copyright = nw.__copyright__,
website = nw.__url__, website = self.tr("Website: {0}").format(webLink),
domain = nw.__domain__, title2 = self.tr("Credits"),
credits = "<br/>".join(["%s%s" % (listPrefix, x) for x in nw.__credits__]), credits = "<br/>".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(
"<a href='https://www.gnu.org/licenses/gpl-3.0.html'>GPL v3.0</a>"
),
) )
theTheme = self.theParent.theTheme theTheme = self.theParent.theTheme
theIcons = self.theParent.theTheme.theIcons theIcons = self.theParent.theTheme.theIcons
if theTheme.themeName: if theTheme.themeName:
aboutMsg += ( aboutMsg += "<h4>%s</h4><p>%s<br/>%s<br/>%s</p>" % (
"<h4>Theme: {name:s}</h4>" self.tr("Theme: {0}").format(theTheme.themeName),
"<p>" self.tr("Author: {0}").format(theTheme.themeAuthor),
"<b>Author:</b> {author:s}<br/>" self.tr("Credit: {0}").format(theTheme.themeCredit),
"<b>Credit:</b> {credit:s}<br/>" self.tr("License: {0}").format(
"<b>License:</b> <a href='{lic_url:s}'>{license:s}</a>" f"<a href='{theTheme.themeLicenseUrl}'>{theTheme.themeLicense}</a>"
"</p>" )
).format(
name = theTheme.themeName,
author = theTheme.themeAuthor,
credit = theTheme.themeCredit,
license = theTheme.themeLicense,
lic_url = theTheme.themeLicenseUrl,
) )
if theIcons.themeName: if theIcons.themeName:
aboutMsg += ( aboutMsg += "<h4>%s</h4><p>%s<br/>%s<br/>%s</p>" % (
"<h4>Icons: {name:s}</h4>" self.tr("Icons: {0}").format(theIcons.themeName),
"<p>" self.tr("Author: {0}").format(theIcons.themeAuthor),
"<b>Author:</b> {author:s}<br/>" self.tr("Credit: {0}").format(theIcons.themeCredit),
"<b>Credit:</b> {credit:s}<br/>" self.tr("License: {0}").format(
"<b>License:</b> <a href='{lic_url:s}'>{license:s}</a>" f"<a href='{theIcons.themeLicenseUrl}'>{theIcons.themeLicense}</a>"
"</p>" )
).format(
name = theIcons.themeName,
author = theIcons.themeAuthor,
credit = theIcons.themeCredit,
license = theIcons.themeLicense,
lic_url = theIcons.themeLicenseUrl,
) )
if theTheme.syntaxName: if theTheme.syntaxName:
aboutMsg += ( aboutMsg += "<h4>%s</h4><p>%s<br/>%s<br/>%s</p>" % (
"<h4>Syntax: {name:s}</h4>" self.tr("Syntax: {0}").format(theTheme.syntaxName),
"<p>" self.tr("Author: {0}").format(theTheme.syntaxAuthor),
"<b>Author:</b> {author:s}<br/>" self.tr("Credit: {0}").format(theTheme.syntaxCredit),
"<b>Credit:</b> {credit:s}<br/>" self.tr("License: {0}").format(
"<b>License:</b> <a href='{lic_url:s}'>{license:s}</a>" f"<a href='{theTheme.syntaxLicenseUrl}'>{theTheme.syntaxLicense}</a>"
"</p>" )
).format(
name = theTheme.syntaxName,
author = theTheme.syntaxAuthor,
credit = theTheme.syntaxCredit,
license = theTheme.syntaxLicense,
lic_url = theTheme.syntaxLicenseUrl,
) )
self.pageAbout.setHtml(aboutMsg) self.pageAbout.setHtml(aboutMsg)
+90 -90
View File
@@ -84,7 +84,7 @@ class GuiBuildNovel(QDialog):
self.htmlSize = 0 # Size of the html document self.htmlSize = 0 # Size of the html document
self.buildTime = 0 # The timestamp of the last build 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.setMinimumWidth(self.mainConf.pxInt(700))
self.setMinimumHeight(self.mainConf.pxInt(600)) self.setMinimumHeight(self.mainConf.pxInt(600))
@@ -101,27 +101,26 @@ class GuiBuildNovel(QDialog):
# Title Formats # 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.titleForm = QGridLayout(self)
self.titleGroup.setLayout(self.titleForm) self.titleGroup.setLayout(self.titleForm)
fmtHelp = ( fmtHelp = "<br>".join([
r"<b>Formatting Codes:</b><br>" "<b>%s</b>" % self.tr("Formatting Codes:"),
r"%title% for the title as set in the document<br>" self.tr("{0} for the title as set in the document").format(r"%title%"),
r"%ch% for chapter number (1, 2, 3)<br>" self.tr("{0} for chapter number (1, 2, 3)").format(r"%ch%"),
r"%chw% for chapter number as a word (one, two)<br>" self.tr("{0} for chapter number as a word (one, two)").format(r"%chw%"),
r"%chI% for chapter number in upper case Roman<br>" self.tr("{0} for chapter number in upper case Roman").format(r"%chI%"),
r"%chi% for chapter number in lower case Roman<br>" self.tr("{0} for chapter number in lower case Roman").format(r"%chi%"),
r"%sc% for scene number within chapter<br>" self.tr("{0} for scene number within chapter").format(r"%sc%"),
r"%sca% for scene number within novel" self.tr("{0} for scene number within novel").format(r"%sca%"),
) ])
fmtScHelp = ( fmtScHelp = "<br><br>%s" % self.tr(
r"<br><br>" "Leave blank to skip this heading, or set to a static text, like "
r"Leave blank to skip this heading, or set to a static text, like " "for instance '{0}', to make a separator. The separator will "
r"for instance '* * *', to make a separator. The separator will " "be centred automatically and only appear between sections of "
r"be centred automatically and only appear between sections of " "the same type."
r"the same type." ).format("* * *")
)
xFmt = self.mainConf.pxInt(100) xFmt = self.mainConf.pxInt(100)
self.fmtTitle = QLineEdit() self.fmtTitle = QLineEdit()
@@ -176,11 +175,11 @@ class GuiBuildNovel(QDialog):
self.boxSection = QHBoxLayout() self.boxSection = QHBoxLayout()
self.boxSection.addWidget(self.fmtSection) self.boxSection.addWidget(self.fmtSection)
titleLabel = QLabel("Title") titleLabel = QLabel(self.tr("Title"))
chapterLabel = QLabel("Chapter") chapterLabel = QLabel(self.tr("Chapter"))
unnumbLabel = QLabel("Unnumbered") unnumbLabel = QLabel(self.tr("Unnumbered"))
sceneLabel = QLabel("Scene") sceneLabel = QLabel(self.tr("Scene"))
sectionLabel = QLabel("Section") sectionLabel = QLabel(self.tr("Section"))
self.titleForm.addWidget(titleLabel, 0, 0, 1, 1, Qt.AlignLeft) self.titleForm.addWidget(titleLabel, 0, 0, 1, 1, Qt.AlignLeft)
self.titleForm.addLayout(self.boxTitle, 0, 1, 1, 1, Qt.AlignRight) self.titleForm.addLayout(self.boxTitle, 0, 1, 1, 1, Qt.AlignRight)
@@ -199,7 +198,7 @@ class GuiBuildNovel(QDialog):
# Font Options # Font Options
# ============ # ============
self.fontGroup = QGroupBox("Font Options", self) self.fontGroup = QGroupBox(self.tr("Font Options"), self)
self.fontForm = QGridLayout(self) self.fontForm = QGridLayout(self)
self.fontGroup.setLayout(self.fontForm) self.fontGroup.setLayout(self.fontForm)
@@ -237,11 +236,11 @@ class GuiBuildNovel(QDialog):
self.boxFont = QHBoxLayout() self.boxFont = QHBoxLayout()
self.boxFont.addWidget(self.textFont) self.boxFont.addWidget(self.textFont)
fontFamilyLabel = QLabel("Font family") fontFamilyLabel = QLabel(self.tr("Font family"))
fontSizeLabel = QLabel("Font size") fontSizeLabel = QLabel(self.tr("Font size"))
lineHeightLabel = QLabel("Line height") lineHeightLabel = QLabel(self.tr("Line height"))
justifyLabel = QLabel("Justify text") justifyLabel = QLabel(self.tr("Justify text"))
stylingLabel = QLabel("Disable styling") stylingLabel = QLabel(self.tr("Disable styling"))
self.fontForm.addWidget(fontFamilyLabel, 0, 0, 1, 1, Qt.AlignLeft) self.fontForm.addWidget(fontFamilyLabel, 0, 0, 1, 1, Qt.AlignLeft)
self.fontForm.addLayout(self.boxFont, 0, 1, 1, 1, Qt.AlignRight) self.fontForm.addLayout(self.boxFont, 0, 1, 1, 1, Qt.AlignRight)
@@ -283,7 +282,7 @@ class GuiBuildNovel(QDialog):
# Include Options # Include Options
# =============== # ===============
self.textGroup = QGroupBox("Include Options", self) self.textGroup = QGroupBox(self.tr("Include Options"), self)
self.textForm = QGridLayout(self) self.textForm = QGridLayout(self)
self.textGroup.setLayout(self.textForm) self.textGroup.setLayout(self.textForm)
@@ -307,10 +306,10 @@ class GuiBuildNovel(QDialog):
self.optState.getBool("GuiBuildNovel", "incBodyText", True) self.optState.getBool("GuiBuildNovel", "incBodyText", True)
) )
synopsisLabel = QLabel("Include synopsis") synopsisLabel = QLabel(self.tr("Include synopsis"))
commentsLabel = QLabel("Include comments") commentsLabel = QLabel(self.tr("Include comments"))
keywordsLabel = QLabel("Include keywords") keywordsLabel = QLabel(self.tr("Include keywords"))
bodyLabel = QLabel("Include body text") bodyLabel = QLabel(self.tr("Include body text"))
self.textForm.addWidget(synopsisLabel, 0, 0, 1, 1, Qt.AlignLeft) self.textForm.addWidget(synopsisLabel, 0, 0, 1, 1, Qt.AlignLeft)
self.textForm.addWidget(self.includeSynopsis, 0, 1, 1, 1, Qt.AlignRight) self.textForm.addWidget(self.includeSynopsis, 0, 1, 1, 1, Qt.AlignRight)
@@ -327,37 +326,34 @@ class GuiBuildNovel(QDialog):
# File Filter Options # File Filter Options
# =================== # ===================
self.fileGroup = QGroupBox("File Filter Options", self) self.fileGroup = QGroupBox(self.tr("File Filter Options"), self)
self.fileForm = QGridLayout(self) self.fileForm = QGridLayout(self)
self.fileGroup.setLayout(self.fileForm) self.fileGroup.setLayout(self.fileForm)
self.novelFiles = QSwitch(width=wS, height=hS) self.novelFiles = QSwitch(width=wS, height=hS)
self.novelFiles.setToolTip( self.novelFiles.setToolTip(self.tr("Include files with layouts other than 'Note'."))
"Include files with layouts 'Book', 'Page', 'Partition', "
"'Chapter', 'Unnumbered', and 'Scene'."
)
self.novelFiles.setChecked( self.novelFiles.setChecked(
self.optState.getBool("GuiBuildNovel", "addNovel", True) self.optState.getBool("GuiBuildNovel", "addNovel", True)
) )
self.noteFiles = QSwitch(width=wS, height=hS) 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.noteFiles.setChecked(
self.optState.getBool("GuiBuildNovel", "addNotes", False) self.optState.getBool("GuiBuildNovel", "addNotes", False)
) )
self.ignoreFlag = QSwitch(width=wS, height=hS) self.ignoreFlag = QSwitch(width=wS, height=hS)
self.ignoreFlag.setToolTip( self.ignoreFlag.setToolTip(self.tr(
"Ignore the 'Include when building project' setting and include " "Ignore the 'Include when building project' setting and include "
"all files in the output." "all files in the output."
) ))
self.ignoreFlag.setChecked( self.ignoreFlag.setChecked(
self.optState.getBool("GuiBuildNovel", "ignoreFlag", False) self.optState.getBool("GuiBuildNovel", "ignoreFlag", False)
) )
novelLabel = QLabel("Include novel files") novelLabel = QLabel(self.tr("Include novel files"))
notesLabel = QLabel("Include note files") notesLabel = QLabel(self.tr("Include note files"))
exportLabel = QLabel("Ignore export flag") exportLabel = QLabel(self.tr("Ignore export flag"))
self.fileForm.addWidget(novelLabel, 0, 0, 1, 1, Qt.AlignLeft) self.fileForm.addWidget(novelLabel, 0, 0, 1, 1, Qt.AlignLeft)
self.fileForm.addWidget(self.novelFiles, 0, 1, 1, 1, Qt.AlignRight) self.fileForm.addWidget(self.novelFiles, 0, 1, 1, 1, Qt.AlignRight)
@@ -372,7 +368,7 @@ class GuiBuildNovel(QDialog):
# Export Options # Export Options
# ============== # ==============
self.exportGroup = QGroupBox("Export Options", self) self.exportGroup = QGroupBox(self.tr("Export Options"), self)
self.exportForm = QGridLayout(self) self.exportForm = QGridLayout(self)
self.exportGroup.setLayout(self.exportForm) self.exportGroup.setLayout(self.exportForm)
@@ -386,8 +382,8 @@ class GuiBuildNovel(QDialog):
self.optState.getBool("GuiBuildNovel", "replaceUCode", False) self.optState.getBool("GuiBuildNovel", "replaceUCode", False)
) )
tabsLabel = QLabel("Replace tabs with spaces") tabsLabel = QLabel(self.tr("Replace tabs with spaces"))
uCodeLabel = QLabel("Replace Unicode in HTML") uCodeLabel = QLabel(self.tr("Replace Unicode in HTML"))
self.exportForm.addWidget(tabsLabel, 0, 0, 1, 1, Qt.AlignLeft) self.exportForm.addWidget(tabsLabel, 0, 0, 1, 1, Qt.AlignLeft)
self.exportForm.addWidget(self.replaceTabs, 0, 1, 1, 1, Qt.AlignRight) self.exportForm.addWidget(self.replaceTabs, 0, 1, 1, 1, Qt.AlignRight)
@@ -402,7 +398,7 @@ class GuiBuildNovel(QDialog):
self.buildProgress = QProgressBar() self.buildProgress = QProgressBar()
self.buildNovel = QPushButton("Build Preview") self.buildNovel = QPushButton(self.tr("Build Preview"))
self.buildNovel.clicked.connect(self._buildPreview) self.buildNovel.clicked.connect(self._buildPreview)
# Action Buttons # Action Buttons
@@ -413,56 +409,56 @@ class GuiBuildNovel(QDialog):
# Printing # Printing
self.printMenu = QMenu(self) self.printMenu = QMenu(self)
self.btnPrint = QPushButton("Print") self.btnPrint = QPushButton(self.tr("Print"))
self.btnPrint.setMenu(self.printMenu) 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.printSend.triggered.connect(self._printDocument)
self.printMenu.addAction(self.printSend) 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.printFile.triggered.connect(lambda: self._saveDocument(self.FMT_PDF))
self.printMenu.addAction(self.printFile) self.printMenu.addAction(self.printFile)
# Saving to File # Saving to File
self.saveMenu = QMenu(self) self.saveMenu = QMenu(self)
self.btnSave = QPushButton("Save As") self.btnSave = QPushButton(self.tr("Save As"))
self.btnSave.setMenu(self.saveMenu) 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.saveODT.triggered.connect(lambda: self._saveDocument(self.FMT_ODT))
self.saveMenu.addAction(self.saveODT) 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.saveFODT.triggered.connect(lambda: self._saveDocument(self.FMT_FODT))
self.saveMenu.addAction(self.saveFODT) 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.saveHTM.triggered.connect(lambda: self._saveDocument(self.FMT_HTM))
self.saveMenu.addAction(self.saveHTM) 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.saveNWD.triggered.connect(lambda: self._saveDocument(self.FMT_NWD))
self.saveMenu.addAction(self.saveNWD) 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.saveMD.triggered.connect(lambda: self._saveDocument(self.FMT_MD))
self.saveMenu.addAction(self.saveMD) 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.saveGH.triggered.connect(lambda: self._saveDocument(self.FMT_GH))
self.saveMenu.addAction(self.saveGH) 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.saveJsonH.triggered.connect(lambda: self._saveDocument(self.FMT_JSON_H))
self.saveMenu.addAction(self.saveJsonH) 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.saveJsonM.triggered.connect(lambda: self._saveDocument(self.FMT_JSON_M))
self.saveMenu.addAction(self.saveJsonM) self.saveMenu.addAction(self.saveJsonM)
self.btnClose = QPushButton("Close") self.btnClose = QPushButton(self.tr("Close"))
self.btnClose.clicked.connect(self._doClose) self.btnClose.clicked.connect(self._doClose)
self.buttonBox.addWidget(self.btnSave) self.buttonBox.addWidget(self.btnSave)
@@ -567,7 +563,7 @@ class GuiBuildNovel(QDialog):
self.docView.setContent(self.htmlText, self.buildTime) self.docView.setContent(self.htmlText, self.buildTime)
else: else:
self.docView.setText( self.docView.setText(
"Failed to generate preview. The result is too big." self.tr("Failed to generate preview. The result is too big.")
) )
else: else:
@@ -736,10 +732,10 @@ class GuiBuildNovel(QDialog):
logger.debug("Built project in %.3f ms" % (1000*(tEnd - tStart))) logger.debug("Built project in %.3f ms" % (1000*(tEnd - tStart)))
if bldObj.errData: if bldObj.errData:
self.theParent.makeAlert(( self.theParent.makeAlert("%s:<br>-&nbsp;%s" % (
"There were problems when building the project:" self.tr("There were problems when building the project"),
"<br>-&nbsp;%s" "<br>-&nbsp;".join(bldObj.errData)), nwAlert.ERROR
) % "<br>-&nbsp;".join(bldObj.errData), nwAlert.ERROR) )
return return
@@ -796,39 +792,39 @@ class GuiBuildNovel(QDialog):
if theFmt == self.FMT_ODT: if theFmt == self.FMT_ODT:
fileExt = "odt" fileExt = "odt"
textFmt = "Open Document" textFmt = self.tr("Open Document")
elif theFmt == self.FMT_FODT: elif theFmt == self.FMT_FODT:
fileExt = "fodt" fileExt = "fodt"
textFmt = "Flat Open Document" textFmt = self.tr("Flat Open Document")
elif theFmt == self.FMT_HTM: elif theFmt == self.FMT_HTM:
fileExt = "htm" fileExt = "htm"
textFmt = "Plain HTML" textFmt = self.tr("Plain HTML")
elif theFmt == self.FMT_NWD: elif theFmt == self.FMT_NWD:
fileExt = "nwd" fileExt = "nwd"
textFmt = "novelWriter Markdown" textFmt = self.tr("novelWriter Markdown")
elif theFmt == self.FMT_MD: elif theFmt == self.FMT_MD:
fileExt = "md" fileExt = "md"
textFmt = "Standard Markdown" textFmt = self.tr("Standard Markdown")
elif theFmt == self.FMT_GH: elif theFmt == self.FMT_GH:
fileExt = "md" fileExt = "md"
textFmt = "GitHub Markdown" textFmt = self.tr("GitHub Markdown")
elif theFmt == self.FMT_JSON_H: elif theFmt == self.FMT_JSON_H:
fileExt = "json" fileExt = "json"
textFmt = "JSON + novelWriter HTML" textFmt = self.tr("JSON + novelWriter HTML")
elif theFmt == self.FMT_JSON_M: elif theFmt == self.FMT_JSON_M:
fileExt = "json" fileExt = "json"
textFmt = "JSON + novelWriter Markdown" textFmt = self.tr("JSON + novelWriter Markdown")
elif theFmt == self.FMT_PDF: elif theFmt == self.FMT_PDF:
fileExt = "pdf" fileExt = "pdf"
textFmt = "PDF" textFmt = self.tr("PDF")
else: else:
return False return False
@@ -848,7 +844,7 @@ class GuiBuildNovel(QDialog):
dlgOpt = QFileDialog.Options() dlgOpt = QFileDialog.Options()
dlgOpt |= QFileDialog.DontUseNativeDialog dlgOpt |= QFileDialog.DontUseNativeDialog
savePath, _ = QFileDialog.getSaveFileName( savePath, _ = QFileDialog.getSaveFileName(
self, "Save Document As", savePath, options=dlgOpt self, self.tr("Save Document As"), savePath, options=dlgOpt
) )
if not savePath: if not savePath:
return False return False
@@ -985,20 +981,20 @@ class GuiBuildNovel(QDialog):
errMsg - str(e) errMsg - str(e)
else: else:
errMsg = "Unknown format" errMsg = self.tr("Unknown format")
# Report to user # Report to user
if wSuccess: if wSuccess:
self.theParent.makeAlert( self.theParent.makeAlert(
"%s file successfully written to:<br> %s" % ( "%s<br>%s" % (
textFmt, savePath self.tr("{0} file successfully written to:").format(textFmt), savePath
), nwAlert.INFO ),
nwAlert.INFO
) )
else: else:
self.theParent.makeAlert( self.theParent.makeAlert(
"Failed to write %s file. %s" % ( self.tr("Failed to write {0} file. {1}").format(textFmt, errMsg),
textFmt, errMsg nwAlert.ERROR
), nwAlert.ERROR
) )
return wSuccess return wSuccess
@@ -1193,11 +1189,11 @@ class GuiBuildNovelDocView(QTextBrowser):
self.qDocument = self.document() self.qDocument = self.document()
self.qDocument.setDocumentMargin(self.mainConf.getTextMargin()) self.qDocument.setDocumentMargin(self.mainConf.getTextMargin())
self.setPlaceholderText( self.setPlaceholderText(self.tr(
"This area will show the content of the document to be " "This area will show the content of the document to be "
"exported or printed. Press the \"Build Preview\" button " "exported or printed. Press the \"Build Preview\" button "
"to generate content." "to generate content."
) ))
theFont = QFont() theFont = QFont()
if self.mainConf.textFont is None: if self.mainConf.textFont is None:
@@ -1227,7 +1223,7 @@ class GuiBuildNovelDocView(QTextBrowser):
fPx = int(1.1*self.theTheme.fontPixelSize) fPx = int(1.1*self.theTheme.fontPixelSize)
self.theTitle = QLabel("<b>Build Time:</b> Unknown", self) self.theTitle = QLabel("", self)
self.theTitle.setIndent(0) self.theTitle.setIndent(0)
self.theTitle.setAutoFillBackground(True) self.theTitle.setAutoFillBackground(True)
self.theTitle.setAlignment(Qt.AlignCenter) self.theTitle.setAlignment(Qt.AlignCenter)
@@ -1236,6 +1232,7 @@ class GuiBuildNovelDocView(QTextBrowser):
self.theTitle.setFont(lblFont) self.theTitle.setFont(lblFont)
self._updateDocMargins() self._updateDocMargins()
self._updateBuildAge()
self.setStyleSheet() self.setStyleSheet()
# Age Timer # Age Timer
@@ -1341,8 +1338,11 @@ class GuiBuildNovelDocView(QTextBrowser):
fuzzyTime(time() - self.buildTime) fuzzyTime(time() - self.buildTime)
) )
else: else:
strBuildTime = "Unknown" strBuildTime = self.tr("Unknown")
self.theTitle.setText("<b>Build Time:</b> %s" % strBuildTime)
self.theTitle.setText(self.tr("<b>Build Time:</b> {0}").format(strBuildTime))
return
def _updateDocMargins(self): def _updateDocMargins(self):
"""Automatically adjust the header to fill the top of the """Automatically adjust the header to fill the top of the
+2
View File
@@ -500,6 +500,8 @@ class QuotesDialog(QDialog):
# Buttons # Buttons
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) 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.accepted.connect(self._doAccept)
self.buttonBox.rejected.connect(self._doReject) self.buttonBox.rejected.connect(self._doReject)
+105 -85
View File
@@ -53,8 +53,8 @@ from nw.core import NWDoc, NWSpellSimple, countWords
from nw.gui.dochighlight import GuiDocHighlighter from nw.gui.dochighlight import GuiDocHighlighter
from nw.common import transferCase from nw.common import transferCase
from nw.constants import ( from nw.constants import (
nwConst, nwAlert, nwUnicode, nwDocAction, nwDocInsert, nwItemClass, trConst, nwConst, nwAlert, nwUnicode, nwDocAction, nwDocInsert,
nwKeyWords, nwLabels nwItemClass, nwKeyWords, nwLabels
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -293,11 +293,17 @@ class GuiDocEditor(QTextEdit):
docSize = len(theDoc) docSize = len(theDoc)
if docSize > nwConst.MAX_DOCSIZE: if docSize > nwConst.MAX_DOCSIZE:
self.theParent.makeAlert(( self.theParent.makeAlert(
"The document you are trying to open is too big. " self.tr(
"The document size is %.2f\u202fMB. " "The document you are trying to open is too big. "
"The maximum size allowed is %.2f\u202fMB." "The document size is {0} MB. "
) % (docSize/1.0e6, nwConst.MAX_DOCSIZE/1.0e6), nwAlert.ERROR) "The maximum size allowed is {1} MB."
).format(
f"{docSize/1.0e6:.2f}",
f"{nwConst.MAX_DOCSIZE/1.0e6:.2f}"
),
nwAlert.ERROR
)
self.clearEditor() self.clearEditor()
return False return False
@@ -380,11 +386,17 @@ class GuiDocEditor(QTextEdit):
""" """
docSize = len(theText) docSize = len(theText)
if docSize > nwConst.MAX_DOCSIZE: if docSize > nwConst.MAX_DOCSIZE:
self.theParent.makeAlert(( self.theParent.makeAlert(
"The text you are trying to add is too big. " self.tr(
"The text size is %.2f\u202fMB. " "The text you are trying to add is too big. "
"The maximum size allowed is %.2f\u202fMB." "The text size is {0} MB. "
) % (docSize/1.0e6, nwConst.MAX_DOCSIZE/1.0e6), nwAlert.ERROR) "The maximum size allowed is {1} MB."
).format(
f"{docSize/1.0e6:.2f}",
f"{nwConst.MAX_DOCSIZE/1.0e6:.2f}"
),
nwAlert.ERROR
)
return False return False
qApp.setOverrideCursor(QCursor(Qt.WaitCursor)) qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
@@ -568,12 +580,14 @@ class GuiDocEditor(QTextEdit):
""" """
if not isinstance(theLine, int): if not isinstance(theLine, int):
return False return False
if theLine >= 0: if theLine >= 0:
theBlock = self.qDocument.findBlockByLineNumber(theLine) theBlock = self.qDocument.findBlockByLineNumber(theLine)
if theBlock: if theBlock:
self.setCursorPosition(theBlock.position()) self.setCursorPosition(theBlock.position())
self.docFooter.updateLineCount() self.docFooter.updateLineCount()
logger.verbose("Cursor moved to line %d" % theLine) logger.verbose("Cursor moved to line %d" % theLine)
return True return True
## ##
@@ -591,11 +605,9 @@ class GuiDocEditor(QTextEdit):
theLang = self.theProject.projLang theLang = self.theProject.projLang
self.theDict.setLanguage(theLang, self.theProject.projDict) self.theDict.setLanguage(theLang, self.theProject.projDict)
theTag, theProvider = self.theDict.describeDict()
aLang, aName = self.theDict.describeDict() self.theParent.statusBar.setLanguage(theLang, theProvider)
self.theParent.statusBar.setLanguage(
aLang, "%s [%s]" % (self.mainConf.spellTool.title(), aName.title())
)
if not self.bigDoc: if not self.bigDoc:
self.spellCheckDocument() self.spellCheckDocument()
@@ -641,10 +653,8 @@ class GuiDocEditor(QTextEdit):
self.hLight.rehighlight() self.hLight.rehighlight()
qApp.restoreOverrideCursor() qApp.restoreOverrideCursor()
afTime = time() afTime = time()
logger.debug( logger.debug("Document highlighted in %.3f ms" % (1000*(afTime-bfTime)))
"Document highlighted in %.3f ms" % (1000*(afTime-bfTime)) self.theParent.statusBar.showMessage(self.tr("Spell check complete"))
)
self.theParent.statusBar.showMessage("Spell check complete")
return True return True
@@ -743,14 +753,14 @@ class GuiDocEditor(QTextEdit):
return False return False
msgBox = QMessageBox() msgBox = QMessageBox()
msgBox.information(self, "File Location", ( msgBox.information(
"File details for the currently open file<br>" self,
"Handle: {handle:s}<br>" self.tr("File Location"),
"Location: {fileLoc:s}" "%s<br>%s" % (
).format( self.tr("The currently open file is saved in:"),
handle = self.theHandle, self.nwDocument.getFileLocation()
fileLoc = str(self.nwDocument.getFileLocation()) ),
)) )
return return
@@ -799,7 +809,7 @@ class GuiDocEditor(QTextEdit):
theCursor = self.textCursor() theCursor = self.textCursor()
theBlock = theCursor.block() theBlock = theCursor.block()
if not theBlock.isValid(): if not theBlock.isValid():
logger.error("Filed to insert keyword '%s'" % keyWord) logger.error("Failed to insert keyword '%s'" % keyWord)
return False return False
theCursor.beginEditBlock() theCursor.beginEditBlock()
@@ -933,10 +943,15 @@ class GuiDocEditor(QTextEdit):
self.lastFind = None self.lastFind = None
if self.qDocument.characterCount() > nwConst.MAX_DOCSIZE: if self.qDocument.characterCount() > nwConst.MAX_DOCSIZE:
self.theParent.makeAlert(( self.theParent.makeAlert(
"The document has grown too big and you cannot add more text to it. " self.tr(
"The maximum size of a single novelWriter document is %.2f\u202fMB." "The document has grown too big and you cannot add more text to it. "
) % (nwConst.MAX_DOCSIZE/1.0e6), nwAlert.ERROR) "The maximum size of a single novelWriter document is {0} MB."
).format(
f"{nwConst.MAX_DOCSIZE/1.0e6:.2f}"
),
nwAlert.ERROR
)
self.undo() self.undo()
return return
@@ -966,21 +981,21 @@ class GuiDocEditor(QTextEdit):
# =========================== # ===========================
if self._followTag(theCursor=posCursor, loadTag=False): 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)) mnuTag.triggered.connect(lambda: self._followTag(theCursor=posCursor))
mnuContext.addAction(mnuTag) mnuContext.addAction(mnuTag)
mnuContext.addSeparator() mnuContext.addSeparator()
if userSelection: if userSelection:
mnuCut = QAction("Cut", mnuContext) mnuCut = QAction(self.tr("Cut"), mnuContext)
mnuCut.triggered.connect(lambda: self.docAction(nwDocAction.CUT)) mnuCut.triggered.connect(lambda: self.docAction(nwDocAction.CUT))
mnuContext.addAction(mnuCut) mnuContext.addAction(mnuCut)
mnuCopy = QAction("Copy", mnuContext) mnuCopy = QAction(self.tr("Copy"), mnuContext)
mnuCopy.triggered.connect(lambda: self.docAction(nwDocAction.COPY)) mnuCopy.triggered.connect(lambda: self.docAction(nwDocAction.COPY))
mnuContext.addAction(mnuCopy) mnuContext.addAction(mnuCopy)
mnuPaste = QAction("Paste", mnuContext) mnuPaste = QAction(self.tr("Paste"), mnuContext)
mnuPaste.triggered.connect(lambda: self.docAction(nwDocAction.PASTE)) mnuPaste.triggered.connect(lambda: self.docAction(nwDocAction.PASTE))
mnuContext.addAction(mnuPaste) mnuContext.addAction(mnuPaste)
@@ -989,17 +1004,17 @@ class GuiDocEditor(QTextEdit):
# Selections # Selections
# ========== # ==========
mnuSelAll = QAction("Select All", mnuContext) mnuSelAll = QAction(self.tr("Select All"), mnuContext)
mnuSelAll.triggered.connect(lambda: self.docAction(nwDocAction.SEL_ALL)) mnuSelAll.triggered.connect(lambda: self.docAction(nwDocAction.SEL_ALL))
mnuContext.addAction(mnuSelAll) mnuContext.addAction(mnuSelAll)
mnuSelWord = QAction("Select Word", mnuContext) mnuSelWord = QAction(self.tr("Select Word"), mnuContext)
mnuSelWord.triggered.connect( mnuSelWord.triggered.connect(
lambda: self._makePosSelection(QTextCursor.WordUnderCursor, thePos) lambda: self._makePosSelection(QTextCursor.WordUnderCursor, thePos)
) )
mnuContext.addAction(mnuSelWord) mnuContext.addAction(mnuSelWord)
mnuSelPara = QAction("Select Paragraph", mnuContext) mnuSelPara = QAction(self.tr("Select Paragraph"), mnuContext)
mnuSelPara.triggered.connect( mnuSelPara.triggered.connect(
lambda: self._makePosSelection(QTextCursor.BlockUnderCursor, thePos) lambda: self._makePosSelection(QTextCursor.BlockUnderCursor, thePos)
) )
@@ -1025,7 +1040,7 @@ class GuiDocEditor(QTextEdit):
if spellCheck: if spellCheck:
mnuContext.addSeparator() mnuContext.addSeparator()
mnuHead = QAction("Spelling Suggestion(s)", mnuContext) mnuHead = QAction(self.tr("Spelling Suggestion(s)"), mnuContext)
mnuContext.addAction(mnuHead) mnuContext.addAction(mnuHead)
theSuggest = self.theDict.suggestWords(theWord)[:15] theSuggest = self.theDict.suggestWords(theWord)[:15]
@@ -1037,11 +1052,13 @@ class GuiDocEditor(QTextEdit):
) )
mnuContext.addAction(mnuWord) mnuContext.addAction(mnuWord)
else: 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.addAction(mnuHead)
mnuContext.addSeparator() 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)) mnuAdd.triggered.connect(lambda thePos: self._addWord(posCursor))
mnuContext.addAction(mnuAdd) mnuContext.addAction(mnuAdd)
@@ -1131,15 +1148,11 @@ class GuiDocEditor(QTextEdit):
QPointF(theSize.width(), theSize.height()), Qt.FuzzyHit QPointF(theSize.width(), theSize.height()), Qt.FuzzyHit
) )
if self.queuePos <= thePos: if self.queuePos <= thePos:
logger.verbose( logger.verbose("Allowed cursor move to %d <= %d" % (self.queuePos, thePos))
"Allowed cursor move to %d <= %d" % (self.queuePos, thePos)
)
self.setCursorPosition(self.queuePos) self.setCursorPosition(self.queuePos)
self.queuePos = None self.queuePos = None
else: else:
logger.verbose( logger.verbose("Denied cursor move to %d > %d" % (self.queuePos, thePos))
"Denied cursor move to %d > %d" % (self.queuePos, thePos)
)
return return
## ##
@@ -1317,7 +1330,8 @@ class GuiDocEditor(QTextEdit):
else: else:
self.theParent.makeAlert( 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 return
@@ -1530,9 +1544,7 @@ class GuiDocEditor(QTextEdit):
theText = newText theText = newText
cOffset -= 0 cOffset -= 0
else: else:
logger.error( logger.error("Unknown or unsupported block format requested: %s" % str(docAction))
"Unknown or unsupported block format requested: %s" % str(docAction)
)
return False return False
# Replace the block text # Replace the block text
@@ -1817,12 +1829,12 @@ class GuiDocEditSearch(QFrame):
# ========== # ==========
self.searchBox = QLineEdit(self) self.searchBox = QLineEdit(self)
self.searchBox.setFont(boxFont) self.searchBox.setFont(boxFont)
self.searchBox.setPlaceholderText("Search") self.searchBox.setPlaceholderText(self.tr("Search"))
self.searchBox.returnPressed.connect(self._doSearch) self.searchBox.returnPressed.connect(self._doSearch)
self.replaceBox = QLineEdit(self) self.replaceBox = QLineEdit(self)
self.replaceBox.setFont(boxFont) self.replaceBox.setFont(boxFont)
self.replaceBox.setPlaceholderText("Replace") self.replaceBox.setPlaceholderText(self.tr("Replace"))
self.replaceBox.returnPressed.connect(self._doReplace) self.replaceBox.returnPressed.connect(self._doReplace)
self.searchOpt = QToolBar(self) self.searchOpt = QToolBar(self)
@@ -1831,44 +1843,44 @@ class GuiDocEditSearch(QFrame):
self.searchOpt.setContentsMargins(0, 0, 0, 0) self.searchOpt.setContentsMargins(0, 0, 0, 0)
self.searchOpt.setStyleSheet(r"QToolBar {padding: 0;}") self.searchOpt.setStyleSheet(r"QToolBar {padding: 0;}")
self.searchLabel = QLabel("Search") self.searchLabel = QLabel(self.tr("Search"))
self.searchLabel.setFont(boxFont) self.searchLabel.setFont(boxFont)
self.searchLabel.setIndent(self.mainConf.pxInt(6)) self.searchLabel.setIndent(self.mainConf.pxInt(6))
self.toggleCase = QAction("Case Sensitive", self) self.toggleCase = QAction(self.tr("Case Sensitive"), self)
self.toggleCase.setToolTip("Match case") self.toggleCase.setToolTip(self.tr("Match case"))
self.toggleCase.setIcon(self.theTheme.getIcon("search_case")) self.toggleCase.setIcon(self.theTheme.getIcon("search_case"))
self.toggleCase.setCheckable(True) self.toggleCase.setCheckable(True)
self.toggleCase.setChecked(self.isCaseSense) self.toggleCase.setChecked(self.isCaseSense)
self.toggleCase.toggled.connect(self._doToggleCase) self.toggleCase.toggled.connect(self._doToggleCase)
self.searchOpt.addAction(self.toggleCase) self.searchOpt.addAction(self.toggleCase)
self.toggleWord = QAction("Whole Words Only", self) self.toggleWord = QAction(self.tr("Whole Words Only"), self)
self.toggleWord.setToolTip("Match whole words") self.toggleWord.setToolTip(self.tr("Match whole words"))
self.toggleWord.setIcon(self.theTheme.getIcon("search_word")) self.toggleWord.setIcon(self.theTheme.getIcon("search_word"))
self.toggleWord.setCheckable(True) self.toggleWord.setCheckable(True)
self.toggleWord.setChecked(self.isWholeWord) self.toggleWord.setChecked(self.isWholeWord)
self.toggleWord.toggled.connect(self._doToggleWord) self.toggleWord.toggled.connect(self._doToggleWord)
self.searchOpt.addAction(self.toggleWord) self.searchOpt.addAction(self.toggleWord)
self.toggleRegEx = QAction("RegEx Mode", self) self.toggleRegEx = QAction(self.tr("RegEx Mode"), self)
self.toggleRegEx.setToolTip("Use regular expressions (requires Qt 5.3)") self.toggleRegEx.setToolTip(self.tr("Search using regular expressions"))
self.toggleRegEx.setIcon(self.theTheme.getIcon("search_regex")) self.toggleRegEx.setIcon(self.theTheme.getIcon("search_regex"))
self.toggleRegEx.setCheckable(True) self.toggleRegEx.setCheckable(True)
self.toggleRegEx.setChecked(self.isRegEx) self.toggleRegEx.setChecked(self.isRegEx)
self.toggleRegEx.toggled.connect(self._doToggleRegEx) self.toggleRegEx.toggled.connect(self._doToggleRegEx)
self.searchOpt.addAction(self.toggleRegEx) self.searchOpt.addAction(self.toggleRegEx)
self.toggleLoop = QAction("Loop Search", self) self.toggleLoop = QAction(self.tr("Loop Search"), self)
self.toggleLoop.setToolTip("Loop the search when reaching the end") self.toggleLoop.setToolTip(self.tr("Loop the search when reaching the end"))
self.toggleLoop.setIcon(self.theTheme.getIcon("search_loop")) self.toggleLoop.setIcon(self.theTheme.getIcon("search_loop"))
self.toggleLoop.setCheckable(True) self.toggleLoop.setCheckable(True)
self.toggleLoop.setChecked(self.doLoop) self.toggleLoop.setChecked(self.doLoop)
self.toggleLoop.toggled.connect(self._doToggleLoop) self.toggleLoop.toggled.connect(self._doToggleLoop)
self.searchOpt.addAction(self.toggleLoop) self.searchOpt.addAction(self.toggleLoop)
self.toggleProject = QAction("Search Next File", self) self.toggleProject = QAction(self.tr("Search Next File"), self)
self.toggleProject.setToolTip("Continue searching in the next file") self.toggleProject.setToolTip(self.tr("Continue searching in the next file"))
self.toggleProject.setIcon(self.theTheme.getIcon("search_project")) self.toggleProject.setIcon(self.theTheme.getIcon("search_project"))
self.toggleProject.setCheckable(True) self.toggleProject.setCheckable(True)
self.toggleProject.setChecked(self.doNextFile) self.toggleProject.setChecked(self.doNextFile)
@@ -1877,8 +1889,8 @@ class GuiDocEditSearch(QFrame):
self.searchOpt.addSeparator() self.searchOpt.addSeparator()
self.toggleMatchCap = QAction("Preserve Case", self) self.toggleMatchCap = QAction(self.tr("Preserve Case"), self)
self.toggleMatchCap.setToolTip("Preserve case on replace") self.toggleMatchCap.setToolTip(self.tr("Preserve case on replace"))
self.toggleMatchCap.setIcon(self.theTheme.getIcon("search_preserve")) self.toggleMatchCap.setIcon(self.theTheme.getIcon("search_preserve"))
self.toggleMatchCap.setCheckable(True) self.toggleMatchCap.setCheckable(True)
self.toggleMatchCap.setChecked(self.doMatchCap) self.toggleMatchCap.setChecked(self.doMatchCap)
@@ -1887,8 +1899,8 @@ class GuiDocEditSearch(QFrame):
self.searchOpt.addSeparator() self.searchOpt.addSeparator()
self.cancelSearch = QAction("Close Search", self) self.cancelSearch = QAction(self.tr("Close Search"), self)
self.cancelSearch.setToolTip("Close the search box [Esc]") self.cancelSearch.setToolTip(self.tr("Close the search box [{0}]").format("Esc"))
self.cancelSearch.setIcon(self.theTheme.getIcon("search_cancel")) self.cancelSearch.setIcon(self.theTheme.getIcon("search_cancel"))
self.cancelSearch.triggered.connect(self._doClose) self.cancelSearch.triggered.connect(self._doClose)
self.searchOpt.addAction(self.cancelSearch) self.searchOpt.addAction(self.cancelSearch)
@@ -1900,18 +1912,18 @@ class GuiDocEditSearch(QFrame):
self.showReplace = QToolButton(self) self.showReplace = QToolButton(self)
self.showReplace.setArrowType(Qt.RightArrow) self.showReplace.setArrowType(Qt.RightArrow)
self.showReplace.setCheckable(True) 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.setStyleSheet(r"QToolButton {border: none; background: transparent;}")
self.showReplace.toggled.connect(self._doToggleReplace) self.showReplace.toggled.connect(self._doToggleReplace)
self.searchButton = QPushButton(self.theTheme.getIcon("search"), "") self.searchButton = QPushButton(self.theTheme.getIcon("search"), "")
self.searchButton.setFixedSize(QSize(bPx, bPx)) 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.searchButton.clicked.connect(self._doSearch)
self.replaceButton = QPushButton(self.theTheme.getIcon("search-replace"), "") self.replaceButton = QPushButton(self.theTheme.getIcon("search-replace"), "")
self.replaceButton.setFixedSize(QSize(bPx, bPx)) 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.replaceButton.clicked.connect(self._doReplace)
self.mainBox.addWidget(self.searchLabel, 0, 0, 1, 2, Qt.AlignLeft) self.mainBox.addWidget(self.searchLabel, 0, 0, 1, 2, Qt.AlignLeft)
@@ -2204,7 +2216,7 @@ class GuiDocEditHeader(QWidget):
self.editButton.setStyleSheet(buttonStyle) self.editButton.setStyleSheet(buttonStyle)
self.editButton.setToolButtonStyle(Qt.ToolButtonIconOnly) self.editButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.editButton.setVisible(False) 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.editButton.clicked.connect(self._editDocument)
self.searchButton = QToolButton(self) self.searchButton = QToolButton(self)
@@ -2215,7 +2227,7 @@ class GuiDocEditHeader(QWidget):
self.searchButton.setStyleSheet(buttonStyle) self.searchButton.setStyleSheet(buttonStyle)
self.searchButton.setToolButtonStyle(Qt.ToolButtonIconOnly) self.searchButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.searchButton.setVisible(False) self.searchButton.setVisible(False)
self.searchButton.setToolTip("Search document") self.searchButton.setToolTip(self.tr("Search document"))
self.searchButton.clicked.connect(self._searchDocument) self.searchButton.clicked.connect(self._searchDocument)
self.minmaxButton = QToolButton(self) self.minmaxButton = QToolButton(self)
@@ -2226,7 +2238,7 @@ class GuiDocEditHeader(QWidget):
self.minmaxButton.setStyleSheet(buttonStyle) self.minmaxButton.setStyleSheet(buttonStyle)
self.minmaxButton.setToolButtonStyle(Qt.ToolButtonIconOnly) self.minmaxButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.minmaxButton.setVisible(False) 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.minmaxButton.clicked.connect(self._minmaxDocument)
self.closeButton = QToolButton(self) self.closeButton = QToolButton(self)
@@ -2237,7 +2249,7 @@ class GuiDocEditHeader(QWidget):
self.closeButton.setStyleSheet(buttonStyle) self.closeButton.setStyleSheet(buttonStyle)
self.closeButton.setToolButtonStyle(Qt.ToolButtonIconOnly) self.closeButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.closeButton.setVisible(False) self.closeButton.setVisible(False)
self.closeButton.setToolTip("Close the document") self.closeButton.setToolTip(self.tr("Close the document"))
self.closeButton.clicked.connect(self._closeDocument) self.closeButton.clicked.connect(self._closeDocument)
# Assemble Layout # Assemble Layout
@@ -2413,7 +2425,7 @@ class GuiDocEditFooter(QWidget):
self.statusIcon.setFixedHeight(self.sPx) self.statusIcon.setFixedHeight(self.sPx)
self.statusIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.statusIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop)
self.statusText = QLabel("Status") self.statusText = QLabel(self.tr("Status"))
self.statusText.setIndent(0) self.statusText.setIndent(0)
self.statusText.setMargin(0) self.statusText.setMargin(0)
self.statusText.setContentsMargins(0, 0, 0, 0) self.statusText.setContentsMargins(0, 0, 0, 0)
@@ -2429,7 +2441,7 @@ class GuiDocEditFooter(QWidget):
self.linesIcon.setFixedHeight(self.sPx) self.linesIcon.setFixedHeight(self.sPx)
self.linesIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.linesIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop)
self.linesText = QLabel("Line: 0") self.linesText = QLabel("")
self.linesText.setIndent(0) self.linesText.setIndent(0)
self.linesText.setMargin(0) self.linesText.setMargin(0)
self.linesText.setContentsMargins(0, 0, 0, 0) self.linesText.setContentsMargins(0, 0, 0, 0)
@@ -2445,7 +2457,7 @@ class GuiDocEditFooter(QWidget):
self.wordsIcon.setFixedHeight(self.sPx) self.wordsIcon.setFixedHeight(self.sPx)
self.wordsIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.wordsIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop)
self.wordsText = QLabel("Words: 0") self.wordsText = QLabel("")
self.wordsText.setIndent(0) self.wordsText.setIndent(0)
self.wordsText.setMargin(0) self.wordsText.setMargin(0)
self.wordsText.setContentsMargins(0, 0, 0, 0) self.wordsText.setContentsMargins(0, 0, 0, 0)
@@ -2476,6 +2488,8 @@ class GuiDocEditFooter(QWidget):
# Fix the Colours # Fix the Colours
self.matchColours() self.matchColours()
self.updateLineCount()
self.updateCounts()
logger.debug("GuiDocEditFooter initialisation complete") logger.debug("GuiDocEditFooter initialisation complete")
@@ -2532,8 +2546,8 @@ class GuiDocEditFooter(QWidget):
theIcon = self.theParent.importIcons[iStatus] theIcon = self.theParent.importIcons[iStatus]
sIcon = theIcon.pixmap(self.sPx, self.sPx) sIcon = theIcon.pixmap(self.sPx, self.sPx)
sClass = nwLabels.CLASS_NAME[self.theItem.itemClass] sClass = trConst(nwLabels.CLASS_NAME[self.theItem.itemClass])
sLayout = nwLabels.LAYOUT_NAME[self.theItem.itemLayout] sLayout = trConst(nwLabels.LAYOUT_NAME[self.theItem.itemLayout])
sText = f"{self.theItem.itemStatus} / {sClass} / {sLayout}" sText = f"{self.theItem.itemStatus} / {sClass} / {sLayout}"
self.statusIcon.setPixmap(sIcon) self.statusIcon.setPixmap(sIcon)
@@ -2552,7 +2566,9 @@ class GuiDocEditFooter(QWidget):
iLine = theCursor.blockNumber() + 1 iLine = theCursor.blockNumber() + 1
iDist = 100*iLine/self.docEditor.qDocument.blockCount() 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 return
@@ -2566,10 +2582,14 @@ class GuiDocEditFooter(QWidget):
wCount = self.theItem.wordCount wCount = self.theItem.wordCount
wDiff = wCount - self.theItem.initCount 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() 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 return
+17 -15
View File
@@ -53,11 +53,11 @@ class GuiDocMerge(QDialog):
self.sourceItem = None self.sourceItem = None
self.outerBox = QVBoxLayout() self.outerBox = QVBoxLayout()
self.setWindowTitle("Merge Documents") self.setWindowTitle(self.tr("Merge Documents"))
self.headLabel = QLabel("<b>Documents to Merge</b>") self.headLabel = QLabel("<b>%s</b>" % self.tr("Documents to Merge"))
self.helpLabel = QHelpLabel( 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() self.listBox = QListWidget()
@@ -66,6 +66,8 @@ class GuiDocMerge(QDialog):
self.listBox.setMinimumHeight(self.mainConf.pxInt(180)) self.listBox.setMinimumHeight(self.mainConf.pxInt(180))
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) 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.accepted.connect(self._doMerge)
self.buttonBox.rejected.connect(self._doClose) self.buttonBox.rejected.connect(self._doClose)
@@ -102,9 +104,9 @@ class GuiDocMerge(QDialog):
finalOrder.append(self.listBox.item(i).data(Qt.UserRole)) finalOrder.append(self.listBox.item(i).data(Qt.UserRole))
if len(finalOrder) == 0: if len(finalOrder) == 0:
self.theParent.makeAlert(( self.theParent.makeAlert(
"No source documents found. Nothing to do." self.tr("No source documents found. Nothing to do."), nwAlert.ERROR
), nwAlert.ERROR) )
return return
theDoc = NWDoc(self.theProject, self.theParent) theDoc = NWDoc(self.theProject, self.theParent)
@@ -114,16 +116,16 @@ class GuiDocMerge(QDialog):
theText += "\n\n" theText += "\n\n"
if self.sourceItem is None: if self.sourceItem is None:
self.theParent.makeAlert(( self.theParent.makeAlert(
"No source document selected. Nothing to do." self.tr("No source document selected. Nothing to do."), nwAlert.ERROR
), nwAlert.ERROR) )
return return
srcItem = self.theProject.projTree[self.sourceItem] srcItem = self.theProject.projTree[self.sourceItem]
if srcItem is None: if srcItem is None:
self.theParent.makeAlert(( self.theParent.makeAlert(
"Could not parse source document." self.tr("Could not parse source document."), nwAlert.ERROR
), nwAlert.ERROR) )
return return
nHandle = self.theProject.newFile(srcItem.itemName, srcItem.itemClass, srcItem.itemParent) nHandle = self.theProject.newFile(srcItem.itemName, srcItem.itemClass, srcItem.itemParent)
@@ -164,9 +166,9 @@ class GuiDocMerge(QDialog):
if nwItem is None: if nwItem is None:
return return
if nwItem.itemType is not nwItemType.FOLDER: if nwItem.itemType is not nwItemType.FOLDER:
self.theParent.makeAlert(( self.theParent.makeAlert(
"Element selected in the project tree must be a folder." self.tr("Element selected in the project tree must be a folder."), nwAlert.ERROR
), nwAlert.ERROR) )
return return
for sHandle in self.theParent.treeView.getTreeFromHandle(tHandle): for sHandle in self.theParent.treeView.getTreeFromHandle(tHandle):
+40 -29
View File
@@ -56,11 +56,12 @@ class GuiDocSplit(QDialog):
self.sourceItem = None self.sourceItem = None
self.outerBox = QVBoxLayout() self.outerBox = QVBoxLayout()
self.setWindowTitle("Split Document") self.setWindowTitle(self.tr("Split Document"))
self.headLabel = QLabel("<b>Document Headers</b>") self.headLabel = QLabel("<b>%s</b>" % self.tr("Document Headers"))
self.helpLabel = QHelpLabel( 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() self.listBox = QListWidget()
@@ -69,10 +70,10 @@ class GuiDocSplit(QDialog):
self.listBox.setMinimumHeight(self.mainConf.pxInt(180)) self.listBox.setMinimumHeight(self.mainConf.pxInt(180))
self.splitLevel = QComboBox(self) self.splitLevel = QComboBox(self)
self.splitLevel.addItem("Split on Header Level 1 (Title)", 1) self.splitLevel.addItem(self.tr("Split on Header Level 1 (Title)"), 1)
self.splitLevel.addItem("Split up to Header Level 2 (Chapter)", 2) self.splitLevel.addItem(self.tr("Split up to Header Level 2 (Chapter)"), 2)
self.splitLevel.addItem("Split up to Header Level 3 (Scene)", 3) self.splitLevel.addItem(self.tr("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 up to Header Level 4 (Section)"), 4)
spIndex = self.splitLevel.findData( spIndex = self.splitLevel.findData(
self.optState.getInt("GuiDocSplit", "spLevel", 3) self.optState.getInt("GuiDocSplit", "spLevel", 3)
) )
@@ -81,6 +82,8 @@ class GuiDocSplit(QDialog):
self.splitLevel.currentIndexChanged.connect(self._populateList) self.splitLevel.currentIndexChanged.connect(self._populateList)
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) 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.accepted.connect(self._doSplit)
self.buttonBox.rejected.connect(self._doClose) self.buttonBox.rejected.connect(self._doClose)
@@ -115,16 +118,16 @@ class GuiDocSplit(QDialog):
logger.verbose("GuiDocSplit split button clicked") logger.verbose("GuiDocSplit split button clicked")
if self.sourceItem is None: if self.sourceItem is None:
self.theParent.makeAlert(( self.theParent.makeAlert(
"No source document selected. Nothing to do." self.tr("No source document selected. Nothing to do."), nwAlert.ERROR
), nwAlert.ERROR) )
return return
srcItem = self.theProject.projTree[self.sourceItem] srcItem = self.theProject.projTree[self.sourceItem]
if srcItem is None: if srcItem is None:
self.theParent.makeAlert(( self.theParent.makeAlert(
"Could not parse source document." self.tr("Could not parse source document."), nwAlert.ERROR
), nwAlert.ERROR) )
return return
theDoc = NWDoc(self.theProject, self.theParent) theDoc = NWDoc(self.theProject, self.theParent)
@@ -147,26 +150,34 @@ class GuiDocSplit(QDialog):
nFiles = len(finalOrder) nFiles = len(finalOrder)
if nFiles == 0: if nFiles == 0:
self.theParent.makeAlert(( self.theParent.makeAlert(
"No headers found. Nothing to do." self.tr("No headers found. Nothing to do."), nwAlert.ERROR
), nwAlert.ERROR) )
return return
# Check that another folder can be created # Check that another folder can be created
parTree = self.theProject.projTree.getItemPath(srcItem.itemParent) parTree = self.theProject.projTree.getItemPath(srcItem.itemParent)
if len(parTree) >= nwConst.MAX_DEPTH - 1: if len(parTree) >= nwConst.MAX_DEPTH - 1:
self.theParent.makeAlert(( self.theParent.makeAlert(
"Cannot add new folder for the document split. " self.tr(
"Maximum folder depth has been reached. " "Cannot add new folder for the document split. "
"Please move the file to another level in the project tree." "Maximum folder depth has been reached. "
), nwAlert.ERROR) "Please move the file to another level in the project tree."
), nwAlert.ERROR
)
return return
msgYes = self.theParent.askQuestion("Split Document", ( msgYes = self.theParent.askQuestion(
"The document will be split into %d file(s) in a new folder. " self.tr("Split Document"),
"The original document will remain intact.<br><br>" "%s<br><br>%s" % (
"Continue with the splitting process?" self.tr(
) % nFiles) "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: if not msgYes:
return return
@@ -242,9 +253,9 @@ class GuiDocSplit(QDialog):
if nwItem is None: if nwItem is None:
return return
if nwItem.itemType is not nwItemType.FILE: if nwItem.itemType is not nwItemType.FILE:
self.theParent.makeAlert(( self.theParent.makeAlert(
"Element selected in the project tree must be a file." self.tr("Element selected in the project tree must be a file."), nwAlert.ERROR
), nwAlert.ERROR) )
return return
self.listBox.clear() self.listBox.clear()
+26 -23
View File
@@ -183,7 +183,7 @@ class GuiDocViewer(QTextBrowser):
except Exception: except Exception:
logger.error("Failed to generate preview for document with handle '%s'" % tHandle) logger.error("Failed to generate preview for document with handle '%s'" % tHandle)
nw.logException() nw.logException()
self.setText("An error occurred while generating the preview.") self.setText(self.tr("An error occurred while generating the preview."))
return False return False
# Refresh the tab stops # Refresh the tab stops
@@ -243,11 +243,14 @@ class GuiDocViewer(QTextBrowser):
logger.debug("Loading document from tag '%s'" % theTag) logger.debug("Loading document from tag '%s'" % theTag)
tHandle, _, sTitle = self.theParent.theIndex.getTagSource(theTag) tHandle, _, sTitle = self.theParent.theIndex.getTagSource(theTag)
if tHandle is None: if tHandle is None:
self.theParent.makeAlert(( self.theParent.makeAlert(
"Could not find the reference for tag '%s'. It either doesn't " self.tr(
"exist, or the index is out of date. The index can be updated " "Could not find the reference for tag '{0}'. It either doesn't "
"from the Tools menu, or by pressing F9." "exist, or the index is out of date. The index can be updated "
) % theTag, nwAlert.ERROR) "from the Tools menu, or by pressing {1}."
).format(theTag, "F9"),
nwAlert.ERROR
)
return False return False
else: else:
# Let the parent handle the opening as it also ensures that # Let the parent handle the opening as it also ensures that
@@ -415,7 +418,7 @@ class GuiDocViewer(QTextBrowser):
# =================== # ===================
if userSelection: if userSelection:
mnuCopy = QAction("Copy", mnuContext) mnuCopy = QAction(self.tr("Copy"), mnuContext)
mnuCopy.triggered.connect(lambda: self.docAction(nwDocAction.COPY)) mnuCopy.triggered.connect(lambda: self.docAction(nwDocAction.COPY))
mnuContext.addAction(mnuCopy) mnuContext.addAction(mnuCopy)
@@ -424,17 +427,17 @@ class GuiDocViewer(QTextBrowser):
# Selections # Selections
# ========== # ==========
mnuSelAll = QAction("Select All", mnuContext) mnuSelAll = QAction(self.tr("Select All"), mnuContext)
mnuSelAll.triggered.connect(lambda: self.docAction(nwDocAction.SEL_ALL)) mnuSelAll.triggered.connect(lambda: self.docAction(nwDocAction.SEL_ALL))
mnuContext.addAction(mnuSelAll) mnuContext.addAction(mnuSelAll)
mnuSelWord = QAction("Select Word", mnuContext) mnuSelWord = QAction(self.tr("Select Word"), mnuContext)
mnuSelWord.triggered.connect( mnuSelWord.triggered.connect(
lambda: self._makePosSelection(QTextCursor.WordUnderCursor, thePos) lambda: self._makePosSelection(QTextCursor.WordUnderCursor, thePos)
) )
mnuContext.addAction(mnuSelWord) mnuContext.addAction(mnuSelWord)
mnuSelPara = QAction("Select Paragraph", mnuContext) mnuSelPara = QAction(self.tr("Select Paragraph"), mnuContext)
mnuSelPara.triggered.connect( mnuSelPara.triggered.connect(
lambda: self._makePosSelection(QTextCursor.BlockUnderCursor, thePos) lambda: self._makePosSelection(QTextCursor.BlockUnderCursor, thePos)
) )
@@ -740,7 +743,7 @@ class GuiDocViewHeader(QWidget):
self.backButton.setStyleSheet(buttonStyle) self.backButton.setStyleSheet(buttonStyle)
self.backButton.setToolButtonStyle(Qt.ToolButtonIconOnly) self.backButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.backButton.setVisible(False) self.backButton.setVisible(False)
self.backButton.setToolTip("Go backward") self.backButton.setToolTip(self.tr("Go backward"))
self.backButton.clicked.connect(self.docViewer.navBackward) self.backButton.clicked.connect(self.docViewer.navBackward)
self.forwardButton = QToolButton(self) self.forwardButton = QToolButton(self)
@@ -751,7 +754,7 @@ class GuiDocViewHeader(QWidget):
self.forwardButton.setStyleSheet(buttonStyle) self.forwardButton.setStyleSheet(buttonStyle)
self.forwardButton.setToolButtonStyle(Qt.ToolButtonIconOnly) self.forwardButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.forwardButton.setVisible(False) self.forwardButton.setVisible(False)
self.forwardButton.setToolTip("Go forward") self.forwardButton.setToolTip(self.tr("Go forward"))
self.forwardButton.clicked.connect(self.docViewer.navForward) self.forwardButton.clicked.connect(self.docViewer.navForward)
self.refreshButton = QToolButton(self) self.refreshButton = QToolButton(self)
@@ -762,7 +765,7 @@ class GuiDocViewHeader(QWidget):
self.refreshButton.setStyleSheet(buttonStyle) self.refreshButton.setStyleSheet(buttonStyle)
self.refreshButton.setToolButtonStyle(Qt.ToolButtonIconOnly) self.refreshButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.refreshButton.setVisible(False) 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.refreshButton.clicked.connect(self._refreshDocument)
self.closeButton = QToolButton(self) self.closeButton = QToolButton(self)
@@ -773,7 +776,7 @@ class GuiDocViewHeader(QWidget):
self.closeButton.setStyleSheet(buttonStyle) self.closeButton.setStyleSheet(buttonStyle)
self.closeButton.setToolButtonStyle(Qt.ToolButtonIconOnly) self.closeButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.closeButton.setVisible(False) self.closeButton.setVisible(False)
self.closeButton.setToolTip("Close the document") self.closeButton.setToolTip(self.tr("Close the document"))
self.closeButton.clicked.connect(self._closeDocument) self.closeButton.clicked.connect(self._closeDocument)
# Assemble Layout # Assemble Layout
@@ -944,7 +947,7 @@ class GuiDocViewFooter(QWidget):
self.showHide.setIconSize(QSize(fPx, fPx)) self.showHide.setIconSize(QSize(fPx, fPx))
self.showHide.setFixedSize(QSize(fPx, fPx)) self.showHide.setFixedSize(QSize(fPx, fPx))
self.showHide.clicked.connect(self._doShowHide) 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 # Sticky Button
self.stickyRefs = QToolButton(self) self.stickyRefs = QToolButton(self)
@@ -955,9 +958,9 @@ class GuiDocViewFooter(QWidget):
self.stickyRefs.setIconSize(QSize(fPx, fPx)) self.stickyRefs.setIconSize(QSize(fPx, fPx))
self.stickyRefs.setFixedSize(QSize(fPx, fPx)) self.stickyRefs.setFixedSize(QSize(fPx, fPx))
self.stickyRefs.toggled.connect(self._doToggleSticky) 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" "Activate to freeze the content of the references panel when changing document"
) ))
# Show Comments # Show Comments
self.showComments = QToolButton(self) self.showComments = QToolButton(self)
@@ -969,7 +972,7 @@ class GuiDocViewFooter(QWidget):
self.showComments.setIconSize(QSize(fPx, fPx)) self.showComments.setIconSize(QSize(fPx, fPx))
self.showComments.setFixedSize(QSize(fPx, fPx)) self.showComments.setFixedSize(QSize(fPx, fPx))
self.showComments.toggled.connect(self._doToggleComments) self.showComments.toggled.connect(self._doToggleComments)
self.showComments.setToolTip("Show comments") self.showComments.setToolTip(self.tr("Show comments"))
# Show Synopsis # Show Synopsis
self.showSynopsis = QToolButton(self) self.showSynopsis = QToolButton(self)
@@ -981,10 +984,10 @@ class GuiDocViewFooter(QWidget):
self.showSynopsis.setIconSize(QSize(fPx, fPx)) self.showSynopsis.setIconSize(QSize(fPx, fPx))
self.showSynopsis.setFixedSize(QSize(fPx, fPx)) self.showSynopsis.setFixedSize(QSize(fPx, fPx))
self.showSynopsis.toggled.connect(self._doToggleSynopsis) self.showSynopsis.toggled.connect(self._doToggleSynopsis)
self.showSynopsis.setToolTip("Show synopsis comments") self.showSynopsis.setToolTip(self.tr("Show synopsis comments"))
# Labels # Labels
self.lblRefs = QLabel("References") self.lblRefs = QLabel(self.tr("References"))
self.lblRefs.setBuddy(self.showHide) self.lblRefs.setBuddy(self.showHide)
self.lblRefs.setIndent(0) self.lblRefs.setIndent(0)
self.lblRefs.setMargin(0) self.lblRefs.setMargin(0)
@@ -993,7 +996,7 @@ class GuiDocViewFooter(QWidget):
self.lblRefs.setFixedHeight(fPx) self.lblRefs.setFixedHeight(fPx)
self.lblRefs.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.lblRefs.setAlignment(Qt.AlignLeft | Qt.AlignTop)
self.lblSticky = QLabel("Sticky") self.lblSticky = QLabel(self.tr("Sticky"))
self.lblSticky.setBuddy(self.stickyRefs) self.lblSticky.setBuddy(self.stickyRefs)
self.lblSticky.setIndent(0) self.lblSticky.setIndent(0)
self.lblSticky.setMargin(0) self.lblSticky.setMargin(0)
@@ -1002,7 +1005,7 @@ class GuiDocViewFooter(QWidget):
self.lblSticky.setFixedHeight(fPx) self.lblSticky.setFixedHeight(fPx)
self.lblSticky.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.lblSticky.setAlignment(Qt.AlignLeft | Qt.AlignTop)
self.lblComments = QLabel("Comments") self.lblComments = QLabel(self.tr("Comments"))
self.lblComments.setBuddy(self.showComments) self.lblComments.setBuddy(self.showComments)
self.lblComments.setIndent(0) self.lblComments.setIndent(0)
self.lblComments.setMargin(0) self.lblComments.setMargin(0)
@@ -1011,7 +1014,7 @@ class GuiDocViewFooter(QWidget):
self.lblComments.setFixedHeight(fPx) self.lblComments.setFixedHeight(fPx)
self.lblComments.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.lblComments.setAlignment(Qt.AlignLeft | Qt.AlignTop)
self.lblSynopsis = QLabel("Synopsis") self.lblSynopsis = QLabel(self.tr("Synopsis"))
self.lblSynopsis.setBuddy(self.showSynopsis) self.lblSynopsis.setBuddy(self.showSynopsis)
self.lblSynopsis.setIndent(0) self.lblSynopsis.setIndent(0)
self.lblSynopsis.setMargin(0) self.lblSynopsis.setMargin(0)
+12 -12
View File
@@ -32,7 +32,7 @@ from PyQt5.QtGui import QFont, QPixmap
from PyQt5.QtWidgets import QWidget, QGridLayout, QLabel from PyQt5.QtWidgets import QWidget, QGridLayout, QLabel
from nw.constants import ( from nw.constants import (
nwLabels, nwItemClass, nwItemType, nwItemLayout trConst, nwLabels, nwItemClass, nwItemType, nwItemLayout
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -67,7 +67,7 @@ class GuiItemDetails(QWidget):
self.fntValue.setPointSizeF(0.9*fPt) self.fntValue.setPointSizeF(0.9*fPt)
# Label # Label
self.labelName = QLabel("Label") self.labelName = QLabel(self.tr("Label"))
self.labelName.setFont(self.fntLabel) self.labelName.setFont(self.fntLabel)
self.labelName.setAlignment(Qt.AlignLeft | Qt.AlignBaseline) self.labelName.setAlignment(Qt.AlignLeft | Qt.AlignBaseline)
@@ -80,7 +80,7 @@ class GuiItemDetails(QWidget):
self.labelData.setWordWrap(True) self.labelData.setWordWrap(True)
# Status # Status
self.statusName = QLabel("Status") self.statusName = QLabel(self.tr("Status"))
self.statusName.setFont(self.fntLabel) self.statusName.setFont(self.fntLabel)
self.statusName.setAlignment(Qt.AlignLeft) self.statusName.setAlignment(Qt.AlignLeft)
@@ -92,7 +92,7 @@ class GuiItemDetails(QWidget):
self.statusData.setAlignment(Qt.AlignLeft) self.statusData.setAlignment(Qt.AlignLeft)
# Class # Class
self.className = QLabel("Class") self.className = QLabel(self.tr("Class"))
self.className.setFont(self.fntLabel) self.className.setFont(self.fntLabel)
self.className.setAlignment(Qt.AlignLeft) self.className.setAlignment(Qt.AlignLeft)
@@ -105,7 +105,7 @@ class GuiItemDetails(QWidget):
self.classData.setAlignment(Qt.AlignLeft) self.classData.setAlignment(Qt.AlignLeft)
# Layout # Layout
self.layoutName = QLabel("Layout") self.layoutName = QLabel(self.tr("Layout"))
self.layoutName.setFont(self.fntLabel) self.layoutName.setFont(self.fntLabel)
self.layoutName.setAlignment(Qt.AlignLeft) self.layoutName.setAlignment(Qt.AlignLeft)
@@ -118,7 +118,7 @@ class GuiItemDetails(QWidget):
self.layoutData.setAlignment(Qt.AlignLeft) self.layoutData.setAlignment(Qt.AlignLeft)
# Character Count # Character Count
self.cCountName = QLabel(" Characters") self.cCountName = QLabel(" "+self.tr("Characters"))
self.cCountName.setFont(self.fntLabel) self.cCountName.setFont(self.fntLabel)
self.cCountName.setAlignment(Qt.AlignRight) self.cCountName.setAlignment(Qt.AlignRight)
@@ -127,7 +127,7 @@ class GuiItemDetails(QWidget):
self.cCountData.setAlignment(Qt.AlignRight) self.cCountData.setAlignment(Qt.AlignRight)
# Word Count # Word Count
self.wCountName = QLabel(" Words") self.wCountName = QLabel(" "+self.tr("Words"))
self.wCountName.setFont(self.fntLabel) self.wCountName.setFont(self.fntLabel)
self.wCountName.setAlignment(Qt.AlignRight) self.wCountName.setAlignment(Qt.AlignRight)
@@ -136,7 +136,7 @@ class GuiItemDetails(QWidget):
self.wCountData.setAlignment(Qt.AlignRight) self.wCountData.setAlignment(Qt.AlignRight)
# Paragraph Count # Paragraph Count
self.pCountName = QLabel(" Paragraphs") self.pCountName = QLabel(" "+self.tr("Paragraphs"))
self.pCountName.setFont(self.fntLabel) self.pCountName.setFont(self.fntLabel)
self.pCountName.setAlignment(Qt.AlignRight) self.pCountName.setAlignment(Qt.AlignRight)
@@ -259,17 +259,17 @@ class GuiItemDetails(QWidget):
iPx = int(round(0.8*self.theTheme.baseIconSize)) iPx = int(round(0.8*self.theTheme.baseIconSize))
self.statusFlag.setPixmap(flagIcon.pixmap(iPx, iPx)) 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: if nwItem.itemLayout == nwItemLayout.NO_LAYOUT:
self.layoutFlag.setText("-") self.layoutFlag.setText("-")
else: else:
self.layoutFlag.setText(nwLabels.LAYOUT_FLAG[nwItem.itemLayout]) self.layoutFlag.setText(nwLabels.LAYOUT_FLAG[nwItem.itemLayout]) # NO-I18N
self.labelData.setText(theLabel) self.labelData.setText(theLabel)
self.statusData.setText(nwItem.itemStatus) self.statusData.setText(nwItem.itemStatus)
self.classData.setText(nwLabels.CLASS_NAME[nwItem.itemClass]) self.classData.setText(trConst(nwLabels.CLASS_NAME[nwItem.itemClass]))
self.layoutData.setText(nwLabels.LAYOUT_NAME[nwItem.itemLayout]) self.layoutData.setText(trConst(nwLabels.LAYOUT_NAME[nwItem.itemLayout]))
if nwItem.itemType == nwItemType.FILE: if nwItem.itemType == nwItemType.FILE:
self.cCountData.setText(f"{nwItem.charCount:n}") self.cCountData.setText(f"{nwItem.charCount:n}")
+18 -12
View File
@@ -34,7 +34,7 @@ from PyQt5.QtWidgets import (
) )
from nw.gui.custom import QSwitch 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__) logger = logging.getLogger(__name__)
@@ -58,7 +58,7 @@ class GuiItemEditor(QDialog):
if self.theItem is None: if self.theItem is None:
self._doClose() self._doClose()
self.setWindowTitle("Item Settings") self.setWindowTitle(self.tr("Item Settings"))
mVd = self.mainConf.pxInt(220) mVd = self.mainConf.pxInt(220)
mSp = self.mainConf.pxInt(16) mSp = self.mainConf.pxInt(16)
@@ -103,10 +103,10 @@ class GuiItemEditor(QDialog):
for itemLayout in nwItemLayout: for itemLayout in nwItemLayout:
if itemLayout in validLayouts: if itemLayout in validLayouts:
self.editLayout.addItem(nwLabels.LAYOUT_NAME[itemLayout], itemLayout) self.editLayout.addItem(trConst(nwLabels.LAYOUT_NAME[itemLayout]), itemLayout)
# Export Switch # Export Switch
self.textExport = QLabel("Include when building project") self.textExport = QLabel(self.tr("Include when building project"))
self.editExport = QSwitch() self.editExport = QSwitch()
if self.theItem.itemType == nwItemType.FILE: if self.theItem.itemType == nwItemType.FILE:
self.editExport.setEnabled(True) self.editExport.setEnabled(True)
@@ -117,6 +117,8 @@ class GuiItemEditor(QDialog):
# Buttons # Buttons
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) 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.accepted.connect(self._doSave)
self.buttonBox.rejected.connect(self._doClose) self.buttonBox.rejected.connect(self._doClose)
@@ -136,17 +138,21 @@ class GuiItemEditor(QDialog):
# Assemble # Assemble
## ##
nameLabel = QLabel(self.tr("Label"))
statusLabel = QLabel(self.tr("Status"))
layoutLabel = QLabel(self.tr("Layout"))
self.mainForm = QGridLayout() self.mainForm = QGridLayout()
self.mainForm.setVerticalSpacing(vSp) self.mainForm.setVerticalSpacing(vSp)
self.mainForm.setHorizontalSpacing(mSp) self.mainForm.setHorizontalSpacing(mSp)
self.mainForm.addWidget(QLabel("Label"), 0, 0, 1, 1) self.mainForm.addWidget(nameLabel, 0, 0, 1, 1)
self.mainForm.addWidget(self.editName, 0, 1, 1, 2) self.mainForm.addWidget(self.editName, 0, 1, 1, 2)
self.mainForm.addWidget(QLabel("Status"), 1, 0, 1, 1) self.mainForm.addWidget(statusLabel, 1, 0, 1, 1)
self.mainForm.addWidget(self.editStatus, 1, 1, 1, 2) self.mainForm.addWidget(self.editStatus, 1, 1, 1, 2)
self.mainForm.addWidget(QLabel("Layout"), 2, 0, 1, 1) self.mainForm.addWidget(layoutLabel, 2, 0, 1, 1)
self.mainForm.addWidget(self.editLayout, 2, 1, 1, 2) self.mainForm.addWidget(self.editLayout, 2, 1, 1, 2)
self.mainForm.addWidget(self.textExport, 3, 0, 1, 2) self.mainForm.addWidget(self.textExport, 3, 0, 1, 2)
self.mainForm.addWidget(self.editExport, 3, 2, 1, 1) self.mainForm.addWidget(self.editExport, 3, 2, 1, 1)
self.mainForm.setColumnStretch(0, 0) self.mainForm.setColumnStretch(0, 0)
self.mainForm.setColumnStretch(1, 1) self.mainForm.setColumnStretch(1, 1)
self.mainForm.setColumnStretch(2, 0) self.mainForm.setColumnStretch(2, 0)
+251 -227
View File
File diff suppressed because it is too large Load Diff
+8 -4
View File
@@ -63,7 +63,11 @@ class GuiNovelTree(QTreeWidget):
self.setIconSize(QSize(iPx, iPx)) self.setIconSize(QSize(iPx, iPx))
self.setIndentation(iPx) self.setIndentation(iPx)
self.setColumnCount(3) 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.itemDoubleClicked.connect(self._treeDoubleClick)
self.itemSelectionChanged.connect(self._itemSelected) self.itemSelectionChanged.connect(self._itemSelected)
self.setSelectionBehavior(QAbstractItemView.SelectRows) self.setSelectionBehavior(QAbstractItemView.SelectRows)
@@ -73,9 +77,9 @@ class GuiNovelTree(QTreeWidget):
treeHeadItem = self.headerItem() treeHeadItem = self.headerItem()
treeHeadItem.setTextAlignment(self.C_WORDS, Qt.AlignRight) treeHeadItem.setTextAlignment(self.C_WORDS, Qt.AlignRight)
treeHeadItem.setToolTip(self.C_TITLE, "Section title") treeHeadItem.setToolTip(self.C_TITLE, self.tr("Section title"))
treeHeadItem.setToolTip(self.C_WORDS, "Word count") treeHeadItem.setToolTip(self.C_WORDS, self.tr("Word count"))
treeHeadItem.setToolTip(self.C_POV, "Point-of-view character") treeHeadItem.setToolTip(self.C_POV, self.tr("Point-of-view character"))
treeHeader = self.header() treeHeader = self.header()
treeHeader.setStretchLastSection(True) treeHeader.setStretchLastSection(True)
+5 -5
View File
@@ -34,7 +34,7 @@ from PyQt5.QtWidgets import (
QTreeWidget, QTreeWidgetItem, QMenu, QAction, QAbstractItemView QTreeWidget, QTreeWidgetItem, QMenu, QAction, QAbstractItemView
) )
from nw.constants import nwKeyWords, nwLabels, nwOutline from nw.constants import trConst, nwKeyWords, nwLabels, nwOutline
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -149,7 +149,7 @@ class GuiOutline(QTreeWidget):
""" """
self.clear() self.clear()
self.setColumnCount(1) self.setColumnCount(1)
self.setHeaderLabel(nwLabels.OUTLINE_COLS[nwOutline.TITLE]) self.setHeaderLabel(trConst(nwLabels.OUTLINE_COLS[nwOutline.TITLE]))
self.treeOrder = [] self.treeOrder = []
self.colWidth = {} self.colWidth = {}
@@ -355,7 +355,7 @@ class GuiOutline(QTreeWidget):
if self.firstView: if self.firstView:
theLabels = [] theLabels = []
for i, hItem in enumerate(self.treeOrder): 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.colIndex[hItem] = i
self.setHeaderLabels(theLabels) self.setHeaderLabels(theLabels)
@@ -474,7 +474,7 @@ class GuiOutlineHeaderMenu(QMenu):
self.theParent = theParent self.theParent = theParent
self.acceptToggle = True self.acceptToggle = True
mnuHead = QAction("Select Columns", self) mnuHead = QAction(self.tr("Select Columns"), self)
self.addAction(mnuHead) self.addAction(mnuHead)
self.addSeparator() self.addSeparator()
@@ -482,7 +482,7 @@ class GuiOutlineHeaderMenu(QMenu):
for hItem in nwOutline: for hItem in nwOutline:
if hItem == nwOutline.TITLE: if hItem == nwOutline.TITLE:
continue 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].setCheckable(True)
self.actionMap[hItem].toggled.connect( self.actionMap[hItem].toggled.connect(
lambda isChecked, tItem=hItem : self._columnToggled(isChecked, tItem) lambda isChecked, tItem=hItem : self._columnToggled(isChecked, tItem)
+27 -27
View File
@@ -27,12 +27,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
import nw import nw
import logging import logging
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt, QT_TRANSLATE_NOOP
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QScrollArea, QWidget, QGridLayout, QHBoxLayout, QGroupBox, QLabel QScrollArea, QWidget, QGridLayout, QHBoxLayout, QGroupBox, QLabel
) )
from nw.constants import nwLabels, nwKeyWords from nw.constants import trConst, nwLabels, nwKeyWords
from nw.common import checkInt from nw.common import checkInt
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -40,10 +40,10 @@ logger = logging.getLogger(__name__)
class GuiOutlineDetails(QScrollArea): class GuiOutlineDetails(QScrollArea):
LVL_MAP = { LVL_MAP = {
"H1" : "Title", "H1" : QT_TRANSLATE_NOOP("GuiOutlineDetails", "Title"),
"H2" : "Chapter", "H2" : QT_TRANSLATE_NOOP("GuiOutlineDetails", "Chapter"),
"H3" : "Scene", "H3" : QT_TRANSLATE_NOOP("GuiOutlineDetails", "Scene"),
"H4" : "Section" "H4" : QT_TRANSLATE_NOOP("GuiOutlineDetails", "Section"),
} }
def __init__(self, theParent): def __init__(self, theParent):
@@ -66,9 +66,9 @@ class GuiOutlineDetails(QScrollArea):
vSpace = int(self.mainConf.pxInt(4)) vSpace = int(self.mainConf.pxInt(4))
# Details Area # Details Area
self.titleLabel = QLabel("<b>Title</b>") self.titleLabel = QLabel("<b>%s</b>" % self.tr("Title"))
self.fileLabel = QLabel("<b>Document</b>") self.fileLabel = QLabel("<b>%s</b>" % self.tr("Document"))
self.itemLabel = QLabel("<b>Status</b>") self.itemLabel = QLabel("<b>%s</b>" % self.tr("Status"))
self.titleValue = QLabel("") self.titleValue = QLabel("")
self.fileValue = QLabel("") self.fileValue = QLabel("")
self.itemValue = QLabel("") self.itemValue = QLabel("")
@@ -81,9 +81,9 @@ class GuiOutlineDetails(QScrollArea):
self.itemValue.setMaximumWidth(maxTitle) self.itemValue.setMaximumWidth(maxTitle)
# Stats Area # Stats Area
self.cCLabel = QLabel("<b>Characters</b>") self.cCLabel = QLabel("<b>%s</b>" % self.tr("Characters"))
self.wCLabel = QLabel("<b>Words</b>") self.wCLabel = QLabel("<b>%s</b>" % self.tr("Words"))
self.pCLabel = QLabel("<b>Paragraphs</b>") self.pCLabel = QLabel("<b>%s</b>" % self.tr("Paragraphs"))
self.cCValue = QLabel("") self.cCValue = QLabel("")
self.wCValue = QLabel("") self.wCValue = QLabel("")
self.pCValue = QLabel("") self.pCValue = QLabel("")
@@ -96,7 +96,7 @@ class GuiOutlineDetails(QScrollArea):
self.pCValue.setAlignment(Qt.AlignRight) self.pCValue.setAlignment(Qt.AlignRight)
# Synopsis # Synopsis
self.synopLabel = QLabel("<b>Synopsis</b>") self.synopLabel = QLabel("<b>%s</b>" % self.tr("Synopsis"))
self.synopValue = QLabel("") self.synopValue = QLabel("")
self.synopLWrap = QHBoxLayout() self.synopLWrap = QHBoxLayout()
self.synopValue.setWordWrap(True) self.synopValue.setWordWrap(True)
@@ -104,15 +104,15 @@ class GuiOutlineDetails(QScrollArea):
self.synopLWrap.addWidget(self.synopValue, 1) self.synopLWrap.addWidget(self.synopValue, 1)
# Tags # Tags
self.povKeyLabel = QLabel("<b>%s</b>" % nwLabels.KEY_NAME[nwKeyWords.POV_KEY]) self.povKeyLabel = QLabel("<b>%s</b>" % trConst(nwLabels.KEY_NAME[nwKeyWords.POV_KEY]))
self.focKeyLabel = QLabel("<b>%s</b>" % nwLabels.KEY_NAME[nwKeyWords.FOCUS_KEY]) self.focKeyLabel = QLabel("<b>%s</b>" % trConst(nwLabels.KEY_NAME[nwKeyWords.FOCUS_KEY]))
self.chrKeyLabel = QLabel("<b>%s</b>" % nwLabels.KEY_NAME[nwKeyWords.CHAR_KEY]) self.chrKeyLabel = QLabel("<b>%s</b>" % trConst(nwLabels.KEY_NAME[nwKeyWords.CHAR_KEY]))
self.pltKeyLabel = QLabel("<b>%s</b>" % nwLabels.KEY_NAME[nwKeyWords.PLOT_KEY]) self.pltKeyLabel = QLabel("<b>%s</b>" % trConst(nwLabels.KEY_NAME[nwKeyWords.PLOT_KEY]))
self.timKeyLabel = QLabel("<b>%s</b>" % nwLabels.KEY_NAME[nwKeyWords.TIME_KEY]) self.timKeyLabel = QLabel("<b>%s</b>" % trConst(nwLabels.KEY_NAME[nwKeyWords.TIME_KEY]))
self.wldKeyLabel = QLabel("<b>%s</b>" % nwLabels.KEY_NAME[nwKeyWords.WORLD_KEY]) self.wldKeyLabel = QLabel("<b>%s</b>" % trConst(nwLabels.KEY_NAME[nwKeyWords.WORLD_KEY]))
self.objKeyLabel = QLabel("<b>%s</b>" % nwLabels.KEY_NAME[nwKeyWords.OBJECT_KEY]) self.objKeyLabel = QLabel("<b>%s</b>" % trConst(nwLabels.KEY_NAME[nwKeyWords.OBJECT_KEY]))
self.entKeyLabel = QLabel("<b>%s</b>" % nwLabels.KEY_NAME[nwKeyWords.ENTITY_KEY]) self.entKeyLabel = QLabel("<b>%s</b>" % trConst(nwLabels.KEY_NAME[nwKeyWords.ENTITY_KEY]))
self.cstKeyLabel = QLabel("<b>%s</b>" % nwLabels.KEY_NAME[nwKeyWords.CUSTOM_KEY]) self.cstKeyLabel = QLabel("<b>%s</b>" % trConst(nwLabels.KEY_NAME[nwKeyWords.CUSTOM_KEY]))
self.povKeyLWrap = QHBoxLayout() self.povKeyLWrap = QHBoxLayout()
self.focKeyLWrap = QHBoxLayout() self.focKeyLWrap = QHBoxLayout()
@@ -165,7 +165,7 @@ class GuiOutlineDetails(QScrollArea):
self.cstKeyLWrap.addWidget(self.cstKeyValue, 1) self.cstKeyLWrap.addWidget(self.cstKeyValue, 1)
# Selected Item Details # Selected Item Details
self.mainGroup = QGroupBox("Title Details", self) self.mainGroup = QGroupBox(self.tr("Title Details"), self)
self.mainForm = QGridLayout() self.mainForm = QGridLayout()
self.mainGroup.setLayout(self.mainForm) self.mainGroup.setLayout(self.mainForm)
@@ -190,7 +190,7 @@ class GuiOutlineDetails(QScrollArea):
self.mainForm.setVerticalSpacing(vSpace) self.mainForm.setVerticalSpacing(vSpace)
# Selected Item Tags # Selected Item Tags
self.tagsGroup = QGroupBox("Reference Tags", self) self.tagsGroup = QGroupBox(self.tr("Reference Tags"), self)
self.tagsForm = QGridLayout() self.tagsForm = QGridLayout()
self.tagsGroup.setLayout(self.tagsForm) self.tagsGroup.setLayout(self.tagsForm)
@@ -256,7 +256,7 @@ class GuiOutlineDetails(QScrollArea):
def clearDetails(self): def clearDetails(self):
"""Clear all the data labels. """Clear all the data labels.
""" """
self.titleLabel.setText("<b>Title</b>") self.titleLabel.setText("<b>%s</b>" % self.tr("Title"))
self.titleValue.setText("") self.titleValue.setText("")
self.fileValue.setText("") self.fileValue.setText("")
self.itemValue.setText("") self.itemValue.setText("")
@@ -286,9 +286,9 @@ class GuiOutlineDetails(QScrollArea):
return False return False
if novIdx["level"] in self.LVL_MAP: if novIdx["level"] in self.LVL_MAP:
self.titleLabel.setText("<b>%s</b>" % self.LVL_MAP[novIdx["level"]]) self.titleLabel.setText("<b>%s</b>" % self.tr(self.LVL_MAP[novIdx["level"]]))
else: else:
self.titleLabel.setText("<b>Title</b>") self.titleLabel.setText("<b>%s</b>" % self.tr("Title"))
self.titleValue.setText(novIdx["title"]) self.titleValue.setText(novIdx["title"])
self.fileValue.setText(nwItem.itemName) self.fileValue.setText(nwItem.itemName)
+151 -139
View File
@@ -28,7 +28,7 @@ import nw
import logging import logging
import os import os
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt, QLocale
from PyQt5.QtGui import QFont from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QDialog, QWidget, QComboBox, QSpinBox, QPushButton, QDialogButtonBox, QDialog, QWidget, QComboBox, QSpinBox, QPushButton, QDialogButtonBox,
@@ -53,7 +53,7 @@ class GuiPreferences(PagedDialog):
self.theParent = theParent self.theParent = theParent
self.theProject = theProject self.theProject = theProject
self.setWindowTitle("Preferences") self.setWindowTitle(self.tr("Preferences"))
self.tabGeneral = GuiPreferencesGeneral(self.theParent) self.tabGeneral = GuiPreferencesGeneral(self.theParent)
self.tabProjects = GuiPreferencesProjects(self.theParent) self.tabProjects = GuiPreferencesProjects(self.theParent)
@@ -62,14 +62,16 @@ class GuiPreferences(PagedDialog):
self.tabSyntax = GuiPreferencesSyntax(self.theParent) self.tabSyntax = GuiPreferencesSyntax(self.theParent)
self.tabAuto = GuiPreferencesAutomation(self.theParent) self.tabAuto = GuiPreferencesAutomation(self.theParent)
self.addTab(self.tabGeneral, "General") self.addTab(self.tabGeneral, self.tr("General"))
self.addTab(self.tabProjects, "Projects") self.addTab(self.tabProjects, self.tr("Projects"))
self.addTab(self.tabDocs, "Documents") self.addTab(self.tabDocs, self.tr("Documents"))
self.addTab(self.tabEditor, "Editor") self.addTab(self.tabEditor, self.tr("Editor"))
self.addTab(self.tabSyntax, "Highlighting") self.addTab(self.tabSyntax, self.tr("Highlighting"))
self.addTab(self.tabAuto, "Automation") self.addTab(self.tabAuto, self.tr("Automation"))
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) 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.accepted.connect(self._doSave)
self.buttonBox.rejected.connect(self._doClose) self.buttonBox.rejected.connect(self._doClose)
self.addControls(self.buttonBox) self.addControls(self.buttonBox)
@@ -102,7 +104,7 @@ class GuiPreferences(PagedDialog):
if needsRestart: if needsRestart:
self.theParent.makeAlert( 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 nwAlert.INFO
) )
@@ -134,7 +136,7 @@ class GuiPreferencesGeneral(QWidget):
# Look and Feel # Look and Feel
# ============= # =============
self.mainForm.addGroupLabel("Look and Feel") self.mainForm.addGroupLabel(self.tr("Look and Feel"))
## Select Theme ## Select Theme
self.guiTheme = QComboBox() self.guiTheme = QComboBox()
@@ -147,9 +149,9 @@ class GuiPreferencesGeneral(QWidget):
self.guiTheme.setCurrentIndex(themeIdx) self.guiTheme.setCurrentIndex(themeIdx)
self.mainForm.addRow( self.mainForm.addRow(
"Main GUI theme", self.tr("Main GUI theme"),
self.guiTheme, self.guiTheme,
"Changing this requires restarting novelWriter." self.tr("Changing this requires restarting novelWriter.")
) )
## Select Icon Theme ## Select Icon Theme
@@ -163,18 +165,18 @@ class GuiPreferencesGeneral(QWidget):
self.guiIcons.setCurrentIndex(iconIdx) self.guiIcons.setCurrentIndex(iconIdx)
self.mainForm.addRow( self.mainForm.addRow(
"Main icon theme", self.tr("Main icon theme"),
self.guiIcons, self.guiIcons,
"Changing this requires restarting novelWriter." self.tr("Changing this requires restarting novelWriter.")
) )
## Dark Icons ## Dark Icons
self.guiDark = QSwitch() self.guiDark = QSwitch()
self.guiDark.setChecked(self.mainConf.guiDark) self.guiDark.setChecked(self.mainConf.guiDark)
self.mainForm.addRow( self.mainForm.addRow(
"Prefer icons for dark backgrounds", self.tr("Prefer icons for dark backgrounds"),
self.guiDark, self.guiDark,
"May improve the look of icons on dark themes." self.tr("May improve the look of icons on dark themes.")
) )
## Font Family ## Font Family
@@ -186,9 +188,9 @@ class GuiPreferencesGeneral(QWidget):
self.fontButton.setMaximumWidth(int(2.5*self.theTheme.getTextWidth("..."))) self.fontButton.setMaximumWidth(int(2.5*self.theTheme.getTextWidth("...")))
self.fontButton.clicked.connect(self._selectFont) self.fontButton.clicked.connect(self._selectFont)
self.mainForm.addRow( self.mainForm.addRow(
"Font family", self.tr("Font family"),
self.guiFont, self.guiFont,
"Changing this requires restarting novelWriter.", self.tr("Changing this requires restarting novelWriter."),
theButton = self.fontButton theButton = self.fontButton
) )
@@ -199,38 +201,38 @@ class GuiPreferencesGeneral(QWidget):
self.guiFontSize.setSingleStep(1) self.guiFontSize.setSingleStep(1)
self.guiFontSize.setValue(self.mainConf.guiFontSize) self.guiFontSize.setValue(self.mainConf.guiFontSize)
self.mainForm.addRow( self.mainForm.addRow(
"Font size", self.tr("Font size"),
self.guiFontSize, self.guiFontSize,
"Changing this requires restarting novelWriter.", self.tr("Changing this requires restarting novelWriter."),
theUnit = "pt" theUnit = self.tr("pt")
) )
# GUI Settings # GUI Settings
# ============ # ============
self.mainForm.addGroupLabel("GUI Settings") self.mainForm.addGroupLabel(self.tr("GUI Settings"))
self.showFullPath = QSwitch() self.showFullPath = QSwitch()
self.showFullPath.setChecked(self.mainConf.showFullPath) self.showFullPath.setChecked(self.mainConf.showFullPath)
self.mainForm.addRow( self.mainForm.addRow(
"Show full path in document header", self.tr("Show full path in document header"),
self.showFullPath, 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 = QSwitch()
self.hideVScroll.setChecked(self.mainConf.hideVScroll) self.hideVScroll.setChecked(self.mainConf.hideVScroll)
self.mainForm.addRow( self.mainForm.addRow(
"Hide vertical scroll bars in main windows", self.tr("Hide vertical scroll bars in main windows"),
self.hideVScroll, 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 = QSwitch()
self.hideHScroll.setChecked(self.mainConf.hideHScroll) self.hideHScroll.setChecked(self.mainConf.hideHScroll)
self.mainForm.addRow( self.mainForm.addRow(
"Hide horizontal scroll bars in main windows", self.tr("Hide horizontal scroll bars in main windows"),
self.hideHScroll, self.hideHScroll,
"Scrolling available with mouse wheel and keys only." self.tr("Scrolling available with mouse wheel and keys only.")
) )
return return
@@ -299,7 +301,7 @@ class GuiPreferencesProjects(QWidget):
# Automatic Save # Automatic Save
# ============== # ==============
self.mainForm.addGroupLabel("Automatic Save") self.mainForm.addGroupLabel(self.tr("Automatic Save"))
## Document Save Timer ## Document Save Timer
self.autoSaveDoc = QSpinBox(self) self.autoSaveDoc = QSpinBox(self)
@@ -308,10 +310,10 @@ class GuiPreferencesProjects(QWidget):
self.autoSaveDoc.setSingleStep(1) self.autoSaveDoc.setSingleStep(1)
self.autoSaveDoc.setValue(self.mainConf.autoSaveDoc) self.autoSaveDoc.setValue(self.mainConf.autoSaveDoc)
self.mainForm.addRow( self.mainForm.addRow(
"Save document interval", self.tr("Save document interval"),
self.autoSaveDoc, self.autoSaveDoc,
"How often the open document is automatically saved.", self.tr("How often the open document is automatically saved."),
theUnit="seconds" theUnit=self.tr("seconds")
) )
## Project Save Timer ## Project Save Timer
@@ -321,24 +323,24 @@ class GuiPreferencesProjects(QWidget):
self.autoSaveProj.setSingleStep(1) self.autoSaveProj.setSingleStep(1)
self.autoSaveProj.setValue(self.mainConf.autoSaveProj) self.autoSaveProj.setValue(self.mainConf.autoSaveProj)
self.mainForm.addRow( self.mainForm.addRow(
"Save project interval", self.tr("Save project interval"),
self.autoSaveProj, self.autoSaveProj,
"How often the open project is automatically saved.", self.tr("How often the open project is automatically saved."),
theUnit="seconds" theUnit=self.tr("seconds")
) )
# Project Backup # Project Backup
# ============== # ==============
self.mainForm.addGroupLabel("Project Backup") self.mainForm.addGroupLabel(self.tr("Project Backup"))
## Backup Path ## Backup Path
self.backupPath = self.mainConf.backupPath self.backupPath = self.mainConf.backupPath
self.backupGetPath = QPushButton("Browse") self.backupGetPath = QPushButton(self.tr("Browse"))
self.backupGetPath.clicked.connect(self._backupFolder) self.backupGetPath.clicked.connect(self._backupFolder)
self.backupPathRow = self.mainForm.addRow( self.backupPathRow = self.mainForm.addRow(
"Backup storage location", self.tr("Backup storage location"),
self.backupGetPath, self.backupGetPath,
"Path: %s" % self.backupPath self.tr("Path: {0}").format(self.backupPath)
) )
## Run when closing ## Run when closing
@@ -346,9 +348,9 @@ class GuiPreferencesProjects(QWidget):
self.backupOnClose.setChecked(self.mainConf.backupOnClose) self.backupOnClose.setChecked(self.mainConf.backupOnClose)
self.backupOnClose.toggled.connect(self._toggledBackupOnClose) self.backupOnClose.toggled.connect(self._toggledBackupOnClose)
self.mainForm.addRow( self.mainForm.addRow(
"Run backup when the project is closed", self.tr("Run backup when the project is closed"),
self.backupOnClose, 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 ## Ask before backup
@@ -357,22 +359,22 @@ class GuiPreferencesProjects(QWidget):
self.askBeforeBackup.setChecked(self.mainConf.askBeforeBackup) self.askBeforeBackup.setChecked(self.mainConf.askBeforeBackup)
self.askBeforeBackup.setEnabled(self.mainConf.backupOnClose) self.askBeforeBackup.setEnabled(self.mainConf.backupOnClose)
self.mainForm.addRow( self.mainForm.addRow(
"Ask before running backup", self.tr("Ask before running backup"),
self.askBeforeBackup, self.askBeforeBackup,
"If off, backups will run in the background." self.tr("If off, backups will run in the background.")
) )
# Session Timer # Session Timer
# ============= # =============
self.mainForm.addGroupLabel("Session Timer") self.mainForm.addGroupLabel(self.tr("Session Timer"))
## Pause when idle ## Pause when idle
self.stopWhenIdle = QSwitch() self.stopWhenIdle = QSwitch()
self.stopWhenIdle.setChecked(self.mainConf.stopWhenIdle) self.stopWhenIdle.setChecked(self.mainConf.stopWhenIdle)
self.mainForm.addRow( self.mainForm.addRow(
"Pause the session timer when not writing", self.tr("Pause the session timer when not writing"),
self.stopWhenIdle, 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 ## Inactive time for idle
@@ -383,10 +385,10 @@ class GuiPreferencesProjects(QWidget):
self.userIdleTime.setDecimals(1) self.userIdleTime.setDecimals(1)
self.userIdleTime.setValue(self.mainConf.userIdleTime/60.0) self.userIdleTime.setValue(self.mainConf.userIdleTime/60.0)
self.mainForm.addRow( self.mainForm.addRow(
"Editor inactive time before pausing timer", self.tr("Editor inactive time before pausing timer"),
self.userIdleTime, self.userIdleTime,
"User activity includes typing and changing the content.", self.tr("User activity includes typing and changing the content."),
theUnit="minutes" theUnit=self.tr("minutes")
) )
return return
@@ -426,11 +428,13 @@ class GuiPreferencesProjects(QWidget):
dlgOpt |= QFileDialog.ShowDirsOnly dlgOpt |= QFileDialog.ShowDirsOnly
dlgOpt |= QFileDialog.DontUseNativeDialog dlgOpt |= QFileDialog.DontUseNativeDialog
newDir = QFileDialog.getExistingDirectory( newDir = QFileDialog.getExistingDirectory(
self, "Backup Directory", currDir, options=dlgOpt self, self.tr("Backup Directory"), currDir, options=dlgOpt
) )
if newDir: if newDir:
self.backupPath = 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 True
return False return False
@@ -460,7 +464,7 @@ class GuiPreferencesDocuments(QWidget):
# Text Style # Text Style
# ========== # ==========
self.mainForm.addGroupLabel("Text Style") self.mainForm.addGroupLabel(self.tr("Text Style"))
## Font Family ## Font Family
self.textFont = QLineEdit() self.textFont = QLineEdit()
@@ -471,9 +475,9 @@ class GuiPreferencesDocuments(QWidget):
self.fontButton.setMaximumWidth(int(2.5*self.theTheme.getTextWidth("..."))) self.fontButton.setMaximumWidth(int(2.5*self.theTheme.getTextWidth("...")))
self.fontButton.clicked.connect(self._selectFont) self.fontButton.clicked.connect(self._selectFont)
self.mainForm.addRow( self.mainForm.addRow(
"Font family", self.tr("Font family"),
self.textFont, self.textFont,
"Font for the document editor and viewer.", self.tr("Font for the document editor and viewer."),
theButton = self.fontButton theButton = self.fontButton
) )
@@ -484,15 +488,15 @@ class GuiPreferencesDocuments(QWidget):
self.textSize.setSingleStep(1) self.textSize.setSingleStep(1)
self.textSize.setValue(self.mainConf.textSize) self.textSize.setValue(self.mainConf.textSize)
self.mainForm.addRow( self.mainForm.addRow(
"Font size", self.tr("Font size"),
self.textSize, self.textSize,
"Font size for the document editor and viewer.", self.tr("Font size for the document editor and viewer."),
theUnit = "pt" theUnit = self.tr("pt")
) )
# Text Flow # Text Flow
# ========= # =========
self.mainForm.addGroupLabel("Text Flow") self.mainForm.addGroupLabel(self.tr("Text Flow"))
## Max Text Width in Normal Mode ## Max Text Width in Normal Mode
self.textWidth = QSpinBox(self) self.textWidth = QSpinBox(self)
@@ -501,10 +505,10 @@ class GuiPreferencesDocuments(QWidget):
self.textWidth.setSingleStep(10) self.textWidth.setSingleStep(10)
self.textWidth.setValue(self.mainConf.textWidth) self.textWidth.setValue(self.mainConf.textWidth)
self.mainForm.addRow( self.mainForm.addRow(
"Maximum text width in \"Normal Mode\"", self.tr("Maximum text width in \"Normal Mode\""),
self.textWidth, self.textWidth,
"Horizontal margins are scaled automatically.", self.tr("Horizontal margins are scaled automatically."),
theUnit="px" theUnit=self.tr("px")
) )
## Max Text Width in Focus Mode ## Max Text Width in Focus Mode
@@ -514,37 +518,37 @@ class GuiPreferencesDocuments(QWidget):
self.focusWidth.setSingleStep(10) self.focusWidth.setSingleStep(10)
self.focusWidth.setValue(self.mainConf.focusWidth) self.focusWidth.setValue(self.mainConf.focusWidth)
self.mainForm.addRow( self.mainForm.addRow(
"Maximum text width in \"Focus Mode\"", self.tr("Maximum text width in \"Focus Mode\""),
self.focusWidth, self.focusWidth,
"Horizontal margins are scaled automatically.", self.tr("Horizontal margins are scaled automatically."),
theUnit="px" theUnit=self.tr("px")
) )
## Document Fixed Width ## Document Fixed Width
self.textFixedW = QSwitch() self.textFixedW = QSwitch()
self.textFixedW.setChecked(not self.mainConf.textFixedW) self.textFixedW.setChecked(not self.mainConf.textFixedW)
self.mainForm.addRow( self.mainForm.addRow(
"Disable maximum text width in \"Normal Mode\"", self.tr("Disable maximum text width in \"Normal Mode\""),
self.textFixedW, self.textFixedW,
"Text width is defined by the margins only." self.tr("Text width is defined by the margins only.")
) )
## Focus Mode Footer ## Focus Mode Footer
self.hideFocusFooter = QSwitch() self.hideFocusFooter = QSwitch()
self.hideFocusFooter.setChecked(self.mainConf.hideFocusFooter) self.hideFocusFooter.setChecked(self.mainConf.hideFocusFooter)
self.mainForm.addRow( self.mainForm.addRow(
"Hide document footer in \"Focus Mode\"", self.tr("Hide document footer in \"Focus Mode\""),
self.hideFocusFooter, 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 ## Justify Text
self.doJustify = QSwitch() self.doJustify = QSwitch()
self.doJustify.setChecked(self.mainConf.doJustify) self.doJustify.setChecked(self.mainConf.doJustify)
self.mainForm.addRow( self.mainForm.addRow(
"Justify the text margins in editor and viewer", self.tr("Justify the text margins in editor and viewer"),
self.doJustify, 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 ## Document Margins
@@ -554,10 +558,10 @@ class GuiPreferencesDocuments(QWidget):
self.textMargin.setSingleStep(1) self.textMargin.setSingleStep(1)
self.textMargin.setValue(self.mainConf.textMargin) self.textMargin.setValue(self.mainConf.textMargin)
self.mainForm.addRow( self.mainForm.addRow(
"Text margin", self.tr("Text margin"),
self.textMargin, self.textMargin,
"If maximum width is set, this becomes the minimum margin.", self.tr("If maximum width is set, this becomes the minimum margin."),
theUnit="px" theUnit=self.tr("px")
) )
## Tab Width ## Tab Width
@@ -567,10 +571,10 @@ class GuiPreferencesDocuments(QWidget):
self.tabWidth.setSingleStep(1) self.tabWidth.setSingleStep(1)
self.tabWidth.setValue(self.mainConf.tabWidth) self.tabWidth.setValue(self.mainConf.tabWidth)
self.mainForm.addRow( self.mainForm.addRow(
"Tab width", self.tr("Tab width"),
self.tabWidth, self.tabWidth,
"The width of a tab key press in the editor and viewer.", self.tr("The width of a tab key press in the editor and viewer."),
theUnit="px" theUnit=self.tr("px")
) )
return return
@@ -628,14 +632,19 @@ class GuiPreferencesEditor(QWidget):
self.mainForm.setHelpTextStyle(self.theTheme.helpText) self.mainForm.setHelpTextStyle(self.theTheme.helpText)
self.setLayout(self.mainForm) self.setLayout(self.mainForm)
mW = self.mainConf.pxInt(250)
# Spell Checking # Spell Checking
# ============== # ==============
self.mainForm.addGroupLabel("Spell Checking") self.mainForm.addGroupLabel(self.tr("Spell Checking"))
## Spell Check Provider and Language ## Spell Check Provider and Language
self.spellLangList = QComboBox(self) self.spellLangList = QComboBox(self)
self.spellLangList.setMaximumWidth(mW)
self.spellToolList = QComboBox(self) 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) self.spellToolList.addItem("Spell Enchant (pyenchant)", nwConst.SP_ENCHANT)
theModel = self.spellToolList.model() theModel = self.spellToolList.model()
@@ -649,14 +658,14 @@ class GuiPreferencesEditor(QWidget):
self._doUpdateSpellTool(0) self._doUpdateSpellTool(0)
self.mainForm.addRow( self.mainForm.addRow(
"Spell check provider", self.tr("Spell check provider"),
self.spellToolList, 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( self.mainForm.addRow(
"Spell check language", self.tr("Spell check language"),
self.spellLangList, self.spellLangList,
"Available languages are determined by your system." self.tr("Available languages are determined by your system.")
) )
## Big Document Size Limit ## Big Document Size Limit
@@ -666,15 +675,15 @@ class GuiPreferencesEditor(QWidget):
self.bigDocLimit.setSingleStep(10) self.bigDocLimit.setSingleStep(10)
self.bigDocLimit.setValue(self.mainConf.bigDocLimit) self.bigDocLimit.setValue(self.mainConf.bigDocLimit)
self.mainForm.addRow( self.mainForm.addRow(
"Big document limit", self.tr("Big document limit"),
self.bigDocLimit, self.bigDocLimit,
"Full spell checking is disabled above this limit.", self.tr("Full spell checking is disabled above this limit."),
theUnit="kB" theUnit=self.tr("kB")
) )
# Word Count # Word Count
# ========== # ==========
self.mainForm.addGroupLabel("Word Count") self.mainForm.addGroupLabel(self.tr("Word Count"))
## Word Count Timer ## Word Count Timer
self.wordCountTimer = QDoubleSpinBox(self) self.wordCountTimer = QDoubleSpinBox(self)
@@ -684,54 +693,54 @@ class GuiPreferencesEditor(QWidget):
self.wordCountTimer.setSingleStep(0.1) self.wordCountTimer.setSingleStep(0.1)
self.wordCountTimer.setValue(self.mainConf.wordCountTimer) self.wordCountTimer.setValue(self.mainConf.wordCountTimer)
self.mainForm.addRow( self.mainForm.addRow(
"Word count interval", self.tr("Word count interval"),
self.wordCountTimer, self.wordCountTimer,
"How often the word count is updated.", self.tr("How often the word count is updated."),
theUnit="seconds" theUnit=self.tr("seconds")
) )
# Writing Guides # Writing Guides
# ============== # ==============
self.mainForm.addGroupLabel("Writing Guides") self.mainForm.addGroupLabel(self.tr("Writing Guides"))
## Show Tabs and Spaces ## Show Tabs and Spaces
self.showTabsNSpaces = QSwitch() self.showTabsNSpaces = QSwitch()
self.showTabsNSpaces.setChecked(self.mainConf.showTabsNSpaces) self.showTabsNSpaces.setChecked(self.mainConf.showTabsNSpaces)
self.mainForm.addRow( self.mainForm.addRow(
"Show tabs and spaces", self.tr("Show tabs and spaces"),
self.showTabsNSpaces, 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 ## Show Line Endings
self.showLineEndings = QSwitch() self.showLineEndings = QSwitch()
self.showLineEndings.setChecked(self.mainConf.showLineEndings) self.showLineEndings.setChecked(self.mainConf.showLineEndings)
self.mainForm.addRow( self.mainForm.addRow(
"Show line endings", self.tr("Show line endings"),
self.showLineEndings, 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 # Scroll Behaviour
# ================ # ================
self.mainForm.addGroupLabel("Scroll Behaviour") self.mainForm.addGroupLabel(self.tr("Scroll Behaviour"))
## Scroll Past End ## Scroll Past End
self.scrollPastEnd = QSwitch() self.scrollPastEnd = QSwitch()
self.scrollPastEnd.setChecked(self.mainConf.scrollPastEnd) self.scrollPastEnd.setChecked(self.mainConf.scrollPastEnd)
self.mainForm.addRow( self.mainForm.addRow(
"Scroll past end of the document", self.tr("Scroll past end of the document"),
self.scrollPastEnd, self.scrollPastEnd,
"Also improves trypewriter scrolling for short documents." self.tr("Also improves trypewriter scrolling for short documents.")
) )
## Typewriter Scrolling ## Typewriter Scrolling
self.autoScroll = QSwitch() self.autoScroll = QSwitch()
self.autoScroll.setChecked(self.mainConf.autoScroll) self.autoScroll.setChecked(self.mainConf.autoScroll)
self.mainForm.addRow( self.mainForm.addRow(
"Typewriter style scrolling when you type", self.tr("Typewriter style scrolling when you type"),
self.autoScroll, 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 ## Typewriter Position
@@ -741,9 +750,9 @@ class GuiPreferencesEditor(QWidget):
self.autoScrollPos.setSingleStep(1) self.autoScrollPos.setSingleStep(1)
self.autoScrollPos.setValue(int(self.mainConf.autoScrollPos)) self.autoScrollPos.setValue(int(self.mainConf.autoScrollPos))
self.mainForm.addRow( self.mainForm.addRow(
"Minimum position for Typewriter scrolling", self.tr("Minimum position for Typewriter scrolling"),
self.autoScrollPos, self.autoScrollPos,
"Percentage of the editor height from the top.", self.tr("Percentage of the editor height from the top."),
theUnit = "%" theUnit = "%"
) )
@@ -796,8 +805,11 @@ class GuiPreferencesEditor(QWidget):
theDict = NWSpellSimple() theDict = NWSpellSimple()
self.spellLangList.clear() self.spellLangList.clear()
for spTag, spName in theDict.listDictionaries(): for spTag, spProv in theDict.listDictionaries():
self.spellLangList.addItem(spName, spTag) 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) spellIdx = self.spellLangList.findData(self.mainConf.spellLanguage)
if spellIdx != -1: if spellIdx != -1:
@@ -823,7 +835,7 @@ class GuiPreferencesSyntax(QWidget):
# Highlighting Theme # Highlighting Theme
# ================== # ==================
self.mainForm.addGroupLabel("Highlighting Theme") self.mainForm.addGroupLabel(self.tr("Highlighting Theme"))
self.guiSyntax = QComboBox() self.guiSyntax = QComboBox()
self.guiSyntax.setMinimumWidth(self.mainConf.pxInt(200)) self.guiSyntax.setMinimumWidth(self.mainConf.pxInt(200))
@@ -835,50 +847,50 @@ class GuiPreferencesSyntax(QWidget):
self.guiSyntax.setCurrentIndex(syntaxIdx) self.guiSyntax.setCurrentIndex(syntaxIdx)
self.mainForm.addRow( self.mainForm.addRow(
"Highlighting theme", self.tr("Highlighting theme"),
self.guiSyntax, self.guiSyntax,
"Colour theme to apply to the editor and viewer." self.tr("Colour theme to apply to the editor and viewer.")
) )
# Quotes & Dialogue # Quotes & Dialogue
# ================= # =================
self.mainForm.addGroupLabel("Quotes & Dialogue") self.mainForm.addGroupLabel(self.tr("Quotes & Dialogue"))
self.highlightQuotes = QSwitch() self.highlightQuotes = QSwitch()
self.highlightQuotes.setChecked(self.mainConf.highlightQuotes) self.highlightQuotes.setChecked(self.mainConf.highlightQuotes)
self.highlightQuotes.toggled.connect(self._toggleHighlightQuotes) self.highlightQuotes.toggled.connect(self._toggleHighlightQuotes)
self.mainForm.addRow( self.mainForm.addRow(
"Highlight text wrapped in quotes", self.tr("Highlight text wrapped in quotes"),
self.highlightQuotes, self.highlightQuotes,
"Applies to single, double and straight quotes." self.tr("Applies to single, double and straight quotes.")
) )
self.allowOpenSQuote = QSwitch() self.allowOpenSQuote = QSwitch()
self.allowOpenSQuote.setChecked(self.mainConf.allowOpenSQuote) self.allowOpenSQuote.setChecked(self.mainConf.allowOpenSQuote)
self.mainForm.addRow( self.mainForm.addRow(
"Allow open-ended single quotes", self.tr("Allow open-ended single quotes"),
self.allowOpenSQuote, 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 = QSwitch()
self.allowOpenDQuote.setChecked(self.mainConf.allowOpenDQuote) self.allowOpenDQuote.setChecked(self.mainConf.allowOpenDQuote)
self.mainForm.addRow( self.mainForm.addRow(
"Allow open-ended double quotes", self.tr("Allow open-ended double quotes"),
self.allowOpenDQuote, self.allowOpenDQuote,
"Highlight double-quoted line with no closing quote." self.tr("Highlight double-quoted line with no closing quote.")
) )
# Text Emphasis # Text Emphasis
# ============= # =============
self.mainForm.addGroupLabel("Text Emphasis") self.mainForm.addGroupLabel(self.tr("Text Emphasis"))
self.highlightEmph = QSwitch() self.highlightEmph = QSwitch()
self.highlightEmph.setChecked(self.mainConf.highlightEmph) self.highlightEmph.setChecked(self.mainConf.highlightEmph)
self.mainForm.addRow( self.mainForm.addRow(
"Add highlight colour to emphasised text", self.tr("Add highlight colour to emphasised text"),
self.highlightEmph, self.highlightEmph,
"Applies to emphasis (italic) and strong (bold)." self.tr("Applies to emphasis (italic) and strong (bold).")
) )
return return
@@ -931,15 +943,15 @@ class GuiPreferencesAutomation(QWidget):
# Automatic Features # Automatic Features
# ================== # ==================
self.mainForm.addGroupLabel("Automatic Features") self.mainForm.addGroupLabel(self.tr("Automatic Features"))
## Auto-Select Word Under Cursor ## Auto-Select Word Under Cursor
self.autoSelect = QSwitch() self.autoSelect = QSwitch()
self.autoSelect.setChecked(self.mainConf.autoSelect) self.autoSelect.setChecked(self.mainConf.autoSelect)
self.mainForm.addRow( self.mainForm.addRow(
"Auto-select word under cursor", self.tr("Auto-select word under cursor"),
self.autoSelect, 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 ## Auto-Replace as You Type Main Switch
@@ -947,23 +959,23 @@ class GuiPreferencesAutomation(QWidget):
self.doReplace.setChecked(self.mainConf.doReplace) self.doReplace.setChecked(self.mainConf.doReplace)
self.doReplace.toggled.connect(self._toggleAutoReplaceMain) self.doReplace.toggled.connect(self._toggleAutoReplaceMain)
self.mainForm.addRow( self.mainForm.addRow(
"Auto-replace text as you type", self.tr("Auto-replace text as you type"),
self.doReplace, 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 # Replace as You Type
# =================== # ===================
self.mainForm.addGroupLabel("Replace as You Type") self.mainForm.addGroupLabel(self.tr("Replace as You Type"))
## Auto-Replace Single Quotes ## Auto-Replace Single Quotes
self.doReplaceSQuote = QSwitch() self.doReplaceSQuote = QSwitch()
self.doReplaceSQuote.setChecked(self.mainConf.doReplaceSQuote) self.doReplaceSQuote.setChecked(self.mainConf.doReplaceSQuote)
self.doReplaceSQuote.setEnabled(self.mainConf.doReplace) self.doReplaceSQuote.setEnabled(self.mainConf.doReplace)
self.mainForm.addRow( self.mainForm.addRow(
"Auto-replace single quotes", self.tr("Auto-replace single quotes"),
self.doReplaceSQuote, 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 ## Auto-Replace Double Quotes
@@ -971,9 +983,9 @@ class GuiPreferencesAutomation(QWidget):
self.doReplaceDQuote.setChecked(self.mainConf.doReplaceDQuote) self.doReplaceDQuote.setChecked(self.mainConf.doReplaceDQuote)
self.doReplaceDQuote.setEnabled(self.mainConf.doReplace) self.doReplaceDQuote.setEnabled(self.mainConf.doReplace)
self.mainForm.addRow( self.mainForm.addRow(
"Auto-replace double quotes", self.tr("Auto-replace double quotes"),
self.doReplaceDQuote, 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 ## Auto-Replace Hyphens
@@ -981,9 +993,9 @@ class GuiPreferencesAutomation(QWidget):
self.doReplaceDash.setChecked(self.mainConf.doReplaceDash) self.doReplaceDash.setChecked(self.mainConf.doReplaceDash)
self.doReplaceDash.setEnabled(self.mainConf.doReplace) self.doReplaceDash.setEnabled(self.mainConf.doReplace)
self.mainForm.addRow( self.mainForm.addRow(
"Auto-replace dashes", self.tr("Auto-replace dashes"),
self.doReplaceDash, 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 ## Auto-Replace Dots
@@ -991,14 +1003,14 @@ class GuiPreferencesAutomation(QWidget):
self.doReplaceDots.setChecked(self.mainConf.doReplaceDots) self.doReplaceDots.setChecked(self.mainConf.doReplaceDots)
self.doReplaceDots.setEnabled(self.mainConf.doReplace) self.doReplaceDots.setEnabled(self.mainConf.doReplace)
self.mainForm.addRow( self.mainForm.addRow(
"Auto-replace dots", self.tr("Auto-replace dots"),
self.doReplaceDots, self.doReplaceDots,
"Three consecutive dots become ellipsis." self.tr("Three consecutive dots become ellipsis.")
) )
# Quotation Style # Quotation Style
# =============== # ===============
self.mainForm.addGroupLabel("Quotation Style") self.mainForm.addGroupLabel(self.tr("Quotation Style"))
qWidth = self.mainConf.pxInt(40) qWidth = self.mainConf.pxInt(40)
bWidth = int(2.5*self.theTheme.getTextWidth("...")) bWidth = int(2.5*self.theTheme.getTextWidth("..."))
@@ -1015,9 +1027,9 @@ class GuiPreferencesAutomation(QWidget):
self.btnSingleStyleO.setMaximumWidth(bWidth) self.btnSingleStyleO.setMaximumWidth(bWidth)
self.btnSingleStyleO.clicked.connect(lambda: self._getQuote("SO")) self.btnSingleStyleO.clicked.connect(lambda: self._getQuote("SO"))
self.mainForm.addRow( self.mainForm.addRow(
"Single quote open style", self.tr("Single quote open style"),
self.quoteSym["SO"], 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 theButton=self.btnSingleStyleO
) )
@@ -1031,9 +1043,9 @@ class GuiPreferencesAutomation(QWidget):
self.btnSingleStyleC.setMaximumWidth(bWidth) self.btnSingleStyleC.setMaximumWidth(bWidth)
self.btnSingleStyleC.clicked.connect(lambda: self._getQuote("SC")) self.btnSingleStyleC.clicked.connect(lambda: self._getQuote("SC"))
self.mainForm.addRow( self.mainForm.addRow(
"Single quote close style", self.tr("Single quote close style"),
self.quoteSym["SC"], 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 theButton=self.btnSingleStyleC
) )
@@ -1048,9 +1060,9 @@ class GuiPreferencesAutomation(QWidget):
self.btnDoubleStyleO.setMaximumWidth(bWidth) self.btnDoubleStyleO.setMaximumWidth(bWidth)
self.btnDoubleStyleO.clicked.connect(lambda: self._getQuote("DO")) self.btnDoubleStyleO.clicked.connect(lambda: self._getQuote("DO"))
self.mainForm.addRow( self.mainForm.addRow(
"Double quote open style", self.tr("Double quote open style"),
self.quoteSym["DO"], 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 theButton=self.btnDoubleStyleO
) )
@@ -1064,9 +1076,9 @@ class GuiPreferencesAutomation(QWidget):
self.btnDoubleStyleC.setMaximumWidth(bWidth) self.btnDoubleStyleC.setMaximumWidth(bWidth)
self.btnDoubleStyleC.clicked.connect(lambda: self._getQuote("DC")) self.btnDoubleStyleC.clicked.connect(lambda: self._getQuote("DC"))
self.mainForm.addRow( self.mainForm.addRow(
"Double quote close style", self.tr("Double quote close style"),
self.quoteSym["DC"], 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 theButton=self.btnDoubleStyleC
) )
+30 -20
View File
@@ -54,7 +54,7 @@ class GuiProjectDetails(PagedDialog):
self.theProject = theProject self.theProject = theProject
self.optState = theProject.optState self.optState = theProject.optState
self.setWindowTitle("Project Details") self.setWindowTitle(self.tr("Project Details"))
wW = self.mainConf.pxInt(600) wW = self.mainConf.pxInt(600)
wH = self.mainConf.pxInt(400) wH = self.mainConf.pxInt(400)
@@ -69,10 +69,11 @@ class GuiProjectDetails(PagedDialog):
self.tabMain = GuiProjectDetailsMain(self.theParent, self.theProject) self.tabMain = GuiProjectDetailsMain(self.theParent, self.theProject)
self.tabContents = GuiProjectDetailsContents(self.theParent, self.theProject) self.tabContents = GuiProjectDetailsContents(self.theParent, self.theProject)
self.addTab(self.tabMain, "Overview") self.addTab(self.tabMain, self.tr("Overview"))
self.addTab(self.tabContents, "Contents") self.addTab(self.tabContents, self.tr("Contents"))
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Close) self.buttonBox = QDialogButtonBox(QDialogButtonBox.Close)
self.buttonBox.button(QDialogButtonBox.Close).setText(self.tr("Close"))
self.buttonBox.rejected.connect(self._doClose) self.buttonBox.rejected.connect(self._doClose)
self.addControls(self.buttonBox) self.addControls(self.buttonBox)
@@ -154,7 +155,9 @@ class GuiProjectDetailsMain(QWidget):
self.bookTitle.setAlignment(Qt.AlignHCenter) self.bookTitle.setAlignment(Qt.AlignHCenter)
self.bookTitle.setWordWrap(True) 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 = self.projName.font()
workFont.setPointSizeF(0.8*fPt) workFont.setPointSizeF(0.8*fPt)
workFont.setItalic(True) workFont.setItalic(True)
@@ -162,7 +165,7 @@ class GuiProjectDetailsMain(QWidget):
self.projName.setAlignment(Qt.AlignHCenter) self.projName.setAlignment(Qt.AlignHCenter)
self.projName.setWordWrap(True) 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 = self.bookAuthors.font()
authFont.setPointSizeF(1.2*fPt) authFont.setPointSizeF(1.2*fPt)
self.bookAuthors.setFont(authFont) self.bookAuthors.setFont(authFont)
@@ -175,20 +178,20 @@ class GuiProjectDetailsMain(QWidget):
hCounts = self.theIndex.getNovelTitleCounts() hCounts = self.theIndex.getNovelTitleCounts()
nwCount = self.theIndex.getNovelWordCount() nwCount = self.theIndex.getNovelWordCount()
self.wordCountLbl = QLabel("<b>Words:</b>") self.wordCountLbl = QLabel("<b>%s:</b>" % self.tr("Words"))
self.wordCountVal = QLabel(f"{nwCount:n}") self.wordCountVal = QLabel(f"{nwCount:n}")
self.chapCountLbl = QLabel("<b>Chapters:</b>") self.chapCountLbl = QLabel("<b>%s:</b>" % self.tr("Chapters"))
self.chapCountVal = QLabel(f"{hCounts[2]:n}") self.chapCountVal = QLabel(f"{hCounts[2]:n}")
self.sceneCountLbl = QLabel("<b>Scenes:</b>") self.sceneCountLbl = QLabel("<b>%s:</b>" % self.tr("Scenes"))
self.sceneCountVal = QLabel(f"{hCounts[3]:n}") self.sceneCountVal = QLabel(f"{hCounts[3]:n}")
self.revCountLbl = QLabel("<b>Revisions:</b>") self.revCountLbl = QLabel("<b>%s:</b>" % self.tr("Revisions"))
self.revCountVal = QLabel(f"{self.theProject.saveCount:n}") self.revCountVal = QLabel(f"{self.theProject.saveCount:n}")
edTime = self.theProject.getCurrentEditTime() edTime = self.theProject.getCurrentEditTime()
self.editTimeLbl = QLabel("<b>Editing Time:</b>") self.editTimeLbl = QLabel("<b>%s:</b>" % self.tr("Editing Time"))
self.editTimeVal = QLabel(f"{edTime//3600:02d}:{edTime%3600//60:02d}") self.editTimeVal = QLabel(f"{edTime//3600:02d}:{edTime%3600//60:02d}")
self.statsGrid = QGridLayout() self.statsGrid = QGridLayout()
@@ -208,7 +211,7 @@ class GuiProjectDetailsMain(QWidget):
# Meta # Meta
# ==== # ====
self.projPathLbl = QLabel("<b>Path:</b>") self.projPathLbl = QLabel("<b>%s:</b>" % self.tr("Path"))
self.projPathVal = QLineEdit() self.projPathVal = QLineEdit()
self.projPathVal.setText(self.theProject.projPath) self.projPathVal.setText(self.theProject.projPath)
self.projPathVal.setReadOnly(True) self.projPathVal.setReadOnly(True)
@@ -271,7 +274,14 @@ class GuiProjectDetailsContents(QWidget):
self.tocTree.setIndentation(0) self.tocTree.setIndentation(0)
self.tocTree.setColumnCount(6) self.tocTree.setColumnCount(6)
self.tocTree.setSelectionMode(QAbstractItemView.NoSelection) 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 = self.tocTree.headerItem()
treeHeadItem.setTextAlignment(self.C_WORDS, Qt.AlignRight) treeHeadItem.setTextAlignment(self.C_WORDS, Qt.AlignRight)
@@ -304,16 +314,16 @@ class GuiProjectDetailsContents(QWidget):
clearDouble = self.optState.getInt("GuiProjectDetails", "clearDouble", True) clearDouble = self.optState.getInt("GuiProjectDetails", "clearDouble", True)
wordsHelp = ( 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 = ( offsetHelp = (
"Start counting page numbers from this page." self.tr("Start counting page numbers from this page.")
) )
dblHelp = ( 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.wpLabel.setToolTip(wordsHelp)
self.wpValue = QSpinBox() self.wpValue = QSpinBox()
@@ -324,7 +334,7 @@ class GuiProjectDetailsContents(QWidget):
self.wpValue.setToolTip(wordsHelp) self.wpValue.setToolTip(wordsHelp)
self.wpValue.valueChanged.connect(self._populateTree) 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.poLabel.setToolTip(offsetHelp)
self.poValue = QSpinBox() self.poValue = QSpinBox()
@@ -335,7 +345,7 @@ class GuiProjectDetailsContents(QWidget):
self.poValue.setToolTip(offsetHelp) self.poValue.setToolTip(offsetHelp)
self.poValue.valueChanged.connect(self._populateTree) 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.dblLabel.setToolTip(dblHelp)
self.dblValue = QSwitch(self, 2*iPx, iPx) self.dblValue = QSwitch(self, 2*iPx, iPx)
@@ -358,7 +368,7 @@ class GuiProjectDetailsContents(QWidget):
# ======== # ========
self.outerBox = QVBoxLayout() self.outerBox = QVBoxLayout()
self.outerBox.addWidget(QLabel("<b>Table of Contents</b>")) self.outerBox.addWidget(QLabel("<b>%s</b>" % self.tr("Table of Contents")))
self.outerBox.addWidget(self.tocTree) self.outerBox.addWidget(self.tocTree)
self.outerBox.addLayout(self.optionsBox) self.outerBox.addLayout(self.optionsBox)
@@ -390,7 +400,7 @@ class GuiProjectDetailsContents(QWidget):
""" """
self._theToC = [] self._theToC = []
self._theToC = self.theIndex.getTableOfContents(2) self._theToC = self.theIndex.getTableOfContents(2)
self._theToC.append(("", 0, "END", 0)) self._theToC.append(("", 0, self.tr("END"), 0))
return return
## ##
+25 -13
View File
@@ -74,7 +74,7 @@ class GuiProjectLoad(QDialog):
self.outerBox.setSpacing(sPx) self.outerBox.setSpacing(sPx)
self.innerBox.setSpacing(sPx) self.innerBox.setSpacing(sPx)
self.setWindowTitle("Open Project") self.setWindowTitle(self.tr("Open Project"))
self.setMinimumWidth(self.mainConf.pxInt(650)) self.setMinimumWidth(self.mainConf.pxInt(650))
self.setMinimumHeight(self.mainConf.pxInt(400)) self.setMinimumHeight(self.mainConf.pxInt(400))
self.setModal(True) self.setModal(True)
@@ -90,7 +90,11 @@ class GuiProjectLoad(QDialog):
self.listBox.setSelectionMode(QAbstractItemView.SingleSelection) self.listBox.setSelectionMode(QAbstractItemView.SingleSelection)
self.listBox.setDragDropMode(QAbstractItemView.NoDragDrop) self.listBox.setDragDropMode(QAbstractItemView.NoDragDrop)
self.listBox.setColumnCount(3) 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.setRootIsDecorated(False)
self.listBox.itemSelectionChanged.connect(self._doSelectRecent) self.listBox.itemSelectionChanged.connect(self._doSelectRecent)
self.listBox.itemDoubleClicked.connect(self._doOpenRecent) self.listBox.itemDoubleClicked.connect(self._doOpenRecent)
@@ -100,8 +104,8 @@ class GuiProjectLoad(QDialog):
treeHead.setTextAlignment(self.C_COUNT, Qt.AlignRight) treeHead.setTextAlignment(self.C_COUNT, Qt.AlignRight)
treeHead.setTextAlignment(self.C_TIME, Qt.AlignRight) treeHead.setTextAlignment(self.C_TIME, Qt.AlignRight)
self.lblRecent = QLabel("<b>Recently Opened Projects</b>") self.lblRecent = QLabel("<b>%s</b>" % self.tr("Recently Opened Projects"))
self.lblPath = QLabel("<b>Path</b>") self.lblPath = QLabel("<b>%s</b>" % self.tr("Path"))
self.selPath = QLineEdit("") self.selPath = QLineEdit("")
self.selPath.setReadOnly(True) self.selPath.setReadOnly(True)
@@ -123,13 +127,15 @@ class GuiProjectLoad(QDialog):
self.innerBox.addLayout(self.projectForm) self.innerBox.addLayout(self.projectForm)
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Open | QDialogButtonBox.Cancel) 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.accepted.connect(self._doOpenRecent)
self.buttonBox.rejected.connect(self._doCancel) 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.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.delButton.clicked.connect(self._doDeleteRecent)
self.outerBox.addLayout(self.innerBox) self.outerBox.addLayout(self.innerBox)
@@ -180,12 +186,15 @@ class GuiProjectLoad(QDialog):
"""Browse for a folder path. """Browse for a folder path.
""" """
logger.verbose("GuiProjectLoad browse button clicked") 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.Options()
dlgOpt |= QFileDialog.DontUseNativeDialog dlgOpt |= QFileDialog.DontUseNativeDialog
projFile, _ = QFileDialog.getOpenFileName( projFile, _ = QFileDialog.getOpenFileName(
self, "Open novelWriter Project", "", self, self.tr("Open novelWriter Project"), "",
"novelWriter Project File (%s);;All Files (*)" % nwFiles.PROJ_FILE, filter=";;".join(extFilter), options=dlgOpt
options=dlgOpt
) )
if projFile: if projFile:
thePath = os.path.abspath(os.path.dirname(projFile)) thePath = os.path.abspath(os.path.dirname(projFile))
@@ -221,10 +230,13 @@ class GuiProjectLoad(QDialog):
selList = self.listBox.selectedItems() selList = self.listBox.selectedItems()
if selList: if selList:
projName = selList[0].text(self.C_NAME) projName = selList[0].text(self.C_NAME)
msgYes = self.theParent.askQuestion("Remove Entry", ( msgYes = self.theParent.askQuestion(
"Remove '%s' from the recent projects list? " self.tr("Remove Entry"),
"The project files will not be deleted." self.tr(
) % projName) "Remove '{0}' from the recent projects list? "
"The project files will not be deleted."
).format(projName)
)
if msgYes: if msgYes:
self.mainConf.removeFromRecentCache( self.mainConf.removeFromRecentCache(
selList[0].data(self.C_NAME, Qt.UserRole) selList[0].data(self.C_NAME, Qt.UserRole)
+44 -34
View File
@@ -54,7 +54,7 @@ class GuiProjectSettings(PagedDialog):
self.optState = theProject.optState self.optState = theProject.optState
self.theProject.countStatus() self.theProject.countStatus()
self.setWindowTitle("Project Settings") self.setWindowTitle(self.tr("Project Settings"))
wW = self.mainConf.pxInt(570) wW = self.mainConf.pxInt(570)
wH = self.mainConf.pxInt(375) wH = self.mainConf.pxInt(375)
@@ -71,12 +71,14 @@ class GuiProjectSettings(PagedDialog):
self.tabImport = GuiProjectEditStatus(self.theParent, self.theProject, False) self.tabImport = GuiProjectEditStatus(self.theParent, self.theProject, False)
self.tabReplace = GuiProjectEditReplace(self.theParent, self.theProject) self.tabReplace = GuiProjectEditReplace(self.theParent, self.theProject)
self.addTab(self.tabMain, "Settings") self.addTab(self.tabMain, self.tr("Settings"))
self.addTab(self.tabStatus, "Status") self.addTab(self.tabStatus, self.tr("Status"))
self.addTab(self.tabImport, "Importance") self.addTab(self.tabImport, self.tr("Importance"))
self.addTab(self.tabReplace, "Auto-Replace") self.addTab(self.tabReplace, self.tr("Auto-Replace"))
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) 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.accepted.connect(self._doSave)
self.buttonBox.rejected.connect(self._doClose) self.buttonBox.rejected.connect(self._doClose)
self.addControls(self.buttonBox) self.addControls(self.buttonBox)
@@ -166,7 +168,7 @@ class GuiProjectEditMain(QWidget):
self.mainForm.setHelpTextStyle(self.theParent.theTheme.helpText) self.mainForm.setHelpTextStyle(self.theParent.theTheme.helpText)
self.setLayout(self.mainForm) self.setLayout(self.mainForm)
self.mainForm.addGroupLabel("Project Settings") self.mainForm.addGroupLabel(self.tr("Project Settings"))
xW = self.mainConf.pxInt(250) xW = self.mainConf.pxInt(250)
xH = self.mainConf.pxInt(100) xH = self.mainConf.pxInt(100)
@@ -176,9 +178,9 @@ class GuiProjectEditMain(QWidget):
self.editName.setFixedWidth(xW) self.editName.setFixedWidth(xW)
self.editName.setText(self.theProject.projName) self.editName.setText(self.theProject.projName)
self.mainForm.addRow( self.mainForm.addRow(
"Working title", self.tr("Working title"),
self.editName, self.editName,
"Should be set only once." self.tr("Should be set only once.")
) )
self.editTitle = QLineEdit() self.editTitle = QLineEdit()
@@ -186,9 +188,9 @@ class GuiProjectEditMain(QWidget):
self.editTitle.setFixedWidth(xW) self.editTitle.setFixedWidth(xW)
self.editTitle.setText(self.theProject.bookTitle) self.editTitle.setText(self.theProject.bookTitle)
self.mainForm.addRow( self.mainForm.addRow(
"Novel title", self.tr("Novel title"),
self.editTitle, self.editTitle,
"Change whenever you want!" self.tr("Change whenever you want!")
) )
self.editAuthors = QPlainTextEdit() self.editAuthors = QPlainTextEdit()
@@ -199,22 +201,22 @@ class GuiProjectEditMain(QWidget):
self.editAuthors.setFixedHeight(xH) self.editAuthors.setFixedHeight(xH)
self.editAuthors.setFixedWidth(xW) self.editAuthors.setFixedWidth(xW)
self.mainForm.addRow( self.mainForm.addRow(
"Author(s)", self.tr("Author(s)"),
self.editAuthors, self.editAuthors,
"One name per line." self.tr("One name per line.")
) )
self.spellLang = QComboBox(self) self.spellLang = QComboBox(self)
theDict = self.theParent.docEditor.theDict theDict = self.theParent.docEditor.theDict
self.spellLang.addItem("Default", "None") self.spellLang.addItem(self.tr("Default"), "None")
if theDict is not None: if theDict is not None:
for spTag, spName in theDict.listDictionaries(): for spTag, spName in theDict.listDictionaries():
self.spellLang.addItem(spName, spTag) self.spellLang.addItem(spName, spTag)
self.mainForm.addRow( self.mainForm.addRow(
"Spell check language", self.tr("Spell check language"),
self.spellLang, self.spellLang,
"Overrides main preferences." self.tr("Overrides main preferences.")
) )
spellIdx = 0 spellIdx = 0
@@ -226,9 +228,9 @@ class GuiProjectEditMain(QWidget):
self.doBackup = QSwitch(self) self.doBackup = QSwitch(self)
self.doBackup.setChecked(not self.theProject.doBackup) self.doBackup.setChecked(not self.theProject.doBackup)
self.mainForm.addRow( self.mainForm.addRow(
"No backup on close", self.tr("No backup on close"),
self.doBackup, self.doBackup,
"Overrides main preferences." self.tr("Overrides main preferences.")
) )
return return
@@ -271,12 +273,12 @@ class GuiProjectEditStatus(QWidget):
self.editName = QLineEdit() self.editName = QLineEdit()
self.editName.setMaxLength(40) self.editName.setMaxLength(40)
self.editName.setEnabled(False) self.editName.setEnabled(False)
self.newButton = QPushButton("New") self.newButton = QPushButton(self.tr("New"))
self.delButton = QPushButton("Delete") self.delButton = QPushButton(self.tr("Delete"))
self.saveButton = QPushButton("Save") self.saveButton = QPushButton(self.tr("Save"))
self.colPixmap = QPixmap(self.iPx, self.iPx) self.colPixmap = QPixmap(self.iPx, self.iPx)
self.colPixmap.fill(QColor(120, 120, 120)) 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.colButton.setIconSize(self.colPixmap.rect().size())
self.newButton.clicked.connect(self._newItem) self.newButton.clicked.connect(self._newItem)
@@ -287,7 +289,7 @@ class GuiProjectEditStatus(QWidget):
self.mainForm.addWidget(self.newButton) self.mainForm.addWidget(self.newButton)
self.mainForm.addWidget(self.delButton) self.mainForm.addWidget(self.delButton)
self.mainForm.addStretch(1) self.mainForm.addStretch(1)
self.mainForm.addWidget(QLabel("<b>Name</b>")) self.mainForm.addWidget(QLabel("<b>%s</b>" % self.tr("Name")))
self.mainForm.addWidget(self.editName) self.mainForm.addWidget(self.editName)
self.mainForm.addWidget(self.colButton) self.mainForm.addWidget(self.colButton)
self.mainForm.addStretch(1) self.mainForm.addStretch(1)
@@ -297,9 +299,9 @@ class GuiProjectEditStatus(QWidget):
self.mainBox.addLayout(self.mainForm) self.mainBox.addLayout(self.mainForm)
if isStatus: if isStatus:
self.outerBox.addWidget(QLabel("<b>Novel File Status Levels</b>")) self.outerBox.addWidget(QLabel("<b>%s</b>" % self.tr("Novel File Status Levels")))
else: else:
self.outerBox.addWidget(QLabel("<b>Note File Importance Levels</b>")) self.outerBox.addWidget(QLabel("<b>%s</b>" % self.tr("Note File Importance Levels")))
self.outerBox.addLayout(self.mainBox) self.outerBox.addLayout(self.mainBox)
self.setLayout(self.outerBox) self.setLayout(self.outerBox)
@@ -325,7 +327,8 @@ class GuiProjectEditStatus(QWidget):
""" """
if self.selColour is not None: if self.selColour is not None:
newCol = QColorDialog.getColor( newCol = QColorDialog.getColor(
self.selColour, self, "Select Colour", QColorDialog.DontUseNativeDialog self.selColour, self, self.tr("Select Colour"),
QColorDialog.DontUseNativeDialog
) )
if newCol.isValid(): if newCol.isValid():
self.selColour = newCol self.selColour = newCol
@@ -338,7 +341,7 @@ class GuiProjectEditStatus(QWidget):
def _newItem(self): def _newItem(self):
"""Create a new status item. """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))) newItem.setBackground(QBrush(QColor(0, 255, 0, 80)))
self.colChanged = True self.colChanged = True
return return
@@ -355,7 +358,7 @@ class GuiProjectEditStatus(QWidget):
self.colChanged = True self.colChanged = True
else: else:
self.theParent.makeAlert( 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 return
@@ -372,7 +375,9 @@ class GuiProjectEditStatus(QWidget):
self.selColour.blue(), self.selColour.blue(),
self.colData[selIdx][4] 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()) selItem.setIcon(self.colButton.icon())
self.editName.setEnabled(False) self.editName.setEnabled(False)
self.colChanged = True self.colChanged = True
@@ -384,7 +389,7 @@ class GuiProjectEditStatus(QWidget):
newIcon = QPixmap(self.iPx, self.iPx) newIcon = QPixmap(self.iPx, self.iPx)
newIcon.fill(QColor(*iCol)) newIcon.fill(QColor(*iCol))
newItem = QListWidgetItem() newItem = QListWidgetItem()
newItem.setText("%s [%d]" % (iName, nUse)) newItem.setText(self.tr("{0} [{1}]").format(iName, nUse))
newItem.setIcon(QIcon(newIcon)) newItem.setIcon(QIcon(newIcon))
newItem.setData(Qt.UserRole, len(self.colData)) newItem.setData(Qt.UserRole, len(self.colData))
self.listBox.addItem(newItem) self.listBox.addItem(newItem)
@@ -450,7 +455,10 @@ class GuiProjectEditReplace(QWidget):
self.optState.getInt("GuiProjectSettings", "replaceColW", 100) self.optState.getInt("GuiProjectSettings", "replaceColW", 100)
) )
self.listBox = QTreeWidget() 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.itemSelectionChanged.connect(self._selectedItem)
self.listBox.setColumnWidth(0, wCol0) self.listBox.setColumnWidth(0, wCol0)
self.listBox.setIndentation(0) self.listBox.setIndentation(0)
@@ -467,9 +475,9 @@ class GuiProjectEditReplace(QWidget):
self.saveButton = QPushButton(self.theTheme.getIcon("done"), "") self.saveButton = QPushButton(self.theTheme.getIcon("done"), "")
self.addButton = QPushButton(self.theTheme.getIcon("add"), "") self.addButton = QPushButton(self.theTheme.getIcon("add"), "")
self.delButton = QPushButton(self.theTheme.getIcon("remove"), "") self.delButton = QPushButton(self.theTheme.getIcon("remove"), "")
self.saveButton.setToolTip("Save entry") self.saveButton.setToolTip(self.tr("Save entry"))
self.addButton.setToolTip("Add new entry") self.addButton.setToolTip(self.tr("Add new entry"))
self.delButton.setToolTip("Delete selected entry") self.delButton.setToolTip(self.tr("Delete selected entry"))
self.editKey.setEnabled(False) self.editKey.setEnabled(False)
self.editKey.setMaxLength(40) self.editKey.setMaxLength(40)
@@ -486,7 +494,9 @@ class GuiProjectEditReplace(QWidget):
self.bottomBox.addWidget(self.addButton) self.bottomBox.addWidget(self.addButton)
self.bottomBox.addWidget(self.delButton) self.bottomBox.addWidget(self.delButton)
self.outerBox.addWidget(QLabel("<b>Text Replace List for Preview and Export</b>")) self.outerBox.addWidget(
QLabel("<b>%s</b>" % self.tr("Text Replace List for Preview and Export"))
)
self.outerBox.addWidget(self.listBox) self.outerBox.addWidget(self.listBox)
self.outerBox.addLayout(self.bottomBox) self.outerBox.addLayout(self.bottomBox)
self.setLayout(self.outerBox) self.setLayout(self.outerBox)
+48 -37
View File
@@ -38,7 +38,8 @@ from PyQt5.QtWidgets import (
from nw.core import NWDoc from nw.core import NWDoc
from nw.constants import ( from nw.constants import (
nwLabels, nwItemType, nwItemClass, nwItemLayout, nwAlert, nwConst, nwLists trConst, nwLabels, nwItemType, nwItemClass, nwItemLayout, nwAlert,
nwConst, nwLists
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -85,14 +86,19 @@ class GuiProjectTree(QTreeWidget):
self.setExpandsOnDoubleClick(True) self.setExpandsOnDoubleClick(True)
self.setIndentation(iPx) self.setIndentation(iPx)
self.setColumnCount(4) 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 = self.headerItem()
treeHeadItem.setTextAlignment(self.C_COUNT, Qt.AlignRight) treeHeadItem.setTextAlignment(self.C_COUNT, Qt.AlignRight)
treeHeadItem.setToolTip(self.C_NAME, "Item label") treeHeadItem.setToolTip(self.C_NAME, self.tr("Item label"))
treeHeadItem.setToolTip(self.C_COUNT, "Word count") treeHeadItem.setToolTip(self.C_COUNT, self.tr("Word count"))
treeHeadItem.setToolTip(self.C_EXPORT, "Include in build") treeHeadItem.setToolTip(self.C_EXPORT, self.tr("Include in build"))
treeHeadItem.setToolTip(self.C_FLAGS, "Status, class, and layout flags") treeHeadItem.setToolTip(self.C_FLAGS, self.tr("Status, class, and layout flags"))
# Let the last column stretch, and set the minimum size to the # Let the last column stretch, and set the minimum size to the
# size of the icon as the default Qt font metrics approach fails # size of the icon as the default Qt font metrics approach fails
@@ -193,12 +199,12 @@ class GuiProjectTree(QTreeWidget):
if itemClass is None: if itemClass is None:
if itemType == nwItemType.FILE: if itemType == nwItemType.FILE:
self.makeAlert( 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 nwAlert.ERROR
) )
else: else:
self.makeAlert( 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 nwAlert.ERROR
) )
return False return False
@@ -209,7 +215,7 @@ class GuiProjectTree(QTreeWidget):
) )
if itemType == nwItemType.ROOT: 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: if tHandle is None:
logger.error("No root item added") logger.error("No root item added")
return False return False
@@ -223,7 +229,7 @@ class GuiProjectTree(QTreeWidget):
# If still nothing, give up # If still nothing, give up
if pHandle is None: if pHandle is None:
self.makeAlert( 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 return False
@@ -237,14 +243,14 @@ class GuiProjectTree(QTreeWidget):
# If we again have no home, give up # If we again have no home, give up
if pHandle is None: if pHandle is None:
self.makeAlert( 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 return False
if self.theProject.projTree.isTrashRoot(pHandle): if self.theProject.projTree.isTrashRoot(pHandle):
self.makeAlert( self.makeAlert(
"Cannot add new files or folders to the %s folder." % ( self.tr("Cannot add new files or folders to the {0} folder.").format(
nwLabels.CLASS_NAME[nwItemClass.TRASH] trConst(nwLabels.CLASS_NAME[nwItemClass.TRASH])
), nwAlert.ERROR ), nwAlert.ERROR
) )
return False return False
@@ -253,18 +259,18 @@ class GuiProjectTree(QTreeWidget):
# If we're still here, add the file or folder # If we're still here, add the file or folder
if itemType == nwItemType.FILE: 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: elif itemType == nwItemType.FOLDER:
if len(parTree) >= nwConst.MAX_DEPTH - 1: if len(parTree) >= nwConst.MAX_DEPTH - 1:
# Folders cannot be deeper than MAX_DEPTH - 1, leaving room # Folders cannot be deeper than MAX_DEPTH - 1, leaving room
# for one more level of files. # for one more level of files.
self.makeAlert(( self.makeAlert((
"Cannot add new folder to this item. " self.tr("Cannot add new folder to this item."),
"Maximum folder depth has been reached." self.tr("Maximum folder depth has been reached.")
), nwAlert.ERROR) ), nwAlert.ERROR)
return False return False
tHandle = self.theProject.newFolder("New Folder", itemClass, pHandle) tHandle = self.theProject.newFolder(self.tr("New Folder"), itemClass, pHandle)
else: else:
logger.error("Failed to add new item") logger.error("Failed to add new item")
@@ -428,7 +434,7 @@ class GuiProjectTree(QTreeWidget):
logger.debug("Emptying Trash folder") logger.debug("Emptying Trash folder")
if trashHandle is None: if trashHandle is None:
self.makeAlert( 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 return False
@@ -438,11 +444,12 @@ class GuiProjectTree(QTreeWidget):
nTrash = len(theTrash) nTrash = len(theTrash)
if nTrash == 0: 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 return False
msgYes = self.askQuestion( 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: if not msgYes:
return False return False
@@ -500,7 +507,8 @@ class GuiProjectTree(QTreeWidget):
doPermanent = False doPermanent = False
if not alreadyAsked: if not alreadyAsked:
msgYes = self.askQuestion( 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: if msgYes:
doPermanent = True doPermanent = True
@@ -529,7 +537,8 @@ class GuiProjectTree(QTreeWidget):
doTrash = False doTrash = False
if askForTrash: if askForTrash:
msgYes = self.askQuestion( 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: if msgYes:
doTrash = True doTrash = True
@@ -563,7 +572,7 @@ class GuiProjectTree(QTreeWidget):
self._deleteTreeItem(tHandle) self._deleteTreeItem(tHandle)
self._setTreeChanged(True) self._setTreeChanged(True)
else: else:
self.makeAlert(( self.makeAlert(self.tr(
"Cannot delete folder. It is not empty. " "Cannot delete folder. It is not empty. "
"Recursive deletion is not supported. " "Recursive deletion is not supported. "
"Please delete the content first." "Please delete the content first."
@@ -579,7 +588,7 @@ class GuiProjectTree(QTreeWidget):
self.theParent.mainMenu.setAvailableRoot() self.theParent.mainMenu.setAvailableRoot()
self._setTreeChanged(True) self._setTreeChanged(True)
else: else:
self.makeAlert(( self.makeAlert(self.tr(
"Cannot delete root folder. It is not empty. " "Cannot delete root folder. It is not empty. "
"Recursive deletion is not supported. " "Recursive deletion is not supported. "
"Please delete the content first." "Please delete the content first."
@@ -835,7 +844,7 @@ class GuiProjectTree(QTreeWidget):
snItem = self.theProject.projTree[sHandle] snItem = self.theProject.projTree[sHandle]
dnItem = self.theProject.projTree[dHandle] dnItem = self.theProject.projTree[dHandle]
if dnItem is None: 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 return
pItem = sItem.parent() pItem = sItem.parent()
@@ -866,7 +875,7 @@ class GuiProjectTree(QTreeWidget):
else: else:
theEvent.ignore() theEvent.ignore()
logger.debug("Drag'n'drop of item %s not accepted" % sHandle) 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 return
@@ -964,7 +973,9 @@ class GuiProjectTree(QTreeWidget):
self.addTopLevelItem(newItem) self.addTopLevelItem(newItem)
else: else:
self.makeAlert( 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] del self._treeMap[tHandle]
return None return None
@@ -1083,43 +1094,43 @@ class GuiProjectTreeMenu(QMenu):
self.theTree = theTree self.theTree = theTree
self.theItem = None 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.editItem.triggered.connect(self._doEditItem)
self.addAction(self.editItem) 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.openItem.triggered.connect(self._doOpenItem)
self.addAction(self.openItem) 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.viewItem.triggered.connect(self._doViewItem)
self.addAction(self.viewItem) 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.toggleExp.triggered.connect(self._doToggleExported)
self.addAction(self.toggleExp) 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.newFile.triggered.connect(self._doMakeFile)
self.addAction(self.newFile) 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.newFolder.triggered.connect(self._doMakeFolder)
self.addAction(self.newFolder) 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.deleteItem.triggered.connect(self._doDeleteItem)
self.addAction(self.deleteItem) 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.emptyTrash.triggered.connect(self._doEmptyTrash)
self.addAction(self.emptyTrash) 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.moveUp.triggered.connect(self._doMoveUp)
self.addAction(self.moveUp) 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.moveDown.triggered.connect(self._doMoveDown)
self.addAction(self.moveDown) self.addAction(self.moveDown)
+60 -44
View File
@@ -36,7 +36,7 @@ from PyQt5.QtWidgets import (
) )
from nw.common import makeFileNameSafe from nw.common import makeFileNameSafe
from nw.constants import nwLabels, nwItemClass from nw.constants import trConst, nwLabels, nwItemClass
from nw.gui.custom import QSwitch from nw.gui.custom import QSwitch
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -94,16 +94,18 @@ class ProjWizardIntroPage(QWizardPage):
self.theWizard = theWizard self.theWizard = theWizard
self.theTheme = theWizard.theTheme self.theTheme = theWizard.theTheme
self.setTitle("Create New Project") self.setTitle(self.tr("Create New Project"))
self.theText = QLabel( self.theText = QLabel(self.tr(
"Provide at least a working title. The working title should not " "Provide at least a working title. The working title should not "
"be change beyond this point as it is used by the application for " "be change beyond this point as it is used by the application for "
"generating file names for for instance backups. The other fields " "generating file names for for instance backups. The other fields "
"are optional and can be changed at any time in Project Settings." "are optional and can be changed at any time in Project Settings."
) ))
self.theText.setWordWrap(True) 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 = self.imgCredit.font()
lblFont.setPointSizeF(0.6*self.theTheme.fontPointSize) lblFont.setPointSizeF(0.6*self.theTheme.fontPointSize)
self.imgCredit.setFont(lblFont) self.imgCredit.setFont(lblFont)
@@ -117,22 +119,22 @@ class ProjWizardIntroPage(QWizardPage):
self.projName = QLineEdit() self.projName = QLineEdit()
self.projName.setMaxLength(200) self.projName.setMaxLength(200)
self.projName.setFixedWidth(xW) self.projName.setFixedWidth(xW)
self.projName.setPlaceholderText("Required") self.projName.setPlaceholderText(self.tr("Required"))
self.projTitle = QLineEdit() self.projTitle = QLineEdit()
self.projTitle.setMaxLength(200) self.projTitle.setMaxLength(200)
self.projTitle.setFixedWidth(xW) self.projTitle.setFixedWidth(xW)
self.projTitle.setPlaceholderText("Optional") self.projTitle.setPlaceholderText(self.tr("Optional"))
self.projAuthors = QPlainTextEdit() self.projAuthors = QPlainTextEdit()
self.projAuthors.setFixedHeight(xH) self.projAuthors.setFixedHeight(xH)
self.projAuthors.setFixedWidth(xW) 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 = QFormLayout()
self.mainForm.addRow("Working Title", self.projName) self.mainForm.addRow(self.tr("Working Title"), self.projName)
self.mainForm.addRow("Novel Title", self.projTitle) self.mainForm.addRow(self.tr("Novel Title"), self.projTitle)
self.mainForm.addRow("Author(s)", self.projAuthors) self.mainForm.addRow(self.tr("Author(s)"), self.projAuthors)
self.mainForm.setVerticalSpacing(fS) self.mainForm.setVerticalSpacing(fS)
self.registerField("projName*", self.projName) self.registerField("projName*", self.projName)
@@ -161,11 +163,11 @@ class ProjWizardFolderPage(QWizardPage):
self.theWizard = theWizard self.theWizard = theWizard
self.theTheme = theWizard.theTheme self.theTheme = theWizard.theTheme
self.setTitle("Select Project Folder") self.setTitle(self.tr("Select Project Folder"))
self.theText = QLabel( self.theText = QLabel(self.tr(
"Select a location to store the project. A new project folder " "Select a location to store the project. A new project folder "
"will be created in the selected location." "will be created in the selected location."
) ))
self.theText.setWordWrap(True) self.theText.setWordWrap(True)
xW = self.mainConf.pxInt(300) xW = self.mainConf.pxInt(300)
@@ -174,14 +176,14 @@ class ProjWizardFolderPage(QWizardPage):
self.projPath = QLineEdit("") self.projPath = QLineEdit("")
self.projPath.setFixedWidth(xW) self.projPath.setFixedWidth(xW)
self.projPath.setPlaceholderText("Required") self.projPath.setPlaceholderText(self.tr("Required"))
self.browseButton = QPushButton("...") self.browseButton = QPushButton("...")
self.browseButton.setMaximumWidth(int(2.5*self.theTheme.getTextWidth("..."))) self.browseButton.setMaximumWidth(int(2.5*self.theTheme.getTextWidth("...")))
self.browseButton.clicked.connect(self._doBrowse) self.browseButton.clicked.connect(self._doBrowse)
self.mainForm = QHBoxLayout() 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.projPath, 1)
self.mainForm.addWidget(self.browseButton, 0) self.mainForm.addWidget(self.browseButton, 0)
self.mainForm.setSpacing(fS) self.mainForm.setSpacing(fS)
@@ -213,7 +215,7 @@ class ProjWizardFolderPage(QWizardPage):
dlgOpt |= QFileDialog.ShowDirsOnly dlgOpt |= QFileDialog.ShowDirsOnly
dlgOpt |= QFileDialog.DontUseNativeDialog dlgOpt |= QFileDialog.DontUseNativeDialog
projDir = QFileDialog.getExistingDirectory( projDir = QFileDialog.getExistingDirectory(
self, "Select Project Folder", lastPath, options=dlgOpt self, self.tr("Select Project Folder"), lastPath, options=dlgOpt
) )
if projDir: if projDir:
projName = self.field("projName") projName = self.field("projName")
@@ -235,20 +237,20 @@ class ProjWizardPopulatePage(QWizardPage):
self.mainConf = nw.CONFIG self.mainConf = nw.CONFIG
self.theWizard = theWizard self.theWizard = theWizard
self.setTitle("Populate Project") self.setTitle(self.tr("Populate Project"))
self.theText = QLabel( self.theText = QLabel(self.tr(
"Choose how to pre-fill the project. Either with a minimal set of " "Choose how to pre-fill the project. Either with a minimal set of "
"starter items, an example project explaining and showing many of " "starter items, an example project explaining and showing many of "
"the features, or show further custom options on the next page." "the features, or show further custom options on the next page."
) ))
self.theText.setWordWrap(True) self.theText.setWordWrap(True)
vS = self.mainConf.pxInt(12) vS = self.mainConf.pxInt(12)
fS = self.mainConf.pxInt(4) fS = self.mainConf.pxInt(4)
self.popMinimal = QRadioButton("Fill the project with a minimal set of items") self.popMinimal = QRadioButton(self.tr("Fill the project with a minimal set of items"))
self.popSample = QRadioButton("Fill the project with example files") self.popSample = QRadioButton(self.tr("Fill the project with example files"))
self.popCustom = QRadioButton("Show detailed options for filling the project") self.popCustom = QRadioButton(self.tr("Show detailed options for filling the project"))
self.popMinimal.setChecked(True) self.popMinimal.setChecked(True)
self.popBox = QVBoxLayout() self.popBox = QVBoxLayout()
@@ -290,27 +292,39 @@ class ProjWizardCustomPage(QWizardPage):
self.mainConf = nw.CONFIG self.mainConf = nw.CONFIG
self.theWizard = theWizard self.theWizard = theWizard
self.setTitle("Custom Project Options") self.setTitle(self.tr("Custom Project Options"))
self.theText = QLabel( self.theText = QLabel(self.tr(
"Select which additional root folders to make, and how to populate " "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 Novel folder. If you don't want to add chapters or scenes, set "
"the values to 0. You can add scenes without chapters." "the values to 0. You can add scenes without chapters."
) ))
self.theText.setWordWrap(True) self.theText.setWordWrap(True)
vS = self.mainConf.pxInt(12) vS = self.mainConf.pxInt(12)
# Root Folders # Root Folders
self.rootGroup = QGroupBox("Additional Root Folders") self.rootGroup = QGroupBox(self.tr("Additional Root Folders"))
self.rootForm = QGridLayout() self.rootForm = QGridLayout()
self.rootGroup.setLayout(self.rootForm) self.rootGroup.setLayout(self.rootForm)
self.lblPlot = QLabel("%s folder" % nwLabels.CLASS_NAME[nwItemClass.PLOT]) self.lblPlot = QLabel(self.tr("{0} folder").format(
self.lblChar = QLabel("%s folder" % nwLabels.CLASS_NAME[nwItemClass.CHARACTER]) trConst(nwLabels.CLASS_NAME[nwItemClass.PLOT]))
self.lblWorld = QLabel("%s folder" % nwLabels.CLASS_NAME[nwItemClass.WORLD]) )
self.lblTime = QLabel("%s folder" % nwLabels.CLASS_NAME[nwItemClass.TIMELINE]) self.lblChar = QLabel(self.tr("{0} folder").format(
self.lblObject = QLabel("%s folder" % nwLabels.CLASS_NAME[nwItemClass.OBJECT]) trConst(nwLabels.CLASS_NAME[nwItemClass.CHARACTER]))
self.lblEntity = QLabel("%s folder" % nwLabels.CLASS_NAME[nwItemClass.ENTITY]) )
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.addPlot = QSwitch()
self.addChar = QSwitch() self.addChar = QSwitch()
@@ -338,7 +352,7 @@ class ProjWizardCustomPage(QWizardPage):
self.rootForm.setRowStretch(6, 1) self.rootForm.setRowStretch(6, 1)
# Novel Options # Novel Options
self.novelGroup = QGroupBox("Populate Novel Folder") self.novelGroup = QGroupBox(self.tr("Populate Novel Folder"))
self.novelForm = QGridLayout() self.novelForm = QGridLayout()
self.novelGroup.setLayout(self.novelForm) self.novelGroup.setLayout(self.novelForm)
@@ -353,9 +367,9 @@ class ProjWizardCustomPage(QWizardPage):
self.chFolders = QSwitch() self.chFolders = QSwitch()
self.chFolders.setChecked(True) self.chFolders.setChecked(True)
self.novelForm.addWidget(QLabel("Add chapters"), 0, 0) self.novelForm.addWidget(QLabel(self.tr("Add chapters")), 0, 0)
self.novelForm.addWidget(QLabel("Scenes (per chapter)"), 1, 0) self.novelForm.addWidget(QLabel(self.tr("Scenes (per chapter)")), 1, 0)
self.novelForm.addWidget(QLabel("Add chapter folders"), 2, 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.numChapters, 0, 1, 1, 1, Qt.AlignRight)
self.novelForm.addWidget(self.numScenes, 1, 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) self.novelForm.addWidget(self.chFolders, 2, 1, 1, 1, Qt.AlignRight)
@@ -396,13 +410,15 @@ class ProjWizardFinalPage(QWizardPage):
self.mainConf = nw.CONFIG self.mainConf = nw.CONFIG
self.theWizard = theWizard self.theWizard = theWizard
self.setTitle("Finished") self.setTitle(self.tr("Finished"))
self.theText = QLabel(( self.theText = QLabel(
"<p>All done.</p>" "<p>{done}</p><p>{help}</p>".format(
"<p>Press '{finish}' to create the new project.</p>" done = self.tr("All done."),
).format( help = self.tr("Press '{0}' to create the new project.").format(
finish = "Done" if self.mainConf.osDarwin else "Finish" self.tr("Done") if self.mainConf.osDarwin else self.tr("Finish")
)) )
)
)
self.theText.setWordWrap(True) self.theText.setWordWrap(True)
# Assemble # Assemble
+16 -12
View File
@@ -29,10 +29,10 @@ import logging
from time import time from time import time
from PyQt5.QtCore import QLocale
from PyQt5.QtGui import QColor, QPainter from PyQt5.QtGui import QColor, QPainter
from PyQt5.QtWidgets import qApp, QStatusBar, QLabel, QAbstractButton from PyQt5.QtWidgets import qApp, QStatusBar, QLabel, QAbstractButton
from nw.core import NWSpellCheck
from nw.common import formatTime from nw.common import formatTime
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -63,7 +63,7 @@ class GuiMainStatus(QStatusBar):
## The Spell Checker Language ## The Spell Checker Language
self.langIcon = QLabel("") 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.setPixmap(self.theTheme.getPixmap("status_lang", (iPx, iPx)))
self.langIcon.setContentsMargins(0, 0, 0, 0) self.langIcon.setContentsMargins(0, 0, 0, 0)
self.langText.setContentsMargins(0, 0, xM, 0) self.langText.setContentsMargins(0, 0, xM, 0)
@@ -72,7 +72,7 @@ class GuiMainStatus(QStatusBar):
## The Editor Status ## The Editor Status
self.docIcon = StatusLED(colNone, colTrue, colFalse, iPx, iPx, self) 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.docIcon.setContentsMargins(0, 0, 0, 0)
self.docText.setContentsMargins(0, 0, xM, 0) self.docText.setContentsMargins(0, 0, xM, 0)
self.addPermanentWidget(self.docIcon) self.addPermanentWidget(self.docIcon)
@@ -80,7 +80,7 @@ class GuiMainStatus(QStatusBar):
## The Project Status ## The Project Status
self.projIcon = StatusLED(colNone, colTrue, colFalse, iPx, iPx, self) 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.projIcon.setContentsMargins(0, 0, 0, 0)
self.projText.setContentsMargins(0, 0, xM, 0) self.projText.setContentsMargins(0, 0, xM, 0)
self.addPermanentWidget(self.projIcon) self.addPermanentWidget(self.projIcon)
@@ -103,7 +103,7 @@ class GuiMainStatus(QStatusBar):
self.timeIcon = QLabel() self.timeIcon = QLabel()
self.timeText = QLabel("") self.timeText = QLabel("")
self.timeIcon.setPixmap(self.timePixmap) 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.timeText.setMinimumWidth(self.theTheme.getTextWidth("00:00:00:"))
self.timeIcon.setContentsMargins(0, 0, 0, 0) self.timeIcon.setContentsMargins(0, 0, 0, 0)
self.timeText.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. """Set the language code for the spell checker.
""" """
if theLanguage is None: if theLanguage is None:
self.langText.setText("None") self.langText.setText(self.tr("None"))
self.langText.setToolTip("") self.langText.setToolTip("")
else: else:
self.langText.setText(NWSpellCheck.expandLanguage(theLanguage)) qLocal = QLocale(theLanguage)
self.langText.setToolTip( spLang = qLocal.nativeLanguageName().title()
"Provider: %s" % (theProvider if theProvider else "unknown") self.langText.setText(spLang)
) if theProvider:
self.langText.setToolTip("%s (%s)" % (theLanguage, theProvider))
else:
self.langText.setToolTip(theLanguage)
return return
def setProjectStatus(self, isChanged): def setProjectStatus(self, isChanged):
@@ -175,8 +179,8 @@ class GuiMainStatus(QStatusBar):
def setStats(self, pWC, sWC): def setStats(self, pWC, sWC):
"""Set the current project statistics. """Set the current project statistics.
""" """
self.statsText.setText(f"Words: {pWC:n} ({sWC:+n})") self.statsText.setText(self.tr("Words: {0} ({1})").format(f"{pWC:n}", f"{sWC:+n}"))
self.statsText.setToolTip("Project word count (session change)") self.statsText.setToolTip(self.tr("Project word count (session change)"))
return return
def setUserIdle(self, userIdle): def setUserIdle(self, userIdle):
+12 -7
View File
@@ -31,8 +31,9 @@ import configparser
import os import os
from math import ceil 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.QtWidgets import QStyle, qApp
from PyQt5.QtGui import ( from PyQt5.QtGui import (
QPalette, QColor, QIcon, QFont, QFontMetrics, QFontDatabase, QPixmap 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' Height: %d" % self.textNHeight)
logger.verbose("Text 'N' Width: %d" % self.textNWidth) logger.verbose("Text 'N' Width: %d" % self.textNWidth)
# Internal Mapping
self.makeAlert = self.theParent.makeAlert
self.tr = partial(QCoreApplication.translate, "GuiTheme")
return return
## ##
@@ -392,8 +397,8 @@ class GuiTheme:
with open(themeConf, mode="r", encoding="utf8") as inFile: with open(themeConf, mode="r", encoding="utf8") as inFile:
confParser.read_file(inFile) confParser.read_file(inFile)
except Exception as e: except Exception as e:
self.theParent.makeAlert( self.makeAlert(
["Could not load theme config file.", str(e)], nwAlert.ERROR [self.tr("Could not load theme config file."), str(e)], nwAlert.ERROR
) )
continue continue
themeName = "" themeName = ""
@@ -425,8 +430,8 @@ class GuiTheme:
with open(syntaxPath, mode="r", encoding="utf8") as inFile: with open(syntaxPath, mode="r", encoding="utf8") as inFile:
confParser.read_file(inFile) confParser.read_file(inFile)
except Exception as e: except Exception as e:
self.theParent.makeAlert( self.makeAlert(
["Could not load syntax file.", str(e)], nwAlert.ERROR [self.tr("Could not load syntax file."), str(e)], nwAlert.ERROR
) )
return [] return []
syntaxName = "" syntaxName = ""
@@ -740,8 +745,8 @@ class GuiIcons:
with open(themeConf, mode="r", encoding="utf8") as inFile: with open(themeConf, mode="r", encoding="utf8") as inFile:
confParser.read_file(inFile) confParser.read_file(inFile)
except Exception as e: except Exception as e:
self.theParent.makeAlert( self.makeAlert(
["Could not load theme config file.", str(e)], nwAlert.ERROR [self.tr("Could not load theme config file."), str(e)], nwAlert.ERROR
) )
continue continue
themeName = "" themeName = ""
+9 -6
View File
@@ -52,7 +52,7 @@ class GuiWordList(QDialog):
self.theProject = theProject self.theProject = theProject
self.optState = theProject.optState self.optState = theProject.optState
self.setWindowTitle("Project Word List") self.setWindowTitle(self.tr("Project Word List"))
mS = self.mainConf.pxInt(250) mS = self.mainConf.pxInt(250)
wW = self.mainConf.pxInt(320) wW = self.mainConf.pxInt(320)
@@ -68,7 +68,7 @@ class GuiWordList(QDialog):
# Main Widgets # Main Widgets
# ============ # ============
self.headLabel = QLabel("<b>Project Word List</b>") self.headLabel = QLabel("<b>%s</b>" % self.tr("Project Word List"))
self.listBox = QListWidget() self.listBox = QListWidget()
self.listBox.setDragDropMode(QAbstractItemView.NoDragDrop) self.listBox.setDragDropMode(QAbstractItemView.NoDragDrop)
@@ -77,11 +77,11 @@ class GuiWordList(QDialog):
self.newEntry = QLineEdit() self.newEntry = QLineEdit()
self.addButton = QPushButton(self.theTheme.getIcon("add"), "") 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.addButton.clicked.connect(self._doAdd)
self.delButton = QPushButton(self.theTheme.getIcon("remove"), "") 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.delButton.clicked.connect(self._doDelete)
self.editBox = QHBoxLayout() self.editBox = QHBoxLayout()
@@ -90,6 +90,8 @@ class GuiWordList(QDialog):
self.editBox.addWidget(self.delButton, 0) self.editBox.addWidget(self.delButton, 0)
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Save | QDialogButtonBox.Close) 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.accepted.connect(self._doSave)
self.buttonBox.rejected.connect(self._doClose) self.buttonBox.rejected.connect(self._doClose)
@@ -121,12 +123,13 @@ class GuiWordList(QDialog):
""" """
newWord = self.newEntry.text().strip() newWord = self.newEntry.text().strip()
if newWord == "": 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 return False
if self.listBox.findItems(newWord, Qt.MatchExactly): if self.listBox.findItems(newWord, Qt.MatchExactly):
self.theParent.makeAlert( 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 return False
+40 -24
View File
@@ -72,7 +72,7 @@ class GuiWritingStats(QDialog):
self.timeFilter = 0.0 self.timeFilter = 0.0
self.wordOffset = 0 self.wordOffset = 0
self.setWindowTitle("Writing Statistics") self.setWindowTitle(self.tr("Writing Statistics"))
self.setMinimumWidth(self.mainConf.pxInt(420)) self.setMinimumWidth(self.mainConf.pxInt(420))
self.setMinimumHeight(self.mainConf.pxInt(400)) self.setMinimumHeight(self.mainConf.pxInt(400))
self.resize( self.resize(
@@ -95,7 +95,13 @@ class GuiWritingStats(QDialog):
) )
self.listBox = QTreeWidget() 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.setIndentation(0)
self.listBox.setColumnWidth(self.C_TIME, wCol0) self.listBox.setColumnWidth(self.C_TIME, wCol0)
self.listBox.setColumnWidth(self.C_LENGTH, wCol1) self.listBox.setColumnWidth(self.C_LENGTH, wCol1)
@@ -125,7 +131,7 @@ class GuiWritingStats(QDialog):
self.barImage.fill(self.palette().highlight().color()) self.barImage.fill(self.palette().highlight().color())
# Session Info # Session Info
self.infoBox = QGroupBox("Sum Totals", self) self.infoBox = QGroupBox(self.tr("Sum Totals"), self)
self.infoForm = QGridLayout(self) self.infoForm = QGridLayout(self)
self.infoBox.setLayout(self.infoForm) self.infoBox.setLayout(self.infoForm)
@@ -153,24 +159,33 @@ class GuiWritingStats(QDialog):
self.totalWords.setFont(self.theTheme.guiFontFixed) self.totalWords.setFont(self.theTheme.guiFontFixed)
self.totalWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight) self.totalWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
self.infoForm.addWidget(QLabel("Total Time:"), 0, 0) lblTTime = QLabel(self.tr("Total Time:"))
self.infoForm.addWidget(QLabel("Idle Time:"), 1, 0) lblITime = QLabel(self.tr("Idle Time:"))
self.infoForm.addWidget(QLabel("Filtered Time:"), 2, 0) lblFTime = QLabel(self.tr("Filtered Time:"))
self.infoForm.addWidget(QLabel("Novel Word Count:"), 3, 0) lblNvCount = QLabel(self.tr("Novel Word Count:"))
self.infoForm.addWidget(QLabel("Notes Word Count:"), 4, 0) lblNtCount = QLabel(self.tr("Notes Word Count:"))
self.infoForm.addWidget(QLabel("Total Word Count:"), 5, 0) 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.labelTotal, 0, 1)
self.infoForm.addWidget(self.labelIdleT, 1, 1) self.infoForm.addWidget(self.labelIdleT, 1, 1)
self.infoForm.addWidget(self.labelFilter, 2, 1) self.infoForm.addWidget(self.labelFilter, 2, 1)
self.infoForm.addWidget(self.novelWords, 3, 1) self.infoForm.addWidget(self.novelWords, 3, 1)
self.infoForm.addWidget(self.notesWords, 4, 1) self.infoForm.addWidget(self.notesWords, 4, 1)
self.infoForm.addWidget(self.totalWords, 5, 1) self.infoForm.addWidget(self.totalWords, 5, 1)
self.infoForm.setRowStretch(6, 1) self.infoForm.setRowStretch(6, 1)
# Filter Options # Filter Options
sPx = self.theTheme.baseIconSize sPx = self.theTheme.baseIconSize
self.filterBox = QGroupBox("Filters", self) self.filterBox = QGroupBox(self.tr("Filters"), self)
self.filterForm = QGridLayout(self) self.filterForm = QGridLayout(self)
self.filterBox.setLayout(self.filterForm) self.filterBox.setLayout(self.filterForm)
@@ -210,12 +225,12 @@ class GuiWritingStats(QDialog):
) )
self.showIdleTime.clicked.connect(self._updateListBox) self.showIdleTime.clicked.connect(self._updateListBox)
self.filterForm.addWidget(QLabel("Count novel files"), 0, 0) self.filterForm.addWidget(QLabel(self.tr("Count novel files")), 0, 0)
self.filterForm.addWidget(QLabel("Count note files"), 1, 0) self.filterForm.addWidget(QLabel(self.tr("Count note files")), 1, 0)
self.filterForm.addWidget(QLabel("Hide zero word count"), 2, 0) self.filterForm.addWidget(QLabel(self.tr("Hide zero word count")), 2, 0)
self.filterForm.addWidget(QLabel("Hide negative word count"), 3, 0) self.filterForm.addWidget(QLabel(self.tr("Hide negative word count")), 3, 0)
self.filterForm.addWidget(QLabel("Group entries by day"), 4, 0) self.filterForm.addWidget(QLabel(self.tr("Group entries by day")), 4, 0)
self.filterForm.addWidget(QLabel("Show idle time"), 5, 0) self.filterForm.addWidget(QLabel(self.tr("Show idle time")), 5, 0)
self.filterForm.addWidget(self.incNovel, 0, 1) self.filterForm.addWidget(self.incNovel, 0, 1)
self.filterForm.addWidget(self.incNotes, 1, 1) self.filterForm.addWidget(self.incNotes, 1, 1)
self.filterForm.addWidget(self.hideZeros, 2, 1) self.filterForm.addWidget(self.hideZeros, 2, 1)
@@ -236,7 +251,7 @@ class GuiWritingStats(QDialog):
self.optsBox = QHBoxLayout() self.optsBox = QHBoxLayout()
self.optsBox.addStretch(1) 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) self.optsBox.addWidget(self.histMax, 0)
# Buttons # Buttons
@@ -244,19 +259,20 @@ class GuiWritingStats(QDialog):
self.buttonBox.rejected.connect(self._doClose) self.buttonBox.rejected.connect(self._doClose)
self.btnClose = self.buttonBox.addButton(QDialogButtonBox.Close) self.btnClose = self.buttonBox.addButton(QDialogButtonBox.Close)
self.buttonBox.button(QDialogButtonBox.Close).setText(self.tr("Close"))
self.btnClose.setAutoDefault(False) 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.btnSave.setAutoDefault(False)
self.saveMenu = QMenu(self) self.saveMenu = QMenu(self)
self.btnSave.setMenu(self.saveMenu) 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.saveJSON.triggered.connect(lambda: self._saveData(self.FMT_JSON))
self.saveMenu.addAction(self.saveJSON) 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.saveCSV.triggered.connect(lambda: self._saveData(self.FMT_CSV))
self.saveMenu.addAction(self.saveCSV) self.saveMenu.addAction(self.saveCSV)
@@ -338,10 +354,10 @@ class GuiWritingStats(QDialog):
if dataFmt == self.FMT_JSON: if dataFmt == self.FMT_JSON:
fileExt = "json" fileExt = "json"
textFmt = "JSON Data File" textFmt = self.tr("JSON Data File")
elif dataFmt == self.FMT_CSV: elif dataFmt == self.FMT_CSV:
fileExt = "csv" fileExt = "csv"
textFmt = "CSV Data File" textFmt = self.tr("CSV Data File")
else: else:
return False return False
@@ -356,7 +372,7 @@ class GuiWritingStats(QDialog):
dlgOpt = QFileDialog.Options() dlgOpt = QFileDialog.Options()
dlgOpt |= QFileDialog.DontUseNativeDialog dlgOpt |= QFileDialog.DontUseNativeDialog
savePath, _ = QFileDialog.getSaveFileName( savePath, _ = QFileDialog.getSaveFileName(
self, "Save Document As", savePath, options=dlgOpt self, self.tr("Save Document As"), savePath, options=dlgOpt
) )
if not savePath: if not savePath:
return False return False
@@ -474,7 +490,7 @@ class GuiWritingStats(QDialog):
except Exception as e: except Exception as e:
self.theParent.makeAlert( 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 return False
+82 -55
View File
@@ -76,6 +76,7 @@ class GuiMain(QMainWindow):
logger.info("Python Version: %s (0x%x)" % ( logger.info("Python Version: %s (0x%x)" % (
self.mainConf.verPyString, self.mainConf.verPyHexVal) self.mainConf.verPyString, self.mainConf.verPyHexVal)
) )
logger.info("GUI Language: %s" % self.mainConf.guiLang)
# Core Classes # Core Classes
# ============ # ============
@@ -122,8 +123,8 @@ class GuiMain(QMainWindow):
self.projTabs = QTabWidget() self.projTabs = QTabWidget()
self.projTabs.setTabPosition(QTabWidget.South) self.projTabs.setTabPosition(QTabWidget.South)
self.projTabs.setStyleSheet(r"QTabWidget::pane {border: 0;};") self.projTabs.setStyleSheet(r"QTabWidget::pane {border: 0;};")
self.projTabs.addTab(self.treeView, "Project") self.projTabs.addTab(self.treeView, self.tr("Project"))
self.projTabs.addTab(self.novelView, "Novel") self.projTabs.addTab(self.novelView, self.tr("Novel"))
self.projTabs.currentChanged.connect(self._projTabsChanged) self.projTabs.currentChanged.connect(self._projTabsChanged)
tabFont = self.projTabs.tabBar().font() tabFont = self.projTabs.tabBar().font()
@@ -139,17 +140,17 @@ class GuiMain(QMainWindow):
self.treeButtons.setStyleSheet(r"QToolBar {padding: 0;}") self.treeButtons.setStyleSheet(r"QToolBar {padding: 0;}")
self.projTabs.setCornerWidget(self.treeButtons, Qt.BottomRightCorner) 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.setIcon(self.theTheme.getIcon("status_lines"))
self.projDetailsBtn.triggered.connect(lambda: self.showProjectDetailsDialog()) self.projDetailsBtn.triggered.connect(lambda: self.showProjectDetailsDialog())
self.treeButtons.addAction(self.projDetailsBtn) 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.setIcon(self.theTheme.getIcon("status_stats"))
self.projStatsBtn.triggered.connect(lambda: self.showWritingStatsDialog()) self.projStatsBtn.triggered.connect(lambda: self.showWritingStatsDialog())
self.treeButtons.addAction(self.projStatsBtn) 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.setIcon(self.theTheme.getIcon("settings"))
self.projSettingsBtn.triggered.connect(lambda: self.showProjectSettingsDialog()) self.projSettingsBtn.triggered.connect(lambda: self.showProjectSettingsDialog())
self.treeButtons.addAction(self.projSettingsBtn) self.treeButtons.addAction(self.projSettingsBtn)
@@ -184,8 +185,8 @@ class GuiMain(QMainWindow):
self.mainTabs = QTabWidget() self.mainTabs = QTabWidget()
self.mainTabs.setTabPosition(QTabWidget.East) self.mainTabs.setTabPosition(QTabWidget.East)
self.mainTabs.setStyleSheet(r"QTabWidget::pane {border: 0;}") self.mainTabs.setStyleSheet(r"QTabWidget::pane {border: 0;}")
self.mainTabs.addTab(self.splitDocs, "Editor") self.mainTabs.addTab(self.splitDocs, self.tr("Editor"))
self.mainTabs.addTab(self.splitOutline, "Outline") self.mainTabs.addTab(self.splitOutline, self.tr("Outline"))
self.mainTabs.currentChanged.connect(self._mainTabChanged) self.mainTabs.currentChanged.connect(self._mainTabChanged)
# Splitter : Project Tree / Main Tabs # Splitter : Project Tree / Main Tabs
@@ -339,7 +340,7 @@ class GuiMain(QMainWindow):
if self.hasProject: if self.hasProject:
if not self.closeProject(): if not self.closeProject():
self.makeAlert( self.makeAlert(
"Cannot create new project when another project is open.", self.tr("Cannot create new project when another project is open."),
nwAlert.ERROR nwAlert.ERROR
) )
return False return False
@@ -357,8 +358,10 @@ class GuiMain(QMainWindow):
if os.path.isfile(os.path.join(projPath, self.theProject.projFile)): if os.path.isfile(os.path.join(projPath, self.theProject.projFile)):
self.makeAlert( self.makeAlert(
"A project already exists in that location. Please choose another folder.", self.tr(
nwAlert.ERROR "A project already exists in that location. "
"Please choose another folder."
), nwAlert.ERROR
) )
return False return False
@@ -372,7 +375,7 @@ class GuiMain(QMainWindow):
self.statusBar.setRefTime(self.theProject.projOpened) self.statusBar.setRefTime(self.theProject.projOpened)
self.statusBar.setProjectStatus(True) self.statusBar.setProjectStatus(True)
self.statusBar.setDocumentStatus(None) self.statusBar.setDocumentStatus(None)
self.statusBar.setStatus("New project created ...") self.statusBar.setStatus(self.tr("New project created ..."))
self._updateWindowTitle(self.theProject.projName) self._updateWindowTitle(self.theProject.projName)
else: else:
self.theProject.clearProject() self.theProject.clearProject()
@@ -391,8 +394,11 @@ class GuiMain(QMainWindow):
if not isYes: if not isYes:
msgYes = self.askQuestion( msgYes = self.askQuestion(
"Close Project", self.tr("Close Project"),
"Close the current project?<br>Changes are saved automatically." "%s<br>%s" % (
self.tr("Close the current project?"),
self.tr("Changes are saved automatically.")
)
) )
if not msgYes: if not msgYes:
return False return False
@@ -407,7 +413,8 @@ class GuiMain(QMainWindow):
doBackup = True doBackup = True
if self.mainConf.askBeforeBackup: if self.mainConf.askBeforeBackup:
msgYes = self.askQuestion( msgYes = self.askQuestion(
"Backup Project", "Backup the current project?" self.tr("Backup Project"),
self.tr("Backup the current project?")
) )
if not msgYes: if not msgYes:
doBackup = False doBackup = False
@@ -457,29 +464,35 @@ class GuiMain(QMainWindow):
try: try:
lockDetails = ( lockDetails = (
"<br><br>The project was locked by the computer " "<br>%s" % self.tr(
"'%s' (%s %s), last active on %s" "The project was locked by the computer "
) % ( "'{0}' ({1} {2}), last active on {3}."
)
).format(
self.theProject.lockedBy[0], self.theProject.lockedBy[0],
self.theProject.lockedBy[1], self.theProject.lockedBy[1],
self.theProject.lockedBy[2], self.theProject.lockedBy[2],
datetime.fromtimestamp( datetime.fromtimestamp(int(self.theProject.lockedBy[3])).strftime("%x %X")
int(self.theProject.lockedBy[3])
).strftime("%x %X")
) )
except Exception: except Exception:
lockDetails = "" lockDetails = ""
msgBox = QMessageBox() msgBox = QMessageBox()
msgRes = msgBox.warning( msgRes = msgBox.warning(
self, "Project Locked", ( self, self.tr("Project Locked"),
"The project is already open by another instance of novelWriter, and " "%s<br><br>%s<br>%s" % (
"is therefore locked. Override lock and continue anyway?<br><br>" self.tr(
"Note: If the program or the computer previously crashed, the lock " "The project is already open by another instance of novelWriter, and "
"can safely be overridden. If, however, another instance of " "is therefore locked. Override lock and continue anyway?"
"novelWriter has the project open, overriding the lock may corrupt " ),
"the project, and is not recommended.%s" self.tr(
) % lockDetails, "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 QMessageBox.Yes | QMessageBox.No, QMessageBox.No
) )
if msgRes == QMessageBox.Yes: if msgRes == QMessageBox.Yes:
@@ -514,6 +527,10 @@ class GuiMain(QMainWindow):
# Check if we need to rebuild the index # Check if we need to rebuild the index
if self.theIndex.indexBroken: if self.theIndex.indexBroken:
self.makeAlert(
self.tr("The project index is outdated or broken. Rebuilding index."),
nwAlert.WARN
)
self.rebuildIndex() self.rebuildIndex()
# Make sure the changed status is set to false on all that was # Make sure the changed status is set to false on all that was
@@ -685,15 +702,16 @@ class GuiMain(QMainWindow):
lastPath = self.mainConf.lastPath lastPath = self.mainConf.lastPath
extFilter = [ extFilter = [
"Text files (*.txt)", self.tr("Text files ({0})").format("*.txt"),
"Markdown files (*.md)", self.tr("Markdown files ({0})").format("*.md"),
"novelWriter files (*.nwd)", self.tr("novelWriter files ({0})").format("*.nwd"),
"All files (*.*)", self.tr("All files ({0})").format("*.*"),
] ]
dlgOpt = QFileDialog.Options() dlgOpt = QFileDialog.Options()
dlgOpt |= QFileDialog.DontUseNativeDialog dlgOpt |= QFileDialog.DontUseNativeDialog
loadFile, _ = QFileDialog.getOpenFileName( 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: if not loadFile:
return False return False
@@ -707,24 +725,26 @@ class GuiMain(QMainWindow):
theText = inFile.read() theText = inFile.read()
self.mainConf.setLastPath(loadFile) self.mainConf.setLastPath(loadFile)
except Exception as e: except Exception as e:
self.makeAlert( self.makeAlert([
["Could not read file. The file must be an existing text file.", str(e)], self.tr("Could not read file. The file must be an existing text file."), str(e)
nwAlert.ERROR ], nwAlert.ERROR)
)
return False return False
if self.docEditor.theHandle is None: if self.docEditor.theHandle is None:
self.makeAlert( 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 nwAlert.ERROR
) )
return False return False
if not self.docEditor.isEmpty(): if not self.docEditor.isEmpty():
msgYes = self.askQuestion("Import Document", ( msgYes = self.askQuestion(
"Importing the file will overwrite the current content of the document. " self.tr("Import Document"),
"Do you want to proceed?" self.tr(
)) "Importing the file will overwrite the current content of the document. "
"Do you want to proceed?"
)
)
if not msgYes: if not msgYes:
return False return False
@@ -861,9 +881,9 @@ class GuiMain(QMainWindow):
for nDone, tItem in enumerate(self.theProject.projTree): for nDone, tItem in enumerate(self.theProject.projTree):
if tItem is not None: if tItem is not None:
self.setStatus("Indexing: '%s'" % tItem.itemName) self.setStatus(self.tr("Indexing: '{0}'").format(tItem.itemName))
else: 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: if tItem is not None and tItem.itemType == nwItemType.FILE:
logger.verbose("Scanning: %s" % tItem.itemName) logger.verbose("Scanning: %s" % tItem.itemName)
@@ -881,12 +901,16 @@ class GuiMain(QMainWindow):
self.treeView.projectWordCount() self.treeView.projectWordCount()
tEnd = time() 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() self.docEditor.updateTagHighLighting()
qApp.restoreOverrideCursor() qApp.restoreOverrideCursor()
if not beQuiet: 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 return True
@@ -914,7 +938,7 @@ class GuiMain(QMainWindow):
dlgOpt |= QFileDialog.ShowDirsOnly dlgOpt |= QFileDialog.ShowDirsOnly
dlgOpt |= QFileDialog.DontUseNativeDialog dlgOpt |= QFileDialog.DontUseNativeDialog
projPath = QFileDialog.getExistingDirectory( projPath = QFileDialog.getExistingDirectory(
self, "Save novelWriter Project", "", options=dlgOpt self, self.tr("Save novelWriter Project"), "", options=dlgOpt
) )
if projPath: if projPath:
return projPath return projPath
@@ -1100,14 +1124,14 @@ class GuiMain(QMainWindow):
# Popup # Popup
msgBox = QMessageBox() msgBox = QMessageBox()
if theLevel == nwAlert.INFO: if theLevel == nwAlert.INFO:
msgBox.information(self, "Information", popMsg) msgBox.information(self, self.tr("Information"), popMsg)
elif theLevel == nwAlert.WARN: elif theLevel == nwAlert.WARN:
msgBox.warning(self, "Warning", popMsg) msgBox.warning(self, self.tr("Warning"), popMsg)
elif theLevel == nwAlert.ERROR: elif theLevel == nwAlert.ERROR:
msgBox.critical(self, "Error", popMsg) msgBox.critical(self, self.tr("Error"), popMsg)
elif theLevel == nwAlert.BUG: elif theLevel == nwAlert.BUG:
popMsg += "<br>This is a bug!" popMsg += "<br>%s" % self.tr("This is a bug!")
msgBox.critical(self, "Internal Error", popMsg) msgBox.critical(self, self.tr("Internal Error"), popMsg)
return return
@@ -1115,7 +1139,7 @@ class GuiMain(QMainWindow):
"""Ask the user a Yes/No question. """Ask the user a Yes/No question.
""" """
msgBox = QMessageBox() msgBox = QMessageBox()
msgRes = msgBox.question(self, theTitle, theQuestion) msgRes = msgBox.question(self, theTitle, theQuestion, QMessageBox.Yes | QMessageBox.No)
return msgRes == QMessageBox.Yes return msgRes == QMessageBox.Yes
def reportConfErr(self): def reportConfErr(self):
@@ -1137,8 +1161,11 @@ class GuiMain(QMainWindow):
""" """
if self.hasProject: if self.hasProject:
msgYes = self.askQuestion( msgYes = self.askQuestion(
"Exit", self.tr("Exit"),
"Do you want to exit novelWriter?<br>Changes are saved automatically." "%s<br>%s" % (
self.tr("Do you want to exit novelWriter?"),
self.tr("Changes are saved automatically.")
)
) )
if not msgYes: if not msgYes:
return False return False
+4 -4
View File
@@ -1,13 +1,13 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.2b1" hexVersion="0x010200b1" fileVersion="1.2" timeStamp="2021-02-11 17:04:25"> <novelWriterXML appVersion="1.3a0" hexVersion="0x010300a0" fileVersion="1.2" timeStamp="2021-02-15 17:51:17">
<project> <project>
<name>Sample Project</name> <name>Sample Project</name>
<title>Sample Project</title> <title>Sample Project</title>
<author>Jane Smith</author> <author>Jane Smith</author>
<author>Jay Doh</author> <author>Jay Doh</author>
<saveCount>936</saveCount> <saveCount>1021</saveCount>
<autoCount>161</autoCount> <autoCount>161</autoCount>
<editTime>46791</editTime> <editTime>48283</editTime>
</project> </project>
<settings> <settings>
<doBackup>False</doBackup> <doBackup>False</doBackup>
@@ -120,7 +120,7 @@
<charCount>1810</charCount> <charCount>1810</charCount>
<wordCount>318</wordCount> <wordCount>318</wordCount>
<paraCount>8</paraCount> <paraCount>8</paraCount>
<cursorPos>3</cursorPos> <cursorPos>1112</cursorPos>
</item> </item>
<item handle="bc0cbd2a407f3" order="2" parent="e7ded148d6e4a"> <item handle="bc0cbd2a407f3" order="2" parent="e7ded148d6e4a">
<name>Another Scene</name> <name>Another Scene</name>
+36
View File
@@ -186,6 +186,32 @@ def buildQtDocs():
return 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) # Sample Project ZIP File Builder (sample)
## ##
@@ -849,6 +875,8 @@ if __name__ == "__main__":
"\n" "\n"
" qthelp Build the help documentation for use with the Qt Assistant. Run before\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" " 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" " 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" " creating sample projects in the in-app New Project Wizard.\n"
"\n" "\n"
@@ -898,6 +926,14 @@ if __name__ == "__main__":
sys.argv.remove("qthelp") sys.argv.remove("qthelp")
buildQtDocs() 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: if "sample" in sys.argv:
sys.argv.remove("sample") sys.argv.remove("sample")
buildSampleZip() buildSampleZip()
+4
View File
@@ -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 `qthelp` Build the help documentation for use with the Qt Assistant. Run
before install to have local help enable in the the installed version 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 `sample` Build the sample project as a zip file. Run before install to enable
creating sample projects in the in-app New Project Wizard. creating sample projects in the in-app New Project Wizard.
+2 -1
View File
@@ -1,5 +1,5 @@
[Main] [Main]
timestamp = 2021-02-09 21:07:03 timestamp = 2021-02-15 16:31:24
theme = default theme = default
syntax = default_light syntax = default_light
icons = typicons_colour_light icons = typicons_colour_light
@@ -7,6 +7,7 @@ guidark = False
guifont = guifont =
guifontsize = 11 guifontsize = 11
lastnotes = 0x0 lastnotes = 0x0
guilang = en-GB
[Sizes] [Sizes]
geometry = 1200, 650 geometry = 1200, 650
@@ -7,6 +7,7 @@ guidark = True
guifont = Sans guifont = Sans
guifontsize = 12 guifontsize = 12
lastnotes = 0x0 lastnotes = 0x0
guilang = en-GB
[Sizes] [Sizes]
geometry = 1200, 650 geometry = 1200, 650
+3 -3
View File
@@ -145,7 +145,7 @@ def testBaseConfig_Init(monkeypatch, tmpDir, fncDir, outDir, refDir):
assert os.path.isfile(confFile) assert os.path.isfile(confFile)
copyfile(confFile, testFile) copyfile(confFile, testFile)
assert cmpFiles(testFile, compFile, [2, 9]) assert cmpFiles(testFile, compFile, [2, 9, 10])
monkeypatch.undo() monkeypatch.undo()
# Load and save with OSError # Load and save with OSError
@@ -193,7 +193,7 @@ def testBaseConfig_Init(monkeypatch, tmpDir, fncDir, outDir, refDir):
assert tstConf.saveConfig() assert tstConf.saveConfig()
copyfile(confFile, testFile) copyfile(confFile, testFile)
assert cmpFiles(testFile, compFile, [2, 9]) assert cmpFiles(testFile, compFile, [2, 9, 10])
# END Test testBaseConfig_Init # END Test testBaseConfig_Init
@@ -458,7 +458,7 @@ def testBaseConfig_SettersGetters(tmpConf, tmpDir, outDir, refDir):
assert not tmpConf.confChanged assert not tmpConf.confChanged
copyfile(confFile, testFile) copyfile(confFile, testFile)
assert cmpFiles(testFile, compFile, [2, 9]) assert cmpFiles(testFile, compFile, [2, 9, 10])
# END Test testBaseConfig_SettersGetters # END Test testBaseConfig_SettersGetters
+2 -2
View File
@@ -928,7 +928,7 @@ def testCoreProject_OrphanedFiles(dummyGUI, nwLipsum):
# First Item with Meta Data # First Item with Meta Data
orphPath = os.path.join(nwLipsum, "content", "636b6aa9b697b.nwd") orphPath = os.path.join(nwLipsum, "content", "636b6aa9b697b.nwd")
with open(orphPath, mode="w", encoding="utf8") as outFile: 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("%%~path:5eaea4e8cdee8/636b6aa9b697b\n")
outFile.write("%%~kind:WORLD/NOTE\n") outFile.write("%%~kind:WORLD/NOTE\n")
outFile.write("%%~invalid\n") outFile.write("%%~invalid\n")
@@ -962,7 +962,7 @@ def testCoreProject_OrphanedFiles(dummyGUI, nwLipsum):
# First Item with Meta Data # First Item with Meta Data
oItem = theProject.projTree["636b6aa9b697b"] oItem = theProject.projTree["636b6aa9b697b"]
assert oItem is not None assert oItem is not None
assert oItem.itemName == "Recovered: Mars" assert oItem.itemName == "[Recovered] Mars"
assert oItem.itemHandle == "636b6aa9b697b" assert oItem.itemHandle == "636b6aa9b697b"
assert oItem.itemParent == "60bdf227455cc" assert oItem.itemParent == "60bdf227455cc"
assert oItem.itemClass == nwItemClass.WORLD assert oItem.itemClass == nwItemClass.WORLD
+2 -7
View File
@@ -28,7 +28,6 @@ from dummy import causeOSError
from tools import readFile, writeFile from tools import readFile, writeFile
from nw.core.spellcheck import NWSpellCheck, NWSpellEnchant, NWSpellSimple from nw.core.spellcheck import NWSpellCheck, NWSpellEnchant, NWSpellSimple
from nw.constants import nwConst
@pytest.mark.core @pytest.mark.core
def testCoreSpell_Super(monkeypatch, tmpDir, tmpConf): def testCoreSpell_Super(monkeypatch, tmpDir, tmpConf):
@@ -48,10 +47,6 @@ def testCoreSpell_Super(monkeypatch, tmpDir, tmpConf):
assert spChk.listDictionaries() == [] assert spChk.listDictionaries() == []
assert spChk.describeDict() == ("", "") 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 # Add a word to the user's dictionary
assert spChk._readProjectDictionary("dummy") is False assert spChk._readProjectDictionary("dummy") is False
monkeypatch.setattr("builtins.open", causeOSError) monkeypatch.setattr("builtins.open", causeOSError)
@@ -178,11 +173,11 @@ def testCoreSpell_Simple(monkeypatch, tmpDir, tmpConf):
assert "D_word" in wSuggest assert "D_word" in wSuggest
# List dictionaries # List dictionaries
assert spChk.listDictionaries() == [("en", "English [%s]" % nwConst.SP_INTERNAL)] assert spChk.listDictionaries() == [("en", "difflib")]
# Description # Description
aTag, aName = spChk.describeDict() aTag, aName = spChk.describeDict()
assert aTag == "en" assert aTag == "en"
assert aName == nwConst.SP_INTERNAL assert aName == ""
# END Test testCoreSpell_Simple # END Test testCoreSpell_Simple
+38 -5
View File
@@ -86,6 +86,7 @@ def testCoreTools_RomanNumbers():
def testCoreTools_NumberWords(): def testCoreTools_NumberWords():
"""Test the conversion of integer to English words. """Test the conversion of integer to English words.
""" """
# English
assert numberToWord(0, "en") == "Zero" assert numberToWord(0, "en") == "Zero"
assert numberToWord(1, "en") == "One" assert numberToWord(1, "en") == "One"
assert numberToWord(2, "en") == "Two" assert numberToWord(2, "en") == "Two"
@@ -114,14 +115,46 @@ def testCoreTools_NumberWords():
assert numberToWord(142, "en") == "One Hundred Forty-Two" assert numberToWord(142, "en") == "One Hundred Forty-Two"
assert numberToWord(999, "en") == "Nine Hundred Ninety-Nine" 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 # Check a few with a nonsense language setting
assert numberToWord(1, "foo") == "One" assert numberToWord(1, "foo") == "1"
assert numberToWord(2, "foo") == "Two" assert numberToWord(2, "foo") == "2"
assert numberToWord(3, "foo") == "Three" assert numberToWord(3, "foo") == "3"
# Test out of range values # Test out of range values
assert numberToWord(12345, "en") == "[Out of Range]" assert numberToWord(12345, "en") == "[>999]"
assert numberToWord(-2345, "en") == "[Negative]" assert numberToWord(-2345, "en") == "[<0]"
assert numberToWord("234", "en") == "[NaN]" assert numberToWord("234", "en") == "[NaN]"
# END Test testCoreTools_NumberWords # END Test testCoreTools_NumberWords
+3 -4
View File
@@ -595,10 +595,9 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
nwGUI.mainMenu.aFileDetails.activate(QAction.Trigger) nwGUI.mainMenu.aFileDetails.activate(QAction.Trigger)
theBits = theMessage.split("<br>") theBits = theMessage.split("<br>")
assert len(theBits) == 3 assert len(theBits) == 2
assert theBits[0] == "File details for the currently open file" assert theBits[0] == "The currently open file is saved in:"
assert theBits[1] == "Handle: 0e17daca5f3e1" assert theBits[1] == os.path.join(fncProj, "content", "0e17daca5f3e1.nwd")
assert theBits[2] == "Location: %s" % os.path.join(fncProj, "content", "0e17daca5f3e1.nwd")
# qtbot.stopForInteraction() # qtbot.stopForInteraction()
+1 -7
View File
@@ -245,13 +245,7 @@ def testGuiPreferences_Main(qtbot, monkeypatch, fncDir, outDir, refDir):
testFile = os.path.join(outDir, "guiPreferences_novelwriter.conf") testFile = os.path.join(outDir, "guiPreferences_novelwriter.conf")
compFile = os.path.join(refDir, "guiPreferences_novelwriter.conf") compFile = os.path.join(refDir, "guiPreferences_novelwriter.conf")
copyfile(projFile, testFile) copyfile(projFile, testFile)
ignoreLines = [ ignoreLines = [2, 9, 10, 13, 14, 15, 16, 17, 18, 19, 20, 7, 30, 31]
2, # Timestamp
9, # Release Notes
12, 13, 14, 15, # Window sizes
16, 17, 18, 19, # Window sizes
7, 29, # Fonts (depends on system default)
]
assert cmpFiles(testFile, compFile, ignoreLines) assert cmpFiles(testFile, compFile, ignoreLines)
# Clean up # Clean up