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.
- num2words2/README.md +69 -0
- num2words2/__init__.py +196 -0
- num2words2/_version.py +3 -0
- num2words2/base.py +377 -0
- num2words2/compat.py +29 -0
- num2words2/currency.py +60 -0
- num2words2/lang_AF.py +202 -0
- num2words2/lang_AM.py +193 -0
- num2words2/lang_AR.py +435 -0
- num2words2/lang_AS.py +119 -0
- num2words2/lang_AZ.py +208 -0
- num2words2/lang_BA.py +119 -0
- num2words2/lang_BE.py +356 -0
- num2words2/lang_BG.py +393 -0
- num2words2/lang_BN.py +242 -0
- num2words2/lang_BO.py +122 -0
- num2words2/lang_BR.py +135 -0
- num2words2/lang_BS.py +189 -0
- num2words2/lang_CA.py +476 -0
- num2words2/lang_CE.py +546 -0
- num2words2/lang_CS.py +260 -0
- num2words2/lang_CY.py +574 -0
- num2words2/lang_DA.py +170 -0
- num2words2/lang_DE.py +180 -0
- num2words2/lang_EL.py +338 -0
- num2words2/lang_EN.py +118 -0
- num2words2/lang_EN_IN.py +26 -0
- num2words2/lang_EN_NG.py +45 -0
- num2words2/lang_EO.py +130 -0
- num2words2/lang_ES.py +399 -0
- num2words2/lang_ES_CO.py +37 -0
- num2words2/lang_ES_CR.py +38 -0
- num2words2/lang_ES_GT.py +37 -0
- num2words2/lang_ES_NI.py +35 -0
- num2words2/lang_ES_VE.py +43 -0
- num2words2/lang_ET.py +393 -0
- num2words2/lang_EU.py +158 -0
- num2words2/lang_EUR.py +104 -0
- num2words2/lang_FA.py +187 -0
- num2words2/lang_FI.py +759 -0
- num2words2/lang_FO.py +136 -0
- num2words2/lang_FR.py +133 -0
- num2words2/lang_FR_BE.py +49 -0
- num2words2/lang_FR_CH.py +48 -0
- num2words2/lang_FR_DZ.py +35 -0
- num2words2/lang_GL.py +135 -0
- num2words2/lang_GU.py +172 -0
- num2words2/lang_HA.py +293 -0
- num2words2/lang_HAW.py +144 -0
- num2words2/lang_HE.py +314 -0
- num2words2/lang_HI.py +243 -0
- num2words2/lang_HR.py +304 -0
- num2words2/lang_HT.py +136 -0
- num2words2/lang_HU.py +189 -0
- num2words2/lang_HY.py +364 -0
- num2words2/lang_ID.py +242 -0
- num2words2/lang_IS.py +208 -0
- num2words2/lang_IT.py +265 -0
- num2words2/lang_JA.py +615 -0
- num2words2/lang_JW.py +136 -0
- num2words2/lang_KA.py +169 -0
- num2words2/lang_KK.py +136 -0
- num2words2/lang_KM.py +173 -0
- num2words2/lang_KN.py +182 -0
- num2words2/lang_KO.py +175 -0
- num2words2/lang_KZ.py +161 -0
- num2words2/lang_LA.py +135 -0
- num2words2/lang_LB.py +135 -0
- num2words2/lang_LN.py +136 -0
- num2words2/lang_LO.py +149 -0
- num2words2/lang_LT.py +283 -0
- num2words2/lang_LV.py +266 -0
- num2words2/lang_MG.py +143 -0
- num2words2/lang_MI.py +151 -0
- num2words2/lang_MK.py +136 -0
- num2words2/lang_ML.py +159 -0
- num2words2/lang_MN.py +319 -0
- num2words2/lang_MR.py +187 -0
- num2words2/lang_MS.py +293 -0
- num2words2/lang_MT.py +159 -0
- num2words2/lang_MY.py +152 -0
- num2words2/lang_NE.py +157 -0
- num2words2/lang_NL.py +204 -0
- num2words2/lang_NN.py +136 -0
- num2words2/lang_NO.py +143 -0
- num2words2/lang_OC.py +135 -0
- num2words2/lang_PA.py +159 -0
- num2words2/lang_PL.py +336 -0
- num2words2/lang_PS.py +136 -0
- num2words2/lang_PT.py +280 -0
- num2words2/lang_PT_BR.py +224 -0
- num2words2/lang_RO.py +183 -0
- num2words2/lang_RU.py +570 -0
- num2words2/lang_SA.py +136 -0
- num2words2/lang_SD.py +136 -0
- num2words2/lang_SI.py +152 -0
- num2words2/lang_SK.py +250 -0
- num2words2/lang_SL.py +306 -0
- num2words2/lang_SN.py +447 -0
- num2words2/lang_SO.py +136 -0
- num2words2/lang_SQ.py +252 -0
- num2words2/lang_SR.py +254 -0
- num2words2/lang_SU.py +136 -0
- num2words2/lang_SV.py +169 -0
- num2words2/lang_SW.py +219 -0
- num2words2/lang_TA.py +303 -0
- num2words2/lang_TE.py +169 -0
- num2words2/lang_TET.py +285 -0
- num2words2/lang_TG.py +149 -0
- num2words2/lang_TH.py +214 -0
- num2words2/lang_TK.py +136 -0
- num2words2/lang_TL.py +145 -0
- num2words2/lang_TR.py +882 -0
- num2words2/lang_TT.py +136 -0
- num2words2/lang_UK.py +1101 -0
- num2words2/lang_UR.py +168 -0
- num2words2/lang_UZ.py +136 -0
- num2words2/lang_VI.py +143 -0
- num2words2/lang_WO.py +136 -0
- num2words2/lang_YI.py +135 -0
- num2words2/lang_YO.py +136 -0
- num2words2/lang_ZH.py +296 -0
- num2words2/lang_ZH_CN.py +85 -0
- num2words2/lang_ZH_HK.py +48 -0
- num2words2/lang_ZH_TW.py +198 -0
- num2words2/utils.py +35 -0
- num2words2-0.1.0.dev1.data/scripts/num2words2 +98 -0
- num2words2-0.1.0.dev1.dist-info/METADATA +244 -0
- num2words2-0.1.0.dev1.dist-info/RECORD +133 -0
- num2words2-0.1.0.dev1.dist-info/WHEEL +5 -0
- num2words2-0.1.0.dev1.dist-info/entry_points.txt +2 -0
- num2words2-0.1.0.dev1.dist-info/licenses/COPYING +145 -0
- num2words2-0.1.0.dev1.dist-info/top_level.txt +1 -0
num2words2/lang_SQ.py
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
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 .base import Num2Word_Base
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Num2Word_SQ(Num2Word_Base):
|
|
24
|
+
CURRENCY_FORMS = {
|
|
25
|
+
'ALL': (('lek', 'lekë'), ('qindarkë', 'qindarkë')),
|
|
26
|
+
'EUR': (('euro', 'euro'), ('cent', 'centë')),
|
|
27
|
+
'USD': (('dollar', 'dollarë'), ('cent', 'centë')),
|
|
28
|
+
'GBP': (('paund', 'paund'), ('peni', 'pence')),
|
|
29
|
+
'CHF': (('frank zviceran', 'franka zvicer'), ('centim', 'centimë')),
|
|
30
|
+
'JPY': (('jen', 'jenë'), ('sen', 'senë')),
|
|
31
|
+
'RUB': (('rubël', 'rubla'), ('kopek', 'kopekë')),
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
def setup(self):
|
|
35
|
+
self.negword = "minus "
|
|
36
|
+
self.pointword = "presje"
|
|
37
|
+
self.exclude_title = ["e", "presje", "minus"]
|
|
38
|
+
|
|
39
|
+
self.high_numwords = ["trilion", "miliard", "milion"]
|
|
40
|
+
|
|
41
|
+
self.mid_numwords = [
|
|
42
|
+
(1000, "mijë"),
|
|
43
|
+
(100, "qind"),
|
|
44
|
+
(90, "nëntëdhjetë"),
|
|
45
|
+
(80, "tetëdhjetë"),
|
|
46
|
+
(70, "shtatëdhjetë"),
|
|
47
|
+
(60, "gjashtëdhjetë"),
|
|
48
|
+
(50, "pesëdhjetë"),
|
|
49
|
+
(40, "dyzet"),
|
|
50
|
+
(30, "tridhjetë"),
|
|
51
|
+
(20, "njëzet"),
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
self.low_numwords = [
|
|
55
|
+
"nëntëmbëdhjetë", # 19
|
|
56
|
+
"tetëmbëdhjetë", # 18
|
|
57
|
+
"shtatëmbëdhjetë", # 17
|
|
58
|
+
"gjashtëmbëdhjetë", # 16
|
|
59
|
+
"pesëmbëdhjetë", # 15
|
|
60
|
+
"katërmbëdhjetë", # 14
|
|
61
|
+
"trembëdhjetë", # 13
|
|
62
|
+
"dymbëdhjetë", # 12
|
|
63
|
+
"njëmbëdhjetë", # 11
|
|
64
|
+
"dhjetë", # 10
|
|
65
|
+
"nëntë", # 9
|
|
66
|
+
"tetë", # 8
|
|
67
|
+
"shtatë", # 7
|
|
68
|
+
"gjashtë", # 6
|
|
69
|
+
"pesë", # 5
|
|
70
|
+
"katër", # 4
|
|
71
|
+
"tre", # 3
|
|
72
|
+
"dy", # 2
|
|
73
|
+
"një", # 1
|
|
74
|
+
"zero" # 0
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
# Ordinal forms
|
|
78
|
+
self.ordinal_forms = {
|
|
79
|
+
0: "i zeroi",
|
|
80
|
+
1: "i pari",
|
|
81
|
+
2: "i dyti",
|
|
82
|
+
3: "i treti",
|
|
83
|
+
4: "i katërti",
|
|
84
|
+
5: "i pesti",
|
|
85
|
+
6: "i gjashti",
|
|
86
|
+
7: "i shtati",
|
|
87
|
+
8: "i teti",
|
|
88
|
+
9: "i nënti",
|
|
89
|
+
10: "i dhjeti",
|
|
90
|
+
20: "i njezeti",
|
|
91
|
+
100: "i njëqindi",
|
|
92
|
+
1000: "i një mijti",
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
def set_high_numwords(self, high):
|
|
96
|
+
max = 3 + 3 * len(high)
|
|
97
|
+
for word, n in zip(high, range(max, 3, -3)):
|
|
98
|
+
self.cards[10 ** n] = word
|
|
99
|
+
|
|
100
|
+
def merge(self, curr, next):
|
|
101
|
+
ctext, cnum, ntext, nnum = curr + next
|
|
102
|
+
|
|
103
|
+
if cnum == 1:
|
|
104
|
+
if nnum == 1000:
|
|
105
|
+
return ("një mijë", 1000)
|
|
106
|
+
if nnum == 100:
|
|
107
|
+
return ("njëqind", 100) # Combined as one word
|
|
108
|
+
if nnum < 100:
|
|
109
|
+
return next
|
|
110
|
+
ctext = "një"
|
|
111
|
+
|
|
112
|
+
if nnum > cnum:
|
|
113
|
+
if nnum >= 1000000:
|
|
114
|
+
return ("%s %s" % (ctext, ntext), cnum * nnum)
|
|
115
|
+
elif nnum >= 1000:
|
|
116
|
+
return ("%s %s" % (ctext, ntext), cnum * nnum)
|
|
117
|
+
elif nnum == 100:
|
|
118
|
+
return ("%s %s" % (ctext, ntext), cnum * nnum)
|
|
119
|
+
|
|
120
|
+
if nnum < cnum:
|
|
121
|
+
if cnum >= 1000:
|
|
122
|
+
# For thousands, always use "e" connector for smaller numbers
|
|
123
|
+
return ("%s e %s" % (ctext, ntext), cnum + nnum)
|
|
124
|
+
elif cnum >= 100:
|
|
125
|
+
# For hundreds, use "e" for anything smaller
|
|
126
|
+
return ("%s e %s" % (ctext, ntext), cnum + nnum)
|
|
127
|
+
else:
|
|
128
|
+
# For tens (20, 30, etc.), use "e" for units
|
|
129
|
+
if cnum >= 20 and nnum < 10:
|
|
130
|
+
return ("%s e %s" % (ctext, ntext), cnum + nnum)
|
|
131
|
+
return ("%s%s" % (ctext, ntext), cnum + nnum)
|
|
132
|
+
|
|
133
|
+
return ("%s %s" % (ctext, ntext), cnum + nnum)
|
|
134
|
+
|
|
135
|
+
def to_cardinal(self, value):
|
|
136
|
+
if value == 0:
|
|
137
|
+
return 'zero'
|
|
138
|
+
|
|
139
|
+
result = super(Num2Word_SQ, self).to_cardinal(value)
|
|
140
|
+
return result
|
|
141
|
+
|
|
142
|
+
def to_ordinal(self, value):
|
|
143
|
+
if value in self.ordinal_forms:
|
|
144
|
+
return self.ordinal_forms[value]
|
|
145
|
+
|
|
146
|
+
# For compound ordinals, use pattern "i + cardinal + suffix"
|
|
147
|
+
if value <= 10:
|
|
148
|
+
return self.ordinal_forms.get(value, "i " + self.to_cardinal(value))
|
|
149
|
+
|
|
150
|
+
elif value == 20:
|
|
151
|
+
return "i njezeti"
|
|
152
|
+
elif value == 100:
|
|
153
|
+
return "i njëqindi"
|
|
154
|
+
elif value == 1000:
|
|
155
|
+
return "i një mijti"
|
|
156
|
+
else:
|
|
157
|
+
# For other numbers, use pattern
|
|
158
|
+
cardinal = self.to_cardinal(value)
|
|
159
|
+
return "i " + cardinal
|
|
160
|
+
|
|
161
|
+
def to_ordinal_num(self, value):
|
|
162
|
+
self.verify_ordinal(value)
|
|
163
|
+
|
|
164
|
+
# Albanian ordinal suffixes based on ending
|
|
165
|
+
if value % 10 == 1 and value % 100 != 11:
|
|
166
|
+
suffix = "-shi"
|
|
167
|
+
elif value % 10 == 2 and value % 100 != 12:
|
|
168
|
+
suffix = "-ri"
|
|
169
|
+
elif value % 10 == 3 and value % 100 != 13:
|
|
170
|
+
suffix = "-ti"
|
|
171
|
+
else:
|
|
172
|
+
suffix = "-ti"
|
|
173
|
+
|
|
174
|
+
return str(value) + suffix
|
|
175
|
+
|
|
176
|
+
def pluralize(self, n, forms):
|
|
177
|
+
"""Albanian plural rules"""
|
|
178
|
+
if not forms:
|
|
179
|
+
return ''
|
|
180
|
+
|
|
181
|
+
if len(forms) == 1:
|
|
182
|
+
return forms[0]
|
|
183
|
+
|
|
184
|
+
if len(forms) >= 2:
|
|
185
|
+
# In Albanian, use singular for 1, plural for everything else
|
|
186
|
+
if n == 1:
|
|
187
|
+
return forms[0]
|
|
188
|
+
else:
|
|
189
|
+
return forms[1]
|
|
190
|
+
|
|
191
|
+
return forms[0]
|
|
192
|
+
|
|
193
|
+
def to_currency(self, val, currency='ALL', cents=True, separator=',',
|
|
194
|
+
adjective=False):
|
|
195
|
+
"""
|
|
196
|
+
Convert a value to Albanian currency format.
|
|
197
|
+
"""
|
|
198
|
+
result = []
|
|
199
|
+
is_negative = val < 0
|
|
200
|
+
val = abs(val)
|
|
201
|
+
|
|
202
|
+
if currency in self.CURRENCY_FORMS:
|
|
203
|
+
# Check if value has fractional cents
|
|
204
|
+
from decimal import Decimal
|
|
205
|
+
decimal_val = Decimal(str(val))
|
|
206
|
+
has_fractional_cents = (decimal_val * 100) % 1 != 0
|
|
207
|
+
|
|
208
|
+
if isinstance(val, float):
|
|
209
|
+
# Handle decimal values
|
|
210
|
+
if has_fractional_cents:
|
|
211
|
+
# Keep precision for fractional cents
|
|
212
|
+
whole = int(decimal_val)
|
|
213
|
+
cents_value = decimal_val * 100 - (whole * 100)
|
|
214
|
+
else:
|
|
215
|
+
whole = int(val)
|
|
216
|
+
cents_value = int(round((val - whole) * 100))
|
|
217
|
+
else:
|
|
218
|
+
whole = int(val)
|
|
219
|
+
cents_value = 0
|
|
220
|
+
|
|
221
|
+
# Add the main currency
|
|
222
|
+
if whole > 0:
|
|
223
|
+
result.append(self.to_cardinal(whole))
|
|
224
|
+
result.append(self.pluralize(whole, self.CURRENCY_FORMS[currency][0]))
|
|
225
|
+
|
|
226
|
+
# Add cents if present and cents=True
|
|
227
|
+
if cents_value > 0 and cents:
|
|
228
|
+
if whole > 0:
|
|
229
|
+
# Join the main currency part first, then add separator
|
|
230
|
+
main_part = ' '.join(result)
|
|
231
|
+
result = [main_part + separator]
|
|
232
|
+
# Handle fractional cents
|
|
233
|
+
from decimal import Decimal
|
|
234
|
+
if isinstance(cents_value, Decimal):
|
|
235
|
+
# Convert fractional cents (e.g., 65.3 cents)
|
|
236
|
+
result.append(self.to_cardinal_float(float(cents_value)))
|
|
237
|
+
result.append(self.pluralize(int(cents_value), self.CURRENCY_FORMS[currency][1]))
|
|
238
|
+
else:
|
|
239
|
+
result.append(self.to_cardinal(cents_value))
|
|
240
|
+
result.append(self.pluralize(cents_value, self.CURRENCY_FORMS[currency][1]))
|
|
241
|
+
|
|
242
|
+
if is_negative:
|
|
243
|
+
result.insert(0, self.negword.strip())
|
|
244
|
+
|
|
245
|
+
return ' '.join(result)
|
|
246
|
+
else:
|
|
247
|
+
# Fallback for unknown currency
|
|
248
|
+
return self.to_cardinal(val)
|
|
249
|
+
|
|
250
|
+
def to_year(self, value, **kwargs):
|
|
251
|
+
"""Convert number to year format (same as cardinal for Albanian)."""
|
|
252
|
+
return self.to_cardinal(value)
|
num2words2/lang_SR.py
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
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 .base import Num2Word_Base
|
|
21
|
+
from .currency import parse_currency_parts
|
|
22
|
+
from .utils import get_digits, splitbyx
|
|
23
|
+
|
|
24
|
+
ZERO = ('нула',)
|
|
25
|
+
|
|
26
|
+
ONES = {
|
|
27
|
+
1: ('један', 'једна'),
|
|
28
|
+
2: ('два', 'две'),
|
|
29
|
+
3: ('три', 'три'),
|
|
30
|
+
4: ('четири', 'четири'),
|
|
31
|
+
5: ('пет', 'пет'),
|
|
32
|
+
6: ('шест', 'шест'),
|
|
33
|
+
7: ('седам', 'седам'),
|
|
34
|
+
8: ('осам', 'осам'),
|
|
35
|
+
9: ('девет', 'девет'),
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
TENS = {
|
|
39
|
+
0: ('десет',),
|
|
40
|
+
1: ('једанаест',),
|
|
41
|
+
2: ('дванаест',),
|
|
42
|
+
3: ('тринаест',),
|
|
43
|
+
4: ('четрнаест',),
|
|
44
|
+
5: ('петнаест',),
|
|
45
|
+
6: ('шеснаест',),
|
|
46
|
+
7: ('седамнаест',),
|
|
47
|
+
8: ('осамнаест',),
|
|
48
|
+
9: ('деветнаест',),
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
TWENTIES = {
|
|
52
|
+
2: ('двадесет',),
|
|
53
|
+
3: ('тридесет',),
|
|
54
|
+
4: ('четрдесет',),
|
|
55
|
+
5: ('педесет',),
|
|
56
|
+
6: ('шездесет',),
|
|
57
|
+
7: ('седамдесет',),
|
|
58
|
+
8: ('осамдесет',),
|
|
59
|
+
9: ('деведесет',),
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
HUNDREDS = {
|
|
63
|
+
1: ('сто',),
|
|
64
|
+
2: ('двеста',),
|
|
65
|
+
3: ('триста',),
|
|
66
|
+
4: ('четиристо',),
|
|
67
|
+
5: ('петсто',),
|
|
68
|
+
6: ('шесто',),
|
|
69
|
+
7: ('седамсто',),
|
|
70
|
+
8: ('осамсто',),
|
|
71
|
+
9: ('деветсто',),
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
SCALE = {
|
|
75
|
+
0: ('', '', '', False),
|
|
76
|
+
1: ('хиљада', 'хиљаде', 'хиљада', True), # 10^3
|
|
77
|
+
2: ('милион', 'милиона', 'милиона', False), # 10^6
|
|
78
|
+
3: ('милијарда', 'милијарде', 'милијарди', True), # 10^9 - using long scale
|
|
79
|
+
4: ('билион', 'билиона', 'билиона', False), # 10^12
|
|
80
|
+
5: ('билијарда', 'билијарде', 'билијарди', True), # 10^15
|
|
81
|
+
6: ('трилион', 'трилиона', 'трилиона', False), # 10^18
|
|
82
|
+
7: ('трилијарда', 'трилијарде', 'трилијарди', True), # 10^21
|
|
83
|
+
8: ('квадрилион', 'квадрилиона', 'квадрилиона', False), # 10^24
|
|
84
|
+
9: ('квадрилијарда', 'квадрилијарде', 'квадрилијарди', True), # 10^27
|
|
85
|
+
10: ('квинтилион', 'квинтилиона', 'квинтилиона', False), # 10^30
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class Num2Word_SR(Num2Word_Base):
|
|
90
|
+
CURRENCY_FORMS = {
|
|
91
|
+
'RUB': (
|
|
92
|
+
('rublja', 'rublje', 'rublji', True),
|
|
93
|
+
('kopejka', 'kopejke', 'kopejki', True)
|
|
94
|
+
),
|
|
95
|
+
'EUR': (
|
|
96
|
+
('evro', 'evra', 'evra', False),
|
|
97
|
+
('cent', 'centa', 'centi', False)
|
|
98
|
+
),
|
|
99
|
+
'RSD': (
|
|
100
|
+
('dinar', 'dinara', 'dinara', False),
|
|
101
|
+
('para', 'pare', 'para', True)
|
|
102
|
+
),
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
def setup(self):
|
|
106
|
+
self.negword = "минус"
|
|
107
|
+
self.pointword = "запета"
|
|
108
|
+
|
|
109
|
+
def to_cardinal(self, number, feminine=False):
|
|
110
|
+
n = str(number).replace(',', '.')
|
|
111
|
+
if '.' in n:
|
|
112
|
+
is_negative = n.startswith('-')
|
|
113
|
+
abs_n = n[1:] if is_negative else n
|
|
114
|
+
left, right = abs_n.split('.')
|
|
115
|
+
leading_zero_count = len(right) - len(right.lstrip('0'))
|
|
116
|
+
decimal_part = ((ZERO[0] + ' ') * leading_zero_count +
|
|
117
|
+
self._int2word(int(right), feminine))
|
|
118
|
+
result = u'%s %s %s' % (
|
|
119
|
+
self._int2word(int(left), feminine),
|
|
120
|
+
self.pointword,
|
|
121
|
+
decimal_part
|
|
122
|
+
)
|
|
123
|
+
if is_negative:
|
|
124
|
+
result = self.negword + ' ' + result
|
|
125
|
+
return result
|
|
126
|
+
else:
|
|
127
|
+
return self._int2word(int(n), feminine)
|
|
128
|
+
|
|
129
|
+
def pluralize(self, number, forms):
|
|
130
|
+
if number % 100 < 10 or number % 100 > 20:
|
|
131
|
+
if number % 10 == 1:
|
|
132
|
+
form = 0
|
|
133
|
+
elif 1 < number % 10 < 5:
|
|
134
|
+
form = 1
|
|
135
|
+
else:
|
|
136
|
+
form = 2
|
|
137
|
+
else:
|
|
138
|
+
form = 2
|
|
139
|
+
return forms[form]
|
|
140
|
+
|
|
141
|
+
def to_ordinal(self, number):
|
|
142
|
+
"""Convert to Serbian ordinal numbers (Cyrillic)."""
|
|
143
|
+
try:
|
|
144
|
+
num = int(number)
|
|
145
|
+
except (ValueError, TypeError):
|
|
146
|
+
return str(number)
|
|
147
|
+
|
|
148
|
+
# Serbian ordinals in Cyrillic
|
|
149
|
+
ordinals = {
|
|
150
|
+
1: 'први',
|
|
151
|
+
2: 'други',
|
|
152
|
+
3: 'трећи',
|
|
153
|
+
4: 'четврти',
|
|
154
|
+
5: 'пети',
|
|
155
|
+
6: 'шести',
|
|
156
|
+
7: 'седми',
|
|
157
|
+
8: 'осми',
|
|
158
|
+
9: 'девети',
|
|
159
|
+
10: 'десети',
|
|
160
|
+
11: 'једанаести',
|
|
161
|
+
12: 'дванаести',
|
|
162
|
+
13: 'тринаести',
|
|
163
|
+
14: 'четрнаести',
|
|
164
|
+
15: 'петнаести',
|
|
165
|
+
16: 'шеснаести',
|
|
166
|
+
17: 'седамнаести',
|
|
167
|
+
18: 'осамнаести',
|
|
168
|
+
19: 'деветнаести',
|
|
169
|
+
20: 'двадесети',
|
|
170
|
+
30: 'тридесети',
|
|
171
|
+
40: 'четрдесети',
|
|
172
|
+
50: 'педесети',
|
|
173
|
+
60: 'шездесети',
|
|
174
|
+
70: 'седамдесети',
|
|
175
|
+
80: 'осамдесети',
|
|
176
|
+
90: 'деведесети',
|
|
177
|
+
100: 'стоти',
|
|
178
|
+
1000: 'хиљадити',
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if num in ordinals:
|
|
182
|
+
return ordinals[num]
|
|
183
|
+
|
|
184
|
+
# For other numbers, add 'и' suffix to the cardinal
|
|
185
|
+
# This is a simplified implementation
|
|
186
|
+
cardinal = self.to_cardinal(num)
|
|
187
|
+
return cardinal + 'и'
|
|
188
|
+
|
|
189
|
+
def _cents_verbose(self, number, currency):
|
|
190
|
+
return self._int2word(
|
|
191
|
+
number,
|
|
192
|
+
self.CURRENCY_FORMS[currency][1][-1]
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
def _int2word(self, number, feminine=False):
|
|
196
|
+
if number < 0:
|
|
197
|
+
return ' '.join([self.negword, self._int2word(abs(number))])
|
|
198
|
+
|
|
199
|
+
if number == 0:
|
|
200
|
+
return ZERO[0]
|
|
201
|
+
|
|
202
|
+
words = []
|
|
203
|
+
chunks = list(splitbyx(str(number), 3))
|
|
204
|
+
chunk_len = len(chunks)
|
|
205
|
+
for chunk in chunks:
|
|
206
|
+
chunk_len -= 1
|
|
207
|
+
digit_right, digit_mid, digit_left = get_digits(chunk)
|
|
208
|
+
|
|
209
|
+
if digit_left > 0:
|
|
210
|
+
words.append(HUNDREDS[digit_left][0])
|
|
211
|
+
|
|
212
|
+
if digit_mid > 1:
|
|
213
|
+
words.append(TWENTIES[digit_mid][0])
|
|
214
|
+
|
|
215
|
+
if digit_mid == 1:
|
|
216
|
+
words.append(TENS[digit_right][0])
|
|
217
|
+
elif digit_right > 0:
|
|
218
|
+
# Skip 'један' for thousands (1000, 1001, etc.)
|
|
219
|
+
if not (chunk_len > 0 and chunk == 1):
|
|
220
|
+
is_feminine = feminine or SCALE[chunk_len][-1]
|
|
221
|
+
gender_idx = int(is_feminine)
|
|
222
|
+
words.append(
|
|
223
|
+
ONES[digit_right][gender_idx]
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
if chunk_len > 0 and chunk != 0:
|
|
227
|
+
words.append(self.pluralize(chunk, SCALE[chunk_len]))
|
|
228
|
+
|
|
229
|
+
return ' '.join(words)
|
|
230
|
+
|
|
231
|
+
def to_currency(self, val, currency='RSD', cents=True, separator=',',
|
|
232
|
+
adjective=False):
|
|
233
|
+
# Handle integers specially - no cents
|
|
234
|
+
if isinstance(val, int):
|
|
235
|
+
# Get major currency part only
|
|
236
|
+
left, right, is_negative = parse_currency_parts(val, is_int_with_cents=False)
|
|
237
|
+
words = []
|
|
238
|
+
if is_negative:
|
|
239
|
+
words.append('минус')
|
|
240
|
+
words.append(self.to_cardinal(left))
|
|
241
|
+
|
|
242
|
+
# Add currency name based on the number
|
|
243
|
+
if left % 10 == 1 and left % 100 != 11:
|
|
244
|
+
words.append('динар')
|
|
245
|
+
elif 2 <= left % 10 <= 4 and not (12 <= left % 100 <= 14):
|
|
246
|
+
words.append('динара')
|
|
247
|
+
else:
|
|
248
|
+
words.append('динара')
|
|
249
|
+
|
|
250
|
+
return ' '.join(words)
|
|
251
|
+
|
|
252
|
+
# For floats, use parent implementation
|
|
253
|
+
return super().to_currency(val, currency=currency, cents=cents,
|
|
254
|
+
separator=separator, adjective=adjective)
|
num2words2/lang_SU.py
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
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 .base import Num2Word_Base
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# Sundanese language support
|
|
24
|
+
class Num2Word_SU(Num2Word_Base):
|
|
25
|
+
CURRENCY_FORMS = {
|
|
26
|
+
'IDR': (('rupiah', 'rupiah'), ('sen', 'sen')),
|
|
27
|
+
'USD': (('dollar', 'dollars'), ('cent', 'cents')),
|
|
28
|
+
'EUR': (('euro', 'euros'), ('cent', 'cents'))
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
def setup(self):
|
|
32
|
+
self.negword = "minus "
|
|
33
|
+
self.pointword = "point"
|
|
34
|
+
self.ones = ['', 'hiji', 'dua', 'tilu', 'opat', 'lima', 'genep', 'tujuh', 'dalapan', 'salapan']
|
|
35
|
+
self.tens = ['', 'sapuluh', 'dua puluh', 'tilu puluh', 'opat puluh', 'lima puluh', 'genep puluh', 'tujuh puluh', 'dalapan puluh', 'salapan puluh']
|
|
36
|
+
self.hundred = "ratus"
|
|
37
|
+
self.thousand = "rebu"
|
|
38
|
+
self.million = "juta"
|
|
39
|
+
|
|
40
|
+
def to_cardinal(self, number):
|
|
41
|
+
"""Convert a number to its word representation in Sundanese."""
|
|
42
|
+
n = str(number).strip()
|
|
43
|
+
|
|
44
|
+
if n.startswith('-'):
|
|
45
|
+
n = n[1:]
|
|
46
|
+
ret = self.negword
|
|
47
|
+
else:
|
|
48
|
+
ret = ""
|
|
49
|
+
|
|
50
|
+
if '.' in n:
|
|
51
|
+
left, right = n.split('.', 1)
|
|
52
|
+
ret += self._int_to_word(int(left)) + " " + self.pointword + " "
|
|
53
|
+
for digit in right:
|
|
54
|
+
ret += self._int_to_word(int(digit)) + " "
|
|
55
|
+
return ret.strip()
|
|
56
|
+
else:
|
|
57
|
+
return (ret + self._int_to_word(int(n))).strip()
|
|
58
|
+
|
|
59
|
+
def _int_to_word(self, number):
|
|
60
|
+
"""Convert an integer to its word representation in Sundanese."""
|
|
61
|
+
if number == 0:
|
|
62
|
+
return self.ones[0] if self.ones[0] else "zero"
|
|
63
|
+
|
|
64
|
+
if number < 0:
|
|
65
|
+
return self.negword + self._int_to_word(abs(number))
|
|
66
|
+
elif number < 10:
|
|
67
|
+
return self.ones[number]
|
|
68
|
+
elif number < 100:
|
|
69
|
+
tens_val = number // 10
|
|
70
|
+
ones_val = number % 10
|
|
71
|
+
if ones_val == 0:
|
|
72
|
+
return self.tens[tens_val]
|
|
73
|
+
else:
|
|
74
|
+
return self.tens[tens_val] + " " + self.ones[ones_val]
|
|
75
|
+
elif number < 1000:
|
|
76
|
+
hundreds_val = number // 100
|
|
77
|
+
remainder = number % 100
|
|
78
|
+
result = self.ones[hundreds_val] + " " + self.hundred
|
|
79
|
+
if remainder:
|
|
80
|
+
result += " " + self._int_to_word(remainder)
|
|
81
|
+
return result
|
|
82
|
+
elif number < 1000000:
|
|
83
|
+
thousands_val = number // 1000
|
|
84
|
+
remainder = number % 1000
|
|
85
|
+
result = self._int_to_word(thousands_val) + " " + self.thousand
|
|
86
|
+
if remainder:
|
|
87
|
+
result += " " + self._int_to_word(remainder)
|
|
88
|
+
return result
|
|
89
|
+
elif number < 1000000000:
|
|
90
|
+
millions_val = number // 1000000
|
|
91
|
+
remainder = number % 1000000
|
|
92
|
+
result = self._int_to_word(millions_val) + " " + self.million
|
|
93
|
+
if remainder:
|
|
94
|
+
result += " " + self._int_to_word(remainder)
|
|
95
|
+
return result
|
|
96
|
+
else:
|
|
97
|
+
return str(number) # Fallback for very large numbers
|
|
98
|
+
|
|
99
|
+
def to_ordinal(self, number):
|
|
100
|
+
"""Convert to ordinal in Sundanese."""
|
|
101
|
+
cardinal = self.to_cardinal(number)
|
|
102
|
+
# Language-specific ordinal suffix
|
|
103
|
+
return cardinal + "-na"
|
|
104
|
+
|
|
105
|
+
def to_ordinal_num(self, number):
|
|
106
|
+
"""Convert to abbreviated ordinal."""
|
|
107
|
+
return str(number) + "."
|
|
108
|
+
|
|
109
|
+
def to_year(self, val, longval=True):
|
|
110
|
+
"""Convert to year in Sundanese."""
|
|
111
|
+
return self.to_cardinal(val)
|
|
112
|
+
|
|
113
|
+
def to_currency(self, val, currency='IDR', cents=True, separator=' ', adjective=False):
|
|
114
|
+
"""Convert to currency in Sundanese."""
|
|
115
|
+
is_negative = False
|
|
116
|
+
if val < 0:
|
|
117
|
+
is_negative = True
|
|
118
|
+
val = abs(val)
|
|
119
|
+
|
|
120
|
+
parts = str(val).split('.')
|
|
121
|
+
left = int(parts[0]) if parts[0] else 0
|
|
122
|
+
right = int(parts[1][:2].ljust(2, '0')) if len(parts) > 1 and parts[1] else 0
|
|
123
|
+
|
|
124
|
+
cr1, cr2 = self.CURRENCY_FORMS.get(currency, list(self.CURRENCY_FORMS.values())[0])
|
|
125
|
+
|
|
126
|
+
left_str = self._int_to_word(left)
|
|
127
|
+
result = left_str + " " + (cr1[1] if left != 1 else cr1[0])
|
|
128
|
+
|
|
129
|
+
if cents and right:
|
|
130
|
+
cents_str = self._int_to_word(right)
|
|
131
|
+
result += separator + cents_str + " " + (cr2[1] if right != 1 else cr2[0])
|
|
132
|
+
|
|
133
|
+
if is_negative:
|
|
134
|
+
result = self.negword + result
|
|
135
|
+
|
|
136
|
+
return result.strip()
|