num2words2 0.1.0.dev1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. num2words2/README.md +69 -0
  2. num2words2/__init__.py +196 -0
  3. num2words2/_version.py +3 -0
  4. num2words2/base.py +377 -0
  5. num2words2/compat.py +29 -0
  6. num2words2/currency.py +60 -0
  7. num2words2/lang_AF.py +202 -0
  8. num2words2/lang_AM.py +193 -0
  9. num2words2/lang_AR.py +435 -0
  10. num2words2/lang_AS.py +119 -0
  11. num2words2/lang_AZ.py +208 -0
  12. num2words2/lang_BA.py +119 -0
  13. num2words2/lang_BE.py +356 -0
  14. num2words2/lang_BG.py +393 -0
  15. num2words2/lang_BN.py +242 -0
  16. num2words2/lang_BO.py +122 -0
  17. num2words2/lang_BR.py +135 -0
  18. num2words2/lang_BS.py +189 -0
  19. num2words2/lang_CA.py +476 -0
  20. num2words2/lang_CE.py +546 -0
  21. num2words2/lang_CS.py +260 -0
  22. num2words2/lang_CY.py +574 -0
  23. num2words2/lang_DA.py +170 -0
  24. num2words2/lang_DE.py +180 -0
  25. num2words2/lang_EL.py +338 -0
  26. num2words2/lang_EN.py +118 -0
  27. num2words2/lang_EN_IN.py +26 -0
  28. num2words2/lang_EN_NG.py +45 -0
  29. num2words2/lang_EO.py +130 -0
  30. num2words2/lang_ES.py +399 -0
  31. num2words2/lang_ES_CO.py +37 -0
  32. num2words2/lang_ES_CR.py +38 -0
  33. num2words2/lang_ES_GT.py +37 -0
  34. num2words2/lang_ES_NI.py +35 -0
  35. num2words2/lang_ES_VE.py +43 -0
  36. num2words2/lang_ET.py +393 -0
  37. num2words2/lang_EU.py +158 -0
  38. num2words2/lang_EUR.py +104 -0
  39. num2words2/lang_FA.py +187 -0
  40. num2words2/lang_FI.py +759 -0
  41. num2words2/lang_FO.py +136 -0
  42. num2words2/lang_FR.py +133 -0
  43. num2words2/lang_FR_BE.py +49 -0
  44. num2words2/lang_FR_CH.py +48 -0
  45. num2words2/lang_FR_DZ.py +35 -0
  46. num2words2/lang_GL.py +135 -0
  47. num2words2/lang_GU.py +172 -0
  48. num2words2/lang_HA.py +293 -0
  49. num2words2/lang_HAW.py +144 -0
  50. num2words2/lang_HE.py +314 -0
  51. num2words2/lang_HI.py +243 -0
  52. num2words2/lang_HR.py +304 -0
  53. num2words2/lang_HT.py +136 -0
  54. num2words2/lang_HU.py +189 -0
  55. num2words2/lang_HY.py +364 -0
  56. num2words2/lang_ID.py +242 -0
  57. num2words2/lang_IS.py +208 -0
  58. num2words2/lang_IT.py +265 -0
  59. num2words2/lang_JA.py +615 -0
  60. num2words2/lang_JW.py +136 -0
  61. num2words2/lang_KA.py +169 -0
  62. num2words2/lang_KK.py +136 -0
  63. num2words2/lang_KM.py +173 -0
  64. num2words2/lang_KN.py +182 -0
  65. num2words2/lang_KO.py +175 -0
  66. num2words2/lang_KZ.py +161 -0
  67. num2words2/lang_LA.py +135 -0
  68. num2words2/lang_LB.py +135 -0
  69. num2words2/lang_LN.py +136 -0
  70. num2words2/lang_LO.py +149 -0
  71. num2words2/lang_LT.py +283 -0
  72. num2words2/lang_LV.py +266 -0
  73. num2words2/lang_MG.py +143 -0
  74. num2words2/lang_MI.py +151 -0
  75. num2words2/lang_MK.py +136 -0
  76. num2words2/lang_ML.py +159 -0
  77. num2words2/lang_MN.py +319 -0
  78. num2words2/lang_MR.py +187 -0
  79. num2words2/lang_MS.py +293 -0
  80. num2words2/lang_MT.py +159 -0
  81. num2words2/lang_MY.py +152 -0
  82. num2words2/lang_NE.py +157 -0
  83. num2words2/lang_NL.py +204 -0
  84. num2words2/lang_NN.py +136 -0
  85. num2words2/lang_NO.py +143 -0
  86. num2words2/lang_OC.py +135 -0
  87. num2words2/lang_PA.py +159 -0
  88. num2words2/lang_PL.py +336 -0
  89. num2words2/lang_PS.py +136 -0
  90. num2words2/lang_PT.py +280 -0
  91. num2words2/lang_PT_BR.py +224 -0
  92. num2words2/lang_RO.py +183 -0
  93. num2words2/lang_RU.py +570 -0
  94. num2words2/lang_SA.py +136 -0
  95. num2words2/lang_SD.py +136 -0
  96. num2words2/lang_SI.py +152 -0
  97. num2words2/lang_SK.py +250 -0
  98. num2words2/lang_SL.py +306 -0
  99. num2words2/lang_SN.py +447 -0
  100. num2words2/lang_SO.py +136 -0
  101. num2words2/lang_SQ.py +252 -0
  102. num2words2/lang_SR.py +254 -0
  103. num2words2/lang_SU.py +136 -0
  104. num2words2/lang_SV.py +169 -0
  105. num2words2/lang_SW.py +219 -0
  106. num2words2/lang_TA.py +303 -0
  107. num2words2/lang_TE.py +169 -0
  108. num2words2/lang_TET.py +285 -0
  109. num2words2/lang_TG.py +149 -0
  110. num2words2/lang_TH.py +214 -0
  111. num2words2/lang_TK.py +136 -0
  112. num2words2/lang_TL.py +145 -0
  113. num2words2/lang_TR.py +882 -0
  114. num2words2/lang_TT.py +136 -0
  115. num2words2/lang_UK.py +1101 -0
  116. num2words2/lang_UR.py +168 -0
  117. num2words2/lang_UZ.py +136 -0
  118. num2words2/lang_VI.py +143 -0
  119. num2words2/lang_WO.py +136 -0
  120. num2words2/lang_YI.py +135 -0
  121. num2words2/lang_YO.py +136 -0
  122. num2words2/lang_ZH.py +296 -0
  123. num2words2/lang_ZH_CN.py +85 -0
  124. num2words2/lang_ZH_HK.py +48 -0
  125. num2words2/lang_ZH_TW.py +198 -0
  126. num2words2/utils.py +35 -0
  127. num2words2-0.1.0.dev1.data/scripts/num2words2 +98 -0
  128. num2words2-0.1.0.dev1.dist-info/METADATA +244 -0
  129. num2words2-0.1.0.dev1.dist-info/RECORD +133 -0
  130. num2words2-0.1.0.dev1.dist-info/WHEEL +5 -0
  131. num2words2-0.1.0.dev1.dist-info/entry_points.txt +2 -0
  132. num2words2-0.1.0.dev1.dist-info/licenses/COPYING +145 -0
  133. num2words2-0.1.0.dev1.dist-info/top_level.txt +1 -0
num2words2/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # Add new language
2
+
3
+ for each new language you must create a file `lang_NN.py` where `NN` is the
4
+ ISO 639-1 or ISO 639-3 [language code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes).
5
+
6
+ This class must be a subclass of `Num2Word_EUR` and implement at least the following methods
7
+
8
+ ```
9
+ to_cardinal(self, number)
10
+ to_ordinal(self, number)
11
+ ```
12
+
13
+ To integrate your language into the `num2words` module, add the name of your file
14
+ to the import list in [num2words/__init__.py](num2words/__init__.py) (top of the file),
15
+ and `'nn': lang_NN.Num2Word_NN()` to the `CONVERTER_CLASSES` list in the same file.
16
+ Do not forget to remplace `NN` by the appropriate ISO 639 language code.
17
+
18
+ The following is a template for a new language class
19
+
20
+ ```
21
+ from .lang_EUR import Num2Word_EUR
22
+
23
+ class Num2Word_CY(Num2Word_EUR):
24
+ def setup(self):
25
+ Num2Word_EUR.setup(self)
26
+
27
+ def __init__(self):
28
+ pass
29
+
30
+ def to_ordinal(self, number):
31
+ # implement here your code. number is the integer to be transformed into an ordinal
32
+ # as a word (str)
33
+ # which is returned
34
+ return "NOT IMPLEMENTED"
35
+
36
+ def to_cardinal(self, number):
37
+ # implement here your code. number is the integer to be transformed into an cardinal
38
+ # as a word (str)
39
+ # which is returned
40
+ return "NOT IMPLEMENTED"
41
+ ```
42
+
43
+ You can use as manu auxiliary methods as you need to make your code efficient and readable.
44
+ If you need further options like Gender, Formal/Informal, add those parameters to the methods,
45
+ e.g.
46
+
47
+ ```
48
+ def to_ordinal(self, number, gender="fem", informal=True)
49
+ # your code
50
+ pass
51
+ ```
52
+
53
+ More inspiration can be found in existing `num2words/lang_NN.py` files
54
+
55
+ ## Code validation
56
+
57
+ In order to get your contribution merged into the main project, your code must test the validation tests.
58
+ For this install the packages needed to test
59
+
60
+ ```
61
+ pip install -r requirements-test.txt
62
+ ```
63
+
64
+ run `tox` and `coverage` to check that the code is well formated and all parts of the code are tested
65
+
66
+ ```
67
+ tox
68
+ python3 -m coverage report -m
69
+ ```
num2words2/__init__.py ADDED
@@ -0,0 +1,196 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright (c) 2003, Taro Ogawa. All Rights Reserved.
3
+ # Copyright (c) 2013, Savoir-faire Linux inc. All Rights Reserved.
4
+
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License as published by the Free Software Foundation; either
8
+ # version 2.1 of the License, or (at your option) any later version.
9
+ # This library is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ # You should have received a copy of the GNU Lesser General Public
14
+ # License along with this library; if not, write to the Free Software
15
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
16
+ # MA 02110-1301 USA
17
+
18
+ from __future__ import unicode_literals
19
+
20
+ from . import (lang_AF, lang_AM, lang_AR, lang_AS, lang_AZ, lang_BA, lang_BE,
21
+ lang_BG, lang_BN, lang_BO, lang_BR, lang_BS, lang_CA, lang_CE,
22
+ lang_CS, lang_CY, lang_DA, lang_DE, lang_EL, lang_EN,
23
+ lang_EN_IN, lang_EN_NG, lang_EO, lang_ES, lang_ES_CO,
24
+ lang_ES_CR, lang_ES_GT, lang_ES_NI, lang_ES_VE, lang_ET,
25
+ lang_EU, lang_FA, lang_FI, lang_FO, lang_FR, lang_FR_BE,
26
+ lang_FR_CH, lang_FR_DZ, lang_GL, lang_GU, lang_HA, lang_HAW,
27
+ lang_HE, lang_HI, lang_HR, lang_HT, lang_HU, lang_HY, lang_ID,
28
+ lang_IS, lang_IT, lang_JA, lang_JW, lang_KA, lang_KK, lang_KM,
29
+ lang_KN, lang_KO, lang_KZ, lang_LA, lang_LB, lang_LN, lang_LO,
30
+ lang_LT, lang_LV, lang_MG, lang_MI, lang_MK, lang_ML, lang_MN,
31
+ lang_MR, lang_MS, lang_MT, lang_MY, lang_NE, lang_NL, lang_NN,
32
+ lang_NO, lang_OC, lang_PA, lang_PL, lang_PS, lang_PT,
33
+ lang_PT_BR, lang_RO, lang_RU, lang_SA, lang_SD, lang_SI,
34
+ lang_SK, lang_SL, lang_SN, lang_SO, lang_SQ, lang_SR, lang_SU,
35
+ lang_SV, lang_SW, lang_TA, lang_TE, lang_TET, lang_TG, lang_TH,
36
+ lang_TK, lang_TL, lang_TR, lang_TT, lang_UK, lang_UR, lang_UZ,
37
+ lang_VI, lang_WO, lang_YI, lang_YO, lang_ZH_CN, lang_ZH_HK,
38
+ lang_ZH_TW)
39
+
40
+ # Version information
41
+ try:
42
+ from ._version import __version__, __version_tuple__
43
+ except ImportError:
44
+ # Package is not installed, provide defaults
45
+ __version__ = "unknown"
46
+ __version_tuple__ = (0, 0, 0, "unknown", 0)
47
+
48
+
49
+ CONVERTER_CLASSES = {
50
+ 'af': lang_AF.Num2Word_AF(),
51
+ 'am': lang_AM.Num2Word_AM(),
52
+ 'ar': lang_AR.Num2Word_AR(),
53
+ 'az': lang_AZ.Num2Word_AZ(),
54
+ 'be': lang_BE.Num2Word_BE(),
55
+ 'bg': lang_BG.Num2Word_BG(),
56
+ 'bn': lang_BN.Num2Word_BN(),
57
+ 'ca': lang_CA.Num2Word_CA(),
58
+ 'ce': lang_CE.Num2Word_CE(),
59
+ 'cs': lang_CS.Num2Word_CS(),
60
+ 'cy': lang_CY.Num2Word_CY(),
61
+ 'da': lang_DA.Num2Word_DA(),
62
+ 'de': lang_DE.Num2Word_DE(),
63
+ 'el': lang_EL.Num2Word_EL(),
64
+ 'en': lang_EN.Num2Word_EN(),
65
+ 'en_IN': lang_EN_IN.Num2Word_EN_IN(),
66
+ 'en_NG': lang_EN_NG.Num2Word_EN_NG(),
67
+ 'eo': lang_EO.Num2Word_EO(),
68
+ 'es': lang_ES.Num2Word_ES(),
69
+ 'es_CO': lang_ES_CO.Num2Word_ES_CO(),
70
+ 'es_CR': lang_ES_CR.Num2Word_ES_CR(),
71
+ 'es_GT': lang_ES_GT.Num2Word_ES_GT(),
72
+ 'es_NI': lang_ES_NI.Num2Word_ES_NI(),
73
+ 'es_VE': lang_ES_VE.Num2Word_ES_VE(),
74
+ 'et': lang_ET.Num2Word_ET(),
75
+ 'fa': lang_FA.Num2Word_FA(),
76
+ 'fi': lang_FI.Num2Word_FI(),
77
+ 'fr': lang_FR.Num2Word_FR(),
78
+ 'fr_BE': lang_FR_BE.Num2Word_FR_BE(),
79
+ 'fr_CH': lang_FR_CH.Num2Word_FR_CH(),
80
+ 'fr_DZ': lang_FR_DZ.Num2Word_FR_DZ(),
81
+ 'ha': lang_HA.Num2Word_HA(),
82
+ 'he': lang_HE.Num2Word_HE(),
83
+ 'hi': lang_HI.Num2Word_HI(),
84
+ 'hr': lang_HR.Num2Word_HR(),
85
+ 'hu': lang_HU.Num2Word_HU(),
86
+ 'hy': lang_HY.Num2Word_HY(),
87
+ 'id': lang_ID.Num2Word_ID(),
88
+ 'is': lang_IS.Num2Word_IS(),
89
+ 'it': lang_IT.Num2Word_IT(),
90
+ 'ja': lang_JA.Num2Word_JA(),
91
+ 'kn': lang_KN.Num2Word_KN(),
92
+ 'ko': lang_KO.Num2Word_KO(),
93
+ 'kz': lang_KZ.Num2Word_KZ(),
94
+ 'lt': lang_LT.Num2Word_LT(),
95
+ 'lv': lang_LV.Num2Word_LV(),
96
+ 'mn': lang_MN.Num2Word_MN(),
97
+ 'ms': lang_MS.Num2Word_MS(),
98
+ 'nl': lang_NL.Num2Word_NL(),
99
+ 'no': lang_NO.Num2Word_NO(),
100
+ 'pl': lang_PL.Num2Word_PL(),
101
+ 'pt': lang_PT.Num2Word_PT(),
102
+ 'pt_BR': lang_PT_BR.Num2Word_PT_BR(),
103
+ 'ro': lang_RO.Num2Word_RO(),
104
+ 'ru': lang_RU.Num2Word_RU(),
105
+ 'sk': lang_SK.Num2Word_SK(),
106
+ 'sl': lang_SL.Num2Word_SL(),
107
+ 'sn': lang_SN.Num2Word_SN(),
108
+ 'sq': lang_SQ.Num2Word_SQ(),
109
+ 'sr': lang_SR.Num2Word_SR(),
110
+ 'sv': lang_SV.Num2Word_SV(),
111
+ 'sw': lang_SW.Num2Word_SW(),
112
+ 'ta': lang_TA.Num2Word_TA(),
113
+ 'te': lang_TE.Num2Word_TE(),
114
+ 'tet': lang_TET.Num2Word_TET(),
115
+ 'tg': lang_TG.Num2Word_TG(),
116
+ 'th': lang_TH.Num2Word_TH(),
117
+ 'tr': lang_TR.Num2Word_TR(),
118
+ 'uk': lang_UK.Num2Word_UK(),
119
+ 'vi': lang_VI.Num2Word_VI(),
120
+ 'zh': lang_ZH_CN.Num2Word_ZH_CN(), # Default 'zh' to simplified Chinese
121
+ 'zh_CN': lang_ZH_CN.Num2Word_ZH_CN(),
122
+ 'zh_HK': lang_ZH_HK.Num2Word_ZH_HK(),
123
+ 'zh_TW': lang_ZH_TW.Num2Word_ZH_TW(),
124
+ 'as': lang_AS.Num2Word_AS(),
125
+ 'ba': lang_BA.Num2Word_BA(),
126
+ 'bo': lang_BO.Num2Word_BO(),
127
+ 'br': lang_BR.Num2Word_BR(),
128
+ 'bs': lang_BS.Num2Word_BS(),
129
+ 'eu': lang_EU.Num2Word_EU(),
130
+ 'fo': lang_FO.Num2Word_FO(),
131
+ 'gl': lang_GL.Num2Word_GL(),
132
+ 'gu': lang_GU.Num2Word_GU(),
133
+ 'haw': lang_HAW.Num2Word_HAW(),
134
+ 'ht': lang_HT.Num2Word_HT(),
135
+ 'jw': lang_JW.Num2Word_JW(),
136
+ 'ka': lang_KA.Num2Word_KA(),
137
+ 'kk': lang_KK.Num2Word_KK(),
138
+ 'km': lang_KM.Num2Word_KM(),
139
+ 'la': lang_LA.Num2Word_LA(),
140
+ 'lb': lang_LB.Num2Word_LB(),
141
+ 'ln': lang_LN.Num2Word_LN(),
142
+ 'lo': lang_LO.Num2Word_LO(),
143
+ 'mg': lang_MG.Num2Word_MG(),
144
+ 'mi': lang_MI.Num2Word_MI(),
145
+ 'mk': lang_MK.Num2Word_MK(),
146
+ 'ml': lang_ML.Num2Word_ML(),
147
+ 'mr': lang_MR.Num2Word_MR(),
148
+ 'mt': lang_MT.Num2Word_MT(),
149
+ 'my': lang_MY.Num2Word_MY(),
150
+ 'ne': lang_NE.Num2Word_NE(),
151
+ 'nn': lang_NN.Num2Word_NN(),
152
+ 'oc': lang_OC.Num2Word_OC(),
153
+ 'pa': lang_PA.Num2Word_PA(),
154
+ 'ps': lang_PS.Num2Word_PS(),
155
+ 'sa': lang_SA.Num2Word_SA(),
156
+ 'sd': lang_SD.Num2Word_SD(),
157
+ 'si': lang_SI.Num2Word_SI(),
158
+ 'so': lang_SO.Num2Word_SO(),
159
+ 'su': lang_SU.Num2Word_SU(),
160
+ 'tk': lang_TK.Num2Word_TK(),
161
+ 'tl': lang_TL.Num2Word_TL(),
162
+ 'tt': lang_TT.Num2Word_TT(),
163
+ 'ur': lang_UR.Num2Word_UR(),
164
+ 'uz': lang_UZ.Num2Word_UZ(),
165
+ 'wo': lang_WO.Num2Word_WO(),
166
+ 'yi': lang_YI.Num2Word_YI(),
167
+ 'yo': lang_YO.Num2Word_YO(),
168
+ # Language code aliases
169
+ 'jp': lang_JA.Num2Word_JA(), # Alias for Japanese
170
+ 'cn': lang_ZH_CN.Num2Word_ZH_CN(), # Alias for Chinese (Simplified)
171
+ }
172
+
173
+ CONVERTES_TYPES = ['cardinal', 'ordinal', 'ordinal_num', 'year', 'currency']
174
+ CONVERTER_TYPES = CONVERTES_TYPES # Alias for compatibility
175
+
176
+
177
+ def num2words(number, ordinal=False, lang='en', to='cardinal', **kwargs):
178
+ # We try the full language first
179
+ if lang not in CONVERTER_CLASSES:
180
+ # ... and then try only the first 2 letters
181
+ lang = lang[:2]
182
+ if lang not in CONVERTER_CLASSES:
183
+ raise NotImplementedError()
184
+ converter = CONVERTER_CLASSES[lang]
185
+
186
+ if isinstance(number, str):
187
+ number = converter.str_to_number(number)
188
+
189
+ # backwards compatible
190
+ if ordinal:
191
+ to = 'ordinal'
192
+
193
+ if to not in CONVERTES_TYPES:
194
+ raise NotImplementedError()
195
+
196
+ return getattr(converter, 'to_{}'.format(to))(number, **kwargs)
num2words2/_version.py ADDED
@@ -0,0 +1,3 @@
1
+ # Auto-generated version file
2
+ __version__ = "0.1.0.dev1"
3
+ __version_tuple__ = (0, 1, 0, 'dev1')
num2words2/base.py ADDED
@@ -0,0 +1,377 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright (c) 2003, Taro Ogawa. All Rights Reserved.
3
+ # Copyright (c) 2013, Savoir-faire Linux inc. All Rights Reserved.
4
+
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License as published by the Free Software Foundation; either
8
+ # version 2.1 of the License, or (at your option) any later version.
9
+ # This library is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ # You should have received a copy of the GNU Lesser General Public
14
+ # License along with this library; if not, write to the Free Software
15
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
16
+ # MA 02110-1301 USA
17
+
18
+ from __future__ import unicode_literals
19
+
20
+ import math
21
+ from collections import OrderedDict
22
+ from decimal import Decimal
23
+
24
+ from .compat import to_s
25
+ from .currency import parse_currency_parts, prefix_currency
26
+
27
+
28
+ class Num2Word_Base(object):
29
+ CURRENCY_FORMS = {}
30
+ CURRENCY_ADJECTIVES = {}
31
+
32
+ def __init__(self):
33
+ self.is_title = False
34
+ self.precision = 2
35
+ self.exclude_title = []
36
+ self.negword = "(-) "
37
+ self.pointword = "(.)"
38
+ self.errmsg_nonnum = "type(%s) not in [long, int, float]"
39
+ self.errmsg_floatord = "Cannot treat float %s as ordinal."
40
+ self.errmsg_negord = "Cannot treat negative num %s as ordinal."
41
+ self.errmsg_toobig = "abs(%s) must be less than %s."
42
+
43
+ self.setup()
44
+
45
+ # uses cards
46
+ if any(hasattr(self, field) for field in
47
+ ['high_numwords', 'mid_numwords', 'low_numwords']):
48
+ self.cards = OrderedDict()
49
+ self.set_numwords()
50
+ self.MAXVAL = 1000 * list(self.cards.keys())[0]
51
+
52
+ def set_numwords(self):
53
+ self.set_high_numwords(self.high_numwords)
54
+ self.set_mid_numwords(self.mid_numwords)
55
+ self.set_low_numwords(self.low_numwords)
56
+
57
+ def set_high_numwords(self, *args):
58
+ raise NotImplementedError
59
+
60
+ def set_mid_numwords(self, mid):
61
+ for key, val in mid:
62
+ self.cards[key] = val
63
+
64
+ def set_low_numwords(self, numwords):
65
+ for word, n in zip(numwords, range(len(numwords) - 1, -1, -1)):
66
+ self.cards[n] = word
67
+
68
+ def splitnum(self, value):
69
+ for elem in self.cards:
70
+ if elem > value:
71
+ continue
72
+
73
+ out = []
74
+ if value == 0:
75
+ div, mod = 1, 0
76
+ else:
77
+ div, mod = divmod(value, elem)
78
+
79
+ if div == 1:
80
+ out.append((self.cards[1], 1))
81
+ else:
82
+ if div == value: # The system tallies, eg Roman Numerals
83
+ return [(div * self.cards[elem], div * elem)]
84
+ out.append(self.splitnum(div))
85
+
86
+ out.append((self.cards[elem], elem))
87
+
88
+ if mod:
89
+ out.append(self.splitnum(mod))
90
+
91
+ return out
92
+
93
+ def parse_minus(self, num_str):
94
+ """Detach minus and return it as symbol with new num_str."""
95
+ if num_str.startswith('-'):
96
+ # Extra spacing to compensate if there is no minus.
97
+ return '%s ' % self.negword.strip(), num_str[1:]
98
+ return '', num_str
99
+
100
+ def str_to_number(self, value):
101
+ return Decimal(value)
102
+
103
+ def to_cardinal(self, value):
104
+ try:
105
+ assert int(value) == value
106
+ except (ValueError, TypeError, AssertionError):
107
+ return self.to_cardinal_float(value)
108
+
109
+ out = ""
110
+ if value < 0:
111
+ value = abs(value)
112
+ out = "%s " % self.negword.strip()
113
+
114
+ if value >= self.MAXVAL:
115
+ raise OverflowError(self.errmsg_toobig % (value, self.MAXVAL))
116
+
117
+ val = self.splitnum(value)
118
+ words, num = self.clean(val)
119
+ return self.title(out + words)
120
+
121
+ def float2tuple(self, value):
122
+ pre = int(value)
123
+
124
+ # Simple way of finding decimal places to update the precision
125
+ self.precision = abs(Decimal(str(value)).as_tuple().exponent)
126
+
127
+ post = abs(value - pre) * 10**self.precision
128
+ if abs(round(post) - post) < 0.01:
129
+ # We generally floor all values beyond our precision (rather than
130
+ # rounding), but in cases where we have something like 1.239999999,
131
+ # which is probably due to python's handling of floats, we actually
132
+ # want to consider it as 1.24 instead of 1.23
133
+ post = int(round(post))
134
+ else:
135
+ post = int(math.floor(post))
136
+
137
+ return pre, post
138
+
139
+ def to_cardinal_float(self, value):
140
+ try:
141
+ float(value) == value
142
+ except (ValueError, TypeError, AssertionError, AttributeError):
143
+ raise TypeError(self.errmsg_nonnum % value)
144
+
145
+ pre, post = self.float2tuple(float(value))
146
+
147
+ post = str(post)
148
+ post = '0' * (self.precision - len(post)) + post
149
+
150
+ out = [self.to_cardinal(pre)]
151
+ if value < 0 and pre == 0:
152
+ out = [self.negword.strip()] + out
153
+
154
+ if self.precision:
155
+ out.append(self.title(self.pointword))
156
+
157
+ for i in range(self.precision):
158
+ curr = int(post[i])
159
+ out.append(to_s(self.to_cardinal(curr)))
160
+
161
+ return " ".join(out)
162
+
163
+ def merge(self, curr, next):
164
+ raise NotImplementedError
165
+
166
+ def clean(self, val):
167
+ out = val
168
+ while len(val) != 1:
169
+ out = []
170
+ left, right = val[:2]
171
+ if isinstance(left, tuple) and isinstance(right, tuple):
172
+ out.append(self.merge(left, right))
173
+ if val[2:]:
174
+ out.append(val[2:])
175
+ else:
176
+ for elem in val:
177
+ if isinstance(elem, list):
178
+ if len(elem) == 1:
179
+ out.append(elem[0])
180
+ else:
181
+ out.append(self.clean(elem))
182
+ else:
183
+ out.append(elem)
184
+ val = out
185
+ return out[0]
186
+
187
+ def title(self, value):
188
+ if self.is_title:
189
+ out = []
190
+ value = value.split()
191
+ for word in value:
192
+ if word in self.exclude_title:
193
+ out.append(word)
194
+ else:
195
+ out.append(word[0].upper() + word[1:])
196
+ value = " ".join(out)
197
+ return value
198
+
199
+ def verify_ordinal(self, value):
200
+ if not value == int(value):
201
+ raise TypeError(self.errmsg_floatord % value)
202
+ if not abs(value) == value:
203
+ raise TypeError(self.errmsg_negord % value)
204
+
205
+ def to_ordinal(self, value):
206
+ return self.to_cardinal(value)
207
+
208
+ def to_ordinal_num(self, value):
209
+ return value
210
+
211
+ # Trivial version
212
+ def inflect(self, value, text):
213
+ text = text.split("/")
214
+ if value == 1:
215
+ return text[0]
216
+ return "".join(text)
217
+
218
+ # //CHECK: generalise? Any others like pounds/shillings/pence?
219
+ def to_splitnum(self, val, hightxt="", lowtxt="", jointxt="",
220
+ divisor=100, longval=True, cents=True):
221
+ out = []
222
+
223
+ if isinstance(val, float):
224
+ high, low = self.float2tuple(val)
225
+ else:
226
+ try:
227
+ high, low = val
228
+ except TypeError:
229
+ high, low = divmod(val, divisor)
230
+
231
+ if high:
232
+ hightxt = self.title(self.inflect(high, hightxt))
233
+ out.append(self.to_cardinal(high))
234
+ if low:
235
+ if longval:
236
+ if hightxt:
237
+ out.append(hightxt)
238
+ if jointxt:
239
+ out.append(self.title(jointxt))
240
+ elif hightxt:
241
+ out.append(hightxt)
242
+
243
+ if low:
244
+ if cents:
245
+ out.append(self.to_cardinal(low))
246
+ else:
247
+ out.append("%02d" % low)
248
+ if lowtxt and longval:
249
+ out.append(self.title(self.inflect(low, lowtxt)))
250
+
251
+ return " ".join(out)
252
+
253
+ def to_year(self, value, **kwargs):
254
+ return self.to_cardinal(value)
255
+
256
+ def pluralize(self, n, forms):
257
+ """
258
+ Should resolve gettext form:
259
+ http://docs.translatehouse.org/projects/localization-guide/en/latest/l10n/pluralforms.html
260
+ """
261
+ raise NotImplementedError
262
+
263
+ def _money_verbose(self, number, currency):
264
+ return self.to_cardinal(number)
265
+
266
+ def _cents_verbose(self, number, currency):
267
+ return self.to_cardinal(number)
268
+
269
+ def _cents_terse(self, number, currency):
270
+ return "%02d" % number
271
+
272
+ def to_currency(self, val, currency='EUR', cents=True, separator=',',
273
+ adjective=False):
274
+ """
275
+ Args:
276
+ val: Numeric value
277
+ currency (str): Currency code
278
+ cents (bool): Verbose cents
279
+ separator (str): Cent separator
280
+ adjective (bool): Prefix currency name with adjective
281
+ Returns:
282
+ str: Formatted string
283
+
284
+ Handles whole numbers and decimal numbers differently
285
+ """
286
+ # Handle integers separately - no cents shown
287
+ if isinstance(val, int):
288
+ try:
289
+ cr1, cr2 = self.CURRENCY_FORMS[currency]
290
+ except KeyError:
291
+ raise NotImplementedError(
292
+ 'Currency code "%s" not implemented for "%s"' %
293
+ (currency, self.__class__.__name__))
294
+
295
+ if adjective and currency in self.CURRENCY_ADJECTIVES:
296
+ cr1 = prefix_currency(self.CURRENCY_ADJECTIVES[currency], cr1)
297
+
298
+ minus_str = "%s " % self.negword.strip() if val < 0 else ""
299
+ money_str = self._money_verbose(abs(val), currency)
300
+
301
+ return u'%s%s %s' % (
302
+ minus_str,
303
+ money_str,
304
+ self.pluralize(abs(val), cr1)
305
+ )
306
+
307
+ # For floats, show full currency with cents
308
+ # Check if value has more than 2 decimal places
309
+ from decimal import Decimal
310
+ decimal_val = Decimal(str(val))
311
+ has_fractional_cents = (decimal_val * 100) % 1 != 0
312
+
313
+ left, right, is_negative = parse_currency_parts(val, is_int_with_cents=False,
314
+ keep_precision=has_fractional_cents)
315
+
316
+ try:
317
+ cr1, cr2 = self.CURRENCY_FORMS[currency]
318
+
319
+ except KeyError:
320
+ raise NotImplementedError(
321
+ 'Currency code "%s" not implemented for "%s"' %
322
+ (currency, self.__class__.__name__))
323
+
324
+ if adjective and currency in self.CURRENCY_ADJECTIVES:
325
+ cr1 = prefix_currency(self.CURRENCY_ADJECTIVES[currency], cr1)
326
+
327
+ minus_str = "%s " % self.negword.strip() if is_negative else ""
328
+ money_str = self._money_verbose(left, currency)
329
+
330
+ # Explicitly check if input has decimal point or non-zero cents
331
+ has_decimal = isinstance(val, float) or str(val).find('.') != -1
332
+
333
+ # Only include cents if:
334
+ # 1. Input has decimal point OR
335
+ # 2. Cents are non-zero
336
+ if has_decimal or (isinstance(right, Decimal) and right > 0) or (isinstance(right, int) and right > 0):
337
+ # Handle fractional cents
338
+ if isinstance(right, Decimal):
339
+ # Split into whole cents and fraction
340
+ whole_cents = int(right)
341
+ fractional_part = right - whole_cents
342
+
343
+ if fractional_part > 0:
344
+ # Convert fractional cents (e.g., 65.3 cents)
345
+ cents_str = self.to_cardinal(float(right))
346
+ return u'%s%s %s%s %s %s' % (
347
+ minus_str,
348
+ money_str,
349
+ self.pluralize(left, cr1),
350
+ separator,
351
+ cents_str,
352
+ cr2[1] if isinstance(cr2, tuple) and len(cr2) > 1 else cr2
353
+ )
354
+ else:
355
+ # No fractional part, use normal processing
356
+ right = whole_cents
357
+
358
+ cents_str = self._cents_verbose(right, currency) \
359
+ if cents else self._cents_terse(right, currency)
360
+
361
+ return u'%s%s %s%s %s %s' % (
362
+ minus_str,
363
+ money_str,
364
+ self.pluralize(left, cr1),
365
+ separator,
366
+ cents_str,
367
+ self.pluralize(right, cr2)
368
+ )
369
+ else:
370
+ return u'%s%s %s' % (
371
+ minus_str,
372
+ money_str,
373
+ self.pluralize(left, cr1)
374
+ )
375
+
376
+ def setup(self):
377
+ pass
num2words2/compat.py ADDED
@@ -0,0 +1,29 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright (c) 2003, Taro Ogawa. All Rights Reserved.
3
+ # Copyright (c) 2013, Savoir-faire Linux inc. All Rights Reserved.
4
+
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License as published by the Free Software Foundation; either
8
+ # version 2.1 of the License, or (at your option) any later version.
9
+ # This library is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ # You should have received a copy of the GNU Lesser General Public
14
+ # License along with this library; if not, write to the Free Software
15
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
16
+ # MA 02110-1301 USA
17
+
18
+
19
+ try:
20
+ strtype = basestring
21
+ except NameError:
22
+ strtype = str
23
+
24
+
25
+ def to_s(val):
26
+ try:
27
+ return unicode(val)
28
+ except NameError:
29
+ return str(val)