Big unit reorganisation

* Renamed pynum2word to num2word
* Renamed num2word_* to lang_*
* Re-organised all unit headers
* Made imports relative
This commit is contained in:
Virgil Dupras
2013-05-28 11:50:48 -04:00
parent 5479aa59c6
commit 6e2b0eeda2
15 changed files with 232 additions and 356 deletions

46
num2words/__init__.py Normal file
View File

@@ -0,0 +1,46 @@
# Copyright (c) 2003, Taro Ogawa. All Rights Reserved.
# Copyright (c) 2013, Savoir-faire Linux inc. All Rights Reserved.
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
# This library 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
# Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA
from __future__ import unicode_literals
from . import lang_EN
from . import lang_EN_GB
from . import lang_FR
from . import lang_DE
from . import lang_ES
from . import lang_LT
CONVERTER_CLASSES = {
'en': lang_EN.Num2Word_EN(),
'en_GB': lang_EN_GB.Num2Word_EN_GB(),
'fr': lang_FR.Num2Word_FR(),
'de': lang_DE.Num2Word_DE(),
'es': lang_ES.Num2Word_ES(),
'lt': lang_LT.Num2Word_LT(),
}
def num2words(number, ordinal=False, lang='en'):
# We try the full language first
if lang not in CONVERTER_CLASSES:
# ... and then try only the first 2 letters
lang = lang[:2]
if lang not in CONVERTER_CLASSES:
raise NotImplementedError()
converter = CONVERTER_CLASSES[lang]
if ordinal:
return converter.to_ordinal(number)
else:
return converter.to_cardinal(number)

265
num2words/base.py Normal file
View File

@@ -0,0 +1,265 @@
# Copyright (c) 2003, Taro Ogawa. All Rights Reserved.
# Copyright (c) 2013, Savoir-faire Linux inc. All Rights Reserved.
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
# This library 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
# Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA
from __future__ import unicode_literals
from .orderedmapping import OrderedMapping
class Num2Word_Base(object):
def __init__(self):
self.cards = OrderedMapping()
self.is_title = False
self.precision = 2
self.exclude_title = []
self.negword = "(-) "
self.pointword = "(.)"
self.errmsg_nonnum = "type(%s) not in [long, int, float]"
self.errmsg_floatord = "Cannot treat float %s as ordinal."
self.errmsg_negord = "Cannot treat negative num %s as ordinal."
self.errmsg_toobig = "abs(%s) must be less than %s."
self.base_setup()
self.setup()
self.set_numwords()
self.MAXVAL = 1000 * self.cards.order[0]
def set_numwords(self):
self.set_high_numwords(self.high_numwords)
self.set_mid_numwords(self.mid_numwords)
self.set_low_numwords(self.low_numwords)
def gen_high_numwords(self, units, tens, lows):
out = [u + t for t in tens for u in units]
out.reverse()
return out + lows
def set_mid_numwords(self, mid):
for key, val in mid:
self.cards[key] = val
def set_low_numwords(self, numwords):
for word, n in zip(numwords, range(len(numwords) - 1, -1, -1)):
self.cards[n] = word
def splitnum(self, value):
for elem in self.cards:
if elem > value:
continue
out = []
if value == 0:
div, mod = 1, 0
else:
div, mod = divmod(value, elem)
if div == 1:
out.append((self.cards[1], 1))
else:
if div == value: # The system tallies, eg Roman Numerals
return [(div * self.cards[elem], div*elem)]
out.append(self.splitnum(div))
out.append((self.cards[elem], elem))
if mod:
out.append(self.splitnum(mod))
return out
def to_cardinal(self, value):
try:
assert long(value) == value
except (ValueError, TypeError, AssertionError):
return self.to_cardinal_float(value)
self.verify_num(value)
out = ""
if value < 0:
value = abs(value)
out = self.negword
if value >= self.MAXVAL:
raise OverflowError(self.errmsg_toobig % (value, self.MAXVAL))
val = self.splitnum(value)
words, num = self.clean(val)
return self.title(out + words)
def to_cardinal_float(self, value):
try:
float(value) == value
except (ValueError, TypeError, AssertionError):
raise TypeError(self.errmsg_nonnum % value)
pre = int(value)
post = abs(value - pre)
out = [self.to_cardinal(pre)]
if self.precision:
out.append(self.title(self.pointword))
for i in range(self.precision):
post *= 10
curr = int(post)
out.append(str(self.to_cardinal(curr)))
post -= curr
return " ".join(out)
def merge(self, curr, next):
raise NotImplementedError
def clean(self, val):
out = val
while len(val) != 1:
out = []
left, right = val[:2]
if isinstance(left, tuple) and isinstance(right, tuple):
out.append(self.merge(left, right))
if val[2:]:
out.append(val[2:])
else:
for elem in val:
if isinstance(elem, list):
if len(elem) == 1:
out.append(elem[0])
else:
out.append(self.clean(elem))
else:
out.append(elem)
val = out
return out[0]
def title(self, value):
if self.is_title:
out = []
value = value.split()
for word in value:
if word in self.exclude_title:
out.append(word)
else:
out.append(word[0].upper() + word[1:])
value = " ".join(out)
return value
def verify_ordinal(self, value):
if not value == long(value):
raise TypeError, self.errmsg_floatord %(value)
if not abs(value) == value:
raise TypeError, self.errmsg_negord %(value)
def verify_num(self, value):
return 1
def set_wordnums(self):
pass
def to_ordinal(value):
return self.to_cardinal(value)
def to_ordinal_num(self, value):
return value
# Trivial version
def inflect(self, value, text):
text = text.split("/")
if value == 1:
return text[0]
return "".join(text)
#//CHECK: generalise? Any others like pounds/shillings/pence?
def to_splitnum(self, val, hightxt="", lowtxt="", jointxt="",
divisor=100, longval=True, cents = True):
out = []
try:
high, low = val
except TypeError:
high, low = divmod(val, divisor)
if high:
hightxt = self.title(self.inflect(high, hightxt))
out.append(self.to_cardinal(high))
if low:
if longval:
if hightxt:
out.append(hightxt)
if jointxt:
out.append(self.title(jointxt))
elif hightxt:
out.append(hightxt)
if low:
if cents:
out.append(self.to_cardinal(low))
else:
out.append("%02d" % low)
if lowtxt and longval:
out.append(self.title(self.inflect(low, lowtxt)))
return " ".join(out)
def to_year(self, value, **kwargs):
return self.to_cardinal(value)
def to_currency(self, value, **kwargs):
return self.to_cardinal(value)
def base_setup(self):
pass
def setup(self):
pass
def test(self, value):
try:
_card = self.to_cardinal(value)
except:
_card = "invalid"
try:
_ord = self.to_ordinal(value)
except:
_ord = "invalid"
try:
_ordnum = self.to_ordinal_num(value)
except:
_ordnum = "invalid"
print ("For %s, card is %s;\n\tord is %s; and\n\tordnum is %s." %
(value, _card, _ord, _ordnum))

141
num2words/lang_DE.py Normal file
View File

@@ -0,0 +1,141 @@
# Copyright (c) 2003, Taro Ogawa. All Rights Reserved.
# Copyright (c) 2013, Savoir-faire Linux inc. All Rights Reserved.
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
# This library 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
# Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA
from __future__ import unicode_literals
from .lang_EU import Num2Word_EU
#//TODO: Use German error messages
class Num2Word_DE(Num2Word_EU):
def set_high_numwords(self, high):
max = 3 + 6*len(high)
for word, n in zip(high, range(max, 3, -6)):
self.cards[10**n] = word + "illiarde"
self.cards[10**(n-3)] = word + "illion"
def setup(self):
self.negword = "minus "
self.pointword = "Komma"
self.errmsg_nonnum = "Only numbers may be converted to words."
self.errmsg_toobig = "Number is too large to convert to words."
self.exclude_title = []
lows = ["non", "okt", "sept", "sext", "quint", "quadr", "tr", "b", "m"]
units = ["", "un", "duo", "tre", "quattuor", "quin", "sex", "sept",
"okto", "novem"]
tens = ["dez", "vigint", "trigint", "quadragint", "quinquagint",
"sexagint", "septuagint", "oktogint", "nonagint"]
self.high_numwords = ["zent"]+self.gen_high_numwords(units, tens, lows)
self.mid_numwords = [(1000, "tausand"), (100, "hundert"),
(90, "neunzig"), (80, "achtzig"), (70, "siebzig"),
(60, "sechzig"), (50, "f\xFCnfzig"), (40, "vierzig"),
(30, "drei\xDFig")]
self.low_numwords = ["zwanzig", "neunzehn", "achtzen", "siebzehn",
"sechzehn", "f\xFCnfzehn", "vierzehn", "dreizehn",
"zw\xF6lf", "elf", "zehn", "neun", "acht", "sieben",
"sechs", "f\xFCnf", "vier", "drei", "zwei", "eins",
"null"]
self.ords = { "eins" : "ers",
"drei" : "drit",
"acht" : "ach",
"sieben" : "sieb",
"ig" : "igs" }
self.ordflag = False
def merge(self, curr, next):
ctext, cnum, ntext, nnum = curr + next
if cnum == 1:
if nnum < 10**6 or self.ordflag:
return next
ctext = "eine"
if nnum > cnum:
if nnum >= 10**6:
if cnum > 1:
if ntext.endswith("e") or self.ordflag:
ntext += "s"
else:
ntext += "es"
ctext += " "
val = cnum * nnum
else:
if nnum < 10 < cnum < 100:
if nnum == 1:
ntext = "ein"
ntext, ctext = ctext, ntext + "und"
elif cnum >= 10**6:
ctext += " "
val = cnum + nnum
word = ctext + ntext
return (word, val)
def to_ordinal(self, value):
self.verify_ordinal(value)
self.ordflag = True
outword = self.to_cardinal(value)
self.ordflag = False
for key in self.ords:
if outword.endswith(key):
outword = outword[:len(outword) - len(key)] + self.ords[key]
break
return outword + "te"
# Is this correct??
def to_ordinal_num(self, value):
self.verify_ordinal(value)
return str(value) + "te"
def to_currency(self, val, longval=True, old=False):
if old:
return self.to_splitnum(val, hightxt="mark/s", lowtxt="pfennig/e",
jointxt="und",longval=longval)
return super(Num2Word_DE, self).to_currency(val, jointxt="und",
longval=longval)
def to_year(self, val, longval=True):
if not (val//100)%10:
return self.to_cardinal(val)
return self.to_splitnum(val, hightxt="hundert", longval=longval)
n2w = Num2Word_DE()
to_card = n2w.to_cardinal
to_ord = n2w.to_ordinal
to_ordnum = n2w.to_ordinal_num
def main():
for val in [ 1, 11, 12, 21, 31, 33, 71, 80, 81, 91, 99, 100, 101, 102, 155,
180, 300, 308, 832, 1000, 1001, 1061, 1100, 1500, 1701, 3000,
8280, 8291, 150000, 500000, 1000000, 2000000, 2000001,
-21212121211221211111, -2.121212, -1.0000100]:
n2w.test(val)
n2w.test(1325325436067876801768700107601001012212132143210473207540327057320957032975032975093275093275093270957329057320975093272950730)
print n2w.to_currency(112121)
print n2w.to_year(2000)
if __name__ == "__main__":
main()

113
num2words/lang_EN.py Normal file
View File

@@ -0,0 +1,113 @@
# Copyright (c) 2003, Taro Ogawa. All Rights Reserved.
# Copyright (c) 2013, Savoir-faire Linux inc. All Rights Reserved.
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
# This library 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
# Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA
from __future__ import division, unicode_literals
from . import lang_EU
class Num2Word_EN(lang_EU.Num2Word_EU):
def set_high_numwords(self, high):
max = 3 + 3*len(high)
for word, n in zip(high, range(max, 3, -3)):
self.cards[10**n] = word + "illion"
def setup(self):
self.negword = "minus "
self.pointword = "point"
self.errmsg_nornum = "Only numbers may be converted to words."
self.exclude_title = ["and", "point", "minus"]
self.mid_numwords = [(1000, "thousand"), (100, "hundred"),
(90, "ninety"), (80, "eighty"), (70, "seventy"),
(60, "sixty"), (50, "fifty"), (40, "forty"),
(30, "thirty")]
self.low_numwords = ["twenty", "nineteen", "eighteen", "seventeen",
"sixteen", "fifteen", "fourteen", "thirteen",
"twelve", "eleven", "ten", "nine", "eight",
"seven", "six", "five", "four", "three", "two",
"one", "zero"]
self.ords = { "one" : "first",
"two" : "second",
"three" : "third",
"five" : "fifth",
"eight" : "eighth",
"nine" : "ninth",
"twelve" : "twelfth" }
def merge(self, (ltext, lnum), (rtext, rnum)):
if lnum == 1 and rnum < 100:
return (rtext, rnum + lnum)
elif 100 > lnum > rnum :
return ("%s-%s"%(ltext, rtext), lnum + rnum)
elif lnum >= 100 > rnum:
return ("%s and %s"%(ltext, rtext), lnum + rnum)
elif rnum > lnum:
return ("%s %s"%(ltext, rtext), lnum * rnum)
return ("%s, %s"%(ltext, rtext), lnum + rnum)
def to_ordinal(self, value):
self.verify_ordinal(value)
outwords = self.to_cardinal(value).split(" ")
lastwords = outwords[-1].split("-")
lastword = lastwords[-1].lower()
try:
lastword = self.ords[lastword]
except KeyError:
if lastword[-1] == "y":
lastword = lastword[:-1] + "ie"
lastword += "th"
lastwords[-1] = self.title(lastword)
outwords[-1] = "-".join(lastwords)
return " ".join(outwords)
def to_ordinal_num(self, value):
self.verify_ordinal(value)
return "%s%s"%(value, self.to_ordinal(value)[-2:])
def to_year(self, val, longval=True):
if not (val//100)%10:
return self.to_cardinal(val)
return self.to_splitnum(val, hightxt="hundred", jointxt="and",
longval=longval)
def to_currency(self, val, longval=True):
return self.to_splitnum(val, hightxt="dollar/s", lowtxt="cent/s",
jointxt="and", longval=longval, cents = True)
n2w = Num2Word_EN()
to_card = n2w.to_cardinal
to_ord = n2w.to_ordinal
to_ordnum = n2w.to_ordinal_num
to_year = n2w.to_year
def main():
for val in [ 1, 11, 12, 21, 31, 33, 71, 80, 81, 91, 99, 100, 101, 102, 155,
180, 300, 308, 832, 1000, 1001, 1061, 1100, 1500, 1701, 3000,
8280, 8291, 150000, 500000, 1000000, 2000000, 2000001,
-21212121211221211111, -2.121212, -1.0000100]:
n2w.test(val)
n2w.test(1325325436067876801768700107601001012212132143210473207540327057320957032975032975093275093275093270957329057320975093272950730)
for val in [1,120,1000,1120,1800, 1976,2000,2010,2099,2171]:
print val, "is", n2w.to_currency(val)
print val, "is", n2w.to_year(val)
if __name__ == "__main__":
main()

46
num2words/lang_EN_EUR.py Normal file
View File

@@ -0,0 +1,46 @@
# Copyright (c) 2003, Taro Ogawa. All Rights Reserved.
# Copyright (c) 2013, Savoir-faire Linux inc. All Rights Reserved.
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
# This library 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
# Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA
from __future__ import unicode_literals
from .lang_EN import Num2Word_EN
class Num2Word_EN_EUR(Num2Word_EN):
def to_currency(self, val, longval=True, cents=True, jointxt="and"):
return self.to_splitnum(val, hightxt="euro/s", lowtxt="cents",
jointxt=jointxt, longval=longval, cents = cents)
n2w = Num2Word_EN_EUR()
to_card = n2w.to_cardinal
to_ord = n2w.to_ordinal
to_ordnum = n2w.to_ordinal_num
to_year = n2w.to_year
to_currency = n2w.to_currency
def main():
for val in [ 1, 11, 12, 21, 31, 33, 71, 80, 81, 91, 99, 100, 101, 102, 155,
180, 300, 308, 832, 1000, 1001, 1061, 1100, 1500, 1701, 3000,
8280, 8291, 150000, 500000, 1000000, 2000000, 2000001,
-21212121211221211111, -2.121212, -1.0000100]:
n2w.test(val)
n2w.test(1325325436067876801768700107601001012212132143210473207540327057320957032975032975093275093275093270957329057320975093272950730)
for val in [1,120,1000,1120,1800, 1976,2000,2010,2099,2171]:
print val, "is", n2w.to_currency(val)
print val, "is", n2w.to_year(val)
if __name__ == "__main__":
main()

46
num2words/lang_EN_GB.py Normal file
View File

@@ -0,0 +1,46 @@
# Copyright (c) 2003, Taro Ogawa. All Rights Reserved.
# Copyright (c) 2013, Savoir-faire Linux inc. All Rights Reserved.
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
# This library 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
# Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA
from __future__ import unicode_literals
from .lang_EN import Num2Word_EN
class Num2Word_EN_GB(Num2Word_EN):
def to_currency(self, val, longval=True):
return self.to_splitnum(val, hightxt="pound/s", lowtxt="pence",
jointxt="and", longval=longval)
n2w = Num2Word_EN_GB()
to_card = n2w.to_cardinal
to_ord = n2w.to_ordinal
to_ordnum = n2w.to_ordinal_num
to_year = n2w.to_year
def main():
for val in [ 1, 11, 12, 21, 31, 33, 71, 80, 81, 91, 99, 100, 101, 102, 155,
180, 300, 308, 832, 1000, 1001, 1061, 1100, 1500, 1701, 3000,
8280, 8291, 150000, 500000, 1000000, 2000000, 2000001,
-21212121211221211111, -2.121212, -1.0000100]:
n2w.test(val)
n2w.test(1325325436067876801768700107601001012212132143210473207540327057320957032975032975093275093275093270957329057320975093272950730)
for val in [1,120,1000,1120,1800, 1976,2000,2010,2099,2171]:
print val, "is", n2w.to_currency(val)
print val, "is", n2w.to_year(val)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,45 @@
# Copyright (c) 2003, Taro Ogawa. All Rights Reserved.
# Copyright (c) 2013, Savoir-faire Linux inc. All Rights Reserved.
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
# This library 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
# Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA
from __future__ import unicode_literals
from .lang_EN_GB import Num2Word_EN_GB
class Num2Word_EN_GB_old(Num2Word_EN_GB):
def base_setup(self):
sclass = super(Num2Word_EN_GB, self)
self.set_high_numwords = sclass.set_high_numwords
sclass.base_setup()
n2w = Num2Word_EN_GB_old()
to_card = n2w.to_cardinal
to_ord = n2w.to_ordinal
to_ordnum = n2w.to_ordinal_num
def main():
for val in [ 1, 11, 12, 21, 31, 33, 71, 80, 81, 91, 99, 100, 101, 102, 155,
180, 300, 308, 832, 1000, 1001, 1061, 1100, 1500, 1701, 3000,
8280, 8291, 150000, 500000, 1000000, 2000000, 2000001,
-21212121211221211111, -2.121212, -1.0000100]:
n2w.test(val)
n2w.test(1325325436067876801768700107601001012212132143210473207540327057320957032975032975093275093275093270957329057320975093272950730)
for val in [1,120,1000,1120,1800, 1976,2000,2010,2099,2171]:
print val, "is", n2w.to_currency(val)
print val, "is", n2w.to_year(val)
if __name__ == "__main__":
main()

135
num2words/lang_ES.py Normal file
View File

@@ -0,0 +1,135 @@
# Copyright (c) 2003, Taro Ogawa. All Rights Reserved.
# Copyright (c) 2013, Savoir-faire Linux inc. All Rights Reserved.
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
# This library 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
# Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA
from __future__ import unicode_literals
from .lang_EU import Num2Word_EU
#//TODO: correct orthographics
#//TODO: error messages
class Num2Word_ES(Num2Word_EU):
#//CHECK: Is this sufficient??
def set_high_numwords(self, high):
max = 3 + 6*len(high)
for word, n in zip(high, range(max, 3, -6)):
self.cards[10**(n-3)] = word + "ill\xf2n"
def setup(self):
lows = ["cuatr", "tr", "b", "m"]
self.high_numwords = self.gen_high_numwords([], [], lows)
self.negword = "menos "
self.pointword = "punto"
self.errmsg_nonnum = "Only numbers may be converted to words."
self.errmsg_toobig = "Number is too large to convert to words."
self.gender_stem = "o"
self.exclude_title = ["y", "menos", "punto"]
self.mid_numwords = [(1000, "mil"), (100, "cien"), (90, "noventa"),
(80, "ochenta"), (70, "setenta"), (60, "sesenta"),
(50,"cincuenta"), (40,"cuarenta")]
self.low_numwords = ["vientinueve", "vientiocho", "vientisiete",
"vientis\xE8is", "vienticinco", "vienticuatro",
"vientitr\xE8s", "vientid\xF2s", "vientiuno",
"viente", "diecinueve", "dieciocho", "diecisiete",
"dieciseis", "quince", "catorce", "trece", "doce",
"once", "diez", "nueve", "ocho", "siete", "seis",
"cinco", "cuatro", "tres", "dos", "uno", "cero"]
self.ords = { 1 : "primer",
2 : "segund",
3 : "tercer",
4 : "cuart",
5 : "quint",
6 : "sext",
7 : "s\xE8ptim",
8 : "octav",
9 : "noven",
10 : "d\xE8cim" }
def merge(self, curr, next):
ctext, cnum, ntext, nnum = curr + next
if cnum == 1:
if nnum < 1000000:
return next
ctext = "un"
elif cnum == 100:
ctext += "t" + self.gender_stem
if nnum < cnum:
if cnum < 100:
return ("%s y %s"%(ctext, ntext), cnum + nnum)
return ("%s %s"%(ctext, ntext), cnum + nnum)
elif (not nnum % 1000000) and cnum > 1:
ntext = ntext[:-3] + "ones"
if nnum == 100:
if cnum == 5:
ctext = "quinien"
ntext = ""
elif cnum == 7:
ctext = "sete"
elif cnum == 9:
ctext = "nove"
ntext += "t" + self.gender_stem + "s"
else:
ntext = " " + ntext
return (ctext + ntext, cnum * nnum)
def to_ordinal(self, value):
self.verify_ordinal(value)
try:
return self.ords[value] + self.gender_stem
except KeyError:
return self.to_cardinal(value)
def to_ordinal_num(self, value):
self.verify_ordinal(value)
# Correct for fem?
return "%s\xB0"%value
def to_currency(self, val, longval=True, old=False):
if old:
return self.to_splitnum(val, hightxt="peso/s", lowtxt="peseta/s",
divisor=1000, jointxt="y", longval=longval)
return super(Num2Word_ES, self).to_currency(val, jointxt="y",
longval=longval)
n2w = Num2Word_ES()
to_card = n2w.to_cardinal
to_ord = n2w.to_ordinal
to_ordnum = n2w.to_ordinal_num
def main():
for val in [ 1, 11, 12, 21, 31, 33, 71, 80, 81, 91, 99, 100, 101, 102, 155,
180, 300, 308, 832, 1000, 1001, 1061, 1100, 1500, 1701, 3000,
8280, 8291, 150000, 500000, 1000000, 2000000, 2000001,
-21212121211221211111, -2.121212, -1.0000100]:
n2w.test(val)
n2w.test(1325325436067876801768700107601001012212132143210473207540327057320957032975032975093275093275093270957329057320975093272950730)
print n2w.to_currency(1222)
print n2w.to_currency(1222, old=True)
print n2w.to_year(1222)
if __name__ == "__main__":
main()

40
num2words/lang_EU.py Normal file
View File

@@ -0,0 +1,40 @@
# Copyright (c) 2003, Taro Ogawa. All Rights Reserved.
# Copyright (c) 2013, Savoir-faire Linux inc. All Rights Reserved.
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
# This library 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
# Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA
from __future__ import unicode_literals
from .base import Num2Word_Base
class Num2Word_EU(Num2Word_Base):
def set_high_numwords(self, high):
max = 3 + 6*len(high)
for word, n in zip(high, range(max, 3, -6)):
self.cards[10**n] = word + "illiard"
self.cards[10**(n-3)] = word + "illion"
def base_setup(self):
lows = ["non","oct","sept","sext","quint","quadr","tr","b","m"]
units = ["", "un", "duo", "tre", "quattuor", "quin", "sex", "sept",
"octo", "novem"]
tens = ["dec", "vigint", "trigint", "quadragint", "quinquagint",
"sexagint", "septuagint", "octogint", "nonagint"]
self.high_numwords = ["cent"]+self.gen_high_numwords(units, tens, lows)
def to_currency(self, val, longval=True, jointxt=""):
return self.to_splitnum(val, hightxt="Euro/s", lowtxt="Euro cent/s",
jointxt=jointxt, longval=longval)

106
num2words/lang_FR.py Normal file
View File

@@ -0,0 +1,106 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2003, Taro Ogawa. All Rights Reserved.
# Copyright (c) 2013, Savoir-faire Linux inc. All Rights Reserved.
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
# This library 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
# Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA
from __future__ import unicode_literals
from .lang_EU import Num2Word_EU
#//TODO: error messages in French
#//TODO: ords
class Num2Word_FR(Num2Word_EU):
def setup(self):
self.negword = "moins "
self.pointword = "virgule"
self.errmsg_nonnum = "Only numbers may be converted to words."
self.errmsg_toobig = "Number is too large to convert to words."
self.exclude_title = ["et", "virgule", "moins"]
self.mid_numwords = [(1000, "mille"), (100, "cent"),
(80, "quatre-vingts"), (60, "soixante"),
(50, "cinquante"), (40, "quarante"),
(30, "trente")]
self.low_numwords = ["vingt", "dix-neuf", "dix-huit", "dix-sept",
"seize", "quinze", "quatorze", "treize", "douze",
"onze", "dix", "neuf", "huit", "sept", "six",
"cinq", "quatre", "trois", "deux", "un", "zéro"]
def merge(self, curr, next):
ctext, cnum, ntext, nnum = curr + next
if cnum == 1:
if nnum < 1000000:
return next
else:
if (not (cnum - 80)%100 or not cnum%100) and ctext[-1] == "s":
ctext = ctext[:-1]
if (cnum<1000 and nnum != 1000 and ntext[-1] != "s"
and not nnum%100):
ntext += "s"
if nnum < cnum < 100:
if nnum % 10 == 1 and cnum != 80:
return ("%s et %s"%(ctext, ntext), cnum + nnum)
return ("%s-%s"%(ctext, ntext), cnum + nnum)
elif nnum > cnum:
return ("%s %s"%(ctext, ntext), cnum * nnum)
return ("%s %s"%(ctext, ntext), cnum + nnum)
# Is this right for such things as 1001 - "mille unième" instead of
# "mille premier"?? "millième"??
def to_ordinal(self,value):
self.verify_ordinal(value)
if value == 1:
return "premier"
word = self.to_cardinal(value)
if word[-1] == "e":
word = word[:-1]
return word + "ième"
def to_ordinal_num(self, value):
self.verify_ordinal(value)
out = str(value)
out += {"1" : "er" }.get(out[-1], "me")
return out
def to_currency(self, val, longval=True, old=False):
hightxt = "Euro/s"
if old:
hightxt="franc/s"
return self.to_splitnum(val, hightxt=hightxt, lowtxt="centime/s",
jointxt="et",longval=longval)
n2w = Num2Word_FR()
to_card = n2w.to_cardinal
to_ord = n2w.to_ordinal
to_ordnum = n2w.to_ordinal_num
def main():
for val in [ 1, 11, 12, 21, 31, 33, 71, 80, 81, 91, 99, 100, 101, 102, 155,
180, 300, 308, 832, 1000, 1001, 1061, 1100, 1500, 1701, 3000,
8280, 8291, 150000, 500000, 1000000, 2000000, 2000001,
-21212121211221211111, -2.121212, -1.0000100]:
n2w.test(val)
n2w.test(1325325436067876801768700107601001012212132143210473207540327057320957032975032975093275093275093270957329057320975093272950730)
print n2w.to_currency(112121)
print n2w.to_year(1996)
if __name__ == "__main__":
main()

254
num2words/lang_LT.py Normal file
View File

@@ -0,0 +1,254 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2003, Taro Ogawa. All Rights Reserved.
# Copyright (c) 2013, Savoir-faire Linux inc. All Rights Reserved.
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
# This library 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
# Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA
u"""
>>> from textwrap import fill
>>> ' '.join([str(i) for i in splitby3('1')])
'1'
>>> ' '.join([str(i) for i in splitby3('1123')])
'1 123'
>>> ' '.join([str(i) for i in splitby3('1234567890')])
'1 234 567 890'
>>> print(' '.join([n2w(i) for i in range(10)]))
nulis vienas du trys keturi penki šeši septyni aštuoni devyni
>>> print(fill(' '.join([n2w(i+10) for i in range(10)])))
dešimt vienuolika dvylika trylika keturiolika penkiolika šešiolika
septyniolika aštuoniolika devyniolika
>>> print(fill(' '.join([n2w(i*10) for i in range(10)])))
nulis dešimt dvidešimt trisdešimt keturiasdešimt penkiasdešimt
šešiasdešimt septyniasdešimt aštuoniasdešimt devyniasdešimt
>>> print(n2w(100))
šimtas
>>> print(n2w(101))
šimtas vienas
>>> print(n2w(110))
šimtas dešimt
>>> print(n2w(115))
šimtas penkiolika
>>> print(n2w(123))
šimtas dvidešimt trys
>>> print(n2w(1000))
tūkstantis
>>> print(n2w(1001))
tūkstantis vienas
>>> print(n2w(2012))
du tūkstančiai dvylika
>>> print(fill(n2w(1234567890)))
milijardas du šimtai trisdešimt keturi milijonai penki šimtai
šešiasdešimt septyni tūkstančiai aštuoni šimtai devyniasdešimt
>>> print(fill(n2w(215461407892039002157189883901676)))
du šimtai penkiolika naintilijonų keturi šimtai šešiasdešimt vienas
oktilijonas keturi šimtai septyni septilijonai aštuoni šimtai
devyniasdešimt du sikstilijonai trisdešimt devyni kvintilijonai du
kvadrilijonai šimtas penkiasdešimt septyni trilijonai šimtas
aštuoniasdešimt devyni milijardai aštuoni šimtai aštuoniasdešimt trys
milijonai devyni šimtai vienas tūkstantis šeši šimtai septyniasdešimt
šeši
>>> print(fill(n2w(719094234693663034822824384220291)))
septyni šimtai devyniolika naintilijonų devyniasdešimt keturi
oktilijonai du šimtai trisdešimt keturi septilijonai šeši šimtai
devyniasdešimt trys sikstilijonai šeši šimtai šešiasdešimt trys
kvintilijonai trisdešimt keturi kvadrilijonai aštuoni šimtai dvidešimt
du trilijonai aštuoni šimtai dvidešimt keturi milijardai trys šimtai
aštuoniasdešimt keturi milijonai du šimtai dvidešimt tūkstančių du
šimtai devyniasdešimt vienas
# TODO: fix this:
>>> print(fill(n2w(1000000000000000000000000000000)))
naintilijonas
>>> print(to_currency(1.0, 'LTL'))
vienas litas, nulis centų
>>> print(to_currency(1234.56, 'LTL'))
tūkstantis du šimtai trisdešimt keturi litai, penkiasdešimt šeši centai
>>> print(to_currency(-1251985, cents = False))
minus dvylika tūkstančių penki šimtai devyniolika litų, 85 centai
"""
from __future__ import unicode_literals
ZERO = (u'nulis',)
ONES = {
1: (u'vienas',),
2: (u'du',),
3: (u'trys',),
4: (u'keturi',),
5: (u'penki',),
6: (u'šeši',),
7: (u'septyni',),
8: (u'aštuoni',),
9: (u'devyni',),
}
TENS = {
0: (u'dešimt',),
1: (u'vienuolika',),
2: (u'dvylika',),
3: (u'trylika',),
4: (u'keturiolika',),
5: (u'penkiolika',),
6: (u'šešiolika',),
7: (u'septyniolika',),
8: (u'aštuoniolika',),
9: (u'devyniolika',),
}
TWENTIES = {
2: (u'dvidešimt',),
3: (u'trisdešimt',),
4: (u'keturiasdešimt',),
5: (u'penkiasdešimt',),
6: (u'šešiasdešimt',),
7: (u'septyniasdešimt',),
8: (u'aštuoniasdešimt',),
9: (u'devyniasdešimt',),
}
HUNDRED = (u'šimtas', u'šimtai')
THOUSANDS = {
1: (u'tūkstantis', u'tūkstančiai', u'tūkstančių'),
2: (u'milijonas', u'milijonai', u'milijonų'),
3: (u'milijardas', u'milijardai', u'milijardų'),
4: (u'trilijonas', u'trilijonai', u'trilijonų'),
5: (u'kvadrilijonas', u'kvadrilijonai', u'kvadrilijonų'),
6: (u'kvintilijonas', u'kvintilijonai', u'kvintilijonų'),
7: (u'sikstilijonas', u'sikstilijonai', u'sikstilijonų'),
8: (u'septilijonas', u'septilijonai', u'septilijonų'),
9: (u'oktilijonas', u'oktilijonai', u'oktilijonų'),
10: (u'naintilijonas', u'naintilijonai', u'naintilijonų'),
}
CURRENCIES = {
'LTL': ((u'litas', u'litai', u'litų'), (u'centas', u'centai', u'centų')),
}
def splitby3(n):
length = len(n)
if length > 3:
start = length % 3
if start > 0:
yield int(n[:start])
for i in range(start, length, 3):
yield int(n[i:i+3])
else:
yield int(n)
def get_digits(n):
return [int(x) for x in reversed(list(('%03d' % n)[-3:]))]
def pluralize(n, forms):
n1, n2, n3 = get_digits(n)
if n2 == 1 or n1 == 0 or n == 0:
return forms[2]
elif n1 == 1:
return forms[0]
else:
return forms[1]
def int2word(n):
if n == 0:
return ZERO[0]
words = []
chunks = list(splitby3(str(n)))
i = len(chunks)
for x in chunks:
i -= 1
n1, n2, n3 = get_digits(x)
if n3 > 0:
if n3 > 1:
words.append(ONES[n3][0])
words.append(HUNDRED[1])
else:
words.append(HUNDRED[0])
if n2 > 1:
words.append(TWENTIES[n2][0])
if n2 == 1:
words.append(TENS[n1][0])
elif n1 > 0 and not (i > 0 and x == 1):
words.append(ONES[n1][0])
if i > 0:
words.append(pluralize(x, THOUSANDS[i]))
return ' '.join(words)
def n2w(n):
n = str(n).replace(',', '.')
if '.' in n:
left, right = n.split('.')
return u'%s kablelis %s' % (int2word(int(left)), int2word(int(right)))
else:
return int2word(int(n))
def to_currency(n, currency='LTL', cents = True):
if type(n) == int:
if n < 0:
minus = True
else:
minus = False
n = abs(n)
left = n / 100
right = n % 100
else:
n = str(n).replace(',', '.')
if '.' in n:
left, right = n.split('.')
else:
left, right = n, 0
left, right = int(left), int(right)
minus = False
cr1, cr2 = CURRENCIES[currency]
if minus:
minus_str = "minus "
else:
minus_str = ""
if cents:
cents_str = int2word(right)
else:
cents_str = "%02d" % right
return u'%s%s %s, %s %s' % (minus_str, int2word(left), pluralize(left, cr1),
cents_str, pluralize(right, cr2))
class Num2Word_LT(object):
def to_cardinal(self, number):
return n2w(number)
def to_ordinal(self, number):
raise NotImplementedError()
if __name__ == '__main__':
import doctest
doctest.testmod()

View File

@@ -0,0 +1,35 @@
# Copyright (c) 2003, Taro Ogawa. All Rights Reserved.
# Copyright (c) 2013, Savoir-faire Linux inc. All Rights Reserved.
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
# This library 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
# Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA
class OrderedMapping(dict):
def __init__(self, *pairs):
self.order = []
for key, val in pairs:
self[key] = val
def __setitem__(self, key, val):
if key not in self:
self.order.append(key)
super(OrderedMapping, self).__setitem__(key, val)
def __iter__(self):
for item in self.order:
yield item
def __repr__(self):
out = ["%s: %s"%(repr(item), repr(self[item])) for item in self]
out = ", ".join(out)
return "{%s}"%out