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
# Launch main GUI
CONFIG.initLocalisation(nwApp)
nwGUI = GuiMain()
if not nwGUI.hasProject:
nwGUI.showProjectLoadDialog()
+44 -3
View File
@@ -34,7 +34,10 @@ import os
from time import time
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.common import splitVersionNumber, formatTimeStamp
@@ -85,12 +88,18 @@ class Config:
self.guiSyntax = "default_light"
self.guiIcons = "typicons_colour_light"
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.guiFontSize = 11
self.guiFontSize = 11 # Is overridden if system default is loaded
self.guiScale = 1.0 # Set automatically by Theme class
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
self.winGeometry = [1200, 650]
self.treeColWidth = [200, 50, 30]
@@ -292,6 +301,9 @@ class Config:
self.langPath = os.path.join(self.assetPath, "lang")
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("Last path: %s" % self.lastPath)
@@ -356,6 +368,31 @@ class Config:
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):
"""Load preferences from file and replace default settings.
"""
@@ -399,6 +436,9 @@ class Config:
self.lastNotes = self._parseLine(
cnfParse, cnfSec, "lastnotes", self.CNF_STR, self.lastNotes
)
self.guiLang = self._parseLine(
cnfParse, cnfSec, "guilang", self.CNF_STR, self.guiLang
)
## Sizes
cnfSec = "Sizes"
@@ -628,6 +668,7 @@ class Config:
cnfParse.set(cnfSec, "guifont", str(self.guiFont))
cnfParse.set(cnfSec, "guifontsize", str(self.guiFontSize))
cnfParse.set(cnfSec, "lastnotes", str(self.lastNotes))
cnfParse.set(cnfSec, "guilang", str(self.guiLang))
## Sizes
cnfSec = "Sizes"
+3 -5
View File
@@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
from nw.constants.iso import isoLanguage, isoCountry
from nw.constants.constants import (
nwConst, nwLists, nwRegEx, nwFiles, nwKeyWords, nwLabels, nwQuotes,
nwUnicode, nwHtmlUnicode
trConst, nwConst, nwLists, nwRegEx, nwFiles, nwKeyWords, nwLabels,
nwQuotes, nwUnicode, nwHtmlUnicode
)
from nw.constants.enum import (
nwAlert, nwDocAction, nwItemClass, nwItemLayout, nwItemType, nwOutline,
@@ -10,8 +9,7 @@ from nw.constants.enum import (
)
__all__ = [
"isoCountry",
"isoLanguage",
"trConst",
"nwConst",
"nwLists",
"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/>.
"""
from PyQt5.QtCore import QCoreApplication, QT_TRANSLATE_NOOP
from nw.constants.enum import (
nwItemClass, nwItemLayout, nwItemType, nwOutline
)
def trConst(tString):
"""Wrapper function for locally translating constants.
"""
return QCoreApplication.translate("Constant", tString)
class nwConst():
# Date and Time Formats
@@ -119,17 +126,17 @@ class nwKeyWords:
class nwLabels():
CLASS_NAME = {
nwItemClass.NO_CLASS : "None",
nwItemClass.NOVEL : "Novel",
nwItemClass.PLOT : "Plot",
nwItemClass.CHARACTER : "Characters",
nwItemClass.WORLD : "Locations",
nwItemClass.TIMELINE : "Timeline",
nwItemClass.OBJECT : "Objects",
nwItemClass.ENTITY : "Entity",
nwItemClass.CUSTOM : "Custom",
nwItemClass.ARCHIVE : "Outtakes",
nwItemClass.TRASH : "Trash",
nwItemClass.NO_CLASS : QT_TRANSLATE_NOOP("Constant", "None"),
nwItemClass.NOVEL : QT_TRANSLATE_NOOP("Constant", "Novel"),
nwItemClass.PLOT : QT_TRANSLATE_NOOP("Constant", "Plot"),
nwItemClass.CHARACTER : QT_TRANSLATE_NOOP("Constant", "Characters"),
nwItemClass.WORLD : QT_TRANSLATE_NOOP("Constant", "Locations"),
nwItemClass.TIMELINE : QT_TRANSLATE_NOOP("Constant", "Timeline"),
nwItemClass.OBJECT : QT_TRANSLATE_NOOP("Constant", "Objects"),
nwItemClass.ENTITY : QT_TRANSLATE_NOOP("Constant", "Entity"),
nwItemClass.CUSTOM : QT_TRANSLATE_NOOP("Constant", "Custom"),
nwItemClass.ARCHIVE : QT_TRANSLATE_NOOP("Constant", "Outtakes"),
nwItemClass.TRASH : QT_TRANSLATE_NOOP("Constant", "Trash"),
}
CLASS_FLAG = {
nwItemClass.NO_CLASS : "0",
@@ -158,15 +165,15 @@ class nwLabels():
nwItemClass.TRASH : "cls_trash",
}
LAYOUT_NAME = {
nwItemLayout.NO_LAYOUT : "None",
nwItemLayout.TITLE : "Title Page",
nwItemLayout.BOOK : "Book",
nwItemLayout.PAGE : "Plain Page",
nwItemLayout.PARTITION : "Partition",
nwItemLayout.UNNUMBERED : "Unnumbered",
nwItemLayout.CHAPTER : "Chapter",
nwItemLayout.SCENE : "Scene",
nwItemLayout.NOTE : "Note",
nwItemLayout.NO_LAYOUT : QT_TRANSLATE_NOOP("Constant", "None"),
nwItemLayout.TITLE : QT_TRANSLATE_NOOP("Constant", "Title Page"),
nwItemLayout.BOOK : QT_TRANSLATE_NOOP("Constant", "Book"),
nwItemLayout.PAGE : QT_TRANSLATE_NOOP("Constant", "Plain Page"),
nwItemLayout.PARTITION : QT_TRANSLATE_NOOP("Constant", "Partition"),
nwItemLayout.UNNUMBERED : QT_TRANSLATE_NOOP("Constant", "Unnumbered"),
nwItemLayout.CHAPTER : QT_TRANSLATE_NOOP("Constant", "Chapter"),
nwItemLayout.SCENE : QT_TRANSLATE_NOOP("Constant", "Scene"),
nwItemLayout.NOTE : QT_TRANSLATE_NOOP("Constant", "Note"),
}
LAYOUT_FLAG = {
nwItemLayout.NO_LAYOUT : "Xo",
@@ -180,27 +187,27 @@ class nwLabels():
nwItemLayout.NOTE : "Nt",
}
KEY_NAME = {
nwKeyWords.TAG_KEY : "Tag",
nwKeyWords.POV_KEY : "Point of View",
nwKeyWords.FOCUS_KEY : "Focus",
nwKeyWords.CHAR_KEY : "Characters",
nwKeyWords.PLOT_KEY : "Plot",
nwKeyWords.TIME_KEY : "Timeline",
nwKeyWords.WORLD_KEY : "Locations",
nwKeyWords.OBJECT_KEY : "Objects",
nwKeyWords.ENTITY_KEY : "Entities",
nwKeyWords.CUSTOM_KEY : "Custom",
nwKeyWords.TAG_KEY : QT_TRANSLATE_NOOP("Constant", "Tag"),
nwKeyWords.POV_KEY : QT_TRANSLATE_NOOP("Constant", "Point of View"),
nwKeyWords.FOCUS_KEY : QT_TRANSLATE_NOOP("Constant", "Focus"),
nwKeyWords.CHAR_KEY : QT_TRANSLATE_NOOP("Constant", "Characters"),
nwKeyWords.PLOT_KEY : QT_TRANSLATE_NOOP("Constant", "Plot"),
nwKeyWords.TIME_KEY : QT_TRANSLATE_NOOP("Constant", "Timeline"),
nwKeyWords.WORLD_KEY : QT_TRANSLATE_NOOP("Constant", "Locations"),
nwKeyWords.OBJECT_KEY : QT_TRANSLATE_NOOP("Constant", "Objects"),
nwKeyWords.ENTITY_KEY : QT_TRANSLATE_NOOP("Constant", "Entities"),
nwKeyWords.CUSTOM_KEY : QT_TRANSLATE_NOOP("Constant", "Custom"),
}
OUTLINE_COLS = {
nwOutline.TITLE : "Title",
nwOutline.LEVEL : "Level",
nwOutline.LABEL : "Document",
nwOutline.LINE : "Line",
nwOutline.CCOUNT : "Chars",
nwOutline.WCOUNT : "Words",
nwOutline.PCOUNT : "Pars",
nwOutline.POV : "POV",
nwOutline.FOCUS : "Focus",
nwOutline.TITLE : QT_TRANSLATE_NOOP("Constant", "Title"),
nwOutline.LEVEL : QT_TRANSLATE_NOOP("Constant", "Level"),
nwOutline.LABEL : QT_TRANSLATE_NOOP("Constant", "Document"),
nwOutline.LINE : QT_TRANSLATE_NOOP("Constant", "Line"),
nwOutline.CCOUNT : QT_TRANSLATE_NOOP("Constant", "Chars"),
nwOutline.WCOUNT : QT_TRANSLATE_NOOP("Constant", "Words"),
nwOutline.PCOUNT : QT_TRANSLATE_NOOP("Constant", "Pars"),
nwOutline.POV : QT_TRANSLATE_NOOP("Constant", "POV"),
nwOutline.FOCUS : QT_TRANSLATE_NOOP("Constant", "Focus"),
nwOutline.CHAR : KEY_NAME[nwKeyWords.CHAR_KEY],
nwOutline.PLOT : KEY_NAME[nwKeyWords.PLOT_KEY],
nwOutline.TIME : KEY_NAME[nwKeyWords.TIME_KEY],
@@ -208,7 +215,7 @@ class nwLabels():
nwOutline.OBJECT : KEY_NAME[nwKeyWords.OBJECT_KEY],
nwOutline.ENTITY : KEY_NAME[nwKeyWords.ENTITY_KEY],
nwOutline.CUSTOM : KEY_NAME[nwKeyWords.CUSTOM_KEY],
nwOutline.SYNOP : "Synopsis",
nwOutline.SYNOP : QT_TRANSLATE_NOOP("Constant", "Synopsis"),
}
# END Class nwLabels
@@ -218,28 +225,28 @@ class nwQuotes():
Source: https://en.wikipedia.org/wiki/Quotation_mark
"""
SYMBOLS = {
"\u0027" : "Straight single quotation mark",
"\u0022" : "Straight double quotation mark",
"\u0027" : QT_TRANSLATE_NOOP("Constant", "Straight single quotation mark"),
"\u0022" : QT_TRANSLATE_NOOP("Constant", "Straight double quotation mark"),
"\u2018" : "Left single quotation mark",
"\u2019" : "Right single quotation mark",
"\u201a" : "Single low-9 quotation mark",
"\u201b" : "Single high-reversed-9 quotation mark",
"\u201c" : "Left double quotation mark",
"\u201d" : "Right double quotation mark",
"\u201e" : "Double low-9 quotation mark",
"\u201f" : "Double high-reversed-9 quotation mark",
"\u2e42" : "Double low-reversed-9 quotation mark",
"\u2018" : QT_TRANSLATE_NOOP("Constant", "Left single quotation mark"),
"\u2019" : QT_TRANSLATE_NOOP("Constant", "Right single quotation mark"),
"\u201a" : QT_TRANSLATE_NOOP("Constant", "Single low-9 quotation mark"),
"\u201b" : QT_TRANSLATE_NOOP("Constant", "Single high-reversed-9 quotation mark"),
"\u201c" : QT_TRANSLATE_NOOP("Constant", "Left double quotation mark"),
"\u201d" : QT_TRANSLATE_NOOP("Constant", "Right double quotation mark"),
"\u201e" : QT_TRANSLATE_NOOP("Constant", "Double low-9 quotation mark"),
"\u201f" : QT_TRANSLATE_NOOP("Constant", "Double high-reversed-9 quotation mark"),
"\u2e42" : QT_TRANSLATE_NOOP("Constant", "Double low-reversed-9 quotation mark"),
"\u2039" : "Single left-pointing angle quotation mark",
"\u203a" : "Single right-pointing angle quotation mark",
"\u00ab" : "Left-pointing double angle quotation mark",
"\u00bb" : "Right-pointing double angle quotation mark",
"\u2039" : QT_TRANSLATE_NOOP("Constant", "Single left-pointing angle quotation mark"),
"\u203a" : QT_TRANSLATE_NOOP("Constant", "Single right-pointing angle quotation mark"),
"\u00ab" : QT_TRANSLATE_NOOP("Constant", "Left-pointing double angle quotation mark"),
"\u00bb" : QT_TRANSLATE_NOOP("Constant", "Right-pointing double angle quotation mark"),
"\u300c" : "Left corner bracket",
"\u300d" : "Right corner bracket",
"\u300e" : "Left white corner bracket",
"\u300f" : "Right white corner bracket",
"\u300c" : QT_TRANSLATE_NOOP("Constant", "Left corner bracket"),
"\u300d" : QT_TRANSLATE_NOOP("Constant", "Right corner bracket"),
"\u300e" : QT_TRANSLATE_NOOP("Constant", "Left white corner bracket"),
"\u300f" : QT_TRANSLATE_NOOP("Constant", "Right white corner bracket"),
}
# 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 os
from functools import partial
from PyQt5.QtCore import QCoreApplication
from nw.constants import nwAlert
from nw.common import isHandle
from nw.constants import nwItemLayout, nwItemClass
@@ -48,6 +52,7 @@ class NWDoc():
# Internal Mapping
self.makeAlert = self.theParent.makeAlert
self.tr = partial(QCoreApplication.translate, "NWDoc")
return
@@ -111,7 +116,7 @@ class NWDoc():
theText += inFile.read()
except Exception as e:
self.makeAlert(["Failed to open document file.", str(e)], nwAlert.ERROR)
self.makeAlert([self.tr("Failed to open document file."), str(e)], nwAlert.ERROR)
# Note: Document must be cleared in case of an io error,
# or else the auto-save or save will try to overwrite it
# with an empty file. Return None to alert the caller.
@@ -124,7 +129,9 @@ class NWDoc():
return ""
if showStatus and not isOrphan:
self.theParent.setStatus("Opened Document: %s" % self._theItem.itemName)
self.theParent.setStatus(
self.tr("Opened Document: {0}").format(self._theItem.itemName)
)
return theText
@@ -158,7 +165,7 @@ class NWDoc():
outFile.write(docMeta)
outFile.write(docText)
except Exception as e:
self.makeAlert(["Could not save document.", str(e)], nwAlert.ERROR)
self.makeAlert([self.tr("Could not save document."), str(e)], nwAlert.ERROR)
return False
# If we're here, the file was successfully saved, so we can
@@ -168,7 +175,9 @@ class NWDoc():
os.rename(docTemp, docPath)
if self._theItem is not None:
self.theParent.setStatus("Saved Document: %s" % self._theItem.itemName)
self.theParent.setStatus(
self.tr("Saved Document: {0}").format(self._theItem.itemName)
)
return True
@@ -191,7 +200,9 @@ class NWDoc():
os.unlink(chkFile)
logger.debug("Deleted: %s" % chkFile)
except Exception as e:
self.makeAlert(["Could not delete document file.", str(e)], nwAlert.ERROR)
self.makeAlert(
[self.tr("Could not delete document file."), str(e)], nwAlert.ERROR
)
return False
return True
-4
View File
@@ -228,10 +228,6 @@ class NWIndex():
if self.indexBroken:
self.clearIndex()
self.theParent.makeAlert(
"The project index is outdated or broken. Rebuilding index.",
nwAlert.WARN
)
return
+164 -123
View File
@@ -32,6 +32,9 @@ import shutil
from lxml import etree
from time import time
from functools import partial
from PyQt5.QtCore import QCoreApplication
from nw.core.tree import NWTree
from nw.core.item import NWItem
@@ -43,7 +46,7 @@ from nw.common import (
makeFileNameSafe, hexToInt
)
from nw.constants import (
nwFiles, nwItemType, nwItemClass, nwItemLayout, nwLabels, nwAlert
trConst, nwFiles, nwItemType, nwItemClass, nwItemLayout, nwLabels, nwAlert
)
logger = logging.getLogger(__name__)
@@ -99,11 +102,12 @@ class NWProject():
self.notesWCount = 0 # Total number of words in note files
self.doBackup = True # Run project backup on exit
# Set Defaults
self.clearProject()
# Internal Mapping
self.makeAlert = self.theParent.makeAlert
self.tr = partial(QCoreApplication.translate, "NWProject")
# Set Defaults
self.clearProject()
return
@@ -159,7 +163,7 @@ class NWProject():
trashHandle = self.projTree.trashRoot()
if trashHandle is None:
newItem = NWItem(self)
newItem.setName("Trash")
newItem.setName(self.tr("Trash"))
newItem.setType(nwItemType.TRASH)
newItem.setClass(nwItemClass.TRASH)
self.projTree.append(None, None, newItem)
@@ -199,7 +203,7 @@ class NWProject():
self.autoReplace = {}
self.titleFormat = {
"title" : r"%title%",
"chapter" : r"Chapter %ch%: %title%",
"chapter" : self.tr(r"Chapter %ch%: %title%"),
"unnumbered" : r"%title%",
"scene" : r"* * *",
"section" : r"",
@@ -207,15 +211,15 @@ class NWProject():
self.spellCheck = False
self.autoOutline = True
self.statusItems = NWStatus()
self.statusItems.addEntry("New", (100, 100, 100))
self.statusItems.addEntry("Note", (200, 50, 0))
self.statusItems.addEntry("Draft", (200, 150, 0))
self.statusItems.addEntry("Finished", (50, 200, 0))
self.statusItems.addEntry(self.tr("New"), (100, 100, 100))
self.statusItems.addEntry(self.tr("Note"), (200, 50, 0))
self.statusItems.addEntry(self.tr("Draft"), (200, 150, 0))
self.statusItems.addEntry(self.tr("Finished"), (50, 200, 0))
self.importItems = NWStatus()
self.importItems.addEntry("New", (100, 100, 100))
self.importItems.addEntry("Minor", (200, 50, 0))
self.importItems.addEntry("Major", (200, 150, 0))
self.importItems.addEntry("Main", (50, 200, 0))
self.importItems.addEntry(self.tr("New"), (100, 100, 100))
self.importItems.addEntry(self.tr("Minor"), (200, 50, 0))
self.importItems.addEntry(self.tr("Major"), (200, 150, 0))
self.importItems.addEntry(self.tr("Main"), (50, 200, 0))
self.lastEdited = None
self.lastViewed = None
self.lastWCount = 0
@@ -241,7 +245,7 @@ class NWProject():
# Project Settings
projPath = projData.get("projPath", None)
projName = projData.get("projName", "New Project")
projName = projData.get("projName", self.tr("New Project"))
projTitle = projData.get("projTitle", "")
projAuthors = projData.get("projAuthors", "")
@@ -258,7 +262,7 @@ class NWProject():
titlePage = "# %s\n\n" % (self.bookTitle if self.bookTitle else self.projName)
if self.bookAuthors:
titlePage = "%sBy %s\n" % (titlePage, ", ".join(self.bookAuthors))
titlePage = "%s%s %s\n" % (titlePage, self.tr("By"), self.getAuthors())
# Document object for writing files
aDoc = NWDoc(self, self.theParent)
@@ -267,14 +271,14 @@ class NWProject():
# Creating a minimal project with a few root folders and a
# single chapter folder with a single file.
xHandle = {}
xHandle[1] = self.newRoot("Novel", nwItemClass.NOVEL)
xHandle[2] = self.newRoot("Plot", nwItemClass.PLOT)
xHandle[3] = self.newRoot("Characters", nwItemClass.CHARACTER)
xHandle[4] = self.newRoot("World", nwItemClass.WORLD)
xHandle[5] = self.newFile("Title Page", nwItemClass.NOVEL, xHandle[1])
xHandle[6] = self.newFolder("New Chapter", nwItemClass.NOVEL, xHandle[1])
xHandle[7] = self.newFile("New Chapter", nwItemClass.NOVEL, xHandle[6])
xHandle[8] = self.newFile("New Scene", nwItemClass.NOVEL, xHandle[6])
xHandle[1] = self.newRoot(self.tr("Novel"), nwItemClass.NOVEL)
xHandle[2] = self.newRoot(self.tr("Plot"), nwItemClass.PLOT)
xHandle[3] = self.newRoot(self.tr("Characters"), nwItemClass.CHARACTER)
xHandle[4] = self.newRoot(self.tr("World"), nwItemClass.WORLD)
xHandle[5] = self.newFile(self.tr("Title Page"), nwItemClass.NOVEL, xHandle[1])
xHandle[6] = self.newFolder(self.tr("New Chapter"), nwItemClass.NOVEL, xHandle[1])
xHandle[7] = self.newFile(self.tr("New Chapter"), nwItemClass.NOVEL, xHandle[6])
xHandle[8] = self.newFile(self.tr("New Scene"), nwItemClass.NOVEL, xHandle[6])
self.projTree.setFileItemLayout(xHandle[5], nwItemLayout.TITLE)
self.projTree.setFileItemLayout(xHandle[7], nwItemLayout.CHAPTER)
@@ -284,11 +288,11 @@ class NWProject():
aDoc.clearDocument()
aDoc.openDocument(xHandle[7], showStatus=False)
aDoc.saveDocument("## New Chapter\n\n")
aDoc.saveDocument("## %s\n\n" % self.tr("New Chapter"))
aDoc.clearDocument()
aDoc.openDocument(xHandle[8], showStatus=False)
aDoc.saveDocument("### New Scene\n\n")
aDoc.saveDocument("### %s\n\n" % self.tr("New Scene"))
aDoc.clearDocument()
elif popCustom:
@@ -297,13 +301,13 @@ class NWProject():
# wizard's custom page.
# Create root folders
nHandle = self.newRoot("Novel", nwItemClass.NOVEL)
nHandle = self.newRoot(self.tr("Novel"), nwItemClass.NOVEL)
for newRoot in projData.get("addRoots", []):
if newRoot in nwItemClass:
self.newRoot(nwLabels.CLASS_NAME[newRoot], newRoot)
self.newRoot(trConst(nwLabels.CLASS_NAME[newRoot]), newRoot)
# Create a title page
tHandle = self.newFile("Title Page", nwItemClass.NOVEL, nHandle)
tHandle = self.newFile(self.tr("Title Page"), nwItemClass.NOVEL, nHandle)
self.projTree.setFileItemLayout(tHandle, nwItemLayout.TITLE)
aDoc.openDocument(tHandle, showStatus=False)
@@ -318,7 +322,7 @@ class NWProject():
# Create chapters
if numChapters > 0:
for ch in range(numChapters):
chTitle = "Chapter %d" % (ch+1)
chTitle = self.tr("Chapter {0}").format(f"{ch+1:d}")
pHandle = nHandle
if chFolders:
pHandle = self.newFolder(chTitle, nwItemClass.NOVEL, nHandle)
@@ -333,7 +337,7 @@ class NWProject():
# Create chapter scenes
if numScenes > 0:
for sc in range(numScenes):
scTitle = "Scene %d.%d" % (ch+1, sc+1)
scTitle = self.tr("Scene {0}").format(f"{ch+1:d}.{sc+1:d}")
sHandle = self.newFile(scTitle, nwItemClass.NOVEL, pHandle)
aDoc.openDocument(sHandle, showStatus=False)
@@ -343,7 +347,7 @@ class NWProject():
# Create scenes (no chapters)
elif numScenes > 0:
for sc in range(numScenes):
scTitle = "Scene %d" % (sc+1)
scTitle = self.tr("Scene {0}").format(f"{sc+1:d}")
sHandle = self.newFile(scTitle, nwItemClass.NOVEL, nHandle)
aDoc.openDocument(sHandle, showStatus=False)
@@ -367,7 +371,7 @@ class NWProject():
if not os.path.isfile(fileName):
fileName = os.path.join(fileName, nwFiles.PROJ_FILE)
if not os.path.isfile(fileName):
self.makeAlert("File not found: %s" % fileName, nwAlert.ERROR)
self.makeAlert(self.tr("File not found: {0}").format(fileName), nwAlert.ERROR)
return False
self.clearProject()
@@ -416,16 +420,20 @@ class NWProject():
try:
nwXML = etree.parse(fileName)
except Exception as e:
self.makeAlert(["Failed to parse project xml.", str(e)], nwAlert.ERROR)
self.makeAlert([self.tr("Failed to parse project xml."), str(e)], nwAlert.ERROR)
# Trying to open backup file instead
backFile = fileName[:-3]+"bak"
if os.path.isfile(backFile):
self.makeAlert("Attempting to open backup project file instead.", nwAlert.INFO)
self.makeAlert(
self.tr("Attempting to open backup project file instead."), nwAlert.INFO
)
try:
nwXML = etree.parse(backFile)
except Exception as e:
self.makeAlert(["Failed to parse project xml.", str(e)], nwAlert.ERROR)
self.makeAlert(
[self.tr("Failed to parse project xml."), str(e)], nwAlert.ERROR
)
self.clearProject()
return False
else:
@@ -435,9 +443,9 @@ class NWProject():
xRoot = nwXML.getroot()
nwxRoot = xRoot.tag
appVersion = xRoot.attrib.get("appVersion", "Unknown")
appVersion = xRoot.attrib.get("appVersion", self.tr("Unknown"))
hexVersion = xRoot.attrib.get("hexVersion", "0x0")
fileVersion = xRoot.attrib.get("fileVersion", "Unknown")
fileVersion = xRoot.attrib.get("fileVersion", self.tr("Unknown"))
# The following are deprecated and will be removed
# The settings have been moved to the <project> tag
@@ -453,7 +461,7 @@ class NWProject():
if not nwxRoot == "novelWriterXML":
self.makeAlert(
"Project file does not appear to be a novelWriterXML file.",
self.tr("Project file does not appear to be a novelWriterXML file."),
nwAlert.ERROR
)
self.clearProject()
@@ -472,24 +480,32 @@ class NWProject():
# read the file. Introduced in version 0.10.
if fileVersion == "1.0":
msgYes = self.theParent.askQuestion("Old Project Version", (
"The project file and data is created by a novelWriter version "
"lower than 0.7. Do you want to upgrade the project to the "
"most recent format?<br><br>Note that after the upgrade, you "
"cannot open the project with an older version of novelWriter "
"any more, so make sure you have a recent backup."
))
msgYes = self.theParent.askQuestion(
self.tr("Old Project Version"),
"%s<br><br>%s" % (
self.tr(
"The project file and data is created by a novelWriter version "
"lower than 0.7. Do you want to upgrade the project to the "
"most recent format?"
),
self.tr(
"Note that after the upgrade, you cannot open the project with "
"an older version of novelWriter any more, so make sure you "
"have a recent backup."
)
)
)
if not msgYes:
self.clearProject()
return False
elif fileVersion != "1.1" and fileVersion != "1.2":
self.makeAlert((
"Unknown or unsupported novelWriter project file format. "
"The project cannot be opened by this version of novelWriter. "
"The file was saved with novelWriter version {vers:s}."
).format(
vers = appVersion,
self.tr(
"Unknown or unsupported novelWriter project file format. "
"The project cannot be opened by this version of novelWriter. "
"The file was saved with novelWriter version {0}."
).format(appVersion)
), nwAlert.ERROR)
self.clearProject()
return False
@@ -498,14 +514,15 @@ class NWProject():
# =========================
if hexToInt(hexVersion) > hexToInt(nw.__hexversion__):
msgYes = self.theParent.askQuestion("Version Conflict", (
"This project was saved by a newer version of novelWriter, version %s. "
"This is version %s. If you continue to open the project, some attributes "
"and settings may not be preserved, but the overall project should be fine. "
"Continue opening the project?"
) % (
appVersion, nw.__version__
))
msgYes = self.theParent.askQuestion(
self.tr("Version Conflict"),
self.tr(
"This project was saved by a newer version of novelWriter, version "
"{0}. This is version {1}. If you continue to open the project, "
"some attributes and settings may not be preserved, but the "
"overall project should be fine. Continue opening the project?"
).format(appVersion, nw.__version__)
)
if not msgYes:
self.clearProject()
return False
@@ -604,7 +621,7 @@ class NWProject():
self.mainConf.updateRecentCache(self.projPath, self.projName, self.lastWCount, time())
self.mainConf.saveRecentCache()
self.theParent.setStatus("Opened Project: %s" % self.projName)
self.theParent.setStatus(self.tr("Opened Project: {0}").format(self.projName))
self._scanProjectFolder()
self.loadProjectLocalisation(self.projLang)
@@ -626,7 +643,7 @@ class NWProject():
"""
if self.projPath is None:
self.makeAlert(
"Project path not set, cannot save project.", nwAlert.ERROR
self.tr("Project path not set, cannot save project."), nwAlert.ERROR
)
return False
@@ -705,7 +722,7 @@ class NWProject():
xml_declaration = True
))
except Exception as e:
self.makeAlert(["Failed to save project.", str(e)], nwAlert.ERROR)
self.makeAlert([self.tr("Failed to save project."), str(e)], nwAlert.ERROR)
return False
# If we're here, the file was successfully saved,
@@ -724,7 +741,7 @@ class NWProject():
self.mainConf.saveRecentCache()
self._writeLockFile()
self.theParent.setStatus("Saved Project: %s" % self.projName)
self.theParent.setStatus(self.tr("Saved Project: {0}").format(self.projName))
self.setProjectChanged(False)
return True
@@ -776,27 +793,33 @@ class NWProject():
return False
logger.info("Backing up project")
self.theParent.setStatus("Backing up project ...")
self.theParent.setStatus(self.tr("Backing up project ..."))
if self.mainConf.backupPath is None or self.mainConf.backupPath == "":
self.theParent.makeAlert((
"Cannot backup project because no backup path is set. "
"Please set a valid backup location in Tools > Preferences."
), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr(
"Cannot backup project because no backup path is set. "
"Please set a valid backup location in Tools > Preferences."
), nwAlert.ERROR
)
return False
if self.projName is None or self.projName == "":
self.theParent.makeAlert((
"Cannot backup project because no project name is set. "
"Please set a Working Title in Project > Project Settings."
), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr(
"Cannot backup project because no project name is set. "
"Please set a Working Title in Project > Project Settings."
), nwAlert.ERROR
)
return False
if not os.path.isdir(self.mainConf.backupPath):
self.theParent.makeAlert((
"Cannot backup project because the backup path does not exist. "
"Please set a valid backup location in Tools > Preferences."
), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr(
"Cannot backup project because the backup path does not exist. "
"Please set a valid backup location in Tools > Preferences."
), nwAlert.ERROR
)
return False
cleanName = makeFileNameSafe(self.projName)
@@ -807,20 +830,22 @@ class NWProject():
logger.debug("Created folder %s" % baseDir)
except Exception as e:
self.theParent.makeAlert(
["Could not create backup folder.", str(e)],
[self.tr("Could not create backup folder."), str(e)],
nwAlert.ERROR
)
return False
if os.path.commonpath([self.projPath, baseDir]) == self.projPath:
self.theParent.makeAlert((
"Cannot backup project because the backup path is within the "
"project folder to be backed up. Please choose a different "
"backup path in Tools > Preferences."
), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr(
"Cannot backup project because the backup path is within the "
"project folder to be backed up. Please choose a different "
"backup path in Tools > Preferences."
), nwAlert.ERROR
)
return False
archName = "Backup from %s" % formatTimeStamp(time(), fileSafe=True)
archName = self.tr("Backup from {0}").format(formatTimeStamp(time(), fileSafe=True))
baseName = os.path.join(baseDir, archName)
try:
@@ -830,18 +855,21 @@ class NWProject():
logger.info("Backup written to: %s" % archName)
if doNotify:
self.theParent.makeAlert(
"Backup archive file written to: %s.zip" % os.path.join(cleanName, archName),
nwAlert.INFO
self.tr(
"Backup archive file written to: {0}"
).format(
f"{os.path.join(cleanName, archName)}.zip"
), nwAlert.INFO
)
except Exception as e:
self.theParent.makeAlert(
["Could not write backup archive.", str(e)],
[self.tr("Could not write backup archive."), str(e)],
nwAlert.ERROR
)
return False
self.theParent.setStatus("Project backed up to '%s.zip'" % baseName)
self.theParent.setStatus(self.tr("Project backed up to '{0}'").format(f"{baseName}.zip"))
return True
@@ -868,7 +896,7 @@ class NWProject():
isSuccess = True
except Exception as e:
self.makeAlert(
["Failed to create a new example project.", str(e)], nwAlert.ERROR
[self.tr("Failed to create a new example project."), str(e)], nwAlert.ERROR
)
elif os.path.isdir(srcSample):
@@ -890,14 +918,16 @@ class NWProject():
except Exception as e:
self.makeAlert(
["Failed to create a new example project.", str(e)], nwAlert.ERROR
[self.tr("Failed to create a new example project."), str(e)], nwAlert.ERROR
)
else:
self.makeAlert((
"Failed to create a new example project. Could not find the "
"necessary files. They seem to be missing from this installation."
), nwAlert.ERROR)
self.makeAlert(
self.tr(
"Failed to create a new example project. Could not find the "
"necessary files. They seem to be missing from this installation."
), nwAlert.ERROR
)
if isSuccess:
self.clearProject()
@@ -928,16 +958,18 @@ class NWProject():
logger.debug("Created folder %s" % projPath)
except Exception as e:
self.theParent.makeAlert((
["Could not create new project folder.", str(e)]
[self.tr("Could not create new project folder."), str(e)]
), nwAlert.ERROR)
return False
if os.path.isdir(projPath):
if os.listdir(self.projPath):
self.theParent.makeAlert((
"New project folder is not empty. "
"Each project requires a dedicated project folder."
), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr(
"New project folder is not empty. "
"Each project requires a dedicated project folder."
), nwAlert.ERROR
)
return False
self.ensureFolderStructure()
@@ -984,17 +1016,21 @@ class NWProject():
self.doBackup = doBackup
if doBackup:
if not os.path.isdir(self.mainConf.backupPath):
self.theParent.makeAlert((
"You must set a valid backup path in preferences to use "
"the automatic project backup feature."
), nwAlert.WARN)
self.theParent.makeAlert(
self.tr(
"You must set a valid backup path in preferences to use "
"the automatic project backup feature."
), nwAlert.WARN
)
return False
if self.projName == "":
self.theParent.makeAlert((
"You must set a valid project name in project settings to "
"use the automatic project backup feature."
), nwAlert.WARN)
self.theParent.makeAlert(
self.tr(
"You must set a valid project name in project settings to "
"use the automatic project backup feature."
), nwAlert.WARN
)
return False
return True
@@ -1124,8 +1160,8 @@ class NWProject():
if nAuth == 1:
authString = self.bookAuthors[0]
elif nAuth > 1:
authString = "%s and %s" % (
", ".join(self.bookAuthors[0:-1]), self.bookAuthors[-1]
authString = "%s %s %s" % (
", ".join(self.bookAuthors[0:-1]), self.tr("and"), self.bookAuthors[-1]
)
return authString
@@ -1374,7 +1410,7 @@ class NWProject():
# Report status
if len(orphanFiles) > 0:
self.makeAlert(
"Found %d orphaned file(s) in project folder." % len(orphanFiles),
self.tr("Found {0} orphaned file(s) in project folder.").format(len(orphanFiles)),
nwAlert.WARN
)
else:
@@ -1385,6 +1421,7 @@ class NWProject():
aDoc = NWDoc(self, self.theParent)
nOrph = 0
noWhere = False
oPrefix = self.tr("Recovered")
for oHandle in orphanFiles:
# Look for meta data
@@ -1396,10 +1433,12 @@ class NWProject():
oName, oParent, oClass, oLayout = aDoc.getMeta()
if oName:
oName = "Recovered: %s" % oName.lstrip("Recovered: ")
oName = self.tr("[{0}] {1}").format(
oPrefix, oName.replace("[%s]" % oPrefix, "").strip()
)
else:
nOrph += 1
oName = "Recovered File %d" % nOrph
oName = self.tr("Recovered File {0}").format(nOrph)
# Recover file meta data
if oClass is None:
@@ -1426,10 +1465,12 @@ class NWProject():
self.projTree.append(oHandle, oParent, orphItem)
if noWhere:
self.makeAlert((
"One or more orphaned files could not be added back into the "
"project. Make sure at least a Novel root folder exists."
), nwAlert.WARN)
self.makeAlert(
self.tr(
"One or more orphaned files could not be added back into the "
"project. Make sure at least a Novel root folder exists."
), nwAlert.WARN
)
return True
@@ -1476,7 +1517,7 @@ class NWProject():
"""
theData = os.path.join(self.projPath, theFolder)
if not os.path.isdir(theData):
errList.append("Not a folder: %s" % theData)
errList.append(self.tr("Not a folder: {0}").format(theData))
return errList
logger.info("Old data folder %s found" % theFolder)
@@ -1499,7 +1540,7 @@ class NWProject():
logger.info("Moved file: %s" % theFile)
logger.info("New location: %s" % newPath)
except Exception:
errList.append("Could not move: %s" % theFile)
errList.append(self.tr("Could not move: {0}").format(theFile))
logger.error("Could not move: %s" % theFile)
nw.logException()
@@ -1508,7 +1549,7 @@ class NWProject():
os.unlink(theFile)
logger.info("Deleted file: %s" % theFile)
except Exception:
errList.append("Could not delete: %s" % theFile)
errList.append(self.tr("Could not delete: {0}").format(theFile))
logger.error("Could not delete: %s" % theFile)
nw.logException()
@@ -1521,10 +1562,10 @@ class NWProject():
# ==================
try:
os.rmdir(theData)
logger.info("Removed folder: %s" % theFolder)
logger.info("Deleted folder: %s" % theFolder)
except Exception:
errList.append("Failed to remove: %s" % theFolder)
logger.error("Failed to remove: %s" % theFolder)
errList.append(self.tr("Could not delete: {0}").format(theFolder))
logger.error("Could not delete: %s" % theFolder)
nw.logException()
return errList
@@ -1535,7 +1576,7 @@ class NWProject():
"""
theJunk = os.path.join(self.projPath, "junk")
if not self._checkFolder(theJunk):
return "Could not make folder: %s" % theJunk
return self.tr("Could not make folder: {0}").format(theJunk)
theSrc = os.path.join(theDir, theItem)
theDst = os.path.join(theJunk, theItem)
@@ -1546,7 +1587,7 @@ class NWProject():
except Exception:
logger.error("Could not move item %s to junk." % theSrc)
nw.logException()
return "Could not move item %s to junk." % theSrc
return self.tr("Could not move item {0} to {1}.").format(theSrc, theJunk)
return ""
+4 -17
View File
@@ -29,8 +29,6 @@ import logging
import os
import difflib
from nw.constants import nwConst, isoLanguage
logger = logging.getLogger(__name__)
# =============================================================================================== #
@@ -89,16 +87,6 @@ class NWSpellCheck():
"""
return "", ""
@staticmethod
def expandLanguage(spTag):
"""Translate a language tag to something more user friendly.
"""
spBits = spTag.split("_")
spLang = isoLanguage.ISO_639_1.get(spBits[0], spBits[0])
if len(spBits) > 1:
spLang += " (%s)" % spBits[1]
return spLang
##
# Internal Functions
##
@@ -196,10 +184,10 @@ class NWSpellEnchant(NWSpellCheck):
try:
import enchant
for spTag, spProvider in enchant.list_dicts():
spName = "%s [%s]" % (self.expandLanguage(spTag), spProvider.name)
retList.append((spTag, spName))
retList.append((spTag, spProvider.name))
except Exception:
logger.error("Failed to list languages for enchant spell checking")
return retList
def describeDict(self):
@@ -332,8 +320,7 @@ class NWSpellSimple(NWSpellCheck):
if fExt != ".dict":
continue
spName = "%s [%s]" % (self.expandLanguage(fRoot), nwConst.SP_INTERNAL)
retList.append((fRoot, spName))
retList.append((fRoot, "difflib"))
return retList
@@ -341,6 +328,6 @@ class NWSpellSimple(NWSpellCheck):
"""Return the tag and provider of the currently loaded
dictionary.
"""
return self.theLang, nwConst.SP_INTERNAL
return self.theLang, ""
# END Class NWSpellSimple
+4 -3
View File
@@ -28,6 +28,7 @@ import logging
import re
from operator import itemgetter
from PyQt5.QtCore import QRegularExpression
from nw.core.document import NWDoc
@@ -281,10 +282,10 @@ class Tokenizer():
docSize = len(self.theText)
if docSize > nwConst.MAX_DOCSIZE:
errVal = "Document '%s' is too big (%.2f MB). Skipping." % (
self.theItem.itemName, docSize/1.0e6
errVal = self.tr("Document '{0}' is too big ({1} MB). Skipping.").format(
self.theItem.itemName, f"{docSize/1.0e6:.2f}"
)
self.theText = "# ERROR\n\n%s\n\n" % errVal
self.theText = "# %s\n\n%s\n\n" % (self.tr("ERROR"), errVal)
self.errData.append(errVal)
self.isNone = self.theItem.itemLayout == nwItemLayout.NO_LAYOUT
+91 -41
View File
@@ -120,35 +120,40 @@ def numberToRoman(numVal, isLower=False):
return romNum.lower() if isLower else romNum
# =============================================================================================== #
# Convert an Integer to a Word Number
# Convert an Integer to a Number String
# =============================================================================================== #
def numberToWord(numVal, theLanguage):
def numberToWord(numVal, theLang):
"""Wrapper for converting numbers to words for chapter headings.
"""
if not isinstance(numVal, int):
return "[NaN]"
if numVal < 0:
return "[<0]"
if numVal > 999:
return "[>999]"
numWord = ""
if theLanguage == "en":
numWord = _numberToWordEN(numVal)
theCode = theLang.split("_")[0]
if theCode == "en":
numWord = _numberToWord_EN(numVal)
elif theCode in ("nb", "nn"):
numWord = _numberToWord_NO(numVal, theCode)
else:
numWord = _numberToWordEN(numVal)
# Default to return the number as a string
numWord = str(numVal)
return numWord
def _numberToWordEN(numVal):
def _numberToWord_EN(numVal):
"""Convert numbers to English words.
"""
oneWord = ""
tenWord = ""
hunWord = ""
if not isinstance(numVal, int):
return "[NaN]"
if numVal < 0:
return "[Negative]"
if numVal > 999:
return "[Out of Range]"
if numVal == 0:
return "Zero"
@@ -156,38 +161,83 @@ def _numberToWordEN(numVal):
tenVal = (numVal - oneVal) % 100
hunVal = (numVal - tenVal - oneVal) % 1000
theHundreds = {
100: "One Hundred", 200: "Two Hundred", 300: "Three Hundred",
400: "Four Hundred", 500: "Five Hundred", 600: "Six Hundred",
700: "Seven Hundred", 800: "Eight Hundred", 900: "Nine Hundred",
}
theTens = {
20: "Twenty", 30: "Thirty", 40: "Forty", 50: "Fifty",
60: "Sixty", 70: "Seventy", 80: "Eighty", 90: "Ninety",
}
theTeens = {
0: "Ten", 1: "Eleven", 2: "Twelve", 3: "Thirteen", 4: "Fourteen",
5: "Fifteen", 6: "Sixteen", 7: "Seventeen", 8: "Eighteen", 9: "Nineteen",
}
theOnes = {
0: "", 1: "One", 2: "Two", 3: "Three", 4: "Four",
5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine",
theWords = {
0: "", 1: "One", 2: "Two", 3: "Three", 4: "Four", 5: "Five",
6: "Six", 7: "Seven", 8: "Eight", 9: "Nine", 10: "Ten",
11: "Eleven", 12: "Twelve", 13: "Thirteen", 14: "Fourteen",
15: "Fifteen", 16: "Sixteen", 17: "Seventeen", 18: "Eighteen",
19: "Nineteen", 20: "Twenty", 30: "Thirty", 40: "Forty",
50: "Fifty", 60: "Sixty", 70: "Seventy", 80: "Eighty",
90: "Ninety", 100: "One Hundred", 200: "Two Hundred",
300: "Three Hundred", 400: "Four Hundred", 500: "Five Hundred",
600: "Six Hundred", 700: "Seven Hundred", 800: "Eight Hundred",
900: "Nine Hundred",
}
retVale = ""
hunWord = theHundreds.get(hunVal, "")
retVal = ""
hunWord = theWords.get(hunVal, "")
if tenVal == 10:
oneWord = theTeens.get(oneVal, "")
retVale = f"{hunWord} {oneWord}".strip()
oneWord = theWords.get(oneVal+10, "")
retVal = f"{hunWord} {oneWord}".strip()
else:
oneWord = theOnes.get(oneVal, "")
oneWord = theWords.get(oneVal, "")
if tenVal == 0:
retVale = f"{hunWord} {oneWord}".strip()
retVal = f"{hunWord} {oneWord}".strip()
else:
tenWord = theTens.get(tenVal, "")
tenWord = theWords.get(tenVal, "")
if oneVal == 0:
retVale = f"{hunWord} {tenWord}".strip()
retVal = f"{hunWord} {tenWord}".strip()
else:
retVale = f"{hunWord} {tenWord}-{oneWord}".strip()
retVal = f"{hunWord} {tenWord}-{oneWord}".strip()
return retVale
return retVal
def _numberToWord_NO(numVal, theCode):
"""Convert numbers to Norwegian words.
"""
oneWord = ""
tenWord = ""
hunWord = ""
if numVal == 0:
return "null"
oneVal = numVal % 10
tenVal = (numVal - oneVal) % 100
hunVal = (numVal - tenVal - oneVal) % 1000
theWords = {
0: "", 1: "én", 2: "to", 3: "tre", 4: "fire", 5: "fem", 6: "seks",
7: "sju", 8: "åtte", 9: "ni", 10: "ti", 11: "elleve", 12: "tolv",
13: "tretten", 14: "fjorten", 15: "femten", 16: "seksten", 17: "sytten",
18: "atten", 19: "nitten", 20: "tjue", 30: "tretti", 40: "førti",
50: "femti", 60: "seksti", 70: "sytti", 80: "åtti", 90: "nitti",
100: "ett hundre", 200: "to hundre", 300: "tre hundre", 400: "fire hundre",
500: "fem hundre", 600: "seks hundre", 700: "sju hundre",
800: "åtte hundre", 900: "ni hundre",
}
if theCode == "nn":
theWords[1] = "ein"
theWords[100] = "eitt hundre"
retVal = ""
hunWord = theWords.get(hunVal, "")
if tenVal == 10:
oneWord = theWords.get(oneVal+10, "")
sepVal = " og " if hunWord and oneWord else " "
retVal = f"{hunWord}{sepVal}{oneWord}"
else:
oneWord = theWords.get(oneVal, "")
if tenVal == 0:
sepVal = " og " if hunWord and oneWord else " "
retVal = f"{hunWord}{sepVal}{oneWord}".strip()
else:
tenWord = theWords.get(tenVal, "")
sepVal = " og " if hunWord and tenWord else " "
if oneVal == 0:
retVal = f"{hunWord}{sepVal}{tenWord}".strip()
else:
retVal = f"{hunWord}{sepVal}{tenWord}{oneWord}".strip()
return retVal.strip()
+1
View File
@@ -68,6 +68,7 @@ class NWErrorMessage(QDialog):
self.msgBody.setReadOnly(True)
self.btnBox = QDialogButtonBox(QDialogButtonBox.Close)
self.btnBox.button(QDialogButtonBox.Close).setText(self.tr("Close"))
self.btnBox.rejected.connect(self._doClose)
# Assemble
+63 -66
View File
@@ -55,7 +55,7 @@ class GuiAbout(QDialog):
self.innerBox = QHBoxLayout()
self.innerBox.setSpacing(self.mainConf.pxInt(16))
self.setWindowTitle("About novelWriter")
self.setWindowTitle(self.tr("About novelWriter"))
self.setMinimumWidth(self.mainConf.pxInt(650))
self.setMinimumHeight(self.mainConf.pxInt(600))
@@ -90,13 +90,14 @@ class GuiAbout(QDialog):
# Main Tab Area
self.tabBox = QTabWidget()
self.tabBox.addTab(self.pageAbout, "About")
self.tabBox.addTab(self.pageNotes, "Release")
self.tabBox.addTab(self.pageLicense, "License")
self.tabBox.addTab(self.pageAbout, self.tr("About"))
self.tabBox.addTab(self.pageNotes, self.tr("Release"))
self.tabBox.addTab(self.pageLicense, self.tr("License"))
self.innerBox.addWidget(self.tabBox)
# OK Button
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok)
self.buttonBox.button(QDialogButtonBox.Ok).setText(self.tr("OK"))
self.buttonBox.accepted.connect(self._doClose)
self.outerBox.addLayout(self.innerBox)
@@ -132,80 +133,76 @@ class GuiAbout(QDialog):
"""Generate the content for the About page.
"""
listPrefix = "&nbsp;&nbsp;&bull;&nbsp;&nbsp;"
aboutMsg = (
"<h2>About novelWriter</h2>"
"<p>{copyright:s}.</p>"
"<p>Website: <a href='{website:s}'>{domain:s}</a></p>"
"<p>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.</p>"
"<p>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.</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>"
webLink = f"<a href='{nw.__url__:s}'>{nw.__domain__:s}</a>"
aboutMsg = (
"<h2>{title1}</h2>"
"<p>{copyright}.</p>"
"<p>{website}</p>"
"<p>{intro}</p>"
"<p>{license1}</p>"
"<p>{license2}</p>"
"<p>{license3}</p>"
"<h3>{title2}</h3>"
"<p>{credits}</p>"
).format(
title1 = self.tr("About novelWriter"),
copyright = nw.__copyright__,
website = nw.__url__,
domain = nw.__domain__,
website = self.tr("Website: {0}").format(webLink),
title2 = self.tr("Credits"),
credits = "<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
theIcons = self.theParent.theTheme.theIcons
if theTheme.themeName:
aboutMsg += (
"<h4>Theme: {name:s}</h4>"
"<p>"
"<b>Author:</b> {author:s}<br/>"
"<b>Credit:</b> {credit:s}<br/>"
"<b>License:</b> <a href='{lic_url:s}'>{license:s}</a>"
"</p>"
).format(
name = theTheme.themeName,
author = theTheme.themeAuthor,
credit = theTheme.themeCredit,
license = theTheme.themeLicense,
lic_url = theTheme.themeLicenseUrl,
aboutMsg += "<h4>%s</h4><p>%s<br/>%s<br/>%s</p>" % (
self.tr("Theme: {0}").format(theTheme.themeName),
self.tr("Author: {0}").format(theTheme.themeAuthor),
self.tr("Credit: {0}").format(theTheme.themeCredit),
self.tr("License: {0}").format(
f"<a href='{theTheme.themeLicenseUrl}'>{theTheme.themeLicense}</a>"
)
)
if theIcons.themeName:
aboutMsg += (
"<h4>Icons: {name:s}</h4>"
"<p>"
"<b>Author:</b> {author:s}<br/>"
"<b>Credit:</b> {credit:s}<br/>"
"<b>License:</b> <a href='{lic_url:s}'>{license:s}</a>"
"</p>"
).format(
name = theIcons.themeName,
author = theIcons.themeAuthor,
credit = theIcons.themeCredit,
license = theIcons.themeLicense,
lic_url = theIcons.themeLicenseUrl,
aboutMsg += "<h4>%s</h4><p>%s<br/>%s<br/>%s</p>" % (
self.tr("Icons: {0}").format(theIcons.themeName),
self.tr("Author: {0}").format(theIcons.themeAuthor),
self.tr("Credit: {0}").format(theIcons.themeCredit),
self.tr("License: {0}").format(
f"<a href='{theIcons.themeLicenseUrl}'>{theIcons.themeLicense}</a>"
)
)
if theTheme.syntaxName:
aboutMsg += (
"<h4>Syntax: {name:s}</h4>"
"<p>"
"<b>Author:</b> {author:s}<br/>"
"<b>Credit:</b> {credit:s}<br/>"
"<b>License:</b> <a href='{lic_url:s}'>{license:s}</a>"
"</p>"
).format(
name = theTheme.syntaxName,
author = theTheme.syntaxAuthor,
credit = theTheme.syntaxCredit,
license = theTheme.syntaxLicense,
lic_url = theTheme.syntaxLicenseUrl,
aboutMsg += "<h4>%s</h4><p>%s<br/>%s<br/>%s</p>" % (
self.tr("Syntax: {0}").format(theTheme.syntaxName),
self.tr("Author: {0}").format(theTheme.syntaxAuthor),
self.tr("Credit: {0}").format(theTheme.syntaxCredit),
self.tr("License: {0}").format(
f"<a href='{theTheme.syntaxLicenseUrl}'>{theTheme.syntaxLicense}</a>"
)
)
self.pageAbout.setHtml(aboutMsg)
+90 -90
View File
@@ -84,7 +84,7 @@ class GuiBuildNovel(QDialog):
self.htmlSize = 0 # Size of the html document
self.buildTime = 0 # The timestamp of the last build
self.setWindowTitle("Build Novel Project")
self.setWindowTitle(self.tr("Build Novel Project"))
self.setMinimumWidth(self.mainConf.pxInt(700))
self.setMinimumHeight(self.mainConf.pxInt(600))
@@ -101,27 +101,26 @@ class GuiBuildNovel(QDialog):
# Title Formats
# =============
self.titleGroup = QGroupBox("Title Formats for Novel Files", self)
self.titleGroup = QGroupBox(self.tr("Title Formats for Novel Files"), self)
self.titleForm = QGridLayout(self)
self.titleGroup.setLayout(self.titleForm)
fmtHelp = (
r"<b>Formatting Codes:</b><br>"
r"%title% for the title as set in the document<br>"
r"%ch% for chapter number (1, 2, 3)<br>"
r"%chw% for chapter number as a word (one, two)<br>"
r"%chI% for chapter number in upper case Roman<br>"
r"%chi% for chapter number in lower case Roman<br>"
r"%sc% for scene number within chapter<br>"
r"%sca% for scene number within novel"
)
fmtScHelp = (
r"<br><br>"
r"Leave blank to skip this heading, or set to a static text, like "
r"for instance '* * *', to make a separator. The separator will "
r"be centred automatically and only appear between sections of "
r"the same type."
)
fmtHelp = "<br>".join([
"<b>%s</b>" % self.tr("Formatting Codes:"),
self.tr("{0} for the title as set in the document").format(r"%title%"),
self.tr("{0} for chapter number (1, 2, 3)").format(r"%ch%"),
self.tr("{0} for chapter number as a word (one, two)").format(r"%chw%"),
self.tr("{0} for chapter number in upper case Roman").format(r"%chI%"),
self.tr("{0} for chapter number in lower case Roman").format(r"%chi%"),
self.tr("{0} for scene number within chapter").format(r"%sc%"),
self.tr("{0} for scene number within novel").format(r"%sca%"),
])
fmtScHelp = "<br><br>%s" % self.tr(
"Leave blank to skip this heading, or set to a static text, like "
"for instance '{0}', to make a separator. The separator will "
"be centred automatically and only appear between sections of "
"the same type."
).format("* * *")
xFmt = self.mainConf.pxInt(100)
self.fmtTitle = QLineEdit()
@@ -176,11 +175,11 @@ class GuiBuildNovel(QDialog):
self.boxSection = QHBoxLayout()
self.boxSection.addWidget(self.fmtSection)
titleLabel = QLabel("Title")
chapterLabel = QLabel("Chapter")
unnumbLabel = QLabel("Unnumbered")
sceneLabel = QLabel("Scene")
sectionLabel = QLabel("Section")
titleLabel = QLabel(self.tr("Title"))
chapterLabel = QLabel(self.tr("Chapter"))
unnumbLabel = QLabel(self.tr("Unnumbered"))
sceneLabel = QLabel(self.tr("Scene"))
sectionLabel = QLabel(self.tr("Section"))
self.titleForm.addWidget(titleLabel, 0, 0, 1, 1, Qt.AlignLeft)
self.titleForm.addLayout(self.boxTitle, 0, 1, 1, 1, Qt.AlignRight)
@@ -199,7 +198,7 @@ class GuiBuildNovel(QDialog):
# Font Options
# ============
self.fontGroup = QGroupBox("Font Options", self)
self.fontGroup = QGroupBox(self.tr("Font Options"), self)
self.fontForm = QGridLayout(self)
self.fontGroup.setLayout(self.fontForm)
@@ -237,11 +236,11 @@ class GuiBuildNovel(QDialog):
self.boxFont = QHBoxLayout()
self.boxFont.addWidget(self.textFont)
fontFamilyLabel = QLabel("Font family")
fontSizeLabel = QLabel("Font size")
lineHeightLabel = QLabel("Line height")
justifyLabel = QLabel("Justify text")
stylingLabel = QLabel("Disable styling")
fontFamilyLabel = QLabel(self.tr("Font family"))
fontSizeLabel = QLabel(self.tr("Font size"))
lineHeightLabel = QLabel(self.tr("Line height"))
justifyLabel = QLabel(self.tr("Justify text"))
stylingLabel = QLabel(self.tr("Disable styling"))
self.fontForm.addWidget(fontFamilyLabel, 0, 0, 1, 1, Qt.AlignLeft)
self.fontForm.addLayout(self.boxFont, 0, 1, 1, 1, Qt.AlignRight)
@@ -283,7 +282,7 @@ class GuiBuildNovel(QDialog):
# Include Options
# ===============
self.textGroup = QGroupBox("Include Options", self)
self.textGroup = QGroupBox(self.tr("Include Options"), self)
self.textForm = QGridLayout(self)
self.textGroup.setLayout(self.textForm)
@@ -307,10 +306,10 @@ class GuiBuildNovel(QDialog):
self.optState.getBool("GuiBuildNovel", "incBodyText", True)
)
synopsisLabel = QLabel("Include synopsis")
commentsLabel = QLabel("Include comments")
keywordsLabel = QLabel("Include keywords")
bodyLabel = QLabel("Include body text")
synopsisLabel = QLabel(self.tr("Include synopsis"))
commentsLabel = QLabel(self.tr("Include comments"))
keywordsLabel = QLabel(self.tr("Include keywords"))
bodyLabel = QLabel(self.tr("Include body text"))
self.textForm.addWidget(synopsisLabel, 0, 0, 1, 1, Qt.AlignLeft)
self.textForm.addWidget(self.includeSynopsis, 0, 1, 1, 1, Qt.AlignRight)
@@ -327,37 +326,34 @@ class GuiBuildNovel(QDialog):
# File Filter Options
# ===================
self.fileGroup = QGroupBox("File Filter Options", self)
self.fileGroup = QGroupBox(self.tr("File Filter Options"), self)
self.fileForm = QGridLayout(self)
self.fileGroup.setLayout(self.fileForm)
self.novelFiles = QSwitch(width=wS, height=hS)
self.novelFiles.setToolTip(
"Include files with layouts 'Book', 'Page', 'Partition', "
"'Chapter', 'Unnumbered', and 'Scene'."
)
self.novelFiles.setToolTip(self.tr("Include files with layouts other than 'Note'."))
self.novelFiles.setChecked(
self.optState.getBool("GuiBuildNovel", "addNovel", True)
)
self.noteFiles = QSwitch(width=wS, height=hS)
self.noteFiles.setToolTip("Include files with layout 'Note'.")
self.noteFiles.setToolTip(self.tr("Include files with layout 'Note'."))
self.noteFiles.setChecked(
self.optState.getBool("GuiBuildNovel", "addNotes", False)
)
self.ignoreFlag = QSwitch(width=wS, height=hS)
self.ignoreFlag.setToolTip(
self.ignoreFlag.setToolTip(self.tr(
"Ignore the 'Include when building project' setting and include "
"all files in the output."
)
))
self.ignoreFlag.setChecked(
self.optState.getBool("GuiBuildNovel", "ignoreFlag", False)
)
novelLabel = QLabel("Include novel files")
notesLabel = QLabel("Include note files")
exportLabel = QLabel("Ignore export flag")
novelLabel = QLabel(self.tr("Include novel files"))
notesLabel = QLabel(self.tr("Include note files"))
exportLabel = QLabel(self.tr("Ignore export flag"))
self.fileForm.addWidget(novelLabel, 0, 0, 1, 1, Qt.AlignLeft)
self.fileForm.addWidget(self.novelFiles, 0, 1, 1, 1, Qt.AlignRight)
@@ -372,7 +368,7 @@ class GuiBuildNovel(QDialog):
# Export Options
# ==============
self.exportGroup = QGroupBox("Export Options", self)
self.exportGroup = QGroupBox(self.tr("Export Options"), self)
self.exportForm = QGridLayout(self)
self.exportGroup.setLayout(self.exportForm)
@@ -386,8 +382,8 @@ class GuiBuildNovel(QDialog):
self.optState.getBool("GuiBuildNovel", "replaceUCode", False)
)
tabsLabel = QLabel("Replace tabs with spaces")
uCodeLabel = QLabel("Replace Unicode in HTML")
tabsLabel = QLabel(self.tr("Replace tabs with spaces"))
uCodeLabel = QLabel(self.tr("Replace Unicode in HTML"))
self.exportForm.addWidget(tabsLabel, 0, 0, 1, 1, Qt.AlignLeft)
self.exportForm.addWidget(self.replaceTabs, 0, 1, 1, 1, Qt.AlignRight)
@@ -402,7 +398,7 @@ class GuiBuildNovel(QDialog):
self.buildProgress = QProgressBar()
self.buildNovel = QPushButton("Build Preview")
self.buildNovel = QPushButton(self.tr("Build Preview"))
self.buildNovel.clicked.connect(self._buildPreview)
# Action Buttons
@@ -413,56 +409,56 @@ class GuiBuildNovel(QDialog):
# Printing
self.printMenu = QMenu(self)
self.btnPrint = QPushButton("Print")
self.btnPrint = QPushButton(self.tr("Print"))
self.btnPrint.setMenu(self.printMenu)
self.printSend = QAction("Print Preview", self)
self.printSend = QAction(self.tr("Print Preview"), self)
self.printSend.triggered.connect(self._printDocument)
self.printMenu.addAction(self.printSend)
self.printFile = QAction("Print to PDF", self)
self.printFile = QAction(self.tr("Print to PDF"), self)
self.printFile.triggered.connect(lambda: self._saveDocument(self.FMT_PDF))
self.printMenu.addAction(self.printFile)
# Saving to File
self.saveMenu = QMenu(self)
self.btnSave = QPushButton("Save As")
self.btnSave = QPushButton(self.tr("Save As"))
self.btnSave.setMenu(self.saveMenu)
self.saveODT = QAction("Open Document (.odt)", self)
self.saveODT = QAction(self.tr("Open Document (.odt)"), self)
self.saveODT.triggered.connect(lambda: self._saveDocument(self.FMT_ODT))
self.saveMenu.addAction(self.saveODT)
self.saveFODT = QAction("Flat Open Document (.fodt)", self)
self.saveFODT = QAction(self.tr("Flat Open Document (.fodt)"), self)
self.saveFODT.triggered.connect(lambda: self._saveDocument(self.FMT_FODT))
self.saveMenu.addAction(self.saveFODT)
self.saveHTM = QAction("novelWriter HTML (.htm)", self)
self.saveHTM = QAction(self.tr("novelWriter HTML (.htm)"), self)
self.saveHTM.triggered.connect(lambda: self._saveDocument(self.FMT_HTM))
self.saveMenu.addAction(self.saveHTM)
self.saveNWD = QAction("novelWriter Markdown (.nwd)", self)
self.saveNWD = QAction(self.tr("novelWriter Markdown (.nwd)"), self)
self.saveNWD.triggered.connect(lambda: self._saveDocument(self.FMT_NWD))
self.saveMenu.addAction(self.saveNWD)
self.saveMD = QAction("Standard Markdown (.md)", self)
self.saveMD = QAction(self.tr("Standard Markdown (.md)"), self)
self.saveMD.triggered.connect(lambda: self._saveDocument(self.FMT_MD))
self.saveMenu.addAction(self.saveMD)
self.saveGH = QAction("GitHub Markdown (.md)", self)
self.saveGH = QAction(self.tr("GitHub Markdown (.md)"), self)
self.saveGH.triggered.connect(lambda: self._saveDocument(self.FMT_GH))
self.saveMenu.addAction(self.saveGH)
self.saveJsonH = QAction("JSON + novelWriter HTML (.json)", self)
self.saveJsonH = QAction(self.tr("JSON + novelWriter HTML (.json)"), self)
self.saveJsonH.triggered.connect(lambda: self._saveDocument(self.FMT_JSON_H))
self.saveMenu.addAction(self.saveJsonH)
self.saveJsonM = QAction("JSON + novelWriters Markdown (.json)", self)
self.saveJsonM = QAction(self.tr("JSON + novelWriters Markdown (.json)"), self)
self.saveJsonM.triggered.connect(lambda: self._saveDocument(self.FMT_JSON_M))
self.saveMenu.addAction(self.saveJsonM)
self.btnClose = QPushButton("Close")
self.btnClose = QPushButton(self.tr("Close"))
self.btnClose.clicked.connect(self._doClose)
self.buttonBox.addWidget(self.btnSave)
@@ -567,7 +563,7 @@ class GuiBuildNovel(QDialog):
self.docView.setContent(self.htmlText, self.buildTime)
else:
self.docView.setText(
"Failed to generate preview. The result is too big."
self.tr("Failed to generate preview. The result is too big.")
)
else:
@@ -736,10 +732,10 @@ class GuiBuildNovel(QDialog):
logger.debug("Built project in %.3f ms" % (1000*(tEnd - tStart)))
if bldObj.errData:
self.theParent.makeAlert((
"There were problems when building the project:"
"<br>-&nbsp;%s"
) % "<br>-&nbsp;".join(bldObj.errData), nwAlert.ERROR)
self.theParent.makeAlert("%s:<br>-&nbsp;%s" % (
self.tr("There were problems when building the project"),
"<br>-&nbsp;".join(bldObj.errData)), nwAlert.ERROR
)
return
@@ -796,39 +792,39 @@ class GuiBuildNovel(QDialog):
if theFmt == self.FMT_ODT:
fileExt = "odt"
textFmt = "Open Document"
textFmt = self.tr("Open Document")
elif theFmt == self.FMT_FODT:
fileExt = "fodt"
textFmt = "Flat Open Document"
textFmt = self.tr("Flat Open Document")
elif theFmt == self.FMT_HTM:
fileExt = "htm"
textFmt = "Plain HTML"
textFmt = self.tr("Plain HTML")
elif theFmt == self.FMT_NWD:
fileExt = "nwd"
textFmt = "novelWriter Markdown"
textFmt = self.tr("novelWriter Markdown")
elif theFmt == self.FMT_MD:
fileExt = "md"
textFmt = "Standard Markdown"
textFmt = self.tr("Standard Markdown")
elif theFmt == self.FMT_GH:
fileExt = "md"
textFmt = "GitHub Markdown"
textFmt = self.tr("GitHub Markdown")
elif theFmt == self.FMT_JSON_H:
fileExt = "json"
textFmt = "JSON + novelWriter HTML"
textFmt = self.tr("JSON + novelWriter HTML")
elif theFmt == self.FMT_JSON_M:
fileExt = "json"
textFmt = "JSON + novelWriter Markdown"
textFmt = self.tr("JSON + novelWriter Markdown")
elif theFmt == self.FMT_PDF:
fileExt = "pdf"
textFmt = "PDF"
textFmt = self.tr("PDF")
else:
return False
@@ -848,7 +844,7 @@ class GuiBuildNovel(QDialog):
dlgOpt = QFileDialog.Options()
dlgOpt |= QFileDialog.DontUseNativeDialog
savePath, _ = QFileDialog.getSaveFileName(
self, "Save Document As", savePath, options=dlgOpt
self, self.tr("Save Document As"), savePath, options=dlgOpt
)
if not savePath:
return False
@@ -985,20 +981,20 @@ class GuiBuildNovel(QDialog):
errMsg - str(e)
else:
errMsg = "Unknown format"
errMsg = self.tr("Unknown format")
# Report to user
if wSuccess:
self.theParent.makeAlert(
"%s file successfully written to:<br> %s" % (
textFmt, savePath
), nwAlert.INFO
"%s<br>%s" % (
self.tr("{0} file successfully written to:").format(textFmt), savePath
),
nwAlert.INFO
)
else:
self.theParent.makeAlert(
"Failed to write %s file. %s" % (
textFmt, errMsg
), nwAlert.ERROR
self.tr("Failed to write {0} file. {1}").format(textFmt, errMsg),
nwAlert.ERROR
)
return wSuccess
@@ -1193,11 +1189,11 @@ class GuiBuildNovelDocView(QTextBrowser):
self.qDocument = self.document()
self.qDocument.setDocumentMargin(self.mainConf.getTextMargin())
self.setPlaceholderText(
self.setPlaceholderText(self.tr(
"This area will show the content of the document to be "
"exported or printed. Press the \"Build Preview\" button "
"to generate content."
)
))
theFont = QFont()
if self.mainConf.textFont is None:
@@ -1227,7 +1223,7 @@ class GuiBuildNovelDocView(QTextBrowser):
fPx = int(1.1*self.theTheme.fontPixelSize)
self.theTitle = QLabel("<b>Build Time:</b> Unknown", self)
self.theTitle = QLabel("", self)
self.theTitle.setIndent(0)
self.theTitle.setAutoFillBackground(True)
self.theTitle.setAlignment(Qt.AlignCenter)
@@ -1236,6 +1232,7 @@ class GuiBuildNovelDocView(QTextBrowser):
self.theTitle.setFont(lblFont)
self._updateDocMargins()
self._updateBuildAge()
self.setStyleSheet()
# Age Timer
@@ -1341,8 +1338,11 @@ class GuiBuildNovelDocView(QTextBrowser):
fuzzyTime(time() - self.buildTime)
)
else:
strBuildTime = "Unknown"
self.theTitle.setText("<b>Build Time:</b> %s" % strBuildTime)
strBuildTime = self.tr("Unknown")
self.theTitle.setText(self.tr("<b>Build Time:</b> {0}").format(strBuildTime))
return
def _updateDocMargins(self):
"""Automatically adjust the header to fill the top of the
+2
View File
@@ -500,6 +500,8 @@ class QuotesDialog(QDialog):
# Buttons
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.buttonBox.button(QDialogButtonBox.Ok).setText(self.tr("Ok"))
self.buttonBox.button(QDialogButtonBox.Cancel).setText(self.tr("Cancel"))
self.buttonBox.accepted.connect(self._doAccept)
self.buttonBox.rejected.connect(self._doReject)
+105 -85
View File
@@ -53,8 +53,8 @@ from nw.core import NWDoc, NWSpellSimple, countWords
from nw.gui.dochighlight import GuiDocHighlighter
from nw.common import transferCase
from nw.constants import (
nwConst, nwAlert, nwUnicode, nwDocAction, nwDocInsert, nwItemClass,
nwKeyWords, nwLabels
trConst, nwConst, nwAlert, nwUnicode, nwDocAction, nwDocInsert,
nwItemClass, nwKeyWords, nwLabels
)
logger = logging.getLogger(__name__)
@@ -293,11 +293,17 @@ class GuiDocEditor(QTextEdit):
docSize = len(theDoc)
if docSize > nwConst.MAX_DOCSIZE:
self.theParent.makeAlert((
"The document you are trying to open is too big. "
"The document size is %.2f\u202fMB. "
"The maximum size allowed is %.2f\u202fMB."
) % (docSize/1.0e6, nwConst.MAX_DOCSIZE/1.0e6), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr(
"The document you are trying to open is too big. "
"The document size is {0} MB. "
"The maximum size allowed is {1} MB."
).format(
f"{docSize/1.0e6:.2f}",
f"{nwConst.MAX_DOCSIZE/1.0e6:.2f}"
),
nwAlert.ERROR
)
self.clearEditor()
return False
@@ -380,11 +386,17 @@ class GuiDocEditor(QTextEdit):
"""
docSize = len(theText)
if docSize > nwConst.MAX_DOCSIZE:
self.theParent.makeAlert((
"The text you are trying to add is too big. "
"The text size is %.2f\u202fMB. "
"The maximum size allowed is %.2f\u202fMB."
) % (docSize/1.0e6, nwConst.MAX_DOCSIZE/1.0e6), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr(
"The text you are trying to add is too big. "
"The text size is {0} MB. "
"The maximum size allowed is {1} MB."
).format(
f"{docSize/1.0e6:.2f}",
f"{nwConst.MAX_DOCSIZE/1.0e6:.2f}"
),
nwAlert.ERROR
)
return False
qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
@@ -568,12 +580,14 @@ class GuiDocEditor(QTextEdit):
"""
if not isinstance(theLine, int):
return False
if theLine >= 0:
theBlock = self.qDocument.findBlockByLineNumber(theLine)
if theBlock:
self.setCursorPosition(theBlock.position())
self.docFooter.updateLineCount()
logger.verbose("Cursor moved to line %d" % theLine)
return True
##
@@ -591,11 +605,9 @@ class GuiDocEditor(QTextEdit):
theLang = self.theProject.projLang
self.theDict.setLanguage(theLang, self.theProject.projDict)
theTag, theProvider = self.theDict.describeDict()
aLang, aName = self.theDict.describeDict()
self.theParent.statusBar.setLanguage(
aLang, "%s [%s]" % (self.mainConf.spellTool.title(), aName.title())
)
self.theParent.statusBar.setLanguage(theLang, theProvider)
if not self.bigDoc:
self.spellCheckDocument()
@@ -641,10 +653,8 @@ class GuiDocEditor(QTextEdit):
self.hLight.rehighlight()
qApp.restoreOverrideCursor()
afTime = time()
logger.debug(
"Document highlighted in %.3f ms" % (1000*(afTime-bfTime))
)
self.theParent.statusBar.showMessage("Spell check complete")
logger.debug("Document highlighted in %.3f ms" % (1000*(afTime-bfTime)))
self.theParent.statusBar.showMessage(self.tr("Spell check complete"))
return True
@@ -743,14 +753,14 @@ class GuiDocEditor(QTextEdit):
return False
msgBox = QMessageBox()
msgBox.information(self, "File Location", (
"File details for the currently open file<br>"
"Handle: {handle:s}<br>"
"Location: {fileLoc:s}"
).format(
handle = self.theHandle,
fileLoc = str(self.nwDocument.getFileLocation())
))
msgBox.information(
self,
self.tr("File Location"),
"%s<br>%s" % (
self.tr("The currently open file is saved in:"),
self.nwDocument.getFileLocation()
),
)
return
@@ -799,7 +809,7 @@ class GuiDocEditor(QTextEdit):
theCursor = self.textCursor()
theBlock = theCursor.block()
if not theBlock.isValid():
logger.error("Filed to insert keyword '%s'" % keyWord)
logger.error("Failed to insert keyword '%s'" % keyWord)
return False
theCursor.beginEditBlock()
@@ -933,10 +943,15 @@ class GuiDocEditor(QTextEdit):
self.lastFind = None
if self.qDocument.characterCount() > nwConst.MAX_DOCSIZE:
self.theParent.makeAlert((
"The document has grown too big and you cannot add more text to it. "
"The maximum size of a single novelWriter document is %.2f\u202fMB."
) % (nwConst.MAX_DOCSIZE/1.0e6), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr(
"The document has grown too big and you cannot add more text to it. "
"The maximum size of a single novelWriter document is {0} MB."
).format(
f"{nwConst.MAX_DOCSIZE/1.0e6:.2f}"
),
nwAlert.ERROR
)
self.undo()
return
@@ -966,21 +981,21 @@ class GuiDocEditor(QTextEdit):
# ===========================
if self._followTag(theCursor=posCursor, loadTag=False):
mnuTag = QAction("Follow Tag", mnuContext)
mnuTag = QAction(self.tr("Follow Tag"), mnuContext)
mnuTag.triggered.connect(lambda: self._followTag(theCursor=posCursor))
mnuContext.addAction(mnuTag)
mnuContext.addSeparator()
if userSelection:
mnuCut = QAction("Cut", mnuContext)
mnuCut = QAction(self.tr("Cut"), mnuContext)
mnuCut.triggered.connect(lambda: self.docAction(nwDocAction.CUT))
mnuContext.addAction(mnuCut)
mnuCopy = QAction("Copy", mnuContext)
mnuCopy = QAction(self.tr("Copy"), mnuContext)
mnuCopy.triggered.connect(lambda: self.docAction(nwDocAction.COPY))
mnuContext.addAction(mnuCopy)
mnuPaste = QAction("Paste", mnuContext)
mnuPaste = QAction(self.tr("Paste"), mnuContext)
mnuPaste.triggered.connect(lambda: self.docAction(nwDocAction.PASTE))
mnuContext.addAction(mnuPaste)
@@ -989,17 +1004,17 @@ class GuiDocEditor(QTextEdit):
# Selections
# ==========
mnuSelAll = QAction("Select All", mnuContext)
mnuSelAll = QAction(self.tr("Select All"), mnuContext)
mnuSelAll.triggered.connect(lambda: self.docAction(nwDocAction.SEL_ALL))
mnuContext.addAction(mnuSelAll)
mnuSelWord = QAction("Select Word", mnuContext)
mnuSelWord = QAction(self.tr("Select Word"), mnuContext)
mnuSelWord.triggered.connect(
lambda: self._makePosSelection(QTextCursor.WordUnderCursor, thePos)
)
mnuContext.addAction(mnuSelWord)
mnuSelPara = QAction("Select Paragraph", mnuContext)
mnuSelPara = QAction(self.tr("Select Paragraph"), mnuContext)
mnuSelPara.triggered.connect(
lambda: self._makePosSelection(QTextCursor.BlockUnderCursor, thePos)
)
@@ -1025,7 +1040,7 @@ class GuiDocEditor(QTextEdit):
if spellCheck:
mnuContext.addSeparator()
mnuHead = QAction("Spelling Suggestion(s)", mnuContext)
mnuHead = QAction(self.tr("Spelling Suggestion(s)"), mnuContext)
mnuContext.addAction(mnuHead)
theSuggest = self.theDict.suggestWords(theWord)[:15]
@@ -1037,11 +1052,13 @@ class GuiDocEditor(QTextEdit):
)
mnuContext.addAction(mnuWord)
else:
mnuHead = QAction("%s No Suggestions" % nwUnicode.U_ENDASH, mnuContext)
mnuHead = QAction(
"%s %s" % (nwUnicode.U_ENDASH, self.tr("No Suggestions")), mnuContext
)
mnuContext.addAction(mnuHead)
mnuContext.addSeparator()
mnuAdd = QAction("Add Word to Dictionary", mnuContext)
mnuAdd = QAction(self.tr("Add Word to Dictionary"), mnuContext)
mnuAdd.triggered.connect(lambda thePos: self._addWord(posCursor))
mnuContext.addAction(mnuAdd)
@@ -1131,15 +1148,11 @@ class GuiDocEditor(QTextEdit):
QPointF(theSize.width(), theSize.height()), Qt.FuzzyHit
)
if self.queuePos <= thePos:
logger.verbose(
"Allowed cursor move to %d <= %d" % (self.queuePos, thePos)
)
logger.verbose("Allowed cursor move to %d <= %d" % (self.queuePos, thePos))
self.setCursorPosition(self.queuePos)
self.queuePos = None
else:
logger.verbose(
"Denied cursor move to %d > %d" % (self.queuePos, thePos)
)
logger.verbose("Denied cursor move to %d > %d" % (self.queuePos, thePos))
return
##
@@ -1317,7 +1330,8 @@ class GuiDocEditor(QTextEdit):
else:
self.theParent.makeAlert(
"Please selection some text before calling replace quotes.", nwAlert.ERROR
self.tr("Please selection some text before calling replace quotes."),
nwAlert.ERROR
)
return
@@ -1530,9 +1544,7 @@ class GuiDocEditor(QTextEdit):
theText = newText
cOffset -= 0
else:
logger.error(
"Unknown or unsupported block format requested: %s" % str(docAction)
)
logger.error("Unknown or unsupported block format requested: %s" % str(docAction))
return False
# Replace the block text
@@ -1817,12 +1829,12 @@ class GuiDocEditSearch(QFrame):
# ==========
self.searchBox = QLineEdit(self)
self.searchBox.setFont(boxFont)
self.searchBox.setPlaceholderText("Search")
self.searchBox.setPlaceholderText(self.tr("Search"))
self.searchBox.returnPressed.connect(self._doSearch)
self.replaceBox = QLineEdit(self)
self.replaceBox.setFont(boxFont)
self.replaceBox.setPlaceholderText("Replace")
self.replaceBox.setPlaceholderText(self.tr("Replace"))
self.replaceBox.returnPressed.connect(self._doReplace)
self.searchOpt = QToolBar(self)
@@ -1831,44 +1843,44 @@ class GuiDocEditSearch(QFrame):
self.searchOpt.setContentsMargins(0, 0, 0, 0)
self.searchOpt.setStyleSheet(r"QToolBar {padding: 0;}")
self.searchLabel = QLabel("Search")
self.searchLabel = QLabel(self.tr("Search"))
self.searchLabel.setFont(boxFont)
self.searchLabel.setIndent(self.mainConf.pxInt(6))
self.toggleCase = QAction("Case Sensitive", self)
self.toggleCase.setToolTip("Match case")
self.toggleCase = QAction(self.tr("Case Sensitive"), self)
self.toggleCase.setToolTip(self.tr("Match case"))
self.toggleCase.setIcon(self.theTheme.getIcon("search_case"))
self.toggleCase.setCheckable(True)
self.toggleCase.setChecked(self.isCaseSense)
self.toggleCase.toggled.connect(self._doToggleCase)
self.searchOpt.addAction(self.toggleCase)
self.toggleWord = QAction("Whole Words Only", self)
self.toggleWord.setToolTip("Match whole words")
self.toggleWord = QAction(self.tr("Whole Words Only"), self)
self.toggleWord.setToolTip(self.tr("Match whole words"))
self.toggleWord.setIcon(self.theTheme.getIcon("search_word"))
self.toggleWord.setCheckable(True)
self.toggleWord.setChecked(self.isWholeWord)
self.toggleWord.toggled.connect(self._doToggleWord)
self.searchOpt.addAction(self.toggleWord)
self.toggleRegEx = QAction("RegEx Mode", self)
self.toggleRegEx.setToolTip("Use regular expressions (requires Qt 5.3)")
self.toggleRegEx = QAction(self.tr("RegEx Mode"), self)
self.toggleRegEx.setToolTip(self.tr("Search using regular expressions"))
self.toggleRegEx.setIcon(self.theTheme.getIcon("search_regex"))
self.toggleRegEx.setCheckable(True)
self.toggleRegEx.setChecked(self.isRegEx)
self.toggleRegEx.toggled.connect(self._doToggleRegEx)
self.searchOpt.addAction(self.toggleRegEx)
self.toggleLoop = QAction("Loop Search", self)
self.toggleLoop.setToolTip("Loop the search when reaching the end")
self.toggleLoop = QAction(self.tr("Loop Search"), self)
self.toggleLoop.setToolTip(self.tr("Loop the search when reaching the end"))
self.toggleLoop.setIcon(self.theTheme.getIcon("search_loop"))
self.toggleLoop.setCheckable(True)
self.toggleLoop.setChecked(self.doLoop)
self.toggleLoop.toggled.connect(self._doToggleLoop)
self.searchOpt.addAction(self.toggleLoop)
self.toggleProject = QAction("Search Next File", self)
self.toggleProject.setToolTip("Continue searching in the next file")
self.toggleProject = QAction(self.tr("Search Next File"), self)
self.toggleProject.setToolTip(self.tr("Continue searching in the next file"))
self.toggleProject.setIcon(self.theTheme.getIcon("search_project"))
self.toggleProject.setCheckable(True)
self.toggleProject.setChecked(self.doNextFile)
@@ -1877,8 +1889,8 @@ class GuiDocEditSearch(QFrame):
self.searchOpt.addSeparator()
self.toggleMatchCap = QAction("Preserve Case", self)
self.toggleMatchCap.setToolTip("Preserve case on replace")
self.toggleMatchCap = QAction(self.tr("Preserve Case"), self)
self.toggleMatchCap.setToolTip(self.tr("Preserve case on replace"))
self.toggleMatchCap.setIcon(self.theTheme.getIcon("search_preserve"))
self.toggleMatchCap.setCheckable(True)
self.toggleMatchCap.setChecked(self.doMatchCap)
@@ -1887,8 +1899,8 @@ class GuiDocEditSearch(QFrame):
self.searchOpt.addSeparator()
self.cancelSearch = QAction("Close Search", self)
self.cancelSearch.setToolTip("Close the search box [Esc]")
self.cancelSearch = QAction(self.tr("Close Search"), self)
self.cancelSearch.setToolTip(self.tr("Close the search box [{0}]").format("Esc"))
self.cancelSearch.setIcon(self.theTheme.getIcon("search_cancel"))
self.cancelSearch.triggered.connect(self._doClose)
self.searchOpt.addAction(self.cancelSearch)
@@ -1900,18 +1912,18 @@ class GuiDocEditSearch(QFrame):
self.showReplace = QToolButton(self)
self.showReplace.setArrowType(Qt.RightArrow)
self.showReplace.setCheckable(True)
self.showReplace.setToolTip("Show/hide the replace text box")
self.showReplace.setToolTip(self.tr("Show/hide the replace text box"))
self.showReplace.setStyleSheet(r"QToolButton {border: none; background: transparent;}")
self.showReplace.toggled.connect(self._doToggleReplace)
self.searchButton = QPushButton(self.theTheme.getIcon("search"), "")
self.searchButton.setFixedSize(QSize(bPx, bPx))
self.searchButton.setToolTip("Find in current document")
self.searchButton.setToolTip(self.tr("Find in current document"))
self.searchButton.clicked.connect(self._doSearch)
self.replaceButton = QPushButton(self.theTheme.getIcon("search-replace"), "")
self.replaceButton.setFixedSize(QSize(bPx, bPx))
self.replaceButton.setToolTip("Find and replace in current document")
self.replaceButton.setToolTip(self.tr("Find and replace in current document"))
self.replaceButton.clicked.connect(self._doReplace)
self.mainBox.addWidget(self.searchLabel, 0, 0, 1, 2, Qt.AlignLeft)
@@ -2204,7 +2216,7 @@ class GuiDocEditHeader(QWidget):
self.editButton.setStyleSheet(buttonStyle)
self.editButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.editButton.setVisible(False)
self.editButton.setToolTip("Edit document meta")
self.editButton.setToolTip(self.tr("Edit document meta"))
self.editButton.clicked.connect(self._editDocument)
self.searchButton = QToolButton(self)
@@ -2215,7 +2227,7 @@ class GuiDocEditHeader(QWidget):
self.searchButton.setStyleSheet(buttonStyle)
self.searchButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.searchButton.setVisible(False)
self.searchButton.setToolTip("Search document")
self.searchButton.setToolTip(self.tr("Search document"))
self.searchButton.clicked.connect(self._searchDocument)
self.minmaxButton = QToolButton(self)
@@ -2226,7 +2238,7 @@ class GuiDocEditHeader(QWidget):
self.minmaxButton.setStyleSheet(buttonStyle)
self.minmaxButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.minmaxButton.setVisible(False)
self.minmaxButton.setToolTip("Toggle Focus Mode")
self.minmaxButton.setToolTip(self.tr("Toggle Focus Mode"))
self.minmaxButton.clicked.connect(self._minmaxDocument)
self.closeButton = QToolButton(self)
@@ -2237,7 +2249,7 @@ class GuiDocEditHeader(QWidget):
self.closeButton.setStyleSheet(buttonStyle)
self.closeButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.closeButton.setVisible(False)
self.closeButton.setToolTip("Close the document")
self.closeButton.setToolTip(self.tr("Close the document"))
self.closeButton.clicked.connect(self._closeDocument)
# Assemble Layout
@@ -2413,7 +2425,7 @@ class GuiDocEditFooter(QWidget):
self.statusIcon.setFixedHeight(self.sPx)
self.statusIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop)
self.statusText = QLabel("Status")
self.statusText = QLabel(self.tr("Status"))
self.statusText.setIndent(0)
self.statusText.setMargin(0)
self.statusText.setContentsMargins(0, 0, 0, 0)
@@ -2429,7 +2441,7 @@ class GuiDocEditFooter(QWidget):
self.linesIcon.setFixedHeight(self.sPx)
self.linesIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop)
self.linesText = QLabel("Line: 0")
self.linesText = QLabel("")
self.linesText.setIndent(0)
self.linesText.setMargin(0)
self.linesText.setContentsMargins(0, 0, 0, 0)
@@ -2445,7 +2457,7 @@ class GuiDocEditFooter(QWidget):
self.wordsIcon.setFixedHeight(self.sPx)
self.wordsIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop)
self.wordsText = QLabel("Words: 0")
self.wordsText = QLabel("")
self.wordsText.setIndent(0)
self.wordsText.setMargin(0)
self.wordsText.setContentsMargins(0, 0, 0, 0)
@@ -2476,6 +2488,8 @@ class GuiDocEditFooter(QWidget):
# Fix the Colours
self.matchColours()
self.updateLineCount()
self.updateCounts()
logger.debug("GuiDocEditFooter initialisation complete")
@@ -2532,8 +2546,8 @@ class GuiDocEditFooter(QWidget):
theIcon = self.theParent.importIcons[iStatus]
sIcon = theIcon.pixmap(self.sPx, self.sPx)
sClass = nwLabels.CLASS_NAME[self.theItem.itemClass]
sLayout = nwLabels.LAYOUT_NAME[self.theItem.itemLayout]
sClass = trConst(nwLabels.CLASS_NAME[self.theItem.itemClass])
sLayout = trConst(nwLabels.LAYOUT_NAME[self.theItem.itemLayout])
sText = f"{self.theItem.itemStatus} / {sClass} / {sLayout}"
self.statusIcon.setPixmap(sIcon)
@@ -2552,7 +2566,9 @@ class GuiDocEditFooter(QWidget):
iLine = theCursor.blockNumber() + 1
iDist = 100*iLine/self.docEditor.qDocument.blockCount()
self.linesText.setText(f"Line: {iLine:n} ({iDist:.0f}\u202f%)")
self.linesText.setText(
self.tr("Line: {0} ({1})").format(f"{iLine:n}", f"{iDist:.0f} %")
)
return
@@ -2566,10 +2582,14 @@ class GuiDocEditFooter(QWidget):
wCount = self.theItem.wordCount
wDiff = wCount - self.theItem.initCount
self.wordsText.setText(f"Words: {wCount:n} ({wDiff:+n})")
self.wordsText.setText(
self.tr("Words: {0} ({1})").format(f"{wCount:n}", f"{wDiff:+n}")
)
byteSize = self.docEditor.qDocument.characterCount()
self.wordsText.setToolTip(f"Document size is {byteSize:n} bytes")
self.wordsText.setToolTip(
self.tr("Document size is {0} bytes").format(f"{byteSize:n}")
)
return
+17 -15
View File
@@ -53,11 +53,11 @@ class GuiDocMerge(QDialog):
self.sourceItem = None
self.outerBox = QVBoxLayout()
self.setWindowTitle("Merge Documents")
self.setWindowTitle(self.tr("Merge Documents"))
self.headLabel = QLabel("<b>Documents to Merge</b>")
self.headLabel = QLabel("<b>%s</b>" % self.tr("Documents to Merge"))
self.helpLabel = QHelpLabel(
"Drag and drop items to change the order.", self.theParent.theTheme.helpText
self.tr("Drag and drop items to change the order."), self.theParent.theTheme.helpText
)
self.listBox = QListWidget()
@@ -66,6 +66,8 @@ class GuiDocMerge(QDialog):
self.listBox.setMinimumHeight(self.mainConf.pxInt(180))
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.buttonBox.button(QDialogButtonBox.Ok).setText(self.tr("Ok"))
self.buttonBox.button(QDialogButtonBox.Cancel).setText(self.tr("Cancel"))
self.buttonBox.accepted.connect(self._doMerge)
self.buttonBox.rejected.connect(self._doClose)
@@ -102,9 +104,9 @@ class GuiDocMerge(QDialog):
finalOrder.append(self.listBox.item(i).data(Qt.UserRole))
if len(finalOrder) == 0:
self.theParent.makeAlert((
"No source documents found. Nothing to do."
), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr("No source documents found. Nothing to do."), nwAlert.ERROR
)
return
theDoc = NWDoc(self.theProject, self.theParent)
@@ -114,16 +116,16 @@ class GuiDocMerge(QDialog):
theText += "\n\n"
if self.sourceItem is None:
self.theParent.makeAlert((
"No source document selected. Nothing to do."
), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr("No source document selected. Nothing to do."), nwAlert.ERROR
)
return
srcItem = self.theProject.projTree[self.sourceItem]
if srcItem is None:
self.theParent.makeAlert((
"Could not parse source document."
), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr("Could not parse source document."), nwAlert.ERROR
)
return
nHandle = self.theProject.newFile(srcItem.itemName, srcItem.itemClass, srcItem.itemParent)
@@ -164,9 +166,9 @@ class GuiDocMerge(QDialog):
if nwItem is None:
return
if nwItem.itemType is not nwItemType.FOLDER:
self.theParent.makeAlert((
"Element selected in the project tree must be a folder."
), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr("Element selected in the project tree must be a folder."), nwAlert.ERROR
)
return
for sHandle in self.theParent.treeView.getTreeFromHandle(tHandle):
+40 -29
View File
@@ -56,11 +56,12 @@ class GuiDocSplit(QDialog):
self.sourceItem = None
self.outerBox = QVBoxLayout()
self.setWindowTitle("Split Document")
self.setWindowTitle(self.tr("Split Document"))
self.headLabel = QLabel("<b>Document Headers</b>")
self.headLabel = QLabel("<b>%s</b>" % self.tr("Document Headers"))
self.helpLabel = QHelpLabel(
"Select the maximum level to split into files.", self.theParent.theTheme.helpText
self.tr("Select the maximum level to split into files."),
self.theParent.theTheme.helpText
)
self.listBox = QListWidget()
@@ -69,10 +70,10 @@ class GuiDocSplit(QDialog):
self.listBox.setMinimumHeight(self.mainConf.pxInt(180))
self.splitLevel = QComboBox(self)
self.splitLevel.addItem("Split on Header Level 1 (Title)", 1)
self.splitLevel.addItem("Split up to Header Level 2 (Chapter)", 2)
self.splitLevel.addItem("Split up to Header Level 3 (Scene)", 3)
self.splitLevel.addItem("Split up to Header Level 4 (Section)", 4)
self.splitLevel.addItem(self.tr("Split on Header Level 1 (Title)"), 1)
self.splitLevel.addItem(self.tr("Split up to Header Level 2 (Chapter)"), 2)
self.splitLevel.addItem(self.tr("Split up to Header Level 3 (Scene)"), 3)
self.splitLevel.addItem(self.tr("Split up to Header Level 4 (Section)"), 4)
spIndex = self.splitLevel.findData(
self.optState.getInt("GuiDocSplit", "spLevel", 3)
)
@@ -81,6 +82,8 @@ class GuiDocSplit(QDialog):
self.splitLevel.currentIndexChanged.connect(self._populateList)
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.buttonBox.button(QDialogButtonBox.Ok).setText(self.tr("Ok"))
self.buttonBox.button(QDialogButtonBox.Cancel).setText(self.tr("Cancel"))
self.buttonBox.accepted.connect(self._doSplit)
self.buttonBox.rejected.connect(self._doClose)
@@ -115,16 +118,16 @@ class GuiDocSplit(QDialog):
logger.verbose("GuiDocSplit split button clicked")
if self.sourceItem is None:
self.theParent.makeAlert((
"No source document selected. Nothing to do."
), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr("No source document selected. Nothing to do."), nwAlert.ERROR
)
return
srcItem = self.theProject.projTree[self.sourceItem]
if srcItem is None:
self.theParent.makeAlert((
"Could not parse source document."
), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr("Could not parse source document."), nwAlert.ERROR
)
return
theDoc = NWDoc(self.theProject, self.theParent)
@@ -147,26 +150,34 @@ class GuiDocSplit(QDialog):
nFiles = len(finalOrder)
if nFiles == 0:
self.theParent.makeAlert((
"No headers found. Nothing to do."
), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr("No headers found. Nothing to do."), nwAlert.ERROR
)
return
# Check that another folder can be created
parTree = self.theProject.projTree.getItemPath(srcItem.itemParent)
if len(parTree) >= nwConst.MAX_DEPTH - 1:
self.theParent.makeAlert((
"Cannot add new folder for the document split. "
"Maximum folder depth has been reached. "
"Please move the file to another level in the project tree."
), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr(
"Cannot add new folder for the document split. "
"Maximum folder depth has been reached. "
"Please move the file to another level in the project tree."
), nwAlert.ERROR
)
return
msgYes = self.theParent.askQuestion("Split Document", (
"The document will be split into %d file(s) in a new folder. "
"The original document will remain intact.<br><br>"
"Continue with the splitting process?"
) % nFiles)
msgYes = self.theParent.askQuestion(
self.tr("Split Document"),
"%s<br><br>%s" % (
self.tr(
"The document will be split into {0} file(s) in a new folder. "
"The original document will remain intact.").format(nFiles),
self.tr(
"Continue with the splitting process?"
)
)
)
if not msgYes:
return
@@ -242,9 +253,9 @@ class GuiDocSplit(QDialog):
if nwItem is None:
return
if nwItem.itemType is not nwItemType.FILE:
self.theParent.makeAlert((
"Element selected in the project tree must be a file."
), nwAlert.ERROR)
self.theParent.makeAlert(
self.tr("Element selected in the project tree must be a file."), nwAlert.ERROR
)
return
self.listBox.clear()
+26 -23
View File
@@ -183,7 +183,7 @@ class GuiDocViewer(QTextBrowser):
except Exception:
logger.error("Failed to generate preview for document with handle '%s'" % tHandle)
nw.logException()
self.setText("An error occurred while generating the preview.")
self.setText(self.tr("An error occurred while generating the preview."))
return False
# Refresh the tab stops
@@ -243,11 +243,14 @@ class GuiDocViewer(QTextBrowser):
logger.debug("Loading document from tag '%s'" % theTag)
tHandle, _, sTitle = self.theParent.theIndex.getTagSource(theTag)
if tHandle is None:
self.theParent.makeAlert((
"Could not find the reference for tag '%s'. It either doesn't "
"exist, or the index is out of date. The index can be updated "
"from the Tools menu, or by pressing F9."
) % theTag, nwAlert.ERROR)
self.theParent.makeAlert(
self.tr(
"Could not find the reference for tag '{0}'. It either doesn't "
"exist, or the index is out of date. The index can be updated "
"from the Tools menu, or by pressing {1}."
).format(theTag, "F9"),
nwAlert.ERROR
)
return False
else:
# Let the parent handle the opening as it also ensures that
@@ -415,7 +418,7 @@ class GuiDocViewer(QTextBrowser):
# ===================
if userSelection:
mnuCopy = QAction("Copy", mnuContext)
mnuCopy = QAction(self.tr("Copy"), mnuContext)
mnuCopy.triggered.connect(lambda: self.docAction(nwDocAction.COPY))
mnuContext.addAction(mnuCopy)
@@ -424,17 +427,17 @@ class GuiDocViewer(QTextBrowser):
# Selections
# ==========
mnuSelAll = QAction("Select All", mnuContext)
mnuSelAll = QAction(self.tr("Select All"), mnuContext)
mnuSelAll.triggered.connect(lambda: self.docAction(nwDocAction.SEL_ALL))
mnuContext.addAction(mnuSelAll)
mnuSelWord = QAction("Select Word", mnuContext)
mnuSelWord = QAction(self.tr("Select Word"), mnuContext)
mnuSelWord.triggered.connect(
lambda: self._makePosSelection(QTextCursor.WordUnderCursor, thePos)
)
mnuContext.addAction(mnuSelWord)
mnuSelPara = QAction("Select Paragraph", mnuContext)
mnuSelPara = QAction(self.tr("Select Paragraph"), mnuContext)
mnuSelPara.triggered.connect(
lambda: self._makePosSelection(QTextCursor.BlockUnderCursor, thePos)
)
@@ -740,7 +743,7 @@ class GuiDocViewHeader(QWidget):
self.backButton.setStyleSheet(buttonStyle)
self.backButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.backButton.setVisible(False)
self.backButton.setToolTip("Go backward")
self.backButton.setToolTip(self.tr("Go backward"))
self.backButton.clicked.connect(self.docViewer.navBackward)
self.forwardButton = QToolButton(self)
@@ -751,7 +754,7 @@ class GuiDocViewHeader(QWidget):
self.forwardButton.setStyleSheet(buttonStyle)
self.forwardButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.forwardButton.setVisible(False)
self.forwardButton.setToolTip("Go forward")
self.forwardButton.setToolTip(self.tr("Go forward"))
self.forwardButton.clicked.connect(self.docViewer.navForward)
self.refreshButton = QToolButton(self)
@@ -762,7 +765,7 @@ class GuiDocViewHeader(QWidget):
self.refreshButton.setStyleSheet(buttonStyle)
self.refreshButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.refreshButton.setVisible(False)
self.refreshButton.setToolTip("Reload the document")
self.refreshButton.setToolTip(self.tr("Reload the document"))
self.refreshButton.clicked.connect(self._refreshDocument)
self.closeButton = QToolButton(self)
@@ -773,7 +776,7 @@ class GuiDocViewHeader(QWidget):
self.closeButton.setStyleSheet(buttonStyle)
self.closeButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.closeButton.setVisible(False)
self.closeButton.setToolTip("Close the document")
self.closeButton.setToolTip(self.tr("Close the document"))
self.closeButton.clicked.connect(self._closeDocument)
# Assemble Layout
@@ -944,7 +947,7 @@ class GuiDocViewFooter(QWidget):
self.showHide.setIconSize(QSize(fPx, fPx))
self.showHide.setFixedSize(QSize(fPx, fPx))
self.showHide.clicked.connect(self._doShowHide)
self.showHide.setToolTip("Show/hide the references panel")
self.showHide.setToolTip(self.tr("Show/hide the references panel"))
# Sticky Button
self.stickyRefs = QToolButton(self)
@@ -955,9 +958,9 @@ class GuiDocViewFooter(QWidget):
self.stickyRefs.setIconSize(QSize(fPx, fPx))
self.stickyRefs.setFixedSize(QSize(fPx, fPx))
self.stickyRefs.toggled.connect(self._doToggleSticky)
self.stickyRefs.setToolTip(
self.stickyRefs.setToolTip(self.tr(
"Activate to freeze the content of the references panel when changing document"
)
))
# Show Comments
self.showComments = QToolButton(self)
@@ -969,7 +972,7 @@ class GuiDocViewFooter(QWidget):
self.showComments.setIconSize(QSize(fPx, fPx))
self.showComments.setFixedSize(QSize(fPx, fPx))
self.showComments.toggled.connect(self._doToggleComments)
self.showComments.setToolTip("Show comments")
self.showComments.setToolTip(self.tr("Show comments"))
# Show Synopsis
self.showSynopsis = QToolButton(self)
@@ -981,10 +984,10 @@ class GuiDocViewFooter(QWidget):
self.showSynopsis.setIconSize(QSize(fPx, fPx))
self.showSynopsis.setFixedSize(QSize(fPx, fPx))
self.showSynopsis.toggled.connect(self._doToggleSynopsis)
self.showSynopsis.setToolTip("Show synopsis comments")
self.showSynopsis.setToolTip(self.tr("Show synopsis comments"))
# Labels
self.lblRefs = QLabel("References")
self.lblRefs = QLabel(self.tr("References"))
self.lblRefs.setBuddy(self.showHide)
self.lblRefs.setIndent(0)
self.lblRefs.setMargin(0)
@@ -993,7 +996,7 @@ class GuiDocViewFooter(QWidget):
self.lblRefs.setFixedHeight(fPx)
self.lblRefs.setAlignment(Qt.AlignLeft | Qt.AlignTop)
self.lblSticky = QLabel("Sticky")
self.lblSticky = QLabel(self.tr("Sticky"))
self.lblSticky.setBuddy(self.stickyRefs)
self.lblSticky.setIndent(0)
self.lblSticky.setMargin(0)
@@ -1002,7 +1005,7 @@ class GuiDocViewFooter(QWidget):
self.lblSticky.setFixedHeight(fPx)
self.lblSticky.setAlignment(Qt.AlignLeft | Qt.AlignTop)
self.lblComments = QLabel("Comments")
self.lblComments = QLabel(self.tr("Comments"))
self.lblComments.setBuddy(self.showComments)
self.lblComments.setIndent(0)
self.lblComments.setMargin(0)
@@ -1011,7 +1014,7 @@ class GuiDocViewFooter(QWidget):
self.lblComments.setFixedHeight(fPx)
self.lblComments.setAlignment(Qt.AlignLeft | Qt.AlignTop)
self.lblSynopsis = QLabel("Synopsis")
self.lblSynopsis = QLabel(self.tr("Synopsis"))
self.lblSynopsis.setBuddy(self.showSynopsis)
self.lblSynopsis.setIndent(0)
self.lblSynopsis.setMargin(0)
+12 -12
View File
@@ -32,7 +32,7 @@ from PyQt5.QtGui import QFont, QPixmap
from PyQt5.QtWidgets import QWidget, QGridLayout, QLabel
from nw.constants import (
nwLabels, nwItemClass, nwItemType, nwItemLayout
trConst, nwLabels, nwItemClass, nwItemType, nwItemLayout
)
logger = logging.getLogger(__name__)
@@ -67,7 +67,7 @@ class GuiItemDetails(QWidget):
self.fntValue.setPointSizeF(0.9*fPt)
# Label
self.labelName = QLabel("Label")
self.labelName = QLabel(self.tr("Label"))
self.labelName.setFont(self.fntLabel)
self.labelName.setAlignment(Qt.AlignLeft | Qt.AlignBaseline)
@@ -80,7 +80,7 @@ class GuiItemDetails(QWidget):
self.labelData.setWordWrap(True)
# Status
self.statusName = QLabel("Status")
self.statusName = QLabel(self.tr("Status"))
self.statusName.setFont(self.fntLabel)
self.statusName.setAlignment(Qt.AlignLeft)
@@ -92,7 +92,7 @@ class GuiItemDetails(QWidget):
self.statusData.setAlignment(Qt.AlignLeft)
# Class
self.className = QLabel("Class")
self.className = QLabel(self.tr("Class"))
self.className.setFont(self.fntLabel)
self.className.setAlignment(Qt.AlignLeft)
@@ -105,7 +105,7 @@ class GuiItemDetails(QWidget):
self.classData.setAlignment(Qt.AlignLeft)
# Layout
self.layoutName = QLabel("Layout")
self.layoutName = QLabel(self.tr("Layout"))
self.layoutName.setFont(self.fntLabel)
self.layoutName.setAlignment(Qt.AlignLeft)
@@ -118,7 +118,7 @@ class GuiItemDetails(QWidget):
self.layoutData.setAlignment(Qt.AlignLeft)
# Character Count
self.cCountName = QLabel(" Characters")
self.cCountName = QLabel(" "+self.tr("Characters"))
self.cCountName.setFont(self.fntLabel)
self.cCountName.setAlignment(Qt.AlignRight)
@@ -127,7 +127,7 @@ class GuiItemDetails(QWidget):
self.cCountData.setAlignment(Qt.AlignRight)
# Word Count
self.wCountName = QLabel(" Words")
self.wCountName = QLabel(" "+self.tr("Words"))
self.wCountName.setFont(self.fntLabel)
self.wCountName.setAlignment(Qt.AlignRight)
@@ -136,7 +136,7 @@ class GuiItemDetails(QWidget):
self.wCountData.setAlignment(Qt.AlignRight)
# Paragraph Count
self.pCountName = QLabel(" Paragraphs")
self.pCountName = QLabel(" "+self.tr("Paragraphs"))
self.pCountName.setFont(self.fntLabel)
self.pCountName.setAlignment(Qt.AlignRight)
@@ -259,17 +259,17 @@ class GuiItemDetails(QWidget):
iPx = int(round(0.8*self.theTheme.baseIconSize))
self.statusFlag.setPixmap(flagIcon.pixmap(iPx, iPx))
self.classFlag.setText(nwLabels.CLASS_FLAG[nwItem.itemClass])
self.classFlag.setText(nwLabels.CLASS_FLAG[nwItem.itemClass]) # NO-I18N
if nwItem.itemLayout == nwItemLayout.NO_LAYOUT:
self.layoutFlag.setText("-")
else:
self.layoutFlag.setText(nwLabels.LAYOUT_FLAG[nwItem.itemLayout])
self.layoutFlag.setText(nwLabels.LAYOUT_FLAG[nwItem.itemLayout]) # NO-I18N
self.labelData.setText(theLabel)
self.statusData.setText(nwItem.itemStatus)
self.classData.setText(nwLabels.CLASS_NAME[nwItem.itemClass])
self.layoutData.setText(nwLabels.LAYOUT_NAME[nwItem.itemLayout])
self.classData.setText(trConst(nwLabels.CLASS_NAME[nwItem.itemClass]))
self.layoutData.setText(trConst(nwLabels.LAYOUT_NAME[nwItem.itemLayout]))
if nwItem.itemType == nwItemType.FILE:
self.cCountData.setText(f"{nwItem.charCount:n}")
+18 -12
View File
@@ -34,7 +34,7 @@ from PyQt5.QtWidgets import (
)
from nw.gui.custom import QSwitch
from nw.constants import nwLabels, nwItemLayout, nwItemType, nwLists
from nw.constants import trConst, nwLabels, nwItemLayout, nwItemType, nwLists
logger = logging.getLogger(__name__)
@@ -58,7 +58,7 @@ class GuiItemEditor(QDialog):
if self.theItem is None:
self._doClose()
self.setWindowTitle("Item Settings")
self.setWindowTitle(self.tr("Item Settings"))
mVd = self.mainConf.pxInt(220)
mSp = self.mainConf.pxInt(16)
@@ -103,10 +103,10 @@ class GuiItemEditor(QDialog):
for itemLayout in nwItemLayout:
if itemLayout in validLayouts:
self.editLayout.addItem(nwLabels.LAYOUT_NAME[itemLayout], itemLayout)
self.editLayout.addItem(trConst(nwLabels.LAYOUT_NAME[itemLayout]), itemLayout)
# Export Switch
self.textExport = QLabel("Include when building project")
self.textExport = QLabel(self.tr("Include when building project"))
self.editExport = QSwitch()
if self.theItem.itemType == nwItemType.FILE:
self.editExport.setEnabled(True)
@@ -117,6 +117,8 @@ class GuiItemEditor(QDialog):
# Buttons
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.buttonBox.button(QDialogButtonBox.Ok).setText(self.tr("Ok"))
self.buttonBox.button(QDialogButtonBox.Cancel).setText(self.tr("Cancel"))
self.buttonBox.accepted.connect(self._doSave)
self.buttonBox.rejected.connect(self._doClose)
@@ -136,17 +138,21 @@ class GuiItemEditor(QDialog):
# Assemble
##
nameLabel = QLabel(self.tr("Label"))
statusLabel = QLabel(self.tr("Status"))
layoutLabel = QLabel(self.tr("Layout"))
self.mainForm = QGridLayout()
self.mainForm.setVerticalSpacing(vSp)
self.mainForm.setHorizontalSpacing(mSp)
self.mainForm.addWidget(QLabel("Label"), 0, 0, 1, 1)
self.mainForm.addWidget(self.editName, 0, 1, 1, 2)
self.mainForm.addWidget(QLabel("Status"), 1, 0, 1, 1)
self.mainForm.addWidget(self.editStatus, 1, 1, 1, 2)
self.mainForm.addWidget(QLabel("Layout"), 2, 0, 1, 1)
self.mainForm.addWidget(self.editLayout, 2, 1, 1, 2)
self.mainForm.addWidget(self.textExport, 3, 0, 1, 2)
self.mainForm.addWidget(self.editExport, 3, 2, 1, 1)
self.mainForm.addWidget(nameLabel, 0, 0, 1, 1)
self.mainForm.addWidget(self.editName, 0, 1, 1, 2)
self.mainForm.addWidget(statusLabel, 1, 0, 1, 1)
self.mainForm.addWidget(self.editStatus, 1, 1, 1, 2)
self.mainForm.addWidget(layoutLabel, 2, 0, 1, 1)
self.mainForm.addWidget(self.editLayout, 2, 1, 1, 2)
self.mainForm.addWidget(self.textExport, 3, 0, 1, 2)
self.mainForm.addWidget(self.editExport, 3, 2, 1, 1)
self.mainForm.setColumnStretch(0, 0)
self.mainForm.setColumnStretch(1, 1)
self.mainForm.setColumnStretch(2, 0)
+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.setIndentation(iPx)
self.setColumnCount(3)
self.setHeaderLabels(["Title", "Words", "POV"])
self.setHeaderLabels([
self.tr("Title"),
self.tr("Words"),
self.tr("POV")
])
self.itemDoubleClicked.connect(self._treeDoubleClick)
self.itemSelectionChanged.connect(self._itemSelected)
self.setSelectionBehavior(QAbstractItemView.SelectRows)
@@ -73,9 +77,9 @@ class GuiNovelTree(QTreeWidget):
treeHeadItem = self.headerItem()
treeHeadItem.setTextAlignment(self.C_WORDS, Qt.AlignRight)
treeHeadItem.setToolTip(self.C_TITLE, "Section title")
treeHeadItem.setToolTip(self.C_WORDS, "Word count")
treeHeadItem.setToolTip(self.C_POV, "Point-of-view character")
treeHeadItem.setToolTip(self.C_TITLE, self.tr("Section title"))
treeHeadItem.setToolTip(self.C_WORDS, self.tr("Word count"))
treeHeadItem.setToolTip(self.C_POV, self.tr("Point-of-view character"))
treeHeader = self.header()
treeHeader.setStretchLastSection(True)
+5 -5
View File
@@ -34,7 +34,7 @@ from PyQt5.QtWidgets import (
QTreeWidget, QTreeWidgetItem, QMenu, QAction, QAbstractItemView
)
from nw.constants import nwKeyWords, nwLabels, nwOutline
from nw.constants import trConst, nwKeyWords, nwLabels, nwOutline
logger = logging.getLogger(__name__)
@@ -149,7 +149,7 @@ class GuiOutline(QTreeWidget):
"""
self.clear()
self.setColumnCount(1)
self.setHeaderLabel(nwLabels.OUTLINE_COLS[nwOutline.TITLE])
self.setHeaderLabel(trConst(nwLabels.OUTLINE_COLS[nwOutline.TITLE]))
self.treeOrder = []
self.colWidth = {}
@@ -355,7 +355,7 @@ class GuiOutline(QTreeWidget):
if self.firstView:
theLabels = []
for i, hItem in enumerate(self.treeOrder):
theLabels.append(nwLabels.OUTLINE_COLS[hItem])
theLabels.append(trConst(nwLabels.OUTLINE_COLS[hItem]))
self.colIndex[hItem] = i
self.setHeaderLabels(theLabels)
@@ -474,7 +474,7 @@ class GuiOutlineHeaderMenu(QMenu):
self.theParent = theParent
self.acceptToggle = True
mnuHead = QAction("Select Columns", self)
mnuHead = QAction(self.tr("Select Columns"), self)
self.addAction(mnuHead)
self.addSeparator()
@@ -482,7 +482,7 @@ class GuiOutlineHeaderMenu(QMenu):
for hItem in nwOutline:
if hItem == nwOutline.TITLE:
continue
self.actionMap[hItem] = QAction(nwLabels.OUTLINE_COLS[hItem], self)
self.actionMap[hItem] = QAction(trConst(nwLabels.OUTLINE_COLS[hItem]), self)
self.actionMap[hItem].setCheckable(True)
self.actionMap[hItem].toggled.connect(
lambda isChecked, tItem=hItem : self._columnToggled(isChecked, tItem)
+27 -27
View File
@@ -27,12 +27,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
import nw
import logging
from PyQt5.QtCore import Qt
from PyQt5.QtCore import Qt, QT_TRANSLATE_NOOP
from PyQt5.QtWidgets import (
QScrollArea, QWidget, QGridLayout, QHBoxLayout, QGroupBox, QLabel
)
from nw.constants import nwLabels, nwKeyWords
from nw.constants import trConst, nwLabels, nwKeyWords
from nw.common import checkInt
logger = logging.getLogger(__name__)
@@ -40,10 +40,10 @@ logger = logging.getLogger(__name__)
class GuiOutlineDetails(QScrollArea):
LVL_MAP = {
"H1" : "Title",
"H2" : "Chapter",
"H3" : "Scene",
"H4" : "Section"
"H1" : QT_TRANSLATE_NOOP("GuiOutlineDetails", "Title"),
"H2" : QT_TRANSLATE_NOOP("GuiOutlineDetails", "Chapter"),
"H3" : QT_TRANSLATE_NOOP("GuiOutlineDetails", "Scene"),
"H4" : QT_TRANSLATE_NOOP("GuiOutlineDetails", "Section"),
}
def __init__(self, theParent):
@@ -66,9 +66,9 @@ class GuiOutlineDetails(QScrollArea):
vSpace = int(self.mainConf.pxInt(4))
# Details Area
self.titleLabel = QLabel("<b>Title</b>")
self.fileLabel = QLabel("<b>Document</b>")
self.itemLabel = QLabel("<b>Status</b>")
self.titleLabel = QLabel("<b>%s</b>" % self.tr("Title"))
self.fileLabel = QLabel("<b>%s</b>" % self.tr("Document"))
self.itemLabel = QLabel("<b>%s</b>" % self.tr("Status"))
self.titleValue = QLabel("")
self.fileValue = QLabel("")
self.itemValue = QLabel("")
@@ -81,9 +81,9 @@ class GuiOutlineDetails(QScrollArea):
self.itemValue.setMaximumWidth(maxTitle)
# Stats Area
self.cCLabel = QLabel("<b>Characters</b>")
self.wCLabel = QLabel("<b>Words</b>")
self.pCLabel = QLabel("<b>Paragraphs</b>")
self.cCLabel = QLabel("<b>%s</b>" % self.tr("Characters"))
self.wCLabel = QLabel("<b>%s</b>" % self.tr("Words"))
self.pCLabel = QLabel("<b>%s</b>" % self.tr("Paragraphs"))
self.cCValue = QLabel("")
self.wCValue = QLabel("")
self.pCValue = QLabel("")
@@ -96,7 +96,7 @@ class GuiOutlineDetails(QScrollArea):
self.pCValue.setAlignment(Qt.AlignRight)
# Synopsis
self.synopLabel = QLabel("<b>Synopsis</b>")
self.synopLabel = QLabel("<b>%s</b>" % self.tr("Synopsis"))
self.synopValue = QLabel("")
self.synopLWrap = QHBoxLayout()
self.synopValue.setWordWrap(True)
@@ -104,15 +104,15 @@ class GuiOutlineDetails(QScrollArea):
self.synopLWrap.addWidget(self.synopValue, 1)
# Tags
self.povKeyLabel = QLabel("<b>%s</b>" % nwLabels.KEY_NAME[nwKeyWords.POV_KEY])
self.focKeyLabel = QLabel("<b>%s</b>" % nwLabels.KEY_NAME[nwKeyWords.FOCUS_KEY])
self.chrKeyLabel = QLabel("<b>%s</b>" % nwLabels.KEY_NAME[nwKeyWords.CHAR_KEY])
self.pltKeyLabel = QLabel("<b>%s</b>" % nwLabels.KEY_NAME[nwKeyWords.PLOT_KEY])
self.timKeyLabel = QLabel("<b>%s</b>" % nwLabels.KEY_NAME[nwKeyWords.TIME_KEY])
self.wldKeyLabel = QLabel("<b>%s</b>" % nwLabels.KEY_NAME[nwKeyWords.WORLD_KEY])
self.objKeyLabel = QLabel("<b>%s</b>" % nwLabels.KEY_NAME[nwKeyWords.OBJECT_KEY])
self.entKeyLabel = QLabel("<b>%s</b>" % nwLabels.KEY_NAME[nwKeyWords.ENTITY_KEY])
self.cstKeyLabel = QLabel("<b>%s</b>" % nwLabels.KEY_NAME[nwKeyWords.CUSTOM_KEY])
self.povKeyLabel = QLabel("<b>%s</b>" % trConst(nwLabels.KEY_NAME[nwKeyWords.POV_KEY]))
self.focKeyLabel = QLabel("<b>%s</b>" % trConst(nwLabels.KEY_NAME[nwKeyWords.FOCUS_KEY]))
self.chrKeyLabel = QLabel("<b>%s</b>" % trConst(nwLabels.KEY_NAME[nwKeyWords.CHAR_KEY]))
self.pltKeyLabel = QLabel("<b>%s</b>" % trConst(nwLabels.KEY_NAME[nwKeyWords.PLOT_KEY]))
self.timKeyLabel = QLabel("<b>%s</b>" % trConst(nwLabels.KEY_NAME[nwKeyWords.TIME_KEY]))
self.wldKeyLabel = QLabel("<b>%s</b>" % trConst(nwLabels.KEY_NAME[nwKeyWords.WORLD_KEY]))
self.objKeyLabel = QLabel("<b>%s</b>" % trConst(nwLabels.KEY_NAME[nwKeyWords.OBJECT_KEY]))
self.entKeyLabel = QLabel("<b>%s</b>" % trConst(nwLabels.KEY_NAME[nwKeyWords.ENTITY_KEY]))
self.cstKeyLabel = QLabel("<b>%s</b>" % trConst(nwLabels.KEY_NAME[nwKeyWords.CUSTOM_KEY]))
self.povKeyLWrap = QHBoxLayout()
self.focKeyLWrap = QHBoxLayout()
@@ -165,7 +165,7 @@ class GuiOutlineDetails(QScrollArea):
self.cstKeyLWrap.addWidget(self.cstKeyValue, 1)
# Selected Item Details
self.mainGroup = QGroupBox("Title Details", self)
self.mainGroup = QGroupBox(self.tr("Title Details"), self)
self.mainForm = QGridLayout()
self.mainGroup.setLayout(self.mainForm)
@@ -190,7 +190,7 @@ class GuiOutlineDetails(QScrollArea):
self.mainForm.setVerticalSpacing(vSpace)
# Selected Item Tags
self.tagsGroup = QGroupBox("Reference Tags", self)
self.tagsGroup = QGroupBox(self.tr("Reference Tags"), self)
self.tagsForm = QGridLayout()
self.tagsGroup.setLayout(self.tagsForm)
@@ -256,7 +256,7 @@ class GuiOutlineDetails(QScrollArea):
def clearDetails(self):
"""Clear all the data labels.
"""
self.titleLabel.setText("<b>Title</b>")
self.titleLabel.setText("<b>%s</b>" % self.tr("Title"))
self.titleValue.setText("")
self.fileValue.setText("")
self.itemValue.setText("")
@@ -286,9 +286,9 @@ class GuiOutlineDetails(QScrollArea):
return False
if novIdx["level"] in self.LVL_MAP:
self.titleLabel.setText("<b>%s</b>" % self.LVL_MAP[novIdx["level"]])
self.titleLabel.setText("<b>%s</b>" % self.tr(self.LVL_MAP[novIdx["level"]]))
else:
self.titleLabel.setText("<b>Title</b>")
self.titleLabel.setText("<b>%s</b>" % self.tr("Title"))
self.titleValue.setText(novIdx["title"])
self.fileValue.setText(nwItem.itemName)
+151 -139
View File
@@ -28,7 +28,7 @@ import nw
import logging
import os
from PyQt5.QtCore import Qt
from PyQt5.QtCore import Qt, QLocale
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import (
QDialog, QWidget, QComboBox, QSpinBox, QPushButton, QDialogButtonBox,
@@ -53,7 +53,7 @@ class GuiPreferences(PagedDialog):
self.theParent = theParent
self.theProject = theProject
self.setWindowTitle("Preferences")
self.setWindowTitle(self.tr("Preferences"))
self.tabGeneral = GuiPreferencesGeneral(self.theParent)
self.tabProjects = GuiPreferencesProjects(self.theParent)
@@ -62,14 +62,16 @@ class GuiPreferences(PagedDialog):
self.tabSyntax = GuiPreferencesSyntax(self.theParent)
self.tabAuto = GuiPreferencesAutomation(self.theParent)
self.addTab(self.tabGeneral, "General")
self.addTab(self.tabProjects, "Projects")
self.addTab(self.tabDocs, "Documents")
self.addTab(self.tabEditor, "Editor")
self.addTab(self.tabSyntax, "Highlighting")
self.addTab(self.tabAuto, "Automation")
self.addTab(self.tabGeneral, self.tr("General"))
self.addTab(self.tabProjects, self.tr("Projects"))
self.addTab(self.tabDocs, self.tr("Documents"))
self.addTab(self.tabEditor, self.tr("Editor"))
self.addTab(self.tabSyntax, self.tr("Highlighting"))
self.addTab(self.tabAuto, self.tr("Automation"))
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.buttonBox.button(QDialogButtonBox.Ok).setText(self.tr("Ok"))
self.buttonBox.button(QDialogButtonBox.Cancel).setText(self.tr("Cancel"))
self.buttonBox.accepted.connect(self._doSave)
self.buttonBox.rejected.connect(self._doClose)
self.addControls(self.buttonBox)
@@ -102,7 +104,7 @@ class GuiPreferences(PagedDialog):
if needsRestart:
self.theParent.makeAlert(
"Some changes will not be applied until novelWriter has been restarted.",
self.tr("Some changes will not be applied until novelWriter has been restarted."),
nwAlert.INFO
)
@@ -134,7 +136,7 @@ class GuiPreferencesGeneral(QWidget):
# Look and Feel
# =============
self.mainForm.addGroupLabel("Look and Feel")
self.mainForm.addGroupLabel(self.tr("Look and Feel"))
## Select Theme
self.guiTheme = QComboBox()
@@ -147,9 +149,9 @@ class GuiPreferencesGeneral(QWidget):
self.guiTheme.setCurrentIndex(themeIdx)
self.mainForm.addRow(
"Main GUI theme",
self.tr("Main GUI theme"),
self.guiTheme,
"Changing this requires restarting novelWriter."
self.tr("Changing this requires restarting novelWriter.")
)
## Select Icon Theme
@@ -163,18 +165,18 @@ class GuiPreferencesGeneral(QWidget):
self.guiIcons.setCurrentIndex(iconIdx)
self.mainForm.addRow(
"Main icon theme",
self.tr("Main icon theme"),
self.guiIcons,
"Changing this requires restarting novelWriter."
self.tr("Changing this requires restarting novelWriter.")
)
## Dark Icons
self.guiDark = QSwitch()
self.guiDark.setChecked(self.mainConf.guiDark)
self.mainForm.addRow(
"Prefer icons for dark backgrounds",
self.tr("Prefer icons for dark backgrounds"),
self.guiDark,
"May improve the look of icons on dark themes."
self.tr("May improve the look of icons on dark themes.")
)
## Font Family
@@ -186,9 +188,9 @@ class GuiPreferencesGeneral(QWidget):
self.fontButton.setMaximumWidth(int(2.5*self.theTheme.getTextWidth("...")))
self.fontButton.clicked.connect(self._selectFont)
self.mainForm.addRow(
"Font family",
self.tr("Font family"),
self.guiFont,
"Changing this requires restarting novelWriter.",
self.tr("Changing this requires restarting novelWriter."),
theButton = self.fontButton
)
@@ -199,38 +201,38 @@ class GuiPreferencesGeneral(QWidget):
self.guiFontSize.setSingleStep(1)
self.guiFontSize.setValue(self.mainConf.guiFontSize)
self.mainForm.addRow(
"Font size",
self.tr("Font size"),
self.guiFontSize,
"Changing this requires restarting novelWriter.",
theUnit = "pt"
self.tr("Changing this requires restarting novelWriter."),
theUnit = self.tr("pt")
)
# GUI Settings
# ============
self.mainForm.addGroupLabel("GUI Settings")
self.mainForm.addGroupLabel(self.tr("GUI Settings"))
self.showFullPath = QSwitch()
self.showFullPath.setChecked(self.mainConf.showFullPath)
self.mainForm.addRow(
"Show full path in document header",
self.tr("Show full path in document header"),
self.showFullPath,
"Add the parent folder names to the header."
self.tr("Add the parent folder names to the header.")
)
self.hideVScroll = QSwitch()
self.hideVScroll.setChecked(self.mainConf.hideVScroll)
self.mainForm.addRow(
"Hide vertical scroll bars in main windows",
self.tr("Hide vertical scroll bars in main windows"),
self.hideVScroll,
"Scrolling available with mouse wheel and keys only."
self.tr("Scrolling available with mouse wheel and keys only.")
)
self.hideHScroll = QSwitch()
self.hideHScroll.setChecked(self.mainConf.hideHScroll)
self.mainForm.addRow(
"Hide horizontal scroll bars in main windows",
self.tr("Hide horizontal scroll bars in main windows"),
self.hideHScroll,
"Scrolling available with mouse wheel and keys only."
self.tr("Scrolling available with mouse wheel and keys only.")
)
return
@@ -299,7 +301,7 @@ class GuiPreferencesProjects(QWidget):
# Automatic Save
# ==============
self.mainForm.addGroupLabel("Automatic Save")
self.mainForm.addGroupLabel(self.tr("Automatic Save"))
## Document Save Timer
self.autoSaveDoc = QSpinBox(self)
@@ -308,10 +310,10 @@ class GuiPreferencesProjects(QWidget):
self.autoSaveDoc.setSingleStep(1)
self.autoSaveDoc.setValue(self.mainConf.autoSaveDoc)
self.mainForm.addRow(
"Save document interval",
self.tr("Save document interval"),
self.autoSaveDoc,
"How often the open document is automatically saved.",
theUnit="seconds"
self.tr("How often the open document is automatically saved."),
theUnit=self.tr("seconds")
)
## Project Save Timer
@@ -321,24 +323,24 @@ class GuiPreferencesProjects(QWidget):
self.autoSaveProj.setSingleStep(1)
self.autoSaveProj.setValue(self.mainConf.autoSaveProj)
self.mainForm.addRow(
"Save project interval",
self.tr("Save project interval"),
self.autoSaveProj,
"How often the open project is automatically saved.",
theUnit="seconds"
self.tr("How often the open project is automatically saved."),
theUnit=self.tr("seconds")
)
# Project Backup
# ==============
self.mainForm.addGroupLabel("Project Backup")
self.mainForm.addGroupLabel(self.tr("Project Backup"))
## Backup Path
self.backupPath = self.mainConf.backupPath
self.backupGetPath = QPushButton("Browse")
self.backupGetPath = QPushButton(self.tr("Browse"))
self.backupGetPath.clicked.connect(self._backupFolder)
self.backupPathRow = self.mainForm.addRow(
"Backup storage location",
self.tr("Backup storage location"),
self.backupGetPath,
"Path: %s" % self.backupPath
self.tr("Path: {0}").format(self.backupPath)
)
## Run when closing
@@ -346,9 +348,9 @@ class GuiPreferencesProjects(QWidget):
self.backupOnClose.setChecked(self.mainConf.backupOnClose)
self.backupOnClose.toggled.connect(self._toggledBackupOnClose)
self.mainForm.addRow(
"Run backup when the project is closed",
self.tr("Run backup when the project is closed"),
self.backupOnClose,
"Can be overridden for individual projects in project settings."
self.tr("Can be overridden for individual projects in project settings.")
)
## Ask before backup
@@ -357,22 +359,22 @@ class GuiPreferencesProjects(QWidget):
self.askBeforeBackup.setChecked(self.mainConf.askBeforeBackup)
self.askBeforeBackup.setEnabled(self.mainConf.backupOnClose)
self.mainForm.addRow(
"Ask before running backup",
self.tr("Ask before running backup"),
self.askBeforeBackup,
"If off, backups will run in the background."
self.tr("If off, backups will run in the background.")
)
# Session Timer
# =============
self.mainForm.addGroupLabel("Session Timer")
self.mainForm.addGroupLabel(self.tr("Session Timer"))
## Pause when idle
self.stopWhenIdle = QSwitch()
self.stopWhenIdle.setChecked(self.mainConf.stopWhenIdle)
self.mainForm.addRow(
"Pause the session timer when not writing",
self.tr("Pause the session timer when not writing"),
self.stopWhenIdle,
"Also pauses when the application window does not have focus."
self.tr("Also pauses when the application window does not have focus.")
)
## Inactive time for idle
@@ -383,10 +385,10 @@ class GuiPreferencesProjects(QWidget):
self.userIdleTime.setDecimals(1)
self.userIdleTime.setValue(self.mainConf.userIdleTime/60.0)
self.mainForm.addRow(
"Editor inactive time before pausing timer",
self.tr("Editor inactive time before pausing timer"),
self.userIdleTime,
"User activity includes typing and changing the content.",
theUnit="minutes"
self.tr("User activity includes typing and changing the content."),
theUnit=self.tr("minutes")
)
return
@@ -426,11 +428,13 @@ class GuiPreferencesProjects(QWidget):
dlgOpt |= QFileDialog.ShowDirsOnly
dlgOpt |= QFileDialog.DontUseNativeDialog
newDir = QFileDialog.getExistingDirectory(
self, "Backup Directory", currDir, options=dlgOpt
self, self.tr("Backup Directory"), currDir, options=dlgOpt
)
if newDir:
self.backupPath = newDir
self.mainForm.setHelpText(self.backupPathRow, "Path: %s" % self.backupPath)
self.mainForm.setHelpText(
self.backupPathRow, self.tr("Path: {0}").format(self.backupPath)
)
return True
return False
@@ -460,7 +464,7 @@ class GuiPreferencesDocuments(QWidget):
# Text Style
# ==========
self.mainForm.addGroupLabel("Text Style")
self.mainForm.addGroupLabel(self.tr("Text Style"))
## Font Family
self.textFont = QLineEdit()
@@ -471,9 +475,9 @@ class GuiPreferencesDocuments(QWidget):
self.fontButton.setMaximumWidth(int(2.5*self.theTheme.getTextWidth("...")))
self.fontButton.clicked.connect(self._selectFont)
self.mainForm.addRow(
"Font family",
self.tr("Font family"),
self.textFont,
"Font for the document editor and viewer.",
self.tr("Font for the document editor and viewer."),
theButton = self.fontButton
)
@@ -484,15 +488,15 @@ class GuiPreferencesDocuments(QWidget):
self.textSize.setSingleStep(1)
self.textSize.setValue(self.mainConf.textSize)
self.mainForm.addRow(
"Font size",
self.tr("Font size"),
self.textSize,
"Font size for the document editor and viewer.",
theUnit = "pt"
self.tr("Font size for the document editor and viewer."),
theUnit = self.tr("pt")
)
# Text Flow
# =========
self.mainForm.addGroupLabel("Text Flow")
self.mainForm.addGroupLabel(self.tr("Text Flow"))
## Max Text Width in Normal Mode
self.textWidth = QSpinBox(self)
@@ -501,10 +505,10 @@ class GuiPreferencesDocuments(QWidget):
self.textWidth.setSingleStep(10)
self.textWidth.setValue(self.mainConf.textWidth)
self.mainForm.addRow(
"Maximum text width in \"Normal Mode\"",
self.tr("Maximum text width in \"Normal Mode\""),
self.textWidth,
"Horizontal margins are scaled automatically.",
theUnit="px"
self.tr("Horizontal margins are scaled automatically."),
theUnit=self.tr("px")
)
## Max Text Width in Focus Mode
@@ -514,37 +518,37 @@ class GuiPreferencesDocuments(QWidget):
self.focusWidth.setSingleStep(10)
self.focusWidth.setValue(self.mainConf.focusWidth)
self.mainForm.addRow(
"Maximum text width in \"Focus Mode\"",
self.tr("Maximum text width in \"Focus Mode\""),
self.focusWidth,
"Horizontal margins are scaled automatically.",
theUnit="px"
self.tr("Horizontal margins are scaled automatically."),
theUnit=self.tr("px")
)
## Document Fixed Width
self.textFixedW = QSwitch()
self.textFixedW.setChecked(not self.mainConf.textFixedW)
self.mainForm.addRow(
"Disable maximum text width in \"Normal Mode\"",
self.tr("Disable maximum text width in \"Normal Mode\""),
self.textFixedW,
"Text width is defined by the margins only."
self.tr("Text width is defined by the margins only.")
)
## Focus Mode Footer
self.hideFocusFooter = QSwitch()
self.hideFocusFooter.setChecked(self.mainConf.hideFocusFooter)
self.mainForm.addRow(
"Hide document footer in \"Focus Mode\"",
self.tr("Hide document footer in \"Focus Mode\""),
self.hideFocusFooter,
"Hide the information bar at the bottom of the document."
self.tr("Hide the information bar at the bottom of the document.")
)
## Justify Text
self.doJustify = QSwitch()
self.doJustify.setChecked(self.mainConf.doJustify)
self.mainForm.addRow(
"Justify the text margins in editor and viewer",
self.tr("Justify the text margins in editor and viewer"),
self.doJustify,
"Lay out text with straight edges in the editor and viewer."
self.tr("Lay out text with straight edges in the editor and viewer.")
)
## Document Margins
@@ -554,10 +558,10 @@ class GuiPreferencesDocuments(QWidget):
self.textMargin.setSingleStep(1)
self.textMargin.setValue(self.mainConf.textMargin)
self.mainForm.addRow(
"Text margin",
self.tr("Text margin"),
self.textMargin,
"If maximum width is set, this becomes the minimum margin.",
theUnit="px"
self.tr("If maximum width is set, this becomes the minimum margin."),
theUnit=self.tr("px")
)
## Tab Width
@@ -567,10 +571,10 @@ class GuiPreferencesDocuments(QWidget):
self.tabWidth.setSingleStep(1)
self.tabWidth.setValue(self.mainConf.tabWidth)
self.mainForm.addRow(
"Tab width",
self.tr("Tab width"),
self.tabWidth,
"The width of a tab key press in the editor and viewer.",
theUnit="px"
self.tr("The width of a tab key press in the editor and viewer."),
theUnit=self.tr("px")
)
return
@@ -628,14 +632,19 @@ class GuiPreferencesEditor(QWidget):
self.mainForm.setHelpTextStyle(self.theTheme.helpText)
self.setLayout(self.mainForm)
mW = self.mainConf.pxInt(250)
# Spell Checking
# ==============
self.mainForm.addGroupLabel("Spell Checking")
self.mainForm.addGroupLabel(self.tr("Spell Checking"))
## Spell Check Provider and Language
self.spellLangList = QComboBox(self)
self.spellLangList.setMaximumWidth(mW)
self.spellToolList = QComboBox(self)
self.spellToolList.addItem("Internal (difflib)", nwConst.SP_INTERNAL)
self.spellToolList.setMaximumWidth(mW)
self.spellToolList.addItem("%s (difflib)" % self.tr("Internal"), nwConst.SP_INTERNAL)
self.spellToolList.addItem("Spell Enchant (pyenchant)", nwConst.SP_ENCHANT)
theModel = self.spellToolList.model()
@@ -649,14 +658,14 @@ class GuiPreferencesEditor(QWidget):
self._doUpdateSpellTool(0)
self.mainForm.addRow(
"Spell check provider",
self.tr("Spell check provider"),
self.spellToolList,
"Note that the internal spell check tool is quite slow."
self.tr("Note that the internal spell check tool is quite slow.")
)
self.mainForm.addRow(
"Spell check language",
self.tr("Spell check language"),
self.spellLangList,
"Available languages are determined by your system."
self.tr("Available languages are determined by your system.")
)
## Big Document Size Limit
@@ -666,15 +675,15 @@ class GuiPreferencesEditor(QWidget):
self.bigDocLimit.setSingleStep(10)
self.bigDocLimit.setValue(self.mainConf.bigDocLimit)
self.mainForm.addRow(
"Big document limit",
self.tr("Big document limit"),
self.bigDocLimit,
"Full spell checking is disabled above this limit.",
theUnit="kB"
self.tr("Full spell checking is disabled above this limit."),
theUnit=self.tr("kB")
)
# Word Count
# ==========
self.mainForm.addGroupLabel("Word Count")
self.mainForm.addGroupLabel(self.tr("Word Count"))
## Word Count Timer
self.wordCountTimer = QDoubleSpinBox(self)
@@ -684,54 +693,54 @@ class GuiPreferencesEditor(QWidget):
self.wordCountTimer.setSingleStep(0.1)
self.wordCountTimer.setValue(self.mainConf.wordCountTimer)
self.mainForm.addRow(
"Word count interval",
self.tr("Word count interval"),
self.wordCountTimer,
"How often the word count is updated.",
theUnit="seconds"
self.tr("How often the word count is updated."),
theUnit=self.tr("seconds")
)
# Writing Guides
# ==============
self.mainForm.addGroupLabel("Writing Guides")
self.mainForm.addGroupLabel(self.tr("Writing Guides"))
## Show Tabs and Spaces
self.showTabsNSpaces = QSwitch()
self.showTabsNSpaces.setChecked(self.mainConf.showTabsNSpaces)
self.mainForm.addRow(
"Show tabs and spaces",
self.tr("Show tabs and spaces"),
self.showTabsNSpaces,
"Add symbols to indicate tabs and spaces in the editor."
self.tr("Add symbols to indicate tabs and spaces in the editor.")
)
## Show Line Endings
self.showLineEndings = QSwitch()
self.showLineEndings.setChecked(self.mainConf.showLineEndings)
self.mainForm.addRow(
"Show line endings",
self.tr("Show line endings"),
self.showLineEndings,
"Add a symbol to indicate line endings in the editor."
self.tr("Add a symbol to indicate line endings in the editor.")
)
# Scroll Behaviour
# ================
self.mainForm.addGroupLabel("Scroll Behaviour")
self.mainForm.addGroupLabel(self.tr("Scroll Behaviour"))
## Scroll Past End
self.scrollPastEnd = QSwitch()
self.scrollPastEnd.setChecked(self.mainConf.scrollPastEnd)
self.mainForm.addRow(
"Scroll past end of the document",
self.tr("Scroll past end of the document"),
self.scrollPastEnd,
"Also improves trypewriter scrolling for short documents."
self.tr("Also improves trypewriter scrolling for short documents.")
)
## Typewriter Scrolling
self.autoScroll = QSwitch()
self.autoScroll.setChecked(self.mainConf.autoScroll)
self.mainForm.addRow(
"Typewriter style scrolling when you type",
self.tr("Typewriter style scrolling when you type"),
self.autoScroll,
"Try to keep the cursor at a fixed vertical position."
self.tr("Try to keep the cursor at a fixed vertical position.")
)
## Typewriter Position
@@ -741,9 +750,9 @@ class GuiPreferencesEditor(QWidget):
self.autoScrollPos.setSingleStep(1)
self.autoScrollPos.setValue(int(self.mainConf.autoScrollPos))
self.mainForm.addRow(
"Minimum position for Typewriter scrolling",
self.tr("Minimum position for Typewriter scrolling"),
self.autoScrollPos,
"Percentage of the editor height from the top.",
self.tr("Percentage of the editor height from the top."),
theUnit = "%"
)
@@ -796,8 +805,11 @@ class GuiPreferencesEditor(QWidget):
theDict = NWSpellSimple()
self.spellLangList.clear()
for spTag, spName in theDict.listDictionaries():
self.spellLangList.addItem(spName, spTag)
for spTag, spProv in theDict.listDictionaries():
qLocal = QLocale(spTag)
spLang = qLocal.nativeLanguageName().title()
spName = qLocal.bcp47Name()
self.spellLangList.addItem("%s (%s) [%s]" % (spLang, spName, spProv), spTag)
spellIdx = self.spellLangList.findData(self.mainConf.spellLanguage)
if spellIdx != -1:
@@ -823,7 +835,7 @@ class GuiPreferencesSyntax(QWidget):
# Highlighting Theme
# ==================
self.mainForm.addGroupLabel("Highlighting Theme")
self.mainForm.addGroupLabel(self.tr("Highlighting Theme"))
self.guiSyntax = QComboBox()
self.guiSyntax.setMinimumWidth(self.mainConf.pxInt(200))
@@ -835,50 +847,50 @@ class GuiPreferencesSyntax(QWidget):
self.guiSyntax.setCurrentIndex(syntaxIdx)
self.mainForm.addRow(
"Highlighting theme",
self.tr("Highlighting theme"),
self.guiSyntax,
"Colour theme to apply to the editor and viewer."
self.tr("Colour theme to apply to the editor and viewer.")
)
# Quotes & Dialogue
# =================
self.mainForm.addGroupLabel("Quotes & Dialogue")
self.mainForm.addGroupLabel(self.tr("Quotes & Dialogue"))
self.highlightQuotes = QSwitch()
self.highlightQuotes.setChecked(self.mainConf.highlightQuotes)
self.highlightQuotes.toggled.connect(self._toggleHighlightQuotes)
self.mainForm.addRow(
"Highlight text wrapped in quotes",
self.tr("Highlight text wrapped in quotes"),
self.highlightQuotes,
"Applies to single, double and straight quotes."
self.tr("Applies to single, double and straight quotes.")
)
self.allowOpenSQuote = QSwitch()
self.allowOpenSQuote.setChecked(self.mainConf.allowOpenSQuote)
self.mainForm.addRow(
"Allow open-ended single quotes",
self.tr("Allow open-ended single quotes"),
self.allowOpenSQuote,
"Highlight single-quoted line with no closing quote."
self.tr("Highlight single-quoted line with no closing quote.")
)
self.allowOpenDQuote = QSwitch()
self.allowOpenDQuote.setChecked(self.mainConf.allowOpenDQuote)
self.mainForm.addRow(
"Allow open-ended double quotes",
self.tr("Allow open-ended double quotes"),
self.allowOpenDQuote,
"Highlight double-quoted line with no closing quote."
self.tr("Highlight double-quoted line with no closing quote.")
)
# Text Emphasis
# =============
self.mainForm.addGroupLabel("Text Emphasis")
self.mainForm.addGroupLabel(self.tr("Text Emphasis"))
self.highlightEmph = QSwitch()
self.highlightEmph.setChecked(self.mainConf.highlightEmph)
self.mainForm.addRow(
"Add highlight colour to emphasised text",
self.tr("Add highlight colour to emphasised text"),
self.highlightEmph,
"Applies to emphasis (italic) and strong (bold)."
self.tr("Applies to emphasis (italic) and strong (bold).")
)
return
@@ -931,15 +943,15 @@ class GuiPreferencesAutomation(QWidget):
# Automatic Features
# ==================
self.mainForm.addGroupLabel("Automatic Features")
self.mainForm.addGroupLabel(self.tr("Automatic Features"))
## Auto-Select Word Under Cursor
self.autoSelect = QSwitch()
self.autoSelect.setChecked(self.mainConf.autoSelect)
self.mainForm.addRow(
"Auto-select word under cursor",
self.tr("Auto-select word under cursor"),
self.autoSelect,
"Apply formatting to word under cursor if no selection is made."
self.tr("Apply formatting to word under cursor if no selection is made.")
)
## Auto-Replace as You Type Main Switch
@@ -947,23 +959,23 @@ class GuiPreferencesAutomation(QWidget):
self.doReplace.setChecked(self.mainConf.doReplace)
self.doReplace.toggled.connect(self._toggleAutoReplaceMain)
self.mainForm.addRow(
"Auto-replace text as you type",
self.tr("Auto-replace text as you type"),
self.doReplace,
"Allow the editor to replace symbols as you type."
self.tr("Allow the editor to replace symbols as you type.")
)
# Replace as You Type
# ===================
self.mainForm.addGroupLabel("Replace as You Type")
self.mainForm.addGroupLabel(self.tr("Replace as You Type"))
## Auto-Replace Single Quotes
self.doReplaceSQuote = QSwitch()
self.doReplaceSQuote.setChecked(self.mainConf.doReplaceSQuote)
self.doReplaceSQuote.setEnabled(self.mainConf.doReplace)
self.mainForm.addRow(
"Auto-replace single quotes",
self.tr("Auto-replace single quotes"),
self.doReplaceSQuote,
"Try to guess which is an opening or a closing single quote."
self.tr("Try to guess which is an opening or a closing single quote.")
)
## Auto-Replace Double Quotes
@@ -971,9 +983,9 @@ class GuiPreferencesAutomation(QWidget):
self.doReplaceDQuote.setChecked(self.mainConf.doReplaceDQuote)
self.doReplaceDQuote.setEnabled(self.mainConf.doReplace)
self.mainForm.addRow(
"Auto-replace double quotes",
self.tr("Auto-replace double quotes"),
self.doReplaceDQuote,
"Try to guess which is an opening or a closing double quote."
self.tr("Try to guess which is an opening or a closing double quote.")
)
## Auto-Replace Hyphens
@@ -981,9 +993,9 @@ class GuiPreferencesAutomation(QWidget):
self.doReplaceDash.setChecked(self.mainConf.doReplaceDash)
self.doReplaceDash.setEnabled(self.mainConf.doReplace)
self.mainForm.addRow(
"Auto-replace dashes",
self.tr("Auto-replace dashes"),
self.doReplaceDash,
"Double and triple hyphens become short and long dashes."
self.tr("Double and triple hyphens become short and long dashes.")
)
## Auto-Replace Dots
@@ -991,14 +1003,14 @@ class GuiPreferencesAutomation(QWidget):
self.doReplaceDots.setChecked(self.mainConf.doReplaceDots)
self.doReplaceDots.setEnabled(self.mainConf.doReplace)
self.mainForm.addRow(
"Auto-replace dots",
self.tr("Auto-replace dots"),
self.doReplaceDots,
"Three consecutive dots become ellipsis."
self.tr("Three consecutive dots become ellipsis.")
)
# Quotation Style
# ===============
self.mainForm.addGroupLabel("Quotation Style")
self.mainForm.addGroupLabel(self.tr("Quotation Style"))
qWidth = self.mainConf.pxInt(40)
bWidth = int(2.5*self.theTheme.getTextWidth("..."))
@@ -1015,9 +1027,9 @@ class GuiPreferencesAutomation(QWidget):
self.btnSingleStyleO.setMaximumWidth(bWidth)
self.btnSingleStyleO.clicked.connect(lambda: self._getQuote("SO"))
self.mainForm.addRow(
"Single quote open style",
self.tr("Single quote open style"),
self.quoteSym["SO"],
"The symbol to use for a leading single quote.",
self.tr("The symbol to use for a leading single quote."),
theButton=self.btnSingleStyleO
)
@@ -1031,9 +1043,9 @@ class GuiPreferencesAutomation(QWidget):
self.btnSingleStyleC.setMaximumWidth(bWidth)
self.btnSingleStyleC.clicked.connect(lambda: self._getQuote("SC"))
self.mainForm.addRow(
"Single quote close style",
self.tr("Single quote close style"),
self.quoteSym["SC"],
"The symbol to use for a trailing single quote.",
self.tr("The symbol to use for a trailing single quote."),
theButton=self.btnSingleStyleC
)
@@ -1048,9 +1060,9 @@ class GuiPreferencesAutomation(QWidget):
self.btnDoubleStyleO.setMaximumWidth(bWidth)
self.btnDoubleStyleO.clicked.connect(lambda: self._getQuote("DO"))
self.mainForm.addRow(
"Double quote open style",
self.tr("Double quote open style"),
self.quoteSym["DO"],
"The symbol to use for a leading double quote.",
self.tr("The symbol to use for a leading double quote."),
theButton=self.btnDoubleStyleO
)
@@ -1064,9 +1076,9 @@ class GuiPreferencesAutomation(QWidget):
self.btnDoubleStyleC.setMaximumWidth(bWidth)
self.btnDoubleStyleC.clicked.connect(lambda: self._getQuote("DC"))
self.mainForm.addRow(
"Double quote close style",
self.tr("Double quote close style"),
self.quoteSym["DC"],
"The symbol to use for a trailing double quote.",
self.tr("The symbol to use for a trailing double quote."),
theButton=self.btnDoubleStyleC
)
+30 -20
View File
@@ -54,7 +54,7 @@ class GuiProjectDetails(PagedDialog):
self.theProject = theProject
self.optState = theProject.optState
self.setWindowTitle("Project Details")
self.setWindowTitle(self.tr("Project Details"))
wW = self.mainConf.pxInt(600)
wH = self.mainConf.pxInt(400)
@@ -69,10 +69,11 @@ class GuiProjectDetails(PagedDialog):
self.tabMain = GuiProjectDetailsMain(self.theParent, self.theProject)
self.tabContents = GuiProjectDetailsContents(self.theParent, self.theProject)
self.addTab(self.tabMain, "Overview")
self.addTab(self.tabContents, "Contents")
self.addTab(self.tabMain, self.tr("Overview"))
self.addTab(self.tabContents, self.tr("Contents"))
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Close)
self.buttonBox.button(QDialogButtonBox.Close).setText(self.tr("Close"))
self.buttonBox.rejected.connect(self._doClose)
self.addControls(self.buttonBox)
@@ -154,7 +155,9 @@ class GuiProjectDetailsMain(QWidget):
self.bookTitle.setAlignment(Qt.AlignHCenter)
self.bookTitle.setWordWrap(True)
self.projName = QLabel("Working Title: %s" % self.theProject.projName)
self.projName = QLabel(
self.tr("Working Title: {0}").format(self.theProject.projName)
)
workFont = self.projName.font()
workFont.setPointSizeF(0.8*fPt)
workFont.setItalic(True)
@@ -162,7 +165,7 @@ class GuiProjectDetailsMain(QWidget):
self.projName.setAlignment(Qt.AlignHCenter)
self.projName.setWordWrap(True)
self.bookAuthors = QLabel("By %s" % self.theProject.getAuthors())
self.bookAuthors = QLabel(self.tr("By {0}").format(self.theProject.getAuthors()))
authFont = self.bookAuthors.font()
authFont.setPointSizeF(1.2*fPt)
self.bookAuthors.setFont(authFont)
@@ -175,20 +178,20 @@ class GuiProjectDetailsMain(QWidget):
hCounts = self.theIndex.getNovelTitleCounts()
nwCount = self.theIndex.getNovelWordCount()
self.wordCountLbl = QLabel("<b>Words:</b>")
self.wordCountLbl = QLabel("<b>%s:</b>" % self.tr("Words"))
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.sceneCountLbl = QLabel("<b>Scenes:</b>")
self.sceneCountLbl = QLabel("<b>%s:</b>" % self.tr("Scenes"))
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}")
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.statsGrid = QGridLayout()
@@ -208,7 +211,7 @@ class GuiProjectDetailsMain(QWidget):
# Meta
# ====
self.projPathLbl = QLabel("<b>Path:</b>")
self.projPathLbl = QLabel("<b>%s:</b>" % self.tr("Path"))
self.projPathVal = QLineEdit()
self.projPathVal.setText(self.theProject.projPath)
self.projPathVal.setReadOnly(True)
@@ -271,7 +274,14 @@ class GuiProjectDetailsContents(QWidget):
self.tocTree.setIndentation(0)
self.tocTree.setColumnCount(6)
self.tocTree.setSelectionMode(QAbstractItemView.NoSelection)
self.tocTree.setHeaderLabels(["Title", "Words", "Pages", "Page", "Progress", ""])
self.tocTree.setHeaderLabels([
self.tr("Title"),
self.tr("Words"),
self.tr("Pages"),
self.tr("Page"),
self.tr("Progress"),
""
])
treeHeadItem = self.tocTree.headerItem()
treeHeadItem.setTextAlignment(self.C_WORDS, Qt.AlignRight)
@@ -304,16 +314,16 @@ class GuiProjectDetailsContents(QWidget):
clearDouble = self.optState.getInt("GuiProjectDetails", "clearDouble", True)
wordsHelp = (
"Typical word count for a 5 by 8 inch book page with 11 pt font is 350."
self.tr("Typical word count for a 5 by 8 inch book page with 11 pt font is 350.")
)
offsetHelp = (
"Start counting page numbers from this page."
self.tr("Start counting page numbers from this page.")
)
dblHelp = (
"Assume a new chapter or partition always start on an odd numbered page."
self.tr("Assume a new chapter or partition always start on an odd numbered page.")
)
self.wpLabel = QLabel("Words per page")
self.wpLabel = QLabel(self.tr("Words per page"))
self.wpLabel.setToolTip(wordsHelp)
self.wpValue = QSpinBox()
@@ -324,7 +334,7 @@ class GuiProjectDetailsContents(QWidget):
self.wpValue.setToolTip(wordsHelp)
self.wpValue.valueChanged.connect(self._populateTree)
self.poLabel = QLabel("Count pages from")
self.poLabel = QLabel(self.tr("Count pages from"))
self.poLabel.setToolTip(offsetHelp)
self.poValue = QSpinBox()
@@ -335,7 +345,7 @@ class GuiProjectDetailsContents(QWidget):
self.poValue.setToolTip(offsetHelp)
self.poValue.valueChanged.connect(self._populateTree)
self.dblLabel = QLabel("Clear double pages")
self.dblLabel = QLabel(self.tr("Clear double pages"))
self.dblLabel.setToolTip(dblHelp)
self.dblValue = QSwitch(self, 2*iPx, iPx)
@@ -358,7 +368,7 @@ class GuiProjectDetailsContents(QWidget):
# ========
self.outerBox = QVBoxLayout()
self.outerBox.addWidget(QLabel("<b>Table of Contents</b>"))
self.outerBox.addWidget(QLabel("<b>%s</b>" % self.tr("Table of Contents")))
self.outerBox.addWidget(self.tocTree)
self.outerBox.addLayout(self.optionsBox)
@@ -390,7 +400,7 @@ class GuiProjectDetailsContents(QWidget):
"""
self._theToC = []
self._theToC = self.theIndex.getTableOfContents(2)
self._theToC.append(("", 0, "END", 0))
self._theToC.append(("", 0, self.tr("END"), 0))
return
##
+25 -13
View File
@@ -74,7 +74,7 @@ class GuiProjectLoad(QDialog):
self.outerBox.setSpacing(sPx)
self.innerBox.setSpacing(sPx)
self.setWindowTitle("Open Project")
self.setWindowTitle(self.tr("Open Project"))
self.setMinimumWidth(self.mainConf.pxInt(650))
self.setMinimumHeight(self.mainConf.pxInt(400))
self.setModal(True)
@@ -90,7 +90,11 @@ class GuiProjectLoad(QDialog):
self.listBox.setSelectionMode(QAbstractItemView.SingleSelection)
self.listBox.setDragDropMode(QAbstractItemView.NoDragDrop)
self.listBox.setColumnCount(3)
self.listBox.setHeaderLabels(["Working Title", "Words", "Last Opened"])
self.listBox.setHeaderLabels([
self.tr("Working Title"),
self.tr("Words"),
self.tr("Last Opened"),
])
self.listBox.setRootIsDecorated(False)
self.listBox.itemSelectionChanged.connect(self._doSelectRecent)
self.listBox.itemDoubleClicked.connect(self._doOpenRecent)
@@ -100,8 +104,8 @@ class GuiProjectLoad(QDialog):
treeHead.setTextAlignment(self.C_COUNT, Qt.AlignRight)
treeHead.setTextAlignment(self.C_TIME, Qt.AlignRight)
self.lblRecent = QLabel("<b>Recently Opened Projects</b>")
self.lblPath = QLabel("<b>Path</b>")
self.lblRecent = QLabel("<b>%s</b>" % self.tr("Recently Opened Projects"))
self.lblPath = QLabel("<b>%s</b>" % self.tr("Path"))
self.selPath = QLineEdit("")
self.selPath.setReadOnly(True)
@@ -123,13 +127,15 @@ class GuiProjectLoad(QDialog):
self.innerBox.addLayout(self.projectForm)
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Open | QDialogButtonBox.Cancel)
self.buttonBox.button(QDialogButtonBox.Open).setText(self.tr("Open"))
self.buttonBox.button(QDialogButtonBox.Cancel).setText(self.tr("Cancel"))
self.buttonBox.accepted.connect(self._doOpenRecent)
self.buttonBox.rejected.connect(self._doCancel)
self.newButton = self.buttonBox.addButton("New", QDialogButtonBox.ActionRole)
self.newButton = self.buttonBox.addButton(self.tr("New"), QDialogButtonBox.ActionRole)
self.newButton.clicked.connect(self._doNewProject)
self.delButton = self.buttonBox.addButton("Remove", QDialogButtonBox.ActionRole)
self.delButton = self.buttonBox.addButton(self.tr("Remove"), QDialogButtonBox.ActionRole)
self.delButton.clicked.connect(self._doDeleteRecent)
self.outerBox.addLayout(self.innerBox)
@@ -180,12 +186,15 @@ class GuiProjectLoad(QDialog):
"""Browse for a folder path.
"""
logger.verbose("GuiProjectLoad browse button clicked")
extFilter = [
self.tr("novelWriter Project File ({0})").format(nwFiles.PROJ_FILE),
self.tr("All files ({0})").format("*.*"),
]
dlgOpt = QFileDialog.Options()
dlgOpt |= QFileDialog.DontUseNativeDialog
projFile, _ = QFileDialog.getOpenFileName(
self, "Open novelWriter Project", "",
"novelWriter Project File (%s);;All Files (*)" % nwFiles.PROJ_FILE,
options=dlgOpt
self, self.tr("Open novelWriter Project"), "",
filter=";;".join(extFilter), options=dlgOpt
)
if projFile:
thePath = os.path.abspath(os.path.dirname(projFile))
@@ -221,10 +230,13 @@ class GuiProjectLoad(QDialog):
selList = self.listBox.selectedItems()
if selList:
projName = selList[0].text(self.C_NAME)
msgYes = self.theParent.askQuestion("Remove Entry", (
"Remove '%s' from the recent projects list? "
"The project files will not be deleted."
) % projName)
msgYes = self.theParent.askQuestion(
self.tr("Remove Entry"),
self.tr(
"Remove '{0}' from the recent projects list? "
"The project files will not be deleted."
).format(projName)
)
if msgYes:
self.mainConf.removeFromRecentCache(
selList[0].data(self.C_NAME, Qt.UserRole)
+44 -34
View File
@@ -54,7 +54,7 @@ class GuiProjectSettings(PagedDialog):
self.optState = theProject.optState
self.theProject.countStatus()
self.setWindowTitle("Project Settings")
self.setWindowTitle(self.tr("Project Settings"))
wW = self.mainConf.pxInt(570)
wH = self.mainConf.pxInt(375)
@@ -71,12 +71,14 @@ class GuiProjectSettings(PagedDialog):
self.tabImport = GuiProjectEditStatus(self.theParent, self.theProject, False)
self.tabReplace = GuiProjectEditReplace(self.theParent, self.theProject)
self.addTab(self.tabMain, "Settings")
self.addTab(self.tabStatus, "Status")
self.addTab(self.tabImport, "Importance")
self.addTab(self.tabReplace, "Auto-Replace")
self.addTab(self.tabMain, self.tr("Settings"))
self.addTab(self.tabStatus, self.tr("Status"))
self.addTab(self.tabImport, self.tr("Importance"))
self.addTab(self.tabReplace, self.tr("Auto-Replace"))
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.buttonBox.button(QDialogButtonBox.Ok).setText(self.tr("Ok"))
self.buttonBox.button(QDialogButtonBox.Cancel).setText(self.tr("Cancel"))
self.buttonBox.accepted.connect(self._doSave)
self.buttonBox.rejected.connect(self._doClose)
self.addControls(self.buttonBox)
@@ -166,7 +168,7 @@ class GuiProjectEditMain(QWidget):
self.mainForm.setHelpTextStyle(self.theParent.theTheme.helpText)
self.setLayout(self.mainForm)
self.mainForm.addGroupLabel("Project Settings")
self.mainForm.addGroupLabel(self.tr("Project Settings"))
xW = self.mainConf.pxInt(250)
xH = self.mainConf.pxInt(100)
@@ -176,9 +178,9 @@ class GuiProjectEditMain(QWidget):
self.editName.setFixedWidth(xW)
self.editName.setText(self.theProject.projName)
self.mainForm.addRow(
"Working title",
self.tr("Working title"),
self.editName,
"Should be set only once."
self.tr("Should be set only once.")
)
self.editTitle = QLineEdit()
@@ -186,9 +188,9 @@ class GuiProjectEditMain(QWidget):
self.editTitle.setFixedWidth(xW)
self.editTitle.setText(self.theProject.bookTitle)
self.mainForm.addRow(
"Novel title",
self.tr("Novel title"),
self.editTitle,
"Change whenever you want!"
self.tr("Change whenever you want!")
)
self.editAuthors = QPlainTextEdit()
@@ -199,22 +201,22 @@ class GuiProjectEditMain(QWidget):
self.editAuthors.setFixedHeight(xH)
self.editAuthors.setFixedWidth(xW)
self.mainForm.addRow(
"Author(s)",
self.tr("Author(s)"),
self.editAuthors,
"One name per line."
self.tr("One name per line.")
)
self.spellLang = QComboBox(self)
theDict = self.theParent.docEditor.theDict
self.spellLang.addItem("Default", "None")
self.spellLang.addItem(self.tr("Default"), "None")
if theDict is not None:
for spTag, spName in theDict.listDictionaries():
self.spellLang.addItem(spName, spTag)
self.mainForm.addRow(
"Spell check language",
self.tr("Spell check language"),
self.spellLang,
"Overrides main preferences."
self.tr("Overrides main preferences.")
)
spellIdx = 0
@@ -226,9 +228,9 @@ class GuiProjectEditMain(QWidget):
self.doBackup = QSwitch(self)
self.doBackup.setChecked(not self.theProject.doBackup)
self.mainForm.addRow(
"No backup on close",
self.tr("No backup on close"),
self.doBackup,
"Overrides main preferences."
self.tr("Overrides main preferences.")
)
return
@@ -271,12 +273,12 @@ class GuiProjectEditStatus(QWidget):
self.editName = QLineEdit()
self.editName.setMaxLength(40)
self.editName.setEnabled(False)
self.newButton = QPushButton("New")
self.delButton = QPushButton("Delete")
self.saveButton = QPushButton("Save")
self.newButton = QPushButton(self.tr("New"))
self.delButton = QPushButton(self.tr("Delete"))
self.saveButton = QPushButton(self.tr("Save"))
self.colPixmap = QPixmap(self.iPx, self.iPx)
self.colPixmap.fill(QColor(120, 120, 120))
self.colButton = QPushButton(QIcon(self.colPixmap), "Colour")
self.colButton = QPushButton(QIcon(self.colPixmap), self.tr("Colour"))
self.colButton.setIconSize(self.colPixmap.rect().size())
self.newButton.clicked.connect(self._newItem)
@@ -287,7 +289,7 @@ class GuiProjectEditStatus(QWidget):
self.mainForm.addWidget(self.newButton)
self.mainForm.addWidget(self.delButton)
self.mainForm.addStretch(1)
self.mainForm.addWidget(QLabel("<b>Name</b>"))
self.mainForm.addWidget(QLabel("<b>%s</b>" % self.tr("Name")))
self.mainForm.addWidget(self.editName)
self.mainForm.addWidget(self.colButton)
self.mainForm.addStretch(1)
@@ -297,9 +299,9 @@ class GuiProjectEditStatus(QWidget):
self.mainBox.addLayout(self.mainForm)
if isStatus:
self.outerBox.addWidget(QLabel("<b>Novel File Status Levels</b>"))
self.outerBox.addWidget(QLabel("<b>%s</b>" % self.tr("Novel File Status Levels")))
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.setLayout(self.outerBox)
@@ -325,7 +327,8 @@ class GuiProjectEditStatus(QWidget):
"""
if self.selColour is not None:
newCol = QColorDialog.getColor(
self.selColour, self, "Select Colour", QColorDialog.DontUseNativeDialog
self.selColour, self, self.tr("Select Colour"),
QColorDialog.DontUseNativeDialog
)
if newCol.isValid():
self.selColour = newCol
@@ -338,7 +341,7 @@ class GuiProjectEditStatus(QWidget):
def _newItem(self):
"""Create a new status item.
"""
newItem = self._addItem("New Item", (0, 0, 0), None, 0)
newItem = self._addItem(self.tr("New Item"), (0, 0, 0), None, 0)
newItem.setBackground(QBrush(QColor(0, 255, 0, 80)))
self.colChanged = True
return
@@ -355,7 +358,7 @@ class GuiProjectEditStatus(QWidget):
self.colChanged = True
else:
self.theParent.makeAlert(
"Cannot delete status item that is in use.", nwAlert.ERROR
self.tr("Cannot delete status item that is in use."), nwAlert.ERROR
)
return
@@ -372,7 +375,9 @@ class GuiProjectEditStatus(QWidget):
self.selColour.blue(),
self.colData[selIdx][4]
)
selItem.setText("%s [%d]" % (self.colData[selIdx][0], self.colCounts[selIdx]))
selItem.setText(self.tr("{0} [{1}]").format(
self.colData[selIdx][0], self.colCounts[selIdx])
)
selItem.setIcon(self.colButton.icon())
self.editName.setEnabled(False)
self.colChanged = True
@@ -384,7 +389,7 @@ class GuiProjectEditStatus(QWidget):
newIcon = QPixmap(self.iPx, self.iPx)
newIcon.fill(QColor(*iCol))
newItem = QListWidgetItem()
newItem.setText("%s [%d]" % (iName, nUse))
newItem.setText(self.tr("{0} [{1}]").format(iName, nUse))
newItem.setIcon(QIcon(newIcon))
newItem.setData(Qt.UserRole, len(self.colData))
self.listBox.addItem(newItem)
@@ -450,7 +455,10 @@ class GuiProjectEditReplace(QWidget):
self.optState.getInt("GuiProjectSettings", "replaceColW", 100)
)
self.listBox = QTreeWidget()
self.listBox.setHeaderLabels(["Keyword", "Replace With"])
self.listBox.setHeaderLabels([
self.tr("Keyword"),
self.tr("Replace With"),
])
self.listBox.itemSelectionChanged.connect(self._selectedItem)
self.listBox.setColumnWidth(0, wCol0)
self.listBox.setIndentation(0)
@@ -467,9 +475,9 @@ class GuiProjectEditReplace(QWidget):
self.saveButton = QPushButton(self.theTheme.getIcon("done"), "")
self.addButton = QPushButton(self.theTheme.getIcon("add"), "")
self.delButton = QPushButton(self.theTheme.getIcon("remove"), "")
self.saveButton.setToolTip("Save entry")
self.addButton.setToolTip("Add new entry")
self.delButton.setToolTip("Delete selected entry")
self.saveButton.setToolTip(self.tr("Save entry"))
self.addButton.setToolTip(self.tr("Add new entry"))
self.delButton.setToolTip(self.tr("Delete selected entry"))
self.editKey.setEnabled(False)
self.editKey.setMaxLength(40)
@@ -486,7 +494,9 @@ class GuiProjectEditReplace(QWidget):
self.bottomBox.addWidget(self.addButton)
self.bottomBox.addWidget(self.delButton)
self.outerBox.addWidget(QLabel("<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.addLayout(self.bottomBox)
self.setLayout(self.outerBox)
+48 -37
View File
@@ -38,7 +38,8 @@ from PyQt5.QtWidgets import (
from nw.core import NWDoc
from nw.constants import (
nwLabels, nwItemType, nwItemClass, nwItemLayout, nwAlert, nwConst, nwLists
trConst, nwLabels, nwItemType, nwItemClass, nwItemLayout, nwAlert,
nwConst, nwLists
)
logger = logging.getLogger(__name__)
@@ -85,14 +86,19 @@ class GuiProjectTree(QTreeWidget):
self.setExpandsOnDoubleClick(True)
self.setIndentation(iPx)
self.setColumnCount(4)
self.setHeaderLabels(["Label", "Words", "Inc", "Flags"])
self.setHeaderLabels([
self.tr("Label"),
self.tr("Words"),
self.tr("Inc"),
self.tr("Flags")
])
treeHeadItem = self.headerItem()
treeHeadItem.setTextAlignment(self.C_COUNT, Qt.AlignRight)
treeHeadItem.setToolTip(self.C_NAME, "Item label")
treeHeadItem.setToolTip(self.C_COUNT, "Word count")
treeHeadItem.setToolTip(self.C_EXPORT, "Include in build")
treeHeadItem.setToolTip(self.C_FLAGS, "Status, class, and layout flags")
treeHeadItem.setToolTip(self.C_NAME, self.tr("Item label"))
treeHeadItem.setToolTip(self.C_COUNT, self.tr("Word count"))
treeHeadItem.setToolTip(self.C_EXPORT, self.tr("Include in build"))
treeHeadItem.setToolTip(self.C_FLAGS, self.tr("Status, class, and layout flags"))
# Let the last column stretch, and set the minimum size to the
# size of the icon as the default Qt font metrics approach fails
@@ -193,12 +199,12 @@ class GuiProjectTree(QTreeWidget):
if itemClass is None:
if itemType == nwItemType.FILE:
self.makeAlert(
"Please select a valid location in the tree to add the document.",
self.tr("Please select a valid location in the tree to add the document."),
nwAlert.ERROR
)
else:
self.makeAlert(
"Please select a valid location in the tree to add the folder.",
self.tr("Please select a valid location in the tree to add the folder."),
nwAlert.ERROR
)
return False
@@ -209,7 +215,7 @@ class GuiProjectTree(QTreeWidget):
)
if itemType == nwItemType.ROOT:
tHandle = self.theProject.newRoot(nwLabels.CLASS_NAME[itemClass], itemClass)
tHandle = self.theProject.newRoot(trConst(nwLabels.CLASS_NAME[itemClass]), itemClass)
if tHandle is None:
logger.error("No root item added")
return False
@@ -223,7 +229,7 @@ class GuiProjectTree(QTreeWidget):
# If still nothing, give up
if pHandle is None:
self.makeAlert(
"Did not find anywhere to add the file or folder!", nwAlert.ERROR
self.tr("Did not find anywhere to add the file or folder!"), nwAlert.ERROR
)
return False
@@ -237,14 +243,14 @@ class GuiProjectTree(QTreeWidget):
# If we again have no home, give up
if pHandle is None:
self.makeAlert(
"Did not find anywhere to add the file or folder!", nwAlert.ERROR
self.tr("Did not find anywhere to add the file or folder!"), nwAlert.ERROR
)
return False
if self.theProject.projTree.isTrashRoot(pHandle):
self.makeAlert(
"Cannot add new files or folders to the %s folder." % (
nwLabels.CLASS_NAME[nwItemClass.TRASH]
self.tr("Cannot add new files or folders to the {0} folder.").format(
trConst(nwLabels.CLASS_NAME[nwItemClass.TRASH])
), nwAlert.ERROR
)
return False
@@ -253,18 +259,18 @@ class GuiProjectTree(QTreeWidget):
# If we're still here, add the file or folder
if itemType == nwItemType.FILE:
tHandle = self.theProject.newFile("New File", itemClass, pHandle)
tHandle = self.theProject.newFile(self.tr("New File"), itemClass, pHandle)
elif itemType == nwItemType.FOLDER:
if len(parTree) >= nwConst.MAX_DEPTH - 1:
# Folders cannot be deeper than MAX_DEPTH - 1, leaving room
# for one more level of files.
self.makeAlert((
"Cannot add new folder to this item. "
"Maximum folder depth has been reached."
self.tr("Cannot add new folder to this item."),
self.tr("Maximum folder depth has been reached.")
), nwAlert.ERROR)
return False
tHandle = self.theProject.newFolder("New Folder", itemClass, pHandle)
tHandle = self.theProject.newFolder(self.tr("New Folder"), itemClass, pHandle)
else:
logger.error("Failed to add new item")
@@ -428,7 +434,7 @@ class GuiProjectTree(QTreeWidget):
logger.debug("Emptying Trash folder")
if trashHandle is None:
self.makeAlert(
"There is currently no Trash folder in this project.", nwAlert.INFO
self.tr("There is currently no Trash folder in this project."), nwAlert.INFO
)
return False
@@ -438,11 +444,12 @@ class GuiProjectTree(QTreeWidget):
nTrash = len(theTrash)
if nTrash == 0:
self.makeAlert("The Trash folder is already empty.", nwAlert.INFO)
self.makeAlert(self.tr("The Trash folder is already empty."), nwAlert.INFO)
return False
msgYes = self.askQuestion(
"Empty Trash", "Permanently delete %d file(s) from Trash?" % nTrash
self.tr("Empty Trash"),
self.tr("Permanently delete {0} file(s) from Trash?").format(nTrash)
)
if not msgYes:
return False
@@ -500,7 +507,8 @@ class GuiProjectTree(QTreeWidget):
doPermanent = False
if not alreadyAsked:
msgYes = self.askQuestion(
"Delete File", "Permanently delete file '%s'?" % nwItemS.itemName
self.tr("Delete File"),
self.tr("Permanently delete file '{0}'?").format(nwItemS.itemName)
)
if msgYes:
doPermanent = True
@@ -529,7 +537,8 @@ class GuiProjectTree(QTreeWidget):
doTrash = False
if askForTrash:
msgYes = self.askQuestion(
"Delete File", "Move file '%s' to Trash?" % nwItemS.itemName
self.tr("Delete File"),
self.tr("Move file '{0}' to Trash?").format(nwItemS.itemName),
)
if msgYes:
doTrash = True
@@ -563,7 +572,7 @@ class GuiProjectTree(QTreeWidget):
self._deleteTreeItem(tHandle)
self._setTreeChanged(True)
else:
self.makeAlert((
self.makeAlert(self.tr(
"Cannot delete folder. It is not empty. "
"Recursive deletion is not supported. "
"Please delete the content first."
@@ -579,7 +588,7 @@ class GuiProjectTree(QTreeWidget):
self.theParent.mainMenu.setAvailableRoot()
self._setTreeChanged(True)
else:
self.makeAlert((
self.makeAlert(self.tr(
"Cannot delete root folder. It is not empty. "
"Recursive deletion is not supported. "
"Please delete the content first."
@@ -835,7 +844,7 @@ class GuiProjectTree(QTreeWidget):
snItem = self.theProject.projTree[sHandle]
dnItem = self.theProject.projTree[dHandle]
if dnItem is None:
self.makeAlert("The item cannot be moved to that location.", nwAlert.ERROR)
self.makeAlert(self.tr("The item cannot be moved to that location."), nwAlert.ERROR)
return
pItem = sItem.parent()
@@ -866,7 +875,7 @@ class GuiProjectTree(QTreeWidget):
else:
theEvent.ignore()
logger.debug("Drag'n'drop of item %s not accepted" % sHandle)
self.makeAlert("The item cannot be moved to that location.", nwAlert.ERROR)
self.makeAlert(self.tr("The item cannot be moved to that location."), nwAlert.ERROR)
return
@@ -964,7 +973,9 @@ class GuiProjectTree(QTreeWidget):
self.addTopLevelItem(newItem)
else:
self.makeAlert(
"There is nowhere to add item with name '%s'" % nwItem.itemName, nwAlert.ERROR
self.tr(
"There is nowhere to add item with name '{0}'"
).format(nwItem.itemName), nwAlert.ERROR
)
del self._treeMap[tHandle]
return None
@@ -1083,43 +1094,43 @@ class GuiProjectTreeMenu(QMenu):
self.theTree = theTree
self.theItem = None
self.editItem = QAction("Edit Project Item", self)
self.editItem = QAction(self.tr("Edit Project Item"), self)
self.editItem.triggered.connect(self._doEditItem)
self.addAction(self.editItem)
self.openItem = QAction("Open Document", self)
self.openItem = QAction(self.tr("Open Document"), self)
self.openItem.triggered.connect(self._doOpenItem)
self.addAction(self.openItem)
self.viewItem = QAction("View Document", self)
self.viewItem = QAction(self.tr("View Document"), self)
self.viewItem.triggered.connect(self._doViewItem)
self.addAction(self.viewItem)
self.toggleExp = QAction("Toggle Included Flag", self)
self.toggleExp = QAction(self.tr("Toggle Included Flag"), self)
self.toggleExp.triggered.connect(self._doToggleExported)
self.addAction(self.toggleExp)
self.newFile = QAction("New File", self)
self.newFile = QAction(self.tr("New File"), self)
self.newFile.triggered.connect(self._doMakeFile)
self.addAction(self.newFile)
self.newFolder = QAction("New Folder", self)
self.newFolder = QAction(self.tr("New Folder"), self)
self.newFolder.triggered.connect(self._doMakeFolder)
self.addAction(self.newFolder)
self.deleteItem = QAction("Delete Item", self)
self.deleteItem = QAction(self.tr("Delete Item"), self)
self.deleteItem.triggered.connect(self._doDeleteItem)
self.addAction(self.deleteItem)
self.emptyTrash = QAction("Empty Trash", self)
self.emptyTrash = QAction(self.tr("Empty Trash"), self)
self.emptyTrash.triggered.connect(self._doEmptyTrash)
self.addAction(self.emptyTrash)
self.moveUp = QAction("Move Item Up", self)
self.moveUp = QAction(self.tr("Move Item Up"), self)
self.moveUp.triggered.connect(self._doMoveUp)
self.addAction(self.moveUp)
self.moveDown = QAction("Move Item Down", self)
self.moveDown = QAction(self.tr("Move Item Down"), self)
self.moveDown.triggered.connect(self._doMoveDown)
self.addAction(self.moveDown)
+60 -44
View File
@@ -36,7 +36,7 @@ from PyQt5.QtWidgets import (
)
from nw.common import makeFileNameSafe
from nw.constants import nwLabels, nwItemClass
from nw.constants import trConst, nwLabels, nwItemClass
from nw.gui.custom import QSwitch
logger = logging.getLogger(__name__)
@@ -94,16 +94,18 @@ class ProjWizardIntroPage(QWizardPage):
self.theWizard = theWizard
self.theTheme = theWizard.theTheme
self.setTitle("Create New Project")
self.theText = QLabel(
self.setTitle(self.tr("Create New Project"))
self.theText = QLabel(self.tr(
"Provide at least a working title. The working title should not "
"be change beyond this point as it is used by the application for "
"generating file names for for instance backups. The other fields "
"are optional and can be changed at any time in Project Settings."
)
))
self.theText.setWordWrap(True)
self.imgCredit = QLabel("Side image by Peter Mitterhofer, CC BY-SA 4.0")
self.imgCredit = QLabel(self.tr("Side image by {0}, {1}").format(
"Peter Mitterhofer", "CC BY-SA 4.0"
))
lblFont = self.imgCredit.font()
lblFont.setPointSizeF(0.6*self.theTheme.fontPointSize)
self.imgCredit.setFont(lblFont)
@@ -117,22 +119,22 @@ class ProjWizardIntroPage(QWizardPage):
self.projName = QLineEdit()
self.projName.setMaxLength(200)
self.projName.setFixedWidth(xW)
self.projName.setPlaceholderText("Required")
self.projName.setPlaceholderText(self.tr("Required"))
self.projTitle = QLineEdit()
self.projTitle.setMaxLength(200)
self.projTitle.setFixedWidth(xW)
self.projTitle.setPlaceholderText("Optional")
self.projTitle.setPlaceholderText(self.tr("Optional"))
self.projAuthors = QPlainTextEdit()
self.projAuthors.setFixedHeight(xH)
self.projAuthors.setFixedWidth(xW)
self.projAuthors.setPlaceholderText("Optional. One name per line.")
self.projAuthors.setPlaceholderText(self.tr("Optional. One name per line."))
self.mainForm = QFormLayout()
self.mainForm.addRow("Working Title", self.projName)
self.mainForm.addRow("Novel Title", self.projTitle)
self.mainForm.addRow("Author(s)", self.projAuthors)
self.mainForm.addRow(self.tr("Working Title"), self.projName)
self.mainForm.addRow(self.tr("Novel Title"), self.projTitle)
self.mainForm.addRow(self.tr("Author(s)"), self.projAuthors)
self.mainForm.setVerticalSpacing(fS)
self.registerField("projName*", self.projName)
@@ -161,11 +163,11 @@ class ProjWizardFolderPage(QWizardPage):
self.theWizard = theWizard
self.theTheme = theWizard.theTheme
self.setTitle("Select Project Folder")
self.theText = QLabel(
self.setTitle(self.tr("Select Project Folder"))
self.theText = QLabel(self.tr(
"Select a location to store the project. A new project folder "
"will be created in the selected location."
)
))
self.theText.setWordWrap(True)
xW = self.mainConf.pxInt(300)
@@ -174,14 +176,14 @@ class ProjWizardFolderPage(QWizardPage):
self.projPath = QLineEdit("")
self.projPath.setFixedWidth(xW)
self.projPath.setPlaceholderText("Required")
self.projPath.setPlaceholderText(self.tr("Required"))
self.browseButton = QPushButton("...")
self.browseButton.setMaximumWidth(int(2.5*self.theTheme.getTextWidth("...")))
self.browseButton.clicked.connect(self._doBrowse)
self.mainForm = QHBoxLayout()
self.mainForm.addWidget(QLabel("Project Path"), 0)
self.mainForm.addWidget(QLabel(self.tr("Project Path")), 0)
self.mainForm.addWidget(self.projPath, 1)
self.mainForm.addWidget(self.browseButton, 0)
self.mainForm.setSpacing(fS)
@@ -213,7 +215,7 @@ class ProjWizardFolderPage(QWizardPage):
dlgOpt |= QFileDialog.ShowDirsOnly
dlgOpt |= QFileDialog.DontUseNativeDialog
projDir = QFileDialog.getExistingDirectory(
self, "Select Project Folder", lastPath, options=dlgOpt
self, self.tr("Select Project Folder"), lastPath, options=dlgOpt
)
if projDir:
projName = self.field("projName")
@@ -235,20 +237,20 @@ class ProjWizardPopulatePage(QWizardPage):
self.mainConf = nw.CONFIG
self.theWizard = theWizard
self.setTitle("Populate Project")
self.theText = QLabel(
self.setTitle(self.tr("Populate Project"))
self.theText = QLabel(self.tr(
"Choose how to pre-fill the project. Either with a minimal set of "
"starter items, an example project explaining and showing many of "
"the features, or show further custom options on the next page."
)
))
self.theText.setWordWrap(True)
vS = self.mainConf.pxInt(12)
fS = self.mainConf.pxInt(4)
self.popMinimal = QRadioButton("Fill the project with a minimal set of items")
self.popSample = QRadioButton("Fill the project with example files")
self.popCustom = QRadioButton("Show detailed options for filling the project")
self.popMinimal = QRadioButton(self.tr("Fill the project with a minimal set of items"))
self.popSample = QRadioButton(self.tr("Fill the project with example files"))
self.popCustom = QRadioButton(self.tr("Show detailed options for filling the project"))
self.popMinimal.setChecked(True)
self.popBox = QVBoxLayout()
@@ -290,27 +292,39 @@ class ProjWizardCustomPage(QWizardPage):
self.mainConf = nw.CONFIG
self.theWizard = theWizard
self.setTitle("Custom Project Options")
self.theText = QLabel(
self.setTitle(self.tr("Custom Project Options"))
self.theText = QLabel(self.tr(
"Select which additional root folders to make, and how to populate "
"the Novel folder. If you don't want to add chapters or scenes, set "
"the values to 0. You can add scenes without chapters."
)
))
self.theText.setWordWrap(True)
vS = self.mainConf.pxInt(12)
# Root Folders
self.rootGroup = QGroupBox("Additional Root Folders")
self.rootGroup = QGroupBox(self.tr("Additional Root Folders"))
self.rootForm = QGridLayout()
self.rootGroup.setLayout(self.rootForm)
self.lblPlot = QLabel("%s folder" % nwLabels.CLASS_NAME[nwItemClass.PLOT])
self.lblChar = QLabel("%s folder" % nwLabels.CLASS_NAME[nwItemClass.CHARACTER])
self.lblWorld = QLabel("%s folder" % nwLabels.CLASS_NAME[nwItemClass.WORLD])
self.lblTime = QLabel("%s folder" % nwLabels.CLASS_NAME[nwItemClass.TIMELINE])
self.lblObject = QLabel("%s folder" % nwLabels.CLASS_NAME[nwItemClass.OBJECT])
self.lblEntity = QLabel("%s folder" % nwLabels.CLASS_NAME[nwItemClass.ENTITY])
self.lblPlot = QLabel(self.tr("{0} folder").format(
trConst(nwLabels.CLASS_NAME[nwItemClass.PLOT]))
)
self.lblChar = QLabel(self.tr("{0} folder").format(
trConst(nwLabels.CLASS_NAME[nwItemClass.CHARACTER]))
)
self.lblWorld = QLabel(self.tr("{0} folder").format(
trConst(nwLabels.CLASS_NAME[nwItemClass.WORLD]))
)
self.lblTime = QLabel(self.tr("{0} folder").format(
trConst(nwLabels.CLASS_NAME[nwItemClass.TIMELINE]))
)
self.lblObject = QLabel(self.tr("{0} folder").format(
trConst(nwLabels.CLASS_NAME[nwItemClass.OBJECT]))
)
self.lblEntity = QLabel(self.tr("{0} folder").format(
trConst(nwLabels.CLASS_NAME[nwItemClass.ENTITY]))
)
self.addPlot = QSwitch()
self.addChar = QSwitch()
@@ -338,7 +352,7 @@ class ProjWizardCustomPage(QWizardPage):
self.rootForm.setRowStretch(6, 1)
# Novel Options
self.novelGroup = QGroupBox("Populate Novel Folder")
self.novelGroup = QGroupBox(self.tr("Populate Novel Folder"))
self.novelForm = QGridLayout()
self.novelGroup.setLayout(self.novelForm)
@@ -353,9 +367,9 @@ class ProjWizardCustomPage(QWizardPage):
self.chFolders = QSwitch()
self.chFolders.setChecked(True)
self.novelForm.addWidget(QLabel("Add chapters"), 0, 0)
self.novelForm.addWidget(QLabel("Scenes (per chapter)"), 1, 0)
self.novelForm.addWidget(QLabel("Add chapter folders"), 2, 0)
self.novelForm.addWidget(QLabel(self.tr("Add chapters")), 0, 0)
self.novelForm.addWidget(QLabel(self.tr("Scenes (per chapter)")), 1, 0)
self.novelForm.addWidget(QLabel(self.tr("Add chapter folders")), 2, 0)
self.novelForm.addWidget(self.numChapters, 0, 1, 1, 1, Qt.AlignRight)
self.novelForm.addWidget(self.numScenes, 1, 1, 1, 1, Qt.AlignRight)
self.novelForm.addWidget(self.chFolders, 2, 1, 1, 1, Qt.AlignRight)
@@ -396,13 +410,15 @@ class ProjWizardFinalPage(QWizardPage):
self.mainConf = nw.CONFIG
self.theWizard = theWizard
self.setTitle("Finished")
self.theText = QLabel((
"<p>All done.</p>"
"<p>Press '{finish}' to create the new project.</p>"
).format(
finish = "Done" if self.mainConf.osDarwin else "Finish"
))
self.setTitle(self.tr("Finished"))
self.theText = QLabel(
"<p>{done}</p><p>{help}</p>".format(
done = self.tr("All done."),
help = self.tr("Press '{0}' to create the new project.").format(
self.tr("Done") if self.mainConf.osDarwin else self.tr("Finish")
)
)
)
self.theText.setWordWrap(True)
# Assemble
+16 -12
View File
@@ -29,10 +29,10 @@ import logging
from time import time
from PyQt5.QtCore import QLocale
from PyQt5.QtGui import QColor, QPainter
from PyQt5.QtWidgets import qApp, QStatusBar, QLabel, QAbstractButton
from nw.core import NWSpellCheck
from nw.common import formatTime
logger = logging.getLogger(__name__)
@@ -63,7 +63,7 @@ class GuiMainStatus(QStatusBar):
## The Spell Checker Language
self.langIcon = QLabel("")
self.langText = QLabel("None")
self.langText = QLabel(self.tr("None"))
self.langIcon.setPixmap(self.theTheme.getPixmap("status_lang", (iPx, iPx)))
self.langIcon.setContentsMargins(0, 0, 0, 0)
self.langText.setContentsMargins(0, 0, xM, 0)
@@ -72,7 +72,7 @@ class GuiMainStatus(QStatusBar):
## The Editor Status
self.docIcon = StatusLED(colNone, colTrue, colFalse, iPx, iPx, self)
self.docText = QLabel("Editor")
self.docText = QLabel(self.tr("Editor"))
self.docIcon.setContentsMargins(0, 0, 0, 0)
self.docText.setContentsMargins(0, 0, xM, 0)
self.addPermanentWidget(self.docIcon)
@@ -80,7 +80,7 @@ class GuiMainStatus(QStatusBar):
## The Project Status
self.projIcon = StatusLED(colNone, colTrue, colFalse, iPx, iPx, self)
self.projText = QLabel("Project")
self.projText = QLabel(self.tr("Project"))
self.projIcon.setContentsMargins(0, 0, 0, 0)
self.projText.setContentsMargins(0, 0, xM, 0)
self.addPermanentWidget(self.projIcon)
@@ -103,7 +103,7 @@ class GuiMainStatus(QStatusBar):
self.timeIcon = QLabel()
self.timeText = QLabel("")
self.timeIcon.setPixmap(self.timePixmap)
self.timeText.setToolTip("Session Time")
self.timeText.setToolTip(self.tr("Session Time"))
self.timeText.setMinimumWidth(self.theTheme.getTextWidth("00:00:00:"))
self.timeIcon.setContentsMargins(0, 0, 0, 0)
self.timeText.setContentsMargins(0, 0, 0, 0)
@@ -151,13 +151,17 @@ class GuiMainStatus(QStatusBar):
"""Set the language code for the spell checker.
"""
if theLanguage is None:
self.langText.setText("None")
self.langText.setText(self.tr("None"))
self.langText.setToolTip("")
else:
self.langText.setText(NWSpellCheck.expandLanguage(theLanguage))
self.langText.setToolTip(
"Provider: %s" % (theProvider if theProvider else "unknown")
)
qLocal = QLocale(theLanguage)
spLang = qLocal.nativeLanguageName().title()
self.langText.setText(spLang)
if theProvider:
self.langText.setToolTip("%s (%s)" % (theLanguage, theProvider))
else:
self.langText.setToolTip(theLanguage)
return
def setProjectStatus(self, isChanged):
@@ -175,8 +179,8 @@ class GuiMainStatus(QStatusBar):
def setStats(self, pWC, sWC):
"""Set the current project statistics.
"""
self.statsText.setText(f"Words: {pWC:n} ({sWC:+n})")
self.statsText.setToolTip("Project word count (session change)")
self.statsText.setText(self.tr("Words: {0} ({1})").format(f"{pWC:n}", f"{sWC:+n}"))
self.statsText.setToolTip(self.tr("Project word count (session change)"))
return
def setUserIdle(self, userIdle):
+12 -7
View File
@@ -31,8 +31,9 @@ import configparser
import os
from math import ceil
from functools import partial
from PyQt5.QtCore import Qt
from PyQt5.QtCore import QCoreApplication, Qt
from PyQt5.QtWidgets import QStyle, qApp
from PyQt5.QtGui import (
QPalette, QColor, QIcon, QFont, QFontMetrics, QFontDatabase, QPixmap
@@ -157,6 +158,10 @@ class GuiTheme:
logger.verbose("Text 'N' Height: %d" % self.textNHeight)
logger.verbose("Text 'N' Width: %d" % self.textNWidth)
# Internal Mapping
self.makeAlert = self.theParent.makeAlert
self.tr = partial(QCoreApplication.translate, "GuiTheme")
return
##
@@ -392,8 +397,8 @@ class GuiTheme:
with open(themeConf, mode="r", encoding="utf8") as inFile:
confParser.read_file(inFile)
except Exception as e:
self.theParent.makeAlert(
["Could not load theme config file.", str(e)], nwAlert.ERROR
self.makeAlert(
[self.tr("Could not load theme config file."), str(e)], nwAlert.ERROR
)
continue
themeName = ""
@@ -425,8 +430,8 @@ class GuiTheme:
with open(syntaxPath, mode="r", encoding="utf8") as inFile:
confParser.read_file(inFile)
except Exception as e:
self.theParent.makeAlert(
["Could not load syntax file.", str(e)], nwAlert.ERROR
self.makeAlert(
[self.tr("Could not load syntax file."), str(e)], nwAlert.ERROR
)
return []
syntaxName = ""
@@ -740,8 +745,8 @@ class GuiIcons:
with open(themeConf, mode="r", encoding="utf8") as inFile:
confParser.read_file(inFile)
except Exception as e:
self.theParent.makeAlert(
["Could not load theme config file.", str(e)], nwAlert.ERROR
self.makeAlert(
[self.tr("Could not load theme config file."), str(e)], nwAlert.ERROR
)
continue
themeName = ""
+9 -6
View File
@@ -52,7 +52,7 @@ class GuiWordList(QDialog):
self.theProject = theProject
self.optState = theProject.optState
self.setWindowTitle("Project Word List")
self.setWindowTitle(self.tr("Project Word List"))
mS = self.mainConf.pxInt(250)
wW = self.mainConf.pxInt(320)
@@ -68,7 +68,7 @@ class GuiWordList(QDialog):
# Main Widgets
# ============
self.headLabel = QLabel("<b>Project Word List</b>")
self.headLabel = QLabel("<b>%s</b>" % self.tr("Project Word List"))
self.listBox = QListWidget()
self.listBox.setDragDropMode(QAbstractItemView.NoDragDrop)
@@ -77,11 +77,11 @@ class GuiWordList(QDialog):
self.newEntry = QLineEdit()
self.addButton = QPushButton(self.theTheme.getIcon("add"), "")
self.addButton.setToolTip("Add new entry")
self.addButton.setToolTip(self.tr("Add new entry"))
self.addButton.clicked.connect(self._doAdd)
self.delButton = QPushButton(self.theTheme.getIcon("remove"), "")
self.delButton.setToolTip("Delete selected entry")
self.delButton.setToolTip(self.tr("Delete selected entry"))
self.delButton.clicked.connect(self._doDelete)
self.editBox = QHBoxLayout()
@@ -90,6 +90,8 @@ class GuiWordList(QDialog):
self.editBox.addWidget(self.delButton, 0)
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Save | QDialogButtonBox.Close)
self.buttonBox.button(QDialogButtonBox.Save).setText(self.tr("Save"))
self.buttonBox.button(QDialogButtonBox.Close).setText(self.tr("Close"))
self.buttonBox.accepted.connect(self._doSave)
self.buttonBox.rejected.connect(self._doClose)
@@ -121,12 +123,13 @@ class GuiWordList(QDialog):
"""
newWord = self.newEntry.text().strip()
if newWord == "":
self.theParent.makeAlert("Cannot add a blank word.", nwAlert.ERROR)
self.theParent.makeAlert(self.tr("Cannot add a blank word."), nwAlert.ERROR)
return False
if self.listBox.findItems(newWord, Qt.MatchExactly):
self.theParent.makeAlert(
"The word '%s' is already in the word list." % newWord, nwAlert.ERROR
self.tr("The word '{0}' is already in the word list.").format(newWord),
nwAlert.ERROR
)
return False
+40 -24
View File
@@ -72,7 +72,7 @@ class GuiWritingStats(QDialog):
self.timeFilter = 0.0
self.wordOffset = 0
self.setWindowTitle("Writing Statistics")
self.setWindowTitle(self.tr("Writing Statistics"))
self.setMinimumWidth(self.mainConf.pxInt(420))
self.setMinimumHeight(self.mainConf.pxInt(400))
self.resize(
@@ -95,7 +95,13 @@ class GuiWritingStats(QDialog):
)
self.listBox = QTreeWidget()
self.listBox.setHeaderLabels(["Session Start", "Length", "Idle", "Words", "Histogram"])
self.listBox.setHeaderLabels([
self.tr("Session Start"),
self.tr("Length"),
self.tr("Idle"),
self.tr("Words"),
self.tr("Histogram"),
])
self.listBox.setIndentation(0)
self.listBox.setColumnWidth(self.C_TIME, wCol0)
self.listBox.setColumnWidth(self.C_LENGTH, wCol1)
@@ -125,7 +131,7 @@ class GuiWritingStats(QDialog):
self.barImage.fill(self.palette().highlight().color())
# Session Info
self.infoBox = QGroupBox("Sum Totals", self)
self.infoBox = QGroupBox(self.tr("Sum Totals"), self)
self.infoForm = QGridLayout(self)
self.infoBox.setLayout(self.infoForm)
@@ -153,24 +159,33 @@ class GuiWritingStats(QDialog):
self.totalWords.setFont(self.theTheme.guiFontFixed)
self.totalWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
self.infoForm.addWidget(QLabel("Total Time:"), 0, 0)
self.infoForm.addWidget(QLabel("Idle Time:"), 1, 0)
self.infoForm.addWidget(QLabel("Filtered Time:"), 2, 0)
self.infoForm.addWidget(QLabel("Novel Word Count:"), 3, 0)
self.infoForm.addWidget(QLabel("Notes Word Count:"), 4, 0)
self.infoForm.addWidget(QLabel("Total Word Count:"), 5, 0)
lblTTime = QLabel(self.tr("Total Time:"))
lblITime = QLabel(self.tr("Idle Time:"))
lblFTime = QLabel(self.tr("Filtered Time:"))
lblNvCount = QLabel(self.tr("Novel Word Count:"))
lblNtCount = QLabel(self.tr("Notes Word Count:"))
lblTtCount = QLabel(self.tr("Total Word Count:"))
self.infoForm.addWidget(lblTTime, 0, 0)
self.infoForm.addWidget(lblITime, 1, 0)
self.infoForm.addWidget(lblFTime, 2, 0)
self.infoForm.addWidget(lblNvCount, 3, 0)
self.infoForm.addWidget(lblNtCount, 4, 0)
self.infoForm.addWidget(lblTtCount, 5, 0)
self.infoForm.addWidget(self.labelTotal, 0, 1)
self.infoForm.addWidget(self.labelIdleT, 1, 1)
self.infoForm.addWidget(self.labelFilter, 2, 1)
self.infoForm.addWidget(self.novelWords, 3, 1)
self.infoForm.addWidget(self.notesWords, 4, 1)
self.infoForm.addWidget(self.totalWords, 5, 1)
self.infoForm.setRowStretch(6, 1)
# Filter Options
sPx = self.theTheme.baseIconSize
self.filterBox = QGroupBox("Filters", self)
self.filterBox = QGroupBox(self.tr("Filters"), self)
self.filterForm = QGridLayout(self)
self.filterBox.setLayout(self.filterForm)
@@ -210,12 +225,12 @@ class GuiWritingStats(QDialog):
)
self.showIdleTime.clicked.connect(self._updateListBox)
self.filterForm.addWidget(QLabel("Count novel files"), 0, 0)
self.filterForm.addWidget(QLabel("Count note files"), 1, 0)
self.filterForm.addWidget(QLabel("Hide zero word count"), 2, 0)
self.filterForm.addWidget(QLabel("Hide negative word count"), 3, 0)
self.filterForm.addWidget(QLabel("Group entries by day"), 4, 0)
self.filterForm.addWidget(QLabel("Show idle time"), 5, 0)
self.filterForm.addWidget(QLabel(self.tr("Count novel files")), 0, 0)
self.filterForm.addWidget(QLabel(self.tr("Count note files")), 1, 0)
self.filterForm.addWidget(QLabel(self.tr("Hide zero word count")), 2, 0)
self.filterForm.addWidget(QLabel(self.tr("Hide negative word count")), 3, 0)
self.filterForm.addWidget(QLabel(self.tr("Group entries by day")), 4, 0)
self.filterForm.addWidget(QLabel(self.tr("Show idle time")), 5, 0)
self.filterForm.addWidget(self.incNovel, 0, 1)
self.filterForm.addWidget(self.incNotes, 1, 1)
self.filterForm.addWidget(self.hideZeros, 2, 1)
@@ -236,7 +251,7 @@ class GuiWritingStats(QDialog):
self.optsBox = QHBoxLayout()
self.optsBox.addStretch(1)
self.optsBox.addWidget(QLabel("Word count cap for the histogram"), 0)
self.optsBox.addWidget(QLabel(self.tr("Word count cap for the histogram")), 0)
self.optsBox.addWidget(self.histMax, 0)
# Buttons
@@ -244,19 +259,20 @@ class GuiWritingStats(QDialog):
self.buttonBox.rejected.connect(self._doClose)
self.btnClose = self.buttonBox.addButton(QDialogButtonBox.Close)
self.buttonBox.button(QDialogButtonBox.Close).setText(self.tr("Close"))
self.btnClose.setAutoDefault(False)
self.btnSave = self.buttonBox.addButton("Save As", QDialogButtonBox.ActionRole)
self.btnSave = self.buttonBox.addButton(self.tr("Save As"), QDialogButtonBox.ActionRole)
self.btnSave.setAutoDefault(False)
self.saveMenu = QMenu(self)
self.btnSave.setMenu(self.saveMenu)
self.saveJSON = QAction("JSON Data File (.json)", self)
self.saveJSON = QAction(self.tr("JSON Data File (.json)"), self)
self.saveJSON.triggered.connect(lambda: self._saveData(self.FMT_JSON))
self.saveMenu.addAction(self.saveJSON)
self.saveCSV = QAction("CSV Data File (.csv)", self)
self.saveCSV = QAction(self.tr("CSV Data File (.csv)"), self)
self.saveCSV.triggered.connect(lambda: self._saveData(self.FMT_CSV))
self.saveMenu.addAction(self.saveCSV)
@@ -338,10 +354,10 @@ class GuiWritingStats(QDialog):
if dataFmt == self.FMT_JSON:
fileExt = "json"
textFmt = "JSON Data File"
textFmt = self.tr("JSON Data File")
elif dataFmt == self.FMT_CSV:
fileExt = "csv"
textFmt = "CSV Data File"
textFmt = self.tr("CSV Data File")
else:
return False
@@ -356,7 +372,7 @@ class GuiWritingStats(QDialog):
dlgOpt = QFileDialog.Options()
dlgOpt |= QFileDialog.DontUseNativeDialog
savePath, _ = QFileDialog.getSaveFileName(
self, "Save Document As", savePath, options=dlgOpt
self, self.tr("Save Document As"), savePath, options=dlgOpt
)
if not savePath:
return False
@@ -474,7 +490,7 @@ class GuiWritingStats(QDialog):
except Exception as e:
self.theParent.makeAlert(
["Failed to read session log file.", str(e)], nwAlert.ERROR
[self.tr("Failed to read session log file."), str(e)], nwAlert.ERROR
)
return False
+82 -55
View File
@@ -76,6 +76,7 @@ class GuiMain(QMainWindow):
logger.info("Python Version: %s (0x%x)" % (
self.mainConf.verPyString, self.mainConf.verPyHexVal)
)
logger.info("GUI Language: %s" % self.mainConf.guiLang)
# Core Classes
# ============
@@ -122,8 +123,8 @@ class GuiMain(QMainWindow):
self.projTabs = QTabWidget()
self.projTabs.setTabPosition(QTabWidget.South)
self.projTabs.setStyleSheet(r"QTabWidget::pane {border: 0;};")
self.projTabs.addTab(self.treeView, "Project")
self.projTabs.addTab(self.novelView, "Novel")
self.projTabs.addTab(self.treeView, self.tr("Project"))
self.projTabs.addTab(self.novelView, self.tr("Novel"))
self.projTabs.currentChanged.connect(self._projTabsChanged)
tabFont = self.projTabs.tabBar().font()
@@ -139,17 +140,17 @@ class GuiMain(QMainWindow):
self.treeButtons.setStyleSheet(r"QToolBar {padding: 0;}")
self.projTabs.setCornerWidget(self.treeButtons, Qt.BottomRightCorner)
self.projDetailsBtn = QAction("Project Details")
self.projDetailsBtn = QAction(self.tr("Project Details"))
self.projDetailsBtn.setIcon(self.theTheme.getIcon("status_lines"))
self.projDetailsBtn.triggered.connect(lambda: self.showProjectDetailsDialog())
self.treeButtons.addAction(self.projDetailsBtn)
self.projStatsBtn = QAction("Writing Statistics")
self.projStatsBtn = QAction(self.tr("Writing Statistics"))
self.projStatsBtn.setIcon(self.theTheme.getIcon("status_stats"))
self.projStatsBtn.triggered.connect(lambda: self.showWritingStatsDialog())
self.treeButtons.addAction(self.projStatsBtn)
self.projSettingsBtn = QAction("Project Settings")
self.projSettingsBtn = QAction(self.tr("Project Settings"))
self.projSettingsBtn.setIcon(self.theTheme.getIcon("settings"))
self.projSettingsBtn.triggered.connect(lambda: self.showProjectSettingsDialog())
self.treeButtons.addAction(self.projSettingsBtn)
@@ -184,8 +185,8 @@ class GuiMain(QMainWindow):
self.mainTabs = QTabWidget()
self.mainTabs.setTabPosition(QTabWidget.East)
self.mainTabs.setStyleSheet(r"QTabWidget::pane {border: 0;}")
self.mainTabs.addTab(self.splitDocs, "Editor")
self.mainTabs.addTab(self.splitOutline, "Outline")
self.mainTabs.addTab(self.splitDocs, self.tr("Editor"))
self.mainTabs.addTab(self.splitOutline, self.tr("Outline"))
self.mainTabs.currentChanged.connect(self._mainTabChanged)
# Splitter : Project Tree / Main Tabs
@@ -339,7 +340,7 @@ class GuiMain(QMainWindow):
if self.hasProject:
if not self.closeProject():
self.makeAlert(
"Cannot create new project when another project is open.",
self.tr("Cannot create new project when another project is open."),
nwAlert.ERROR
)
return False
@@ -357,8 +358,10 @@ class GuiMain(QMainWindow):
if os.path.isfile(os.path.join(projPath, self.theProject.projFile)):
self.makeAlert(
"A project already exists in that location. Please choose another folder.",
nwAlert.ERROR
self.tr(
"A project already exists in that location. "
"Please choose another folder."
), nwAlert.ERROR
)
return False
@@ -372,7 +375,7 @@ class GuiMain(QMainWindow):
self.statusBar.setRefTime(self.theProject.projOpened)
self.statusBar.setProjectStatus(True)
self.statusBar.setDocumentStatus(None)
self.statusBar.setStatus("New project created ...")
self.statusBar.setStatus(self.tr("New project created ..."))
self._updateWindowTitle(self.theProject.projName)
else:
self.theProject.clearProject()
@@ -391,8 +394,11 @@ class GuiMain(QMainWindow):
if not isYes:
msgYes = self.askQuestion(
"Close Project",
"Close the current project?<br>Changes are saved automatically."
self.tr("Close Project"),
"%s<br>%s" % (
self.tr("Close the current project?"),
self.tr("Changes are saved automatically.")
)
)
if not msgYes:
return False
@@ -407,7 +413,8 @@ class GuiMain(QMainWindow):
doBackup = True
if self.mainConf.askBeforeBackup:
msgYes = self.askQuestion(
"Backup Project", "Backup the current project?"
self.tr("Backup Project"),
self.tr("Backup the current project?")
)
if not msgYes:
doBackup = False
@@ -457,29 +464,35 @@ class GuiMain(QMainWindow):
try:
lockDetails = (
"<br><br>The project was locked by the computer "
"'%s' (%s %s), last active on %s"
) % (
"<br>%s" % self.tr(
"The project was locked by the computer "
"'{0}' ({1} {2}), last active on {3}."
)
).format(
self.theProject.lockedBy[0],
self.theProject.lockedBy[1],
self.theProject.lockedBy[2],
datetime.fromtimestamp(
int(self.theProject.lockedBy[3])
).strftime("%x %X")
datetime.fromtimestamp(int(self.theProject.lockedBy[3])).strftime("%x %X")
)
except Exception:
lockDetails = ""
msgBox = QMessageBox()
msgRes = msgBox.warning(
self, "Project Locked", (
"The project is already open by another instance of novelWriter, and "
"is therefore locked. Override lock and continue anyway?<br><br>"
"Note: If the program or the computer previously crashed, the lock "
"can safely be overridden. If, however, another instance of "
"novelWriter has the project open, overriding the lock may corrupt "
"the project, and is not recommended.%s"
) % lockDetails,
self, self.tr("Project Locked"),
"%s<br><br>%s<br>%s" % (
self.tr(
"The project is already open by another instance of novelWriter, and "
"is therefore locked. Override lock and continue anyway?"
),
self.tr(
"Note: If the program or the computer previously crashed, the lock "
"can safely be overridden. If, however, another instance of "
"novelWriter has the project open, overriding the lock may corrupt "
"the project, and is not recommended."
),
lockDetails
),
QMessageBox.Yes | QMessageBox.No, QMessageBox.No
)
if msgRes == QMessageBox.Yes:
@@ -514,6 +527,10 @@ class GuiMain(QMainWindow):
# Check if we need to rebuild the index
if self.theIndex.indexBroken:
self.makeAlert(
self.tr("The project index is outdated or broken. Rebuilding index."),
nwAlert.WARN
)
self.rebuildIndex()
# Make sure the changed status is set to false on all that was
@@ -685,15 +702,16 @@ class GuiMain(QMainWindow):
lastPath = self.mainConf.lastPath
extFilter = [
"Text files (*.txt)",
"Markdown files (*.md)",
"novelWriter files (*.nwd)",
"All files (*.*)",
self.tr("Text files ({0})").format("*.txt"),
self.tr("Markdown files ({0})").format("*.md"),
self.tr("novelWriter files ({0})").format("*.nwd"),
self.tr("All files ({0})").format("*.*"),
]
dlgOpt = QFileDialog.Options()
dlgOpt |= QFileDialog.DontUseNativeDialog
loadFile, _ = QFileDialog.getOpenFileName(
self, "Import File", lastPath, options=dlgOpt, filter=";;".join(extFilter)
self, self.tr("Import File"), lastPath,
options=dlgOpt, filter=";;".join(extFilter)
)
if not loadFile:
return False
@@ -707,24 +725,26 @@ class GuiMain(QMainWindow):
theText = inFile.read()
self.mainConf.setLastPath(loadFile)
except Exception as e:
self.makeAlert(
["Could not read file. The file must be an existing text file.", str(e)],
nwAlert.ERROR
)
self.makeAlert([
self.tr("Could not read file. The file must be an existing text file."), str(e)
], nwAlert.ERROR)
return False
if self.docEditor.theHandle is None:
self.makeAlert(
"Please open a document to import the text file into.",
self.tr("Please open a document to import the text file into."),
nwAlert.ERROR
)
return False
if not self.docEditor.isEmpty():
msgYes = self.askQuestion("Import Document", (
"Importing the file will overwrite the current content of the document. "
"Do you want to proceed?"
))
msgYes = self.askQuestion(
self.tr("Import Document"),
self.tr(
"Importing the file will overwrite the current content of the document. "
"Do you want to proceed?"
)
)
if not msgYes:
return False
@@ -861,9 +881,9 @@ class GuiMain(QMainWindow):
for nDone, tItem in enumerate(self.theProject.projTree):
if tItem is not None:
self.setStatus("Indexing: '%s'" % tItem.itemName)
self.setStatus(self.tr("Indexing: '{0}'").format(tItem.itemName))
else:
self.setStatus("Indexing: Unknown item")
self.setStatus(self.tr("Indexing: '{0}'").format(self.tr("Unknown item")))
if tItem is not None and tItem.itemType == nwItemType.FILE:
logger.verbose("Scanning: %s" % tItem.itemName)
@@ -881,12 +901,16 @@ class GuiMain(QMainWindow):
self.treeView.projectWordCount()
tEnd = time()
self.setStatus("Indexing completed in %.1f ms" % ((tEnd - tStart)*1000.0))
self.setStatus(
self.tr("Indexing completed in {0} ms").format(f"{(tEnd - tStart)*1000.0:.1f}")
)
self.docEditor.updateTagHighLighting()
qApp.restoreOverrideCursor()
if not beQuiet:
self.makeAlert("The project index has been successfully rebuilt.", nwAlert.INFO)
self.makeAlert(
self.tr("The project index has been successfully rebuilt."), nwAlert.INFO
)
return True
@@ -914,7 +938,7 @@ class GuiMain(QMainWindow):
dlgOpt |= QFileDialog.ShowDirsOnly
dlgOpt |= QFileDialog.DontUseNativeDialog
projPath = QFileDialog.getExistingDirectory(
self, "Save novelWriter Project", "", options=dlgOpt
self, self.tr("Save novelWriter Project"), "", options=dlgOpt
)
if projPath:
return projPath
@@ -1100,14 +1124,14 @@ class GuiMain(QMainWindow):
# Popup
msgBox = QMessageBox()
if theLevel == nwAlert.INFO:
msgBox.information(self, "Information", popMsg)
msgBox.information(self, self.tr("Information"), popMsg)
elif theLevel == nwAlert.WARN:
msgBox.warning(self, "Warning", popMsg)
msgBox.warning(self, self.tr("Warning"), popMsg)
elif theLevel == nwAlert.ERROR:
msgBox.critical(self, "Error", popMsg)
msgBox.critical(self, self.tr("Error"), popMsg)
elif theLevel == nwAlert.BUG:
popMsg += "<br>This is a bug!"
msgBox.critical(self, "Internal Error", popMsg)
popMsg += "<br>%s" % self.tr("This is a bug!")
msgBox.critical(self, self.tr("Internal Error"), popMsg)
return
@@ -1115,7 +1139,7 @@ class GuiMain(QMainWindow):
"""Ask the user a Yes/No question.
"""
msgBox = QMessageBox()
msgRes = msgBox.question(self, theTitle, theQuestion)
msgRes = msgBox.question(self, theTitle, theQuestion, QMessageBox.Yes | QMessageBox.No)
return msgRes == QMessageBox.Yes
def reportConfErr(self):
@@ -1137,8 +1161,11 @@ class GuiMain(QMainWindow):
"""
if self.hasProject:
msgYes = self.askQuestion(
"Exit",
"Do you want to exit novelWriter?<br>Changes are saved automatically."
self.tr("Exit"),
"%s<br>%s" % (
self.tr("Do you want to exit novelWriter?"),
self.tr("Changes are saved automatically.")
)
)
if not msgYes:
return False
+4 -4
View File
@@ -1,13 +1,13 @@
<?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>
<name>Sample Project</name>
<title>Sample Project</title>
<author>Jane Smith</author>
<author>Jay Doh</author>
<saveCount>936</saveCount>
<saveCount>1021</saveCount>
<autoCount>161</autoCount>
<editTime>46791</editTime>
<editTime>48283</editTime>
</project>
<settings>
<doBackup>False</doBackup>
@@ -120,7 +120,7 @@
<charCount>1810</charCount>
<wordCount>318</wordCount>
<paraCount>8</paraCount>
<cursorPos>3</cursorPos>
<cursorPos>1112</cursorPos>
</item>
<item handle="bc0cbd2a407f3" order="2" parent="e7ded148d6e4a">
<name>Another Scene</name>
+36
View File
@@ -186,6 +186,32 @@ def buildQtDocs():
return
##
# Qt Linguist QM Builder (qtlrelease)
##
def buildQtI18n():
"""Build the lang.qm files for Qt Linguist.
"""
try:
subprocess.call(["lrelease", "-verbose", "novelWriter.pro"])
except Exception as e:
print("QtI18n Release Error:")
print(str(e))
##
# Qt Linguist TS Builder (qtlupdate)
##
def buildQtI18nTS():
"""Build the lang.ts files for Qt Linguist.
"""
try:
subprocess.call(["pylupdate5", "-verbose", "-noobsolete", "novelWriter.pro"])
except Exception as e:
print("QtI18n Release Error:")
print(str(e))
##
# Sample Project ZIP File Builder (sample)
##
@@ -849,6 +875,8 @@ if __name__ == "__main__":
"\n"
" qthelp Build the help documentation for use with the Qt Assistant. Run before\n"
" install to have local help enable in the the installed version.\n"
" qtlupdate Update the translation files for internationalisation.\n"
" qtlrelease Build the language files for internationalisation.\n"
" sample Build the sample project as a zip file. Run before install to enable\n"
" creating sample projects in the in-app New Project Wizard.\n"
"\n"
@@ -898,6 +926,14 @@ if __name__ == "__main__":
sys.argv.remove("qthelp")
buildQtDocs()
if "qtlrelease" in sys.argv:
sys.argv.remove("qtlrelease")
buildQtI18n()
if "qtlupdate" in sys.argv:
sys.argv.remove("qtlupdate")
buildQtI18nTS()
if "sample" in sys.argv:
sys.argv.remove("sample")
buildSampleZip()
+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
before install to have local help enable in the the installed version
`qtlupdate` Update the translation files for internationalisation.
`qtlrelease` Build the language files for internationalisation.
`sample` Build the sample project as a zip file. Run before install to enable
creating sample projects in the in-app New Project Wizard.
+2 -1
View File
@@ -1,5 +1,5 @@
[Main]
timestamp = 2021-02-09 21:07:03
timestamp = 2021-02-15 16:31:24
theme = default
syntax = default_light
icons = typicons_colour_light
@@ -7,6 +7,7 @@ guidark = False
guifont =
guifontsize = 11
lastnotes = 0x0
guilang = en-GB
[Sizes]
geometry = 1200, 650
@@ -7,6 +7,7 @@ guidark = True
guifont = Sans
guifontsize = 12
lastnotes = 0x0
guilang = en-GB
[Sizes]
geometry = 1200, 650
+3 -3
View File
@@ -145,7 +145,7 @@ def testBaseConfig_Init(monkeypatch, tmpDir, fncDir, outDir, refDir):
assert os.path.isfile(confFile)
copyfile(confFile, testFile)
assert cmpFiles(testFile, compFile, [2, 9])
assert cmpFiles(testFile, compFile, [2, 9, 10])
monkeypatch.undo()
# Load and save with OSError
@@ -193,7 +193,7 @@ def testBaseConfig_Init(monkeypatch, tmpDir, fncDir, outDir, refDir):
assert tstConf.saveConfig()
copyfile(confFile, testFile)
assert cmpFiles(testFile, compFile, [2, 9])
assert cmpFiles(testFile, compFile, [2, 9, 10])
# END Test testBaseConfig_Init
@@ -458,7 +458,7 @@ def testBaseConfig_SettersGetters(tmpConf, tmpDir, outDir, refDir):
assert not tmpConf.confChanged
copyfile(confFile, testFile)
assert cmpFiles(testFile, compFile, [2, 9])
assert cmpFiles(testFile, compFile, [2, 9, 10])
# END Test testBaseConfig_SettersGetters
+2 -2
View File
@@ -928,7 +928,7 @@ def testCoreProject_OrphanedFiles(dummyGUI, nwLipsum):
# First Item with Meta Data
orphPath = os.path.join(nwLipsum, "content", "636b6aa9b697b.nwd")
with open(orphPath, mode="w", encoding="utf8") as outFile:
outFile.write("%%~name:Mars\n")
outFile.write("%%~name:[Recovered] Mars\n")
outFile.write("%%~path:5eaea4e8cdee8/636b6aa9b697b\n")
outFile.write("%%~kind:WORLD/NOTE\n")
outFile.write("%%~invalid\n")
@@ -962,7 +962,7 @@ def testCoreProject_OrphanedFiles(dummyGUI, nwLipsum):
# First Item with Meta Data
oItem = theProject.projTree["636b6aa9b697b"]
assert oItem is not None
assert oItem.itemName == "Recovered: Mars"
assert oItem.itemName == "[Recovered] Mars"
assert oItem.itemHandle == "636b6aa9b697b"
assert oItem.itemParent == "60bdf227455cc"
assert oItem.itemClass == nwItemClass.WORLD
+2 -7
View File
@@ -28,7 +28,6 @@ from dummy import causeOSError
from tools import readFile, writeFile
from nw.core.spellcheck import NWSpellCheck, NWSpellEnchant, NWSpellSimple
from nw.constants import nwConst
@pytest.mark.core
def testCoreSpell_Super(monkeypatch, tmpDir, tmpConf):
@@ -48,10 +47,6 @@ def testCoreSpell_Super(monkeypatch, tmpDir, tmpConf):
assert spChk.listDictionaries() == []
assert spChk.describeDict() == ("", "")
# Check language info
assert NWSpellCheck.expandLanguage("en") == "English"
assert NWSpellCheck.expandLanguage("en_GB") == "English (GB)"
# Add a word to the user's dictionary
assert spChk._readProjectDictionary("dummy") is False
monkeypatch.setattr("builtins.open", causeOSError)
@@ -178,11 +173,11 @@ def testCoreSpell_Simple(monkeypatch, tmpDir, tmpConf):
assert "D_word" in wSuggest
# List dictionaries
assert spChk.listDictionaries() == [("en", "English [%s]" % nwConst.SP_INTERNAL)]
assert spChk.listDictionaries() == [("en", "difflib")]
# Description
aTag, aName = spChk.describeDict()
assert aTag == "en"
assert aName == nwConst.SP_INTERNAL
assert aName == ""
# END Test testCoreSpell_Simple
+38 -5
View File
@@ -86,6 +86,7 @@ def testCoreTools_RomanNumbers():
def testCoreTools_NumberWords():
"""Test the conversion of integer to English words.
"""
# English
assert numberToWord(0, "en") == "Zero"
assert numberToWord(1, "en") == "One"
assert numberToWord(2, "en") == "Two"
@@ -114,14 +115,46 @@ def testCoreTools_NumberWords():
assert numberToWord(142, "en") == "One Hundred Forty-Two"
assert numberToWord(999, "en") == "Nine Hundred Ninety-Nine"
# Norwegian
assert numberToWord(0, "nb") == "null"
assert numberToWord(1, "nb") == "én"
assert numberToWord(10, "nb") == "ti"
assert numberToWord(20, "nb") == "tjue"
assert numberToWord(21, "nb") == "tjueén"
assert numberToWord(29, "nb") == "tjueni"
assert numberToWord(42, "nb") == "førtito"
assert numberToWord(60, "nb") == "seksti"
assert numberToWord(100, "nb") == "ett hundre"
assert numberToWord(114, "nb") == "ett hundre og fjorten"
assert numberToWord(142, "nb") == "ett hundre og førtito"
assert numberToWord(999, "nb") == "ni hundre og nittini"
assert numberToWord(0, "nn") == "null"
assert numberToWord(1, "nn") == "ein"
assert numberToWord(10, "nn") == "ti"
assert numberToWord(20, "nn") == "tjue"
assert numberToWord(21, "nn") == "tjueein"
assert numberToWord(29, "nn") == "tjueni"
assert numberToWord(42, "nn") == "førtito"
assert numberToWord(60, "nn") == "seksti"
assert numberToWord(100, "nn") == "eitt hundre"
assert numberToWord(114, "nn") == "eitt hundre og fjorten"
assert numberToWord(142, "nn") == "eitt hundre og førtito"
assert numberToWord(999, "nn") == "ni hundre og nittini"
# Check complex language setting
assert numberToWord(1, "en_GB") == "One"
assert numberToWord(2, "en_GB") == "Two"
assert numberToWord(3, "en_GB") == "Three"
# Check a few with a nonsense language setting
assert numberToWord(1, "foo") == "One"
assert numberToWord(2, "foo") == "Two"
assert numberToWord(3, "foo") == "Three"
assert numberToWord(1, "foo") == "1"
assert numberToWord(2, "foo") == "2"
assert numberToWord(3, "foo") == "3"
# Test out of range values
assert numberToWord(12345, "en") == "[Out of Range]"
assert numberToWord(-2345, "en") == "[Negative]"
assert numberToWord(12345, "en") == "[>999]"
assert numberToWord(-2345, "en") == "[<0]"
assert numberToWord("234", "en") == "[NaN]"
# END Test testCoreTools_NumberWords
+3 -4
View File
@@ -595,10 +595,9 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
nwGUI.mainMenu.aFileDetails.activate(QAction.Trigger)
theBits = theMessage.split("<br>")
assert len(theBits) == 3
assert theBits[0] == "File details for the currently open file"
assert theBits[1] == "Handle: 0e17daca5f3e1"
assert theBits[2] == "Location: %s" % os.path.join(fncProj, "content", "0e17daca5f3e1.nwd")
assert len(theBits) == 2
assert theBits[0] == "The currently open file is saved in:"
assert theBits[1] == os.path.join(fncProj, "content", "0e17daca5f3e1.nwd")
# qtbot.stopForInteraction()
+1 -7
View File
@@ -245,13 +245,7 @@ def testGuiPreferences_Main(qtbot, monkeypatch, fncDir, outDir, refDir):
testFile = os.path.join(outDir, "guiPreferences_novelwriter.conf")
compFile = os.path.join(refDir, "guiPreferences_novelwriter.conf")
copyfile(projFile, testFile)
ignoreLines = [
2, # Timestamp
9, # Release Notes
12, 13, 14, 15, # Window sizes
16, 17, 18, 19, # Window sizes
7, 29, # Fonts (depends on system default)
]
ignoreLines = [2, 9, 10, 13, 14, 15, 16, 17, 18, 19, 20, 7, 30, 31]
assert cmpFiles(testFile, compFile, ignoreLines)
# Clean up