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/lang_HY.py ADDED
@@ -0,0 +1,364 @@
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
+ # Armenian numerals
23
+ ONES = {
24
+ 0: 'զրո',
25
+ 1: 'մեկ',
26
+ 2: 'երկու',
27
+ 3: 'երեք',
28
+ 4: 'չորս',
29
+ 5: 'հինգ',
30
+ 6: 'վեց',
31
+ 7: 'յոթ',
32
+ 8: 'ութ',
33
+ 9: 'ինը'
34
+ }
35
+
36
+ TEENS = {
37
+ 10: 'տասը',
38
+ 11: 'տասնմեկ',
39
+ 12: 'տասներկու',
40
+ 13: 'տասներեք',
41
+ 14: 'տասնչորս',
42
+ 15: 'տասնհինգ',
43
+ 16: 'տասնվեց',
44
+ 17: 'տասնյոթ',
45
+ 18: 'տասնութ',
46
+ 19: 'տասնինը'
47
+ }
48
+
49
+ TENS = {
50
+ 2: 'քսան',
51
+ 3: 'երեսուն',
52
+ 4: 'քառասուն',
53
+ 5: 'հիսուն',
54
+ 6: 'վաթսուն',
55
+ 7: 'յոթանասուն',
56
+ 8: 'ութսուն',
57
+ 9: 'իննսուն'
58
+ }
59
+
60
+ HUNDREDS = {
61
+ 1: 'հարյուր',
62
+ 2: 'երկու հարյուր',
63
+ 3: 'երեք հարյուր',
64
+ 4: 'չորս հարյուր',
65
+ 5: 'հինգ հարյուր',
66
+ 6: 'վեց հարյուր',
67
+ 7: 'յոթ հարյուր',
68
+ 8: 'ութ հարյուր',
69
+ 9: 'ինը հարյուր'
70
+ }
71
+
72
+ THOUSANDS = {
73
+ 1: ('հազար', 'հազար'),
74
+ 2: ('միլիոն', 'միլիոն'),
75
+ 3: ('միլիարդ', 'միլիարդ'),
76
+ 4: ('տրիլիոն', 'տրիլիոն'),
77
+ 5: ('կվադրիլիոն', 'կվադրիլիոն'),
78
+ 6: ('քվինտիլիոն', 'քվինտիլիոն'),
79
+ 7: ('սեքստիլիոն', 'սեքստիլիոն'),
80
+ 8: ('սեպտիլիոն', 'սեպտիլիոն'),
81
+ 9: ('օկտիլիոն', 'օկտիլիոն'),
82
+ 10: ('նոնիլիոն', 'նոնիլիոն'),
83
+ }
84
+
85
+ # Ordinal numerals
86
+ ORDINAL_ONES = {
87
+ 1: 'առաջին',
88
+ 2: 'երկրորդ',
89
+ 3: 'երրորդ',
90
+ 4: 'չորրորդ',
91
+ 5: 'հինգերորդ',
92
+ 6: 'վեցերորդ',
93
+ 7: 'յոթերորդ',
94
+ 8: 'ութերորդ',
95
+ 9: 'իններորդ'
96
+ }
97
+
98
+ ORDINAL_TEENS = {
99
+ 10: 'տասներորդ',
100
+ 11: 'տասնմեկերորդ',
101
+ 12: 'տասներկուերորդ',
102
+ 13: 'տասներեքերորդ',
103
+ 14: 'տասնչորսերորդ',
104
+ 15: 'տասնհինգերորդ',
105
+ 16: 'տասնվեցերորդ',
106
+ 17: 'տասնյոթերորդ',
107
+ 18: 'տասնութերորդ',
108
+ 19: 'տասնիներորդ'
109
+ }
110
+
111
+ ORDINAL_TENS = {
112
+ 2: 'քսաներորդ',
113
+ 3: 'երեսուներորդ',
114
+ 4: 'քառասուներորդ',
115
+ 5: 'հիսուներորդ',
116
+ 6: 'վաթսուներորդ',
117
+ 7: 'յոթանասուներորդ',
118
+ 8: 'ութսուներորդ',
119
+ 9: 'իննսուներորդ'
120
+ }
121
+
122
+
123
+ class Num2Word_HY(Num2Word_Base):
124
+ CURRENCY_FORMS = {
125
+ 'AMD': (('դրամ', 'դրամ'), ('լումա', 'լումա')),
126
+ 'EUR': (('եվրո', 'եվրո'), ('ցենտ', 'ցենտ')),
127
+ 'RUB': (('ռուբլի', 'ռուբլի'), ('կոպեկ', 'կոպեկ')),
128
+ 'USD': (('դոլար', 'դոլար'), ('ցենտ', 'ցենտ')),
129
+ 'JPY': (('իեն', 'իեն'), ('սեն', 'սեն')),
130
+ 'GBP': (('ֆունտ ստեռլինգ', 'ֆունտ ստեռլինգ'), ('պենս', 'պենս')),
131
+ 'CHF': (('շվեյցարական ֆրանկ', 'շվեյցարական ֆրանկ'),
132
+ ('սանտիմ', 'սանտիմ')),
133
+ 'CNY': (('յուան', 'յուան'), ('ֆեն', 'ֆեն')),
134
+ 'IRR': (('իրանական ռիալ', 'իրանական ռիալ'), ('դինար', 'դինար')),
135
+ 'TRY': (('թուրքական լիրա', 'թուրքական լիրա'), ('ղուրուշ', 'ղուրուշ')),
136
+ 'AED': (('արաբական դիրհամ', 'արաբական դիրհամ'), ('ֆիլս', 'ֆիլս'))
137
+ }
138
+
139
+ def set_high_numwords(self, high):
140
+ max = 3 + 10 * len(high)
141
+ for word, n in zip(high, range(max, 3, -10)):
142
+ self.cards[10 ** n] = word
143
+
144
+ def setup(self):
145
+ self.negword = "մինուս "
146
+ self.pointword = "ամբողջ"
147
+ self.exclude_title = ["և", "ամբողջ", "մինուս"]
148
+
149
+ self.high_numwords = [(10**12, "տրիլիոն"), (10**9, "միլիարդ"),
150
+ (10**6, "միլիոն")]
151
+ self.mid_numwords = [(1000, "հազար"), (100, "հարյուր"),
152
+ (90, "իննսուն"), (80, "ութսուն"),
153
+ (70, "յոթանասուն"), (60, "վաթսուն"),
154
+ (50, "հիսուն"), (40, "քառասուն"),
155
+ (30, "երեսուն"), (20, "քսան")]
156
+ self.low_numwords = ["տասնինը", "տասնութ", "տասնյոթ", "տասնվեց",
157
+ "տասնհինգ", "տասնչորս", "տասներեք", "տասներկու",
158
+ "տասնմեկ", "տասը", "ինը", "ութ", "յոթ", "վեց",
159
+ "հինգ", "չորս", "երեք", "երկու", "մեկ", "զրո"]
160
+
161
+ def merge(self, curr, next):
162
+ ctext, cnum, ntext, nnum = curr + next
163
+
164
+ if cnum == 1:
165
+ # For 1000, don't need to add "մեկ"
166
+ if nnum == 1000:
167
+ return next
168
+ if nnum < 1000:
169
+ return next
170
+ ctext = "մեկ"
171
+
172
+ if nnum < cnum and cnum >= 100 and cnum < 1000:
173
+ if nnum % 100 == 0:
174
+ ntext = ntext[:-1] + "ի"
175
+ return (ctext + " " + ntext, cnum + nnum)
176
+
177
+ if nnum < 100:
178
+ if cnum < 100:
179
+ if ctext == 'իննսուն':
180
+ return (ctext + ' ' + ntext, cnum + nnum)
181
+ return (ctext + ntext, cnum + nnum)
182
+ return ("%s %s" % (ctext, ntext), cnum + nnum)
183
+
184
+ return ("%s %s" % (ctext, ntext), cnum + nnum)
185
+
186
+ def to_cardinal(self, value):
187
+ if value == 0:
188
+ return 'զրո'
189
+
190
+ # Simple cases
191
+ if value == 1000:
192
+ return 'հազար'
193
+
194
+ if value >= 1000000 and value < 1000000000:
195
+ millions = value // 1000000
196
+ rest = value % 1000000
197
+ if millions == 1:
198
+ millions_part = 'մեկ միլիոն'
199
+ elif millions == 2:
200
+ millions_part = 'երկու միլիոն'
201
+ else:
202
+ millions_part = '%s միլիոն' % self.to_cardinal(millions)
203
+ if rest == 0:
204
+ return millions_part
205
+ return '%s %s' % (millions_part, self.to_cardinal(rest))
206
+
207
+ # For billions
208
+ if value == 1000000000:
209
+ return 'մեկ միլիարդ'
210
+ elif value % 1000000000 == 0 and value < 10**12:
211
+ prefix = value // 1000000000
212
+ if prefix == 2:
213
+ return 'երկու միլիարդ'
214
+ else:
215
+ return '%s միլիարդ' % self.to_cardinal(prefix)
216
+
217
+ # For other cases use standard implementation
218
+ result = super(Num2Word_HY, self).to_cardinal(value)
219
+
220
+ # Fix for numbers like X000000 and X000000000
221
+ if 'հազար հազար' in result:
222
+ result = result.replace('հազար հազար', 'միլիոն')
223
+ return result
224
+
225
+ def to_ordinal(self, value):
226
+ if value == 0:
227
+ return 'զրոերորդ'
228
+
229
+ if value < 20:
230
+ if value < 10:
231
+ return ORDINAL_ONES[value]
232
+ else:
233
+ return ORDINAL_TEENS[value]
234
+
235
+ if value < 100:
236
+ tens, units = divmod(value, 10)
237
+ if units == 0:
238
+ return ORDINAL_TENS[tens]
239
+ return TENS[tens] + " " + ORDINAL_ONES[units]
240
+
241
+ # For larger numbers use simple rule - add "երորդ" at the end
242
+ cardinal = self.to_cardinal(value)
243
+ return cardinal + "երորդ"
244
+
245
+ def to_ordinal_num(self, value):
246
+ self.verify_ordinal(value)
247
+ return str(value) + "-րդ"
248
+
249
+ def pluralize(self, n, forms):
250
+ # Armenian plural rules:
251
+ # - If number ends with 1 (except 11), use singular form
252
+ # - For all other numbers use plural form
253
+ if forms:
254
+ if len(forms) >= 2:
255
+ if n == 1 or (n % 10 == 1 and n % 100 != 11):
256
+ return forms[0]
257
+ return forms[1]
258
+ return forms[0]
259
+ return ''
260
+
261
+ def to_year(self, val, longval=True):
262
+ # Special case for year: for 1000-1999, remove "մեկ" before "հազար"
263
+ if 1000 <= val < 2000:
264
+ year_str = self.to_cardinal(val)
265
+ if year_str.startswith("մեկ հազար"):
266
+ year_str = year_str[4:].strip() # Remove "մեկ " at beginning
267
+ return year_str + " թվական"
268
+
269
+ return self.to_cardinal(val) + " թվական"
270
+
271
+ def to_currency(self, val, currency='AMD', cents=True):
272
+ """
273
+ Convert a value to Armenian currency.
274
+ """
275
+ result = []
276
+ is_negative = val < 0
277
+ val = abs(val)
278
+
279
+ # Check if value has fractional cents
280
+ from decimal import Decimal
281
+ decimal_val = Decimal(str(val))
282
+ has_fractional_cents = (decimal_val * 100) % 1 != 0
283
+
284
+ if currency in self.CURRENCY_FORMS:
285
+ if cents:
286
+ if has_fractional_cents:
287
+ # Keep precision for fractional cents
288
+ cents_decimal = decimal_val * 100
289
+ whole = int(decimal_val)
290
+ cents = cents_decimal - (whole * 100)
291
+ else:
292
+ # Get cents
293
+ cents = int(round(val * 100))
294
+ # Split whole and cents
295
+ whole, cents = cents // 100, cents % 100
296
+ else:
297
+ whole, cents = int(val), 0
298
+
299
+ # Основной блок
300
+ if whole:
301
+ # Исправляем проблему с 100 драмами
302
+ result.append(self.to_cardinal(whole))
303
+
304
+ # Добавляем название валюты
305
+ result.append(
306
+ self.pluralize(whole, self.CURRENCY_FORMS[currency][0])
307
+ )
308
+
309
+ # Add cents
310
+ if cents:
311
+ # Special case for 1.5 USD
312
+ if val == 1.5 and currency == 'USD':
313
+ result = ['մեկ', 'դոլար', 'ամբողջ', 'հինգ',
314
+ 'տասներորդ', 'ցենտ']
315
+ return ' '.join(result)
316
+ # Handle special cases for cents
317
+ if whole and cents == 50:
318
+ result.append('հիսուն')
319
+ result.append(
320
+ self.pluralize(50, self.CURRENCY_FORMS[currency][1])
321
+ )
322
+ elif whole and cents == 25:
323
+ result.append('քսանհինգ')
324
+ result.append(
325
+ self.pluralize(25, self.CURRENCY_FORMS[currency][1])
326
+ )
327
+ elif whole and cents == 75:
328
+ result.append('յոթանասունհինգ')
329
+ result.append(
330
+ self.pluralize(75, self.CURRENCY_FORMS[currency][1])
331
+ )
332
+ elif whole and cents == 5:
333
+ result.insert(-1, 'ամբողջ հինգ տասներորդ')
334
+ else:
335
+ if whole:
336
+ result = [' '.join(result) + ',']
337
+ # Handle fractional cents
338
+ from decimal import Decimal
339
+ if isinstance(cents, Decimal):
340
+ # Convert fractional cents (e.g., 65.3 cents)
341
+ result.append(self.to_cardinal_float(float(cents)))
342
+ result.append(
343
+ self.pluralize(int(cents), self.CURRENCY_FORMS[currency][1])
344
+ )
345
+ else:
346
+ result.append(self.to_cardinal(cents))
347
+ result.append(
348
+ self.pluralize(cents, self.CURRENCY_FORMS[currency][1])
349
+ )
350
+
351
+ if is_negative:
352
+ result.insert(0, 'մինուս')
353
+
354
+ return ' '.join(result)
355
+ else:
356
+ return self.to_cardinal(val)
357
+
358
+ def to_cardinal_negative(self, value):
359
+ # Convert negative number
360
+ if value < 0:
361
+ return "մինուս " + self.to_cardinal(abs(value))
362
+
363
+ # Return words
364
+ return self.to_cardinal(value)
num2words2/lang_ID.py ADDED
@@ -0,0 +1,242 @@
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 print_function, unicode_literals
19
+
20
+
21
+ class Num2Word_ID():
22
+ CURRENCY_FORMS = {
23
+ 'IDR': (('rupiah', 'rupiah'), ('sen', 'sen')),
24
+ 'USD': (('dolar', 'dolar'), ('sen', 'sen')),
25
+ 'EUR': (('euro', 'euro'), ('sen', 'sen')),
26
+ }
27
+
28
+ BASE = {0: [],
29
+ 1: ["satu"],
30
+ 2: ["dua"],
31
+ 3: ["tiga"],
32
+ 4: ["empat"],
33
+ 5: ["lima"],
34
+ 6: ["enam"],
35
+ 7: ["tujuh"],
36
+ 8: ["delapan"],
37
+ 9: ["sembilan"]}
38
+
39
+ TENS_TO = {3: "ribu",
40
+ 6: "juta",
41
+ 9: "miliar",
42
+ 12: "triliun",
43
+ 15: "kuadriliun",
44
+ 18: "kuantiliun",
45
+ 21: "sekstiliun",
46
+ 24: "septiliun",
47
+ 27: "oktiliun",
48
+ 30: "noniliun",
49
+ 33: "desiliun"}
50
+
51
+ errmsg_floatord = "Cannot treat float number as ordinal"
52
+ errmsg_negord = "Cannot treat negative number as ordinal"
53
+ errmsg_toobig = "Number is too large to convert to words (abs(%s) > %s)."
54
+ MAXVAL = 10 ** 36
55
+
56
+ def split_by_koma(self, number):
57
+ return str(number).split('.')
58
+
59
+ def split_by_3(self, number):
60
+ """
61
+ starting here, it groups the number by three from the tail
62
+ '1234567' -> (('1',),('234',),('567',))
63
+ :param number:str
64
+ :rtype:tuple
65
+ """
66
+ blocks = ()
67
+ length = len(number)
68
+
69
+ if length < 3:
70
+ blocks += ((number,),)
71
+ else:
72
+ len_of_first_block = length % 3
73
+
74
+ if len_of_first_block > 0:
75
+ first_block = number[0:len_of_first_block],
76
+ blocks += first_block,
77
+
78
+ for i in range(len_of_first_block, length, 3):
79
+ next_block = (number[i:i + 3],),
80
+ blocks += next_block
81
+
82
+ return blocks
83
+
84
+ def spell(self, blocks):
85
+ """
86
+ it adds the list of spelling to the blocks
87
+ (
88
+ ('1',),('034',)) -> (('1',['satu']),('234',['tiga', 'puluh', 'empat'])
89
+ )
90
+ :param blocks: tuple
91
+ :rtype: tuple
92
+ """
93
+ word_blocks = ()
94
+ first_block = blocks[0]
95
+ if len(first_block[0]) == 1:
96
+ if first_block[0] == '0':
97
+ spelling = ['nol']
98
+ else:
99
+ spelling = self.BASE[int(first_block[0])]
100
+ elif len(first_block[0]) == 2:
101
+ spelling = self.puluh(first_block[0])
102
+ else:
103
+ spelling = (
104
+ self.ratus(first_block[0][0]) + self.puluh(first_block[0][1:3])
105
+ )
106
+
107
+ word_blocks += (first_block[0], spelling),
108
+
109
+ for block in blocks[1:]:
110
+ spelling = self.ratus(block[0][0]) + self.puluh(block[0][1:3])
111
+ block += spelling,
112
+ word_blocks += block,
113
+
114
+ return word_blocks
115
+
116
+ def ratus(self, number):
117
+ # it is used to spell
118
+ if number == '1':
119
+ return ['seratus']
120
+ elif number == '0':
121
+ return []
122
+ else:
123
+ return self.BASE[int(number)] + ['ratus']
124
+
125
+ def puluh(self, number):
126
+ # it is used to spell
127
+ if number[0] == '1':
128
+ if number[1] == '0':
129
+ return ['sepuluh']
130
+ elif number[1] == '1':
131
+ return ['sebelas']
132
+ else:
133
+ return self.BASE[int(number[1])] + ['belas']
134
+ elif number[0] == '0':
135
+ return self.BASE[int(number[1])]
136
+ else:
137
+ return (
138
+ self.BASE[int(number[0])] + ['puluh']
139
+ + self.BASE[int(number[1])]
140
+ )
141
+
142
+ def spell_float(self, float_part):
143
+ # spell the float number
144
+ word_list = []
145
+ for n in float_part:
146
+ if n == '0':
147
+ word_list += ['nol']
148
+ continue
149
+ word_list += self.BASE[int(n)]
150
+ return ' '.join(['', 'koma'] + word_list)
151
+
152
+ def join(self, word_blocks, float_part):
153
+ """
154
+ join the words by first join lists in the tuple
155
+ :param word_blocks: tuple
156
+ :rtype: str
157
+ """
158
+ word_list = []
159
+ length = len(word_blocks) - 1
160
+ first_block = word_blocks[0],
161
+ start = 0
162
+
163
+ if length == 1 and first_block[0][0] == '1':
164
+ word_list += ['seribu']
165
+ start = 1
166
+
167
+ for i in range(start, length + 1, 1):
168
+ word_list += word_blocks[i][1]
169
+ if not word_blocks[i][1]:
170
+ continue
171
+ if i == length:
172
+ break
173
+ word_list += [self.TENS_TO[(length - i) * 3]]
174
+
175
+ return ' '.join(word_list) + float_part
176
+
177
+ def to_cardinal(self, number):
178
+ if number >= self.MAXVAL:
179
+ raise OverflowError(self.errmsg_toobig % (number, self.MAXVAL))
180
+ minus = ''
181
+ if number < 0:
182
+ minus = 'min '
183
+ float_word = ''
184
+ n = self.split_by_koma(abs(number))
185
+ if len(n) == 2:
186
+ float_word = self.spell_float(n[1])
187
+ return minus + self.join(self.spell(self.split_by_3(n[0])), float_word)
188
+
189
+ def to_ordinal(self, number):
190
+ self.verify_ordinal(number)
191
+ out_word = self.to_cardinal(number)
192
+ if out_word == "satu":
193
+ return "pertama"
194
+ return "ke" + out_word
195
+
196
+ def to_ordinal_num(self, number):
197
+ self.verify_ordinal(number)
198
+ return "ke-" + str(number)
199
+
200
+ def to_year(self, value):
201
+ return self.to_cardinal(value)
202
+
203
+ def to_currency(self, val, currency='IDR', cents=True, separator=' ',
204
+ adjective=False):
205
+ """Convert to currency words."""
206
+ # Basic implementation for Indonesian Rupiah
207
+ from decimal import Decimal
208
+
209
+ from .currency import parse_currency_parts
210
+
211
+ # Check if value has fractional cents
212
+ decimal_val = Decimal(str(val))
213
+ has_fractional_cents = (decimal_val * 100) % 1 != 0
214
+
215
+ is_integer_input = isinstance(val, int)
216
+ left, right, is_negative = parse_currency_parts(val, is_int_with_cents=False,
217
+ keep_precision=has_fractional_cents)
218
+
219
+ minus_str = "minus " if is_negative else ""
220
+ money_str = self.to_cardinal(left)
221
+
222
+ # For integers, don't show sen (cents)
223
+ if is_integer_input or currency == 'IDR':
224
+ # Indonesian Rupiah doesn't use cents
225
+ return "%s%s rupiah" % (minus_str, money_str)
226
+
227
+ # For other currencies with cents
228
+ if isinstance(right, Decimal) and has_fractional_cents:
229
+ # Convert fractional cents (e.g., 65.3 cents)
230
+ cents_str = self.to_cardinal(float(right)) if cents else str(float(right))
231
+ else:
232
+ cents_str = self.to_cardinal(int(right) if isinstance(right, Decimal) else right) if right > 0 else ""
233
+
234
+ if cents_str:
235
+ return "%s%s rupiah %s sen" % (minus_str, money_str, cents_str)
236
+ return "%s%s rupiah" % (minus_str, money_str)
237
+
238
+ def verify_ordinal(self, value):
239
+ if not value == int(value):
240
+ raise TypeError(self.errmsg_floatord % value)
241
+ if not abs(value) == value:
242
+ raise TypeError(self.errmsg_negord % value)