Merge pull request #676 from vkbo/tokenizer_localisation
Tokenizer Localisation
This commit is contained in:
+183
-203
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"Synopsis": "Synopsis",
|
||||
"Comment": "Comment",
|
||||
"Notes": "Notes",
|
||||
"0": "Zero",
|
||||
"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",
|
||||
"21": "Twenty-One",
|
||||
"22": "Twenty-Two",
|
||||
"23": "Twenty-Three",
|
||||
"24": "Twenty-Four",
|
||||
"25": "Twenty-Five",
|
||||
"26": "Twenty-Six",
|
||||
"27": "Twenty-Seven",
|
||||
"28": "Twenty-Eight",
|
||||
"29": "Twenty-Nine",
|
||||
"30": "Thirty",
|
||||
"31": "Thirty-One",
|
||||
"32": "Thirty-Two",
|
||||
"33": "Thirty-Three",
|
||||
"34": "Thirty-Four",
|
||||
"35": "Thirty-Five",
|
||||
"36": "Thirty-Six",
|
||||
"37": "Thirty-Seven",
|
||||
"38": "Thirty-Eight",
|
||||
"39": "Thirty-Nine",
|
||||
"40": "Forty",
|
||||
"41": "Forty-One",
|
||||
"42": "Forty-Two",
|
||||
"43": "Forty-Three",
|
||||
"44": "Forty-Four",
|
||||
"45": "Forty-Five",
|
||||
"46": "Forty-Six",
|
||||
"47": "Forty-Seven",
|
||||
"48": "Forty-Eight",
|
||||
"49": "Forty-Nine",
|
||||
"50": "Fifty",
|
||||
"51": "Fifty-One",
|
||||
"52": "Fifty-Two",
|
||||
"53": "Fifty-Three",
|
||||
"54": "Fifty-Four",
|
||||
"55": "Fifty-Five",
|
||||
"56": "Fifty-Six",
|
||||
"57": "Fifty-Seven",
|
||||
"58": "Fifty-Eight",
|
||||
"59": "Fifty-Nine",
|
||||
"60": "Sixty",
|
||||
"61": "Sixty-One",
|
||||
"62": "Sixty-Two",
|
||||
"63": "Sixty-Three",
|
||||
"64": "Sixty-Four",
|
||||
"65": "Sixty-Five",
|
||||
"66": "Sixty-Six",
|
||||
"67": "Sixty-Seven",
|
||||
"68": "Sixty-Eight",
|
||||
"69": "Sixty-Nine",
|
||||
"70": "Seventy",
|
||||
"71": "Seventy-One",
|
||||
"72": "Seventy-Two",
|
||||
"73": "Seventy-Three",
|
||||
"74": "Seventy-Four",
|
||||
"75": "Seventy-Five",
|
||||
"76": "Seventy-Six",
|
||||
"77": "Seventy-Seven",
|
||||
"78": "Seventy-Eight",
|
||||
"79": "Seventy-Nine",
|
||||
"80": "Eighty",
|
||||
"81": "Eighty-One",
|
||||
"82": "Eighty-Two",
|
||||
"83": "Eighty-Three",
|
||||
"84": "Eighty-Four",
|
||||
"85": "Eighty-Five",
|
||||
"86": "Eighty-Six",
|
||||
"87": "Eighty-Seven",
|
||||
"88": "Eighty-Eight",
|
||||
"89": "Eighty-Nine",
|
||||
"90": "Ninety",
|
||||
"91": "Ninety-One",
|
||||
"92": "Ninety-Two",
|
||||
"93": "Ninety-Three",
|
||||
"94": "Ninety-Four",
|
||||
"95": "Ninety-Five",
|
||||
"96": "Ninety-Six",
|
||||
"97": "Ninety-Seven",
|
||||
"98": "Ninety-Eight",
|
||||
"99": "Ninety-Nine"
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"Synopsis": "Sammendrag",
|
||||
"Comment": "Kommentar",
|
||||
"Notes": "Notat",
|
||||
"0": "null",
|
||||
"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",
|
||||
"21": "tjueén",
|
||||
"22": "tjueto",
|
||||
"23": "tjuetre",
|
||||
"24": "tjuefire",
|
||||
"25": "tjuefem",
|
||||
"26": "tjueseks",
|
||||
"27": "tjuesju",
|
||||
"28": "tjueåtte",
|
||||
"29": "tjueni",
|
||||
"30": "tretti",
|
||||
"31": "trettién",
|
||||
"32": "trettito",
|
||||
"33": "trettitre",
|
||||
"34": "trettifire",
|
||||
"35": "trettifem",
|
||||
"36": "trettiseks",
|
||||
"37": "trettisju",
|
||||
"38": "trettiåtte",
|
||||
"39": "trettini",
|
||||
"40": "førti",
|
||||
"41": "førtién",
|
||||
"42": "førtito",
|
||||
"43": "førtitre",
|
||||
"44": "førtifire",
|
||||
"45": "førtifem",
|
||||
"46": "førtiseks",
|
||||
"47": "førtisju",
|
||||
"48": "førtiåtte",
|
||||
"49": "førtini",
|
||||
"50": "femti",
|
||||
"51": "femtién",
|
||||
"52": "femtito",
|
||||
"53": "femtitre",
|
||||
"54": "femtifire",
|
||||
"55": "femtifem",
|
||||
"56": "femtiseks",
|
||||
"57": "femtisju",
|
||||
"58": "femtiåtte",
|
||||
"59": "femtini",
|
||||
"60": "seksti",
|
||||
"61": "sekstién",
|
||||
"62": "sekstito",
|
||||
"63": "sekstitre",
|
||||
"64": "sekstifire",
|
||||
"65": "sekstifem",
|
||||
"66": "sekstiseks",
|
||||
"67": "sekstisju",
|
||||
"68": "sekstiåtte",
|
||||
"69": "sekstini",
|
||||
"70": "sytti",
|
||||
"71": "syttién",
|
||||
"72": "syttito",
|
||||
"73": "syttitre",
|
||||
"74": "syttifire",
|
||||
"75": "syttifem",
|
||||
"76": "syttiseks",
|
||||
"77": "syttisju",
|
||||
"78": "syttiåtte",
|
||||
"79": "syttini",
|
||||
"80": "åtti",
|
||||
"81": "åttién",
|
||||
"82": "åttito",
|
||||
"83": "åttitre",
|
||||
"84": "åttifire",
|
||||
"85": "åttifem",
|
||||
"86": "åttiseks",
|
||||
"87": "åttisju",
|
||||
"88": "åttiåtte",
|
||||
"89": "åttini",
|
||||
"90": "nitti",
|
||||
"91": "nittién",
|
||||
"92": "nittito",
|
||||
"93": "nittitre",
|
||||
"94": "nittifire",
|
||||
"95": "nittifem",
|
||||
"96": "nittiseks",
|
||||
"97": "nittisju",
|
||||
"98": "nittiåtte",
|
||||
"99": "nittini"
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"Synopsis": "Samandrag",
|
||||
"Comment": "Kommentar",
|
||||
"Notes": "Notat",
|
||||
"0": "null",
|
||||
"1": "ein",
|
||||
"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",
|
||||
"21": "tjueein",
|
||||
"22": "tjueto",
|
||||
"23": "tjuetre",
|
||||
"24": "tjuefire",
|
||||
"25": "tjuefem",
|
||||
"26": "tjueseks",
|
||||
"27": "tjuesju",
|
||||
"28": "tjueåtte",
|
||||
"29": "tjueni",
|
||||
"30": "tretti",
|
||||
"31": "trettiein",
|
||||
"32": "trettito",
|
||||
"33": "trettitre",
|
||||
"34": "trettifire",
|
||||
"35": "trettifem",
|
||||
"36": "trettiseks",
|
||||
"37": "trettisju",
|
||||
"38": "trettiåtte",
|
||||
"39": "trettini",
|
||||
"40": "førti",
|
||||
"41": "førtiein",
|
||||
"42": "førtito",
|
||||
"43": "førtitre",
|
||||
"44": "førtifire",
|
||||
"45": "førtifem",
|
||||
"46": "førtiseks",
|
||||
"47": "førtisju",
|
||||
"48": "førtiåtte",
|
||||
"49": "førtini",
|
||||
"50": "femti",
|
||||
"51": "femtiein",
|
||||
"52": "femtito",
|
||||
"53": "femtitre",
|
||||
"54": "femtifire",
|
||||
"55": "femtifem",
|
||||
"56": "femtiseks",
|
||||
"57": "femtisju",
|
||||
"58": "femtiåtte",
|
||||
"59": "femtini",
|
||||
"60": "seksti",
|
||||
"61": "sekstiein",
|
||||
"62": "sekstito",
|
||||
"63": "sekstitre",
|
||||
"64": "sekstifire",
|
||||
"65": "sekstifem",
|
||||
"66": "sekstiseks",
|
||||
"67": "sekstisju",
|
||||
"68": "sekstiåtte",
|
||||
"69": "sekstini",
|
||||
"70": "sytti",
|
||||
"71": "syttiein",
|
||||
"72": "syttito",
|
||||
"73": "syttitre",
|
||||
"74": "syttifire",
|
||||
"75": "syttifem",
|
||||
"76": "syttiseks",
|
||||
"77": "syttisju",
|
||||
"78": "syttiåtte",
|
||||
"79": "syttini",
|
||||
"80": "åtti",
|
||||
"81": "åttiein",
|
||||
"82": "åttito",
|
||||
"83": "åttitre",
|
||||
"84": "åttifire",
|
||||
"85": "åttifem",
|
||||
"86": "åttiseks",
|
||||
"87": "åttisju",
|
||||
"88": "åttiåtte",
|
||||
"89": "åttini",
|
||||
"90": "nitti",
|
||||
"91": "nittiein",
|
||||
"92": "nittito",
|
||||
"93": "nittitre",
|
||||
"94": "nittifire",
|
||||
"95": "nittifem",
|
||||
"96": "nittiseks",
|
||||
"97": "nittisju",
|
||||
"98": "nittiåtte",
|
||||
"99": "nittini"
|
||||
}
|
||||
@@ -76,6 +76,7 @@ class Config:
|
||||
self.themeRoot = None # The full path to the nw/assets/themes folder
|
||||
self.dictPath = None # The full path to the nw/assets/dict folder
|
||||
self.iconPath = None # The full path to the nw/assets/icons folder
|
||||
self.langPath = None # The full path to the nw/assets/lang folder
|
||||
self.helpPath = None # The full path to the novelwriter .qhc help file
|
||||
|
||||
# Runtime Settings and Variables
|
||||
@@ -297,6 +298,7 @@ class Config:
|
||||
self.themeRoot = os.path.join(self.assetPath, "themes")
|
||||
self.dictPath = os.path.join(self.assetPath, "dict")
|
||||
self.iconPath = os.path.join(self.assetPath, "icons")
|
||||
self.langPath = os.path.join(self.assetPath, "lang")
|
||||
self.appIcon = os.path.join(self.iconPath, "novelwriter.svg")
|
||||
|
||||
# Internationalisation
|
||||
|
||||
+1
-2
@@ -7,12 +7,11 @@ from nw.core.spellcheck import NWSpellCheck, NWSpellEnchant, NWSpellSimple
|
||||
from nw.core.tohtml import ToHtml
|
||||
from nw.core.toodt import ToOdt
|
||||
from nw.core.tomd import ToMarkdown
|
||||
from nw.core.tools import countWords, numberToRoman, numberToWord
|
||||
from nw.core.tools import countWords, numberToRoman
|
||||
|
||||
__all__ = [
|
||||
"countWords",
|
||||
"numberToRoman",
|
||||
"numberToWord",
|
||||
"NWDoc",
|
||||
"NWIndex",
|
||||
"NWProject",
|
||||
|
||||
@@ -27,6 +27,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
import nw
|
||||
import logging
|
||||
import os
|
||||
import json
|
||||
import shutil
|
||||
|
||||
from lxml import etree
|
||||
@@ -61,6 +62,7 @@ class NWProject():
|
||||
# Core Elements
|
||||
self.optState = OptionState(self) # Project-specific GUI options
|
||||
self.projTree = NWTree(self) # The project tree
|
||||
self.langData = {} # Localisation data
|
||||
|
||||
# Project Status
|
||||
self.projOpened = 0 # The time stamp of when the project file was opened
|
||||
@@ -622,6 +624,7 @@ class NWProject():
|
||||
self.theParent.setStatus(self.tr("Opened Project: {0}").format(self.projName))
|
||||
|
||||
self._scanProjectFolder()
|
||||
self.loadProjectLocalisation(self.projLang)
|
||||
|
||||
self.currWCount = self.lastWCount
|
||||
self.projOpened = time()
|
||||
@@ -1046,6 +1049,7 @@ class NWProject():
|
||||
theLang = checkString(theLang, None, True)
|
||||
if self.projLang != theLang:
|
||||
self.projLang = theLang
|
||||
self.loadProjectLocalisation(theLang)
|
||||
self.setProjectChanged(True)
|
||||
return True
|
||||
|
||||
@@ -1233,6 +1237,44 @@ class NWProject():
|
||||
self.importItems.countEntry(nwItem.itemStatus)
|
||||
return
|
||||
|
||||
def localLookup(self, theWord):
|
||||
"""Look up a word in the translation map for the project and
|
||||
return it. The variable is cast to a string before lookup. If
|
||||
the word does not exist, it returns itself.
|
||||
"""
|
||||
theValue = str(theWord)
|
||||
return self.langData.get(theValue, theValue)
|
||||
|
||||
def loadProjectLocalisation(self, theLang):
|
||||
"""Load the language data for the current project language.
|
||||
"""
|
||||
if theLang is None:
|
||||
theLang = self.mainConf.spellLanguage
|
||||
if theLang is None:
|
||||
theLang = "en"
|
||||
|
||||
lngShort = theLang.split("_")[0]
|
||||
loadFile = os.path.join(self.mainConf.langPath, "project_en.json")
|
||||
chkFile1 = os.path.join(self.mainConf.langPath, "project_%s.json" % theLang)
|
||||
chkFile2 = os.path.join(self.mainConf.langPath, "project_%s.json" % lngShort)
|
||||
|
||||
if os.path.isfile(chkFile1):
|
||||
loadFile = chkFile1
|
||||
elif os.path.isfile(chkFile2):
|
||||
loadFile = chkFile2
|
||||
|
||||
try:
|
||||
with open(loadFile, mode="r", encoding="utf8") as inFile:
|
||||
self.langData = json.load(inFile)
|
||||
logger.debug("Loaded project language file: %s" % os.path.basename(loadFile))
|
||||
|
||||
except Exception:
|
||||
logger.error("Failed to load index file")
|
||||
nw.logException()
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
+6
-10
@@ -405,24 +405,20 @@ class ToHtml(Tokenizer):
|
||||
def _formatSynopsis(self, tText):
|
||||
"""Apply HTML formatting to synopsis.
|
||||
"""
|
||||
sSynop = self._localLookup("Synopsis")
|
||||
if self.genMode == self.M_PREVIEW:
|
||||
return "<p class='comment'><span class='synopsis'>%s:</span> %s</p>\n" % (
|
||||
self._trSynopsis, tText
|
||||
)
|
||||
return f"<p class='comment'><span class='synopsis'>{sSynop}:</span> {tText}</p>\n"
|
||||
else:
|
||||
return "<p class='synopsis'><strong>%s:</strong> %s</p>\n" % (
|
||||
self._trSynopsis, tText
|
||||
)
|
||||
return f"<p class='synopsis'><strong>{sSynop}:</strong> {tText}</p>\n"
|
||||
|
||||
def _formatComments(self, tText):
|
||||
"""Apply HTML formatting to comments.
|
||||
"""
|
||||
if self.genMode == self.M_PREVIEW:
|
||||
return "<p class='comment'>%s</p>\n" % tText
|
||||
return f"<p class='comment'>{tText}</p>\n"
|
||||
else:
|
||||
return "<p class='comment'><strong>%s:</strong> %s</p>\n" % (
|
||||
self._trComment, tText
|
||||
)
|
||||
sComm = self._localLookup("Comment")
|
||||
return f"<p class='comment'><strong>{sComm}:</strong> {tText}</p>\n"
|
||||
|
||||
def _formatKeywords(self, tText):
|
||||
"""Apply HTML formatting to keywords.
|
||||
|
||||
+7
-14
@@ -33,7 +33,7 @@ from functools import partial
|
||||
from PyQt5.QtCore import QCoreApplication, QRegularExpression
|
||||
|
||||
from nw.core.document import NWDoc
|
||||
from nw.core.tools import numberToWord, numberToRoman
|
||||
from nw.core.tools import numberToRoman
|
||||
from nw.constants import nwConst, nwUnicode, nwItemLayout, nwItemType, nwRegEx
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -143,18 +143,10 @@ class Tokenizer():
|
||||
# Error Handling
|
||||
self.errData = []
|
||||
|
||||
# Internal Mappings
|
||||
# Function Mapping
|
||||
self._localLookup = self.theProject.localLookup
|
||||
self.tr = partial(QCoreApplication.translate, "Tokenizer")
|
||||
|
||||
# Localisation
|
||||
self._trSynopsis = self.tr("Synopsis")
|
||||
self._trComment = self.tr("Comment")
|
||||
self._trNotes = self.tr("Notes")
|
||||
|
||||
self._spellLang = self.theProject.projLang
|
||||
if self._spellLang is None:
|
||||
self._spellLang = self.theParent.mainConf.spellLanguage
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
@@ -263,7 +255,7 @@ class Tokenizer():
|
||||
if theItem.itemType != nwItemType.ROOT:
|
||||
return False
|
||||
|
||||
theTitle = self.tr("{0}: {1}").format(self._trNotes, theItem.itemName)
|
||||
theTitle = "%s: %s" % (self._localLookup("Notes"), theItem.itemName)
|
||||
self.theTokens = []
|
||||
self.theTokens.append((
|
||||
self.T_TITLE, 0, theTitle, None, self.A_PBB | self.A_CENTRE
|
||||
@@ -667,11 +659,12 @@ class Tokenizer():
|
||||
theTitle = theTitle.replace(r"%sc%", str(self.numChScene))
|
||||
theTitle = theTitle.replace(r"%sca%", str(self.numAbsScene))
|
||||
if r"%chw%" in theTitle:
|
||||
theTitle = theTitle.replace(r"%chw%", numberToWord(self.numChapter, self._spellLang))
|
||||
theTitle = theTitle.replace(r"%chw%", self._localLookup(self.numChapter))
|
||||
if r"%chi%" in theTitle:
|
||||
theTitle = theTitle.replace(r"%chi%", numberToRoman(self.numChapter, True))
|
||||
if r"%chI%" in theTitle:
|
||||
theTitle = theTitle.replace(r"%chI%", numberToRoman(self.numChapter, False))
|
||||
return theTitle
|
||||
|
||||
return theTitle[:1].upper() + theTitle[1:]
|
||||
|
||||
# END Class Tokenizer
|
||||
|
||||
+2
-2
@@ -134,10 +134,10 @@ class ToMarkdown(Tokenizer):
|
||||
thisPar.append(tTemp.rstrip() + " ")
|
||||
|
||||
elif tType == self.T_SYNOPSIS and self.doSynopsis:
|
||||
tmpResult.append("**Synopsis:** %s\n\n" % tText)
|
||||
tmpResult.append("**%s:** %s\n\n" % (self._localLookup("Synopsis"), tText))
|
||||
|
||||
elif tType == self.T_COMMENT and self.doComments:
|
||||
tmpResult.append("**Comment:** %s\n\n" % tText)
|
||||
tmpResult.append("**%s:** %s\n\n" % (self._localLookup("Comment"), tText))
|
||||
|
||||
elif tType == self.T_KEYWORD and self.doKeywords:
|
||||
tmpResult.append(self._formatKeywords(tText, tStyle))
|
||||
|
||||
+7
-5
@@ -507,15 +507,17 @@ class ToOdt(Tokenizer):
|
||||
def _formatSynopsis(self, tText):
|
||||
"""Apply formatting to synopsis lines.
|
||||
"""
|
||||
rTxt = "**Synopsis:** %s" % tText
|
||||
rFmt = "_B b_ %s" % (" "*len(tText))
|
||||
sSynop = self._localLookup("Synopsis")
|
||||
rTxt = "**%s:** %s" % (sSynop, tText)
|
||||
rFmt = "_B%s b_ %s" % (" "*len(sSynop), " "*len(tText))
|
||||
return rTxt, rFmt
|
||||
|
||||
def _formatComments(self, tText):
|
||||
"""Apply formatting to comments.
|
||||
"""
|
||||
rTxt = "**Comment:** %s" % tText
|
||||
rFmt = "_B b_ %s" % (" "*len(tText))
|
||||
sComm = self._localLookup("Comment")
|
||||
rTxt = "**%s:** %s" % (sComm, tText)
|
||||
rFmt = "_B%s b_ %s" % (" "*len(sComm), " "*len(tText))
|
||||
return rTxt, rFmt
|
||||
|
||||
def _formatKeywords(self, tText):
|
||||
@@ -530,7 +532,7 @@ class ToOdt(Tokenizer):
|
||||
if theBits[0] in nwLabels.KEY_NAME:
|
||||
tText = nwLabels.KEY_NAME[theBits[0]]
|
||||
rTxt += "**%s:** " % tText
|
||||
rFmt += "_B%s b_ " % (" "*len(tText))
|
||||
rFmt += "_B%s b_ " % (" "*len(tText))
|
||||
if len(theBits) > 1:
|
||||
if theBits[0] == nwKeyWords.TAG_KEY:
|
||||
rTxt += "%s" % theBits[1]
|
||||
|
||||
@@ -6,8 +6,6 @@ Various core tool functions
|
||||
|
||||
File History:
|
||||
Created: 2019-04-22 [0.0.1] countWords
|
||||
Created: 2019-10-13 [0.2.3] numberToWord, _numberToWordEN
|
||||
Merged: 2020-05-08 [0.4.5] All of the above into this file
|
||||
Created: 2020-07-05 [0.10.0] numberToRoman
|
||||
|
||||
This file is a part of novelWriter
|
||||
@@ -118,126 +116,3 @@ def numberToRoman(numVal, isLower=False):
|
||||
break
|
||||
|
||||
return romNum.lower() if isLower else romNum
|
||||
|
||||
# =============================================================================================== #
|
||||
# Convert an Integer to a Number String
|
||||
# =============================================================================================== #
|
||||
|
||||
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 = ""
|
||||
theCode = theLang.split("_")[0]
|
||||
if theCode == "en":
|
||||
numWord = _numberToWord_EN(numVal)
|
||||
elif theCode in ("nb", "nn"):
|
||||
numWord = _numberToWord_NO(numVal, theCode)
|
||||
else:
|
||||
# Default to return the number as a string
|
||||
numWord = str(numVal)
|
||||
|
||||
return numWord
|
||||
|
||||
def _numberToWord_EN(numVal):
|
||||
"""Convert numbers to English words.
|
||||
"""
|
||||
oneWord = ""
|
||||
tenWord = ""
|
||||
hunWord = ""
|
||||
|
||||
if numVal == 0:
|
||||
return "Zero"
|
||||
|
||||
oneVal = numVal % 10
|
||||
tenVal = (numVal - oneVal) % 100
|
||||
hunVal = (numVal - tenVal - oneVal) % 1000
|
||||
|
||||
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",
|
||||
}
|
||||
|
||||
retVal = ""
|
||||
hunWord = theWords.get(hunVal, "")
|
||||
if tenVal == 10:
|
||||
oneWord = theWords.get(oneVal+10, "")
|
||||
retVal = f"{hunWord} {oneWord}".strip()
|
||||
else:
|
||||
oneWord = theWords.get(oneVal, "")
|
||||
if tenVal == 0:
|
||||
retVal = f"{hunWord} {oneWord}".strip()
|
||||
else:
|
||||
tenWord = theWords.get(tenVal, "")
|
||||
if oneVal == 0:
|
||||
retVal = f"{hunWord} {tenWord}".strip()
|
||||
else:
|
||||
retVal = f"{hunWord} {tenWord}-{oneWord}".strip()
|
||||
|
||||
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()
|
||||
|
||||
@@ -91,6 +91,7 @@ class GuiPreferences(PagedDialog):
|
||||
logger.debug("Saving new preferences")
|
||||
|
||||
needsRestart = self.tabGeneral.saveValues()
|
||||
prevSpell = self.mainConf.spellLanguage
|
||||
|
||||
self.tabProjects.saveValues()
|
||||
self.tabDocs.saveValues()
|
||||
@@ -98,6 +99,9 @@ class GuiPreferences(PagedDialog):
|
||||
self.tabSyntax.saveValues()
|
||||
self.tabAuto.saveValues()
|
||||
|
||||
if prevSpell != self.mainConf.spellLanguage:
|
||||
self.theProject.loadProjectLocalisation(self.mainConf.spellLanguage)
|
||||
|
||||
if needsRestart:
|
||||
self.theParent.makeAlert(
|
||||
self.tr("Some changes will not be applied until novelWriter has been restarted."),
|
||||
|
||||
@@ -112,11 +112,14 @@ def testCoreToken_Setters(dummyGUI):
|
||||
# END Test testCoreToken_Setters
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreToken_TextOps(monkeypatch, nwMinimal, dummyGUI):
|
||||
def testCoreToken_TextOps(monkeypatch, nwMinimal, dummyGUI, tmpConf):
|
||||
"""Test handling files and text in the Tokenizer class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject.mainConf = tmpConf
|
||||
theProject.projTree.setSeed(42)
|
||||
theProject.loadProjectLocalisation("en")
|
||||
|
||||
theToken = Tokenizer(theProject, dummyGUI)
|
||||
theToken.setKeepMarkdown(True)
|
||||
|
||||
@@ -408,10 +411,12 @@ def testCoreToken_Tokenize(dummyGUI):
|
||||
# END Test testCoreToken_Tokenize
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreToken_Headers(dummyGUI):
|
||||
def testCoreToken_Headers(dummyGUI, tmpConf):
|
||||
"""Test the header and page parser of the Tokenizer class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject.mainConf = tmpConf
|
||||
theProject.loadProjectLocalisation("en")
|
||||
theToken = Tokenizer(theProject, dummyGUI)
|
||||
|
||||
# Nothing
|
||||
|
||||
@@ -22,7 +22,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import pytest
|
||||
|
||||
from nw.core.tools import countWords, numberToRoman, numberToWord
|
||||
from nw.core.tools import countWords, numberToRoman
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreTools_CountWords():
|
||||
@@ -81,80 +81,3 @@ def testCoreTools_RomanNumbers():
|
||||
assert numberToRoman(999, True) == "cmxcix"
|
||||
|
||||
# END Test testCoreTools_RomanNumbers
|
||||
|
||||
@pytest.mark.core
|
||||
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"
|
||||
assert numberToWord(3, "en") == "Three"
|
||||
assert numberToWord(4, "en") == "Four"
|
||||
assert numberToWord(5, "en") == "Five"
|
||||
assert numberToWord(6, "en") == "Six"
|
||||
assert numberToWord(7, "en") == "Seven"
|
||||
assert numberToWord(8, "en") == "Eight"
|
||||
assert numberToWord(9, "en") == "Nine"
|
||||
assert numberToWord(10, "en") == "Ten"
|
||||
assert numberToWord(11, "en") == "Eleven"
|
||||
assert numberToWord(12, "en") == "Twelve"
|
||||
assert numberToWord(13, "en") == "Thirteen"
|
||||
assert numberToWord(14, "en") == "Fourteen"
|
||||
assert numberToWord(15, "en") == "Fifteen"
|
||||
assert numberToWord(16, "en") == "Sixteen"
|
||||
assert numberToWord(17, "en") == "Seventeen"
|
||||
assert numberToWord(18, "en") == "Eighteen"
|
||||
assert numberToWord(19, "en") == "Nineteen"
|
||||
assert numberToWord(20, "en") == "Twenty"
|
||||
assert numberToWord(21, "en") == "Twenty-One"
|
||||
assert numberToWord(29, "en") == "Twenty-Nine"
|
||||
assert numberToWord(42, "en") == "Forty-Two"
|
||||
assert numberToWord(114, "en") == "One Hundred Fourteen"
|
||||
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") == "1"
|
||||
assert numberToWord(2, "foo") == "2"
|
||||
assert numberToWord(3, "foo") == "3"
|
||||
|
||||
# Test out of range values
|
||||
assert numberToWord(12345, "en") == "[>999]"
|
||||
assert numberToWord(-2345, "en") == "[<0]"
|
||||
assert numberToWord("234", "en") == "[NaN]"
|
||||
|
||||
# END Test testCoreTools_NumberWords
|
||||
|
||||
Reference in New Issue
Block a user