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_FI.py ADDED
@@ -0,0 +1,759 @@
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 division, print_function, unicode_literals
19
+
20
+ from collections import OrderedDict
21
+
22
+ from . import lang_EUR
23
+
24
+ GENERIC_CENTS = ('sentti', 'senttiä')
25
+ GENERIC_CENTAVOS = ('centavo', 'centavoa')
26
+
27
+ # grammatical cases
28
+ NOM = 10 # nominative: the dictionary form
29
+ GEN = 11 # genitive: ~of/'s
30
+ ACC = 12 # accusative: not used; either nominative or genitive
31
+ PTV = 13 # partitive: as an object
32
+ # locative cases (internal)
33
+ INE = 14 # inessive: in
34
+ ELA = 15 # elative: from/out of
35
+ ILL = 16 # illative: into
36
+ # locative cases (external)
37
+ ADE = 17 # adessive: at/on
38
+ ABL = 18 # ablative: from (after being at/on, not in)
39
+ ALL = 19 # allative: to
40
+ # essive
41
+ ESS = 20 # essive: as (in the role of)
42
+ TRANSL = 21 # translative: to (the role of; being sth)
43
+ # rare
44
+ INSTRUC = 22 # instructive: with (plural is the same as singular)
45
+ ABE = 23 # abessive: without
46
+ COM = 24 # comitative: together with (plural = singular)
47
+
48
+ NAME_TO_CASE = {
49
+ 'nominative': NOM,
50
+ 'genitive': GEN,
51
+ 'accusative': ACC,
52
+ 'partitive': PTV,
53
+ 'inessive': INE,
54
+ 'elative': ELA,
55
+ 'illative': ILL,
56
+ 'adessive': ADE,
57
+ 'ablative': ABL,
58
+ 'allative': ALL,
59
+ 'essive': ESS,
60
+ 'translative': TRANSL,
61
+ 'instructive': INSTRUC,
62
+ 'abessive': ABE,
63
+ 'comitative': COM,
64
+ }
65
+
66
+ # https://en.wikibooks.org/wiki/Finnish/Grammar-Vowel_harmony
67
+ BACK_TO_FRONT = {
68
+ 'a': 'ä',
69
+ 'o': 'ö',
70
+ 'u': 'y',
71
+ }
72
+
73
+ # https://en.wiktionary.org/wiki/Appendix:Finnish_nominal_inflection
74
+ # CASE: (SINGULAR_SUFFIX+, PLURAL_SUFFIX+)
75
+ KOTUS_TYPE = {
76
+
77
+ # Kotus type 5/risti, no gradation
78
+ 5: {
79
+ # grammatical
80
+ NOM: ('i', 'it'),
81
+ GEN: ('in', 'ien'),
82
+ PTV: ('ia', 'eja'),
83
+ # locative, internal
84
+ INE: ('issa', 'eissa'),
85
+ ELA: ('ista', 'eista'),
86
+ ILL: ('iin', 'eihin'),
87
+ # locative, external
88
+ ADE: ('illa', 'eilla'),
89
+ ABL: ('ilta', 'eilta'),
90
+ ALL: ('ille', 'eille'),
91
+ # essive
92
+ ESS: ('ina', 'eina'),
93
+ TRANSL: ('iksi', 'eiksi'),
94
+ # rare
95
+ INSTRUC: ('ein', 'ein'),
96
+ ABE: ('itta', 'eitta'),
97
+ COM: ('eine', 'eine'), # works better
98
+ },
99
+
100
+ # Kotus type 7/ovi, no gradation
101
+ 7: {
102
+ # grammatical
103
+ NOM: ('i', 'et'),
104
+ GEN: ('en', 'ien'),
105
+ PTV: ('ea', 'ia'),
106
+ # locative, internal
107
+ INE: ('essa', 'issa'),
108
+ ELA: ('esta', 'ista'),
109
+ ILL: ('een', 'iin'),
110
+ # locative, external
111
+ ADE: ('ella', 'illa'),
112
+ ABL: ('elta', 'ilta'),
113
+ ALL: ('elle', 'ille'),
114
+ # essive
115
+ ESS: ('ena', 'ina'),
116
+ TRANSL: ('eksi', 'iksi'),
117
+ # rare
118
+ INSTRUC: ('in', 'in'),
119
+ ABE: ('etta', 'itta'),
120
+ COM: ('ine', 'ine'), # works better
121
+ },
122
+
123
+ # Kotus type 8/nalle, no gradation
124
+ 8: {
125
+ # grammatical
126
+ NOM: ('e', 'et'),
127
+ GEN: ('en', ('ejen', 'ein')),
128
+ PTV: ('ea', 'eja'),
129
+ # locative, internal
130
+ INE: ('essa', 'eissa'),
131
+ ELA: ('esta', 'eista'),
132
+ ILL: ('een', 'eihin'),
133
+ # locative, external
134
+ ADE: ('ella', 'eilla'),
135
+ ABL: ('elta', 'eilta'),
136
+ ALL: ('elle', 'eille'),
137
+ # essive
138
+ ESS: ('ena', 'eina'),
139
+ TRANSL: ('eksi', 'eiksi'),
140
+ # rare
141
+ INSTRUC: ('ein', 'ein'),
142
+ ABE: ('etta', 'eitta'),
143
+ COM: ('eine', 'eine'), # works better
144
+ },
145
+
146
+ # Kotus type 9/kala, t-d gradation (sata)
147
+ 109: {
148
+ # grammatical
149
+ NOM: ('ta', 'dat'),
150
+ GEN: ('dan', ('tojen', 'tain')),
151
+ PTV: ('taa', 'toja'),
152
+ # locative, internal
153
+ INE: ('dassa', 'doissa'),
154
+ ELA: ('dasta', 'doista'),
155
+ ILL: ('taan', 'toihin'),
156
+ # locative, external
157
+ ADE: ('dalla', 'doilla'),
158
+ ABL: ('dalta', 'doilta'),
159
+ ALL: ('dalle', 'doille'),
160
+ # essive
161
+ ESS: ('tana', 'toina'),
162
+ TRANSL: ('daksi', 'doiksi'),
163
+ # rare
164
+ INSTRUC: ('doin', 'doin'),
165
+ ABE: ('datta', 'doitta'),
166
+ COM: ('toine', 'toine'), # works better
167
+ },
168
+
169
+ # Kotus type 10/koira, no gradation
170
+ 10: {
171
+ # grammatical
172
+ NOM: ('a', 'at'),
173
+ GEN: ('an', ('ien', 'ain')),
174
+ PTV: ('aa', 'ia'),
175
+ # locative, internal
176
+ INE: ('assa', 'issa'),
177
+ ELA: ('asta', 'ista'),
178
+ ILL: ('aan', 'iin'),
179
+ # locative, external
180
+ ADE: ('alla', 'illa'),
181
+ ABL: ('alta', 'ilta'),
182
+ ALL: ('alle', 'ille'),
183
+ # essive
184
+ ESS: ('ana', 'ina'),
185
+ TRANSL: ('aksi', 'iksi'),
186
+ # rare
187
+ INSTRUC: ('in', 'in'),
188
+ ABE: ('atta', 'itta'),
189
+ COM: ('ine', 'ine'), # works better
190
+ },
191
+
192
+ # Kotus type 27/käsi, t-d gradation
193
+ 27: {
194
+ # grammatical
195
+ NOM: ('si', 'det'),
196
+ GEN: ('den', ('sien', 'tten')),
197
+ PTV: ('tta', 'sia'),
198
+ # locative, internal
199
+ INE: ('dessa', 'sissa'),
200
+ ELA: ('desta', 'sista'),
201
+ ILL: ('teen', 'siin'),
202
+ # locative, external
203
+ ADE: ('della', 'silla'),
204
+ ABL: ('delta', 'silta'),
205
+ ALL: ('delle', 'sille'),
206
+ # essive
207
+ ESS: ('tena', 'sina'),
208
+ TRANSL: ('deksi', 'siksi'),
209
+ # rare
210
+ INSTRUC: ('sin', 'sin'),
211
+ ABE: ('detta', 'sitta'),
212
+ COM: ('sine', 'sine'), # works better
213
+ },
214
+
215
+ # Kotus type 31/kaksi, t-d gradation
216
+ 31: {
217
+ # grammatical
218
+ NOM: ('ksi', 'hdet'),
219
+ GEN: ('hden', 'ksien'),
220
+ PTV: ('hta', 'ksia'),
221
+ # locative, internal
222
+ INE: ('hdessa', 'ksissa'),
223
+ ELA: ('hdesta', 'ksista'),
224
+ ILL: ('hteen', 'ksiin'),
225
+ # locative, external
226
+ ADE: ('hdella', 'ksilla'),
227
+ ABL: ('hdelta', 'ksilta'),
228
+ ALL: ('hdelle', 'ksille'),
229
+ # essive
230
+ ESS: ('htena', 'ksina'),
231
+ TRANSL: ('hdeksi', 'ksiksi'),
232
+ # rare
233
+ INSTRUC: ('ksin', 'ksin'),
234
+ ABE: ('hdetta', 'ksitta'),
235
+ COM: ('ksine', 'ksine'), # works better
236
+ },
237
+
238
+ # Kotus type 32/sisar, no gradation
239
+ 32: {
240
+ # grammatical
241
+ NOM: ('', 'et'),
242
+ GEN: ('en', ('ien', 'ten')),
243
+ PTV: ('ta', 'ia'),
244
+ # locative, internal
245
+ INE: ('essa', 'issa'),
246
+ ELA: ('esta', 'ista'),
247
+ ILL: ('een', 'iin'),
248
+ # locative, external
249
+ ADE: ('ella', 'illa'),
250
+ ABL: ('elta', 'ilta'),
251
+ ALL: ('elle', 'ille'),
252
+ # essive
253
+ ESS: ('ena', 'ina'),
254
+ TRANSL: ('eksi', 'iksi'),
255
+ # rare
256
+ INSTRUC: ('in', 'in'),
257
+ ABE: ('etta', 'itta'),
258
+ COM: ('ine', 'ine'), # works better
259
+ },
260
+
261
+ # Kotus type 38/nainen, no gradation
262
+ 38: {
263
+ # grammatical
264
+ NOM: ('nen', 'set'),
265
+ GEN: ('sen', ('sten', 'sien')),
266
+ PTV: ('sta', 'sia'),
267
+ # locative, internal
268
+ INE: ('sessa', 'sissa'),
269
+ ELA: ('sesta', 'sista'),
270
+ ILL: ('seen', 'siin'),
271
+ # locative, external
272
+ ADE: ('sella', 'silla'),
273
+ ABL: ('selta', 'silta'),
274
+ ALL: ('selle', 'sille'),
275
+ # essive
276
+ ESS: ('sena', 'sina'),
277
+ TRANSL: ('seksi', 'siksi'),
278
+ # rare
279
+ INSTRUC: ('sin', 'sin'),
280
+ ABE: ('setta', 'sitta'),
281
+ COM: ('sine', 'sine'), # works better
282
+ },
283
+
284
+ # Kotus type 45/kahdeksas, nt-nn gradation
285
+ 45: {
286
+ # grammatical
287
+ NOM: ('s', 'nnet'),
288
+ GEN: ('nnen', 'nsien'),
289
+ PTV: ('tta', 'nsia'),
290
+ # locative, internal
291
+ INE: ('nnessa', 'nsissa'),
292
+ ELA: ('nnesta', 'nsista'),
293
+ ILL: ('nteen', 'nsiin'),
294
+ # locative, external
295
+ ADE: ('nnella', 'nsilla'),
296
+ ABL: ('nnelta', 'nsilta'),
297
+ ALL: ('nnelle', 'nsille'),
298
+ # essive
299
+ ESS: ('ntena', 'nsina'),
300
+ TRANSL: ('nneksi', 'nsiksi'),
301
+ # rare
302
+ INSTRUC: ('nsin', 'nsin'),
303
+ ABE: ('nnetta', 'nsitta'),
304
+ COM: ('nsine', 'nsine'), # works better
305
+ },
306
+
307
+ # Kotus type 46/tuhat, nt-nn gradation
308
+ 46: {
309
+ # grammatical
310
+ NOM: ('t', 'nnet'),
311
+ GEN: ('nnen', ('nsien', 'nten')),
312
+ PTV: ('tta', 'nsia'),
313
+ # locative, internal
314
+ INE: ('nnessa', 'nsissa'),
315
+ ELA: ('nnesta', 'nsista'),
316
+ ILL: ('nteen', 'nsiin'),
317
+ # locative, external
318
+ ADE: ('nnella', 'nsilla'),
319
+ ABL: ('nnelta', 'nsilta'),
320
+ ALL: ('nnelle', 'nsille'),
321
+ # essive
322
+ ESS: ('ntena', 'nsina'),
323
+ TRANSL: ('nneksi', 'nsiksi'),
324
+ # rare
325
+ INSTRUC: ('nsin', 'nsin'),
326
+ ABE: ('nnetta', 'nsitta'),
327
+ COM: ('nsine', 'nsine'), # works better
328
+ },
329
+ }
330
+
331
+ # kolme
332
+ KOTUS_TYPE[108] = {
333
+ c: (KOTUS_TYPE[8][c][0], KOTUS_TYPE[7][c][1])
334
+ for c in KOTUS_TYPE[8]
335
+ }
336
+ KOTUS_TYPE[108][INSTRUC] = ('en', 'in')
337
+ KOTUS_TYPE[108][ABE] = ('etta', 'itta')
338
+ KOTUS_TYPE[108][COM] = ('ine', 'ine')
339
+
340
+ # seitsemän, kahdeksan, yhdeksän
341
+ KOTUS_TYPE[110] = KOTUS_TYPE[10].copy()
342
+ KOTUS_TYPE[110][NOM] = ('an', 'at')
343
+
344
+ # kymmenen
345
+ KOTUS_TYPE[132] = KOTUS_TYPE[32].copy()
346
+ KOTUS_TYPE[132][NOM] = ('en', 'et')
347
+
348
+
349
+ def inflect(parts, options):
350
+ if not isinstance(parts, list):
351
+ parts = [parts]
352
+
353
+ out = ''
354
+ for part in parts:
355
+ # part is plain text, concat and continue
356
+ if not isinstance(part, tuple):
357
+ out += part
358
+ continue
359
+ # predefined case (kaksikymmentä, ...)
360
+ tmp_case = options.case
361
+ if len(part) == 3:
362
+ # override singular nominative only
363
+ if options.case == NOM and not options.plural:
364
+ tmp_case = part[2]
365
+ part = part[:2]
366
+ # stem and suffix
367
+ stem, kotus_type = part
368
+ suffix = KOTUS_TYPE[kotus_type][tmp_case][options.plural]
369
+ # many choices, choose preferred or first
370
+ if isinstance(suffix, tuple):
371
+ common = set(suffix) & set(options.prefer or set())
372
+ if len(common) == 1:
373
+ suffix = common.pop()
374
+ else:
375
+ suffix = suffix[0]
376
+ # apply vowel harmony
377
+ if not set(BACK_TO_FRONT) & set(stem):
378
+ for back, front in BACK_TO_FRONT.items():
379
+ suffix = suffix.replace(back, front)
380
+ # concat
381
+ out += stem + suffix
382
+
383
+ return out
384
+
385
+
386
+ class Options(object):
387
+ def __init__(self, ordinal, case, plural, prefer):
388
+ self.ordinal = ordinal
389
+ self.case = case
390
+ self.plural = plural
391
+ self.prefer = prefer
392
+
393
+ def variation(self, ordinal=None, case=None, plural=None, prefer=None):
394
+ return Options(
395
+ ordinal if ordinal is not None else self.ordinal,
396
+ case if case is not None else self.case,
397
+ plural if plural is not None else self.plural,
398
+ prefer if prefer is not None else self.prefer,
399
+ )
400
+
401
+
402
+ class Num2Word_FI(lang_EUR.Num2Word_EUR):
403
+ CURRENCY_FORMS = {
404
+ 'BRL': (('real', 'realia'), GENERIC_CENTAVOS),
405
+ 'CHF': (('frangi', 'frangia'), ('rappen', 'rappenia')),
406
+ 'CNY': (('juan', 'juania'), ('fen', 'feniä')),
407
+ 'EUR': (('euro', 'euroa'), GENERIC_CENTS),
408
+ 'FIM': (('markka', 'markkaa'), ('penni', 'penniä')), # historical
409
+ 'INR': (('rupia', 'rupiaa'), ('paisa', 'paisaa')),
410
+ 'JPY': (('jeni', 'jeniä'), ('sen', 'seniä')), # rare subunit
411
+ 'KRW': (('won', 'wonia'), ('jeon', 'jeonia')), # rare subunit
412
+ 'KPW': (('won', 'wonia'), ('chon', 'chonia')), # rare subunit
413
+ 'MXN': (('peso', 'pesoa'), GENERIC_CENTAVOS),
414
+ 'RUB': (('rupla', 'ruplaa'), ('kopeekka', 'kopeekkaa')),
415
+ 'TRY': (('liira', 'liiraa'), ('kuruş', 'kuruşia')),
416
+ 'ZAR': (('randi', 'randia'), GENERIC_CENTS),
417
+ }
418
+
419
+ # crowns
420
+ for curr_code in 'DKK', 'ISK', 'NOK', 'SEK':
421
+ CURRENCY_FORMS[curr_code] = (('kruunu', 'kruunua'), ('äyri', 'äyriä'))
422
+
423
+ # dollars
424
+ for curr_code in 'AUD', 'CAD', 'HKD', 'NZD', 'SGD', 'USD':
425
+ CURRENCY_FORMS[curr_code] = (
426
+ ('dollari', 'dollaria'), GENERIC_CENTS)
427
+
428
+ # pounds
429
+ for curr_code in ('GBP',):
430
+ CURRENCY_FORMS[curr_code] = (('punta', 'puntaa'), ('penny', 'pennyä'))
431
+
432
+ CURRENCY_ADJECTIVES = {
433
+ 'AUD': 'Australian',
434
+ 'BRL': 'Brasilian',
435
+ 'CAD': 'Kanadan',
436
+ 'CHF': 'Sveitsin',
437
+ 'DKK': 'Tanskan',
438
+ 'FIM': 'Suomen', # historical
439
+ 'GBP': 'Englannin',
440
+ 'HKD': 'Hongkongin',
441
+ 'INR': 'Intian',
442
+ 'ISK': 'Islannin',
443
+ 'KRW': 'Etelä-Korean',
444
+ 'KPW': 'Pohjois-Korean',
445
+ 'MXN': 'Meksikon',
446
+ 'NOK': 'Norjan',
447
+ 'NZD': 'Uuden-Seelannin',
448
+ 'RUB': 'Venäjän',
449
+ 'SEK': 'Ruotsin',
450
+ 'SGD': 'Singaporen',
451
+ 'TRY': 'Turkin',
452
+ 'USD': 'Yhdysvaltain',
453
+ 'ZAR': 'Etelä-Afrikan',
454
+ }
455
+
456
+ def __init__(self):
457
+ self.ords = OrderedDict()
458
+ super(Num2Word_FI, self).__init__()
459
+
460
+ def set_numwords(self):
461
+ self.set_high_numwords(self.high_numwords)
462
+ self.set_mid_numwords(self.mid_numwords, self.mid_ords)
463
+ self.set_low_numwords(self.low_numwords, self.low_ords)
464
+
465
+ def set_high_numwords(self, high):
466
+ # references:
467
+ # https://fi.wikipedia.org/wiki/Suurten_lukujen_nimet
468
+ # https://en.wikipedia.org/wiki/Names_of_large_numbers#Standard_dictionary_numbers
469
+
470
+ # translate to Finnish
471
+ replacements = [
472
+ ("qu", "kv"),
473
+ ("x", "ks"),
474
+ ("c", "k"),
475
+ ("kent", "sent"), # applied after c -> k to cent
476
+ ]
477
+ translated = []
478
+ for i, numword in enumerate(high):
479
+ # notes:
480
+ # - 1e6**9 can be either noviljoona or noniljoona
481
+ # - 1e6**38 and above are untested
482
+
483
+ # 1e6**6 is sekstiljoona but 1e6**16 is sedekiljoona
484
+ if numword.startswith("sex") and numword != "sext":
485
+ numword = numword.replace("sex", "se")
486
+ # 1e6**7 is septiljoona but 1e6**17 is septendekiljoona
487
+ elif numword.startswith("sept") and numword != "sept":
488
+ numword = "septen" + numword[len("sept"):]
489
+ # 1e6**8 is oktiljoona but 1e6**18 is duodevigintiljoona
490
+ # (2 from 20)
491
+ elif numword.startswith("octo"):
492
+ numword = high[i + -10]
493
+ numword = "duode" + numword[len("octo"):]
494
+ # 1e6**9 is noniljoona but 1e6**19 is undevigintiljoona (1 from 20)
495
+ elif numword.startswith("nove"):
496
+ numword = high[i + -10]
497
+ numword = "unde" + numword[len("nove") + 1:]
498
+
499
+ # apply general replacements to all numwords
500
+ for repl in replacements:
501
+ numword = numword.replace(repl[0], repl[1])
502
+ translated.append(numword)
503
+
504
+ max = 6 * len(translated)
505
+ for word, n in zip(translated, range(max, 0, -6)):
506
+ if n == 6:
507
+ # irregularity considering short scale and long scale
508
+ self.cards[10 ** 9] = ("miljard", 5)
509
+ self.ords[10 ** 9] = ("miljardi", 45)
510
+ self.cards[10 ** n] = (word + "iljoon", 10)
511
+ self.ords[10 ** n] = (word + "iljoona", 45)
512
+
513
+ def set_mid_numwords(self, cards, ords):
514
+ for key, val in cards:
515
+ self.cards[key] = val
516
+ for key, val in ords:
517
+ self.ords[key] = val
518
+
519
+ def set_low_numwords(self, cards, ords):
520
+ for key, val in cards:
521
+ self.cards[key] = val
522
+ for key, val in ords:
523
+ self.ords[key] = val
524
+
525
+ def setup(self):
526
+ super(Num2Word_FI, self).setup()
527
+
528
+ self.negword = "miinus "
529
+ self.pointword = "pilkku"
530
+ self.exclude_title = ["pilkku", "miinus"]
531
+
532
+ self.mid_numwords = [
533
+ (1000, ("tuha", 46)),
534
+ (100, ("sa", 109)),
535
+ (90, [("yhdeks", 110), ("kymmen", 132, PTV)]),
536
+ (80, [("kahdeks", 110), ("kymmen", 132, PTV)]),
537
+ (70, [("seitsem", 110), ("kymmen", 132, PTV)]),
538
+ (60, [("kuu", 27), ("kymmen", 132, PTV)]),
539
+ (50, [("vii", 27), ("kymmen", 132, PTV)]),
540
+ (40, [("nelj", 10), ("kymmen", 132, PTV)]),
541
+ (30, [("kolm", 108), ("kymmen", 132, PTV)]),
542
+ ]
543
+
544
+ self.mid_ords = [
545
+ (1000, ("tuhanne", 45)),
546
+ (100, ("sada", 45)),
547
+ (90, [("yhdeksä", 45), ("kymmene", 45)]),
548
+ (80, [("kahdeksa", 45), ("kymmene", 45)]),
549
+ (70, [("seitsemä", 45), ("kymmene", 45)]),
550
+ (60, [("kuude", 45), ("kymmene", 45)]),
551
+ (50, [("viide", 45), ("kymmene", 45)]),
552
+ (40, [("neljä", 45), ("kymmene", 45)]),
553
+ (30, [("kolma", 45), ("kymmene", 45)]),
554
+ ]
555
+
556
+ self.low_numwords = [
557
+ (20, [("ka", 31), ("kymmen", 132, PTV)]),
558
+ (19, [("yhdeks", 110), "toista"]),
559
+ (18, [("kahdeks", 110), "toista"]),
560
+ (17, [("seitsem", 110), "toista"]),
561
+ (16, [("kuu", 27), "toista"]),
562
+ (15, [("vii", 27), "toista"]),
563
+ (14, [("nelj", 10), "toista"]),
564
+ (13, [("kolm", 108), "toista"]),
565
+ (12, [("ka", 31), "toista"]),
566
+ (11, [("y", 31), "toista"]),
567
+ (10, ("kymmen", 132)),
568
+ (9, ("yhdeks", 110)),
569
+ (8, ("kahdeks", 110)),
570
+ (7, ("seitsem", 110)),
571
+ (6, ("kuu", 27)),
572
+ (5, ("vii", 27)),
573
+ (4, ("nelj", 10)),
574
+ (3, ("kolm", 108)),
575
+ (2, ("ka", 31)),
576
+ (1, ("y", 31)),
577
+ (0, ("noll", 10)),
578
+ ]
579
+
580
+ self.low_ords = [
581
+ (20, [("kahde", 45), ("kymmene", 45)]),
582
+ (19, [("yhdeksä", 45), "toista"]),
583
+ (18, [("kahdeksa", 45), "toista"]),
584
+ (17, [("seitsemä", 45), "toista"]),
585
+ (16, [("kuude", 45), "toista"]),
586
+ (15, [("viide", 45), "toista"]),
587
+ (14, [("neljä", 45), "toista"]),
588
+ (13, [("kolma", 45), "toista"]),
589
+ (12, [("kahde", 45), "toista"]),
590
+ (11, [("yhde", 45), "toista"]),
591
+ (10, ("kymmene", 45)),
592
+ (9, ("yhdeksä", 45)),
593
+ (8, ("kahdeksa", 45)),
594
+ (7, ("seitsemä", 45)),
595
+ (6, ("kuude", 45)),
596
+ (5, ("viide", 45)),
597
+ (4, ("neljä", 45)),
598
+ (3, ("kolma", 45)),
599
+ (2, ("toi", 38)),
600
+ (1, ("ensimmäi", 38)),
601
+ (0, ("nolla", 45)),
602
+ ]
603
+
604
+ def merge(self, lpair, rpair, options):
605
+ ltext, lnum = lpair
606
+ rtext, rnum = rpair
607
+
608
+ # http://www.kielitoimistonohjepankki.fi/ohje/49
609
+ fmt = "%s%s"
610
+ # ignore lpair if lnum is 1
611
+ if lnum == 1:
612
+ rtext = inflect(rtext, options)
613
+ return (rtext, rnum)
614
+ # rnum is added to lnum
615
+ elif lnum > rnum:
616
+ ltext = inflect(ltext, options)
617
+ rtext = inflect(rtext, options)
618
+ # separate groups with space
619
+ if lnum >= 1000:
620
+ fmt = "%s %s"
621
+ return (fmt % (ltext, rtext), lnum + rnum)
622
+ # rnum is multiplied by lnum
623
+ elif lnum < rnum:
624
+ if options.ordinal:
625
+ # kahdessadas, not toinensadas
626
+ if lnum == 2:
627
+ ltext = ("kahde", 45)
628
+ rtext = inflect(rtext, options)
629
+ else:
630
+ # kaksituhatta but kahdettuhannet
631
+ rcase = options.case
632
+ if options.case == NOM and not options.plural:
633
+ rcase = PTV
634
+ rtext = inflect(rtext, options.variation(case=rcase))
635
+ ltext = inflect(ltext, options)
636
+ return (fmt % (ltext, rtext), lnum * rnum)
637
+
638
+ def to_cardinal(self, value, case='nominative', plural=False, prefer=None):
639
+ case = NAME_TO_CASE[case]
640
+ options = Options(False, case, plural, prefer)
641
+ try:
642
+ assert int(value) == value
643
+ except (ValueError, TypeError, AssertionError):
644
+ if case != NOM:
645
+ raise NotImplementedError(
646
+ "Cases other than nominative are not implemented for "
647
+ "cardinal floating point numbers.")
648
+ return self.to_cardinal_float(value)
649
+
650
+ out = ""
651
+ if value < 0:
652
+ value = abs(value)
653
+ out = self.negword
654
+
655
+ if value >= self.MAXVAL:
656
+ raise OverflowError(self.errmsg_toobig % (value, self.MAXVAL))
657
+
658
+ val = self.splitnum(value, options)
659
+ words, num = self.clean(val, options)
660
+ return self.title(out + words)
661
+
662
+ def to_ordinal(self, value, case='nominative', plural=False, prefer=None):
663
+ case = NAME_TO_CASE[case]
664
+ options = Options(True, case, plural, prefer)
665
+
666
+ self.verify_ordinal(value)
667
+ if value >= self.MAXVAL:
668
+ raise OverflowError(self.errmsg_toobig % (value, self.MAXVAL))
669
+
670
+ val = self.splitnum(value, options)
671
+ words, num = self.clean(val, options)
672
+ return self.title(words)
673
+
674
+ def to_ordinal_num(self, value, case='nominative', plural=False):
675
+ """Convert to abbreviated ordinal form (1., 2., 3. etc)"""
676
+ return str(value) + '.'
677
+
678
+ def to_year(self, val, suffix=None, longval=True):
679
+ suffix = suffix or ""
680
+ if val < 0:
681
+ val = abs(val)
682
+ suffix = suffix or " ennen ajanlaskun alkua"
683
+ return self.to_cardinal(val).replace(" ", "") + suffix
684
+
685
+ def to_currency(self, val, currency="EUR", cents=True, separator=" ja",
686
+ adjective=False):
687
+ # Handle integers specially - just add currency name without cents
688
+ if isinstance(val, int):
689
+ try:
690
+ cr1, cr2 = self.CURRENCY_FORMS[currency]
691
+ except (KeyError, AttributeError):
692
+ # Fallback to base implementation for unknown currency
693
+ return super(Num2Word_FI, self).to_currency(
694
+ val, currency=currency, cents=cents, separator=separator,
695
+ adjective=adjective)
696
+
697
+ minus_str = self.negword if val < 0 else ""
698
+ abs_val = abs(val)
699
+ money_str = self.to_cardinal(abs_val)
700
+
701
+ # Proper pluralization for currency
702
+ if abs_val == 1:
703
+ currency_str = cr1[0] if isinstance(cr1, tuple) else cr1
704
+ else:
705
+ currency_str = cr1[1] if isinstance(cr1, tuple) and len(cr1) > 1 else (cr1[0] if isinstance(cr1, tuple) else cr1)
706
+
707
+ return (u'%s %s %s' % (minus_str, money_str, currency_str)).strip()
708
+
709
+ # For floats, use the parent class implementation
710
+ return super(Num2Word_FI, self).to_currency(
711
+ val, currency=currency, cents=cents, separator=separator,
712
+ adjective=adjective)
713
+
714
+ def splitnum(self, value, options):
715
+ elems = self.ords if options.ordinal else self.cards
716
+ for elem in elems:
717
+ if elem > value:
718
+ continue
719
+
720
+ out = []
721
+ if value == 0:
722
+ div, mod = 1, 0
723
+ else:
724
+ div, mod = divmod(value, elem)
725
+
726
+ if div == 1:
727
+ out.append((elems[1], 1))
728
+ else:
729
+ if div == value: # The system tallies, eg Roman Numerals
730
+ return [(div * elems[elem], div * elem)]
731
+ out.append(self.splitnum(div, options))
732
+
733
+ out.append((elems[elem], elem))
734
+
735
+ if mod:
736
+ out.append(self.splitnum(mod, options))
737
+
738
+ return out
739
+
740
+ def clean(self, val, options):
741
+ out = val
742
+ while len(val) != 1:
743
+ out = []
744
+ left, right = val[:2]
745
+ if isinstance(left, tuple) and isinstance(right, tuple):
746
+ out.append(self.merge(left, right, options))
747
+ if val[2:]:
748
+ out.append(val[2:])
749
+ else:
750
+ for elem in val:
751
+ if isinstance(elem, list):
752
+ if len(elem) == 1:
753
+ out.append(elem[0])
754
+ else:
755
+ out.append(self.clean(elem, options))
756
+ else:
757
+ out.append(elem)
758
+ val = out
759
+ return out[0]