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_OC.py ADDED
@@ -0,0 +1,135 @@
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
+ # Occitan language support
24
+ class Num2Word_OC(Num2Word_Base):
25
+ CURRENCY_FORMS = {
26
+ 'EUR': (('èuro', 'èuros'), ('centim', 'centims')),
27
+ 'USD': (('dollar', 'dollars'), ('cent', 'cents'))
28
+ }
29
+
30
+ def setup(self):
31
+ self.negword = "minus "
32
+ self.pointword = "point"
33
+ self.ones = ['', 'un', 'dos', 'tres', 'quatre', 'cinc', 'sièis', 'sèt', 'uèch', 'nòu']
34
+ self.tens = ['', 'dètz', 'vint', 'trenta', 'quaranta', 'cinquanta', 'seissanta', 'setanta', 'ochanta', 'nonanta']
35
+ self.hundred = "cent"
36
+ self.thousand = "mil"
37
+ self.million = "milion"
38
+
39
+ def to_cardinal(self, number):
40
+ """Convert a number to its word representation in Occitan."""
41
+ n = str(number).strip()
42
+
43
+ if n.startswith('-'):
44
+ n = n[1:]
45
+ ret = self.negword
46
+ else:
47
+ ret = ""
48
+
49
+ if '.' in n:
50
+ left, right = n.split('.', 1)
51
+ ret += self._int_to_word(int(left)) + " " + self.pointword + " "
52
+ for digit in right:
53
+ ret += self._int_to_word(int(digit)) + " "
54
+ return ret.strip()
55
+ else:
56
+ return (ret + self._int_to_word(int(n))).strip()
57
+
58
+ def _int_to_word(self, number):
59
+ """Convert an integer to its word representation in Occitan."""
60
+ if number == 0:
61
+ return self.ones[0] if self.ones[0] else "zero"
62
+
63
+ if number < 0:
64
+ return self.negword + self._int_to_word(abs(number))
65
+ elif number < 10:
66
+ return self.ones[number]
67
+ elif number < 100:
68
+ tens_val = number // 10
69
+ ones_val = number % 10
70
+ if ones_val == 0:
71
+ return self.tens[tens_val]
72
+ else:
73
+ return self.tens[tens_val] + " " + self.ones[ones_val]
74
+ elif number < 1000:
75
+ hundreds_val = number // 100
76
+ remainder = number % 100
77
+ result = self.ones[hundreds_val] + " " + self.hundred
78
+ if remainder:
79
+ result += " " + self._int_to_word(remainder)
80
+ return result
81
+ elif number < 1000000:
82
+ thousands_val = number // 1000
83
+ remainder = number % 1000
84
+ result = self._int_to_word(thousands_val) + " " + self.thousand
85
+ if remainder:
86
+ result += " " + self._int_to_word(remainder)
87
+ return result
88
+ elif number < 1000000000:
89
+ millions_val = number // 1000000
90
+ remainder = number % 1000000
91
+ result = self._int_to_word(millions_val) + " " + self.million
92
+ if remainder:
93
+ result += " " + self._int_to_word(remainder)
94
+ return result
95
+ else:
96
+ return str(number) # Fallback for very large numbers
97
+
98
+ def to_ordinal(self, number):
99
+ """Convert to ordinal in Occitan."""
100
+ cardinal = self.to_cardinal(number)
101
+ # Language-specific ordinal suffix
102
+ return cardinal + "-en"
103
+
104
+ def to_ordinal_num(self, number):
105
+ """Convert to abbreviated ordinal."""
106
+ return str(number) + "."
107
+
108
+ def to_year(self, val, longval=True):
109
+ """Convert to year in Occitan."""
110
+ return self.to_cardinal(val)
111
+
112
+ def to_currency(self, val, currency='EUR', cents=True, separator=' ', adjective=False):
113
+ """Convert to currency in Occitan."""
114
+ is_negative = False
115
+ if val < 0:
116
+ is_negative = True
117
+ val = abs(val)
118
+
119
+ parts = str(val).split('.')
120
+ left = int(parts[0]) if parts[0] else 0
121
+ right = int(parts[1][:2].ljust(2, '0')) if len(parts) > 1 and parts[1] else 0
122
+
123
+ cr1, cr2 = self.CURRENCY_FORMS.get(currency, list(self.CURRENCY_FORMS.values())[0])
124
+
125
+ left_str = self._int_to_word(left)
126
+ result = left_str + " " + (cr1[1] if left != 1 else cr1[0])
127
+
128
+ if cents and right:
129
+ cents_str = self._int_to_word(right)
130
+ result += separator + cents_str + " " + (cr2[1] if right != 1 else cr2[0])
131
+
132
+ if is_negative:
133
+ result = self.negword + result
134
+
135
+ return result.strip()
num2words2/lang_PA.py ADDED
@@ -0,0 +1,159 @@
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
+ # Punjabi language support
24
+ class Num2Word_PA(Num2Word_Base):
25
+ CURRENCY_FORMS = {
26
+ 'INR': (('ਰੁਪਈਆ', 'ਰੁਪਏ'), ('ਪੈਸਾ', 'ਪੈਸੇ')),
27
+ 'USD': (('dollar', 'dollars'), ('cent', 'cents')),
28
+ 'EUR': (('euro', 'euros'), ('cent', 'cents')),
29
+ }
30
+
31
+ def setup(self):
32
+ self.negword = "ਮਾਇਨਸ "
33
+ self.pointword = "ਦਸ਼ਮਲਵ"
34
+ self.ones = ['', 'ਇੱਕ', 'ਦੋ', 'ਤਿੰਨ', 'ਚਾਰ', 'ਪੰਜ', 'ਛੇ', 'ਸੱਤ', 'ਅੱਠ', 'ਨੌ']
35
+ self.tens = ['', 'ਦਸ', 'ਵੀਹ', 'ਤੀਹ', 'ਚਾਲੀ', 'ਪੰਜਾਹ', 'ਸੱਠ', 'ਸੱਤਰ', 'ਅੱਸੀ', 'ਨੱਬੇ']
36
+ self.teens = ['ਦਸ', 'ਗਿਆਰਾਂ', 'ਬਾਰਾਂ', 'ਤੇਰਾਂ', 'ਚੌਦਾਂ', 'ਪੰਦਰਾਂ', 'ਸੋਲਾਂ', 'ਸਤਾਰਾਂ', 'ਅਠਾਰਾਂ', 'ਉੱਨੀ']
37
+ self.hundred = "ਸੌ"
38
+ self.thousand = "ਹਜ਼ਾਰ"
39
+ self.lakh = "ਲੱਖ"
40
+ self.crore = "ਕਰੋੜ"
41
+ self.zero = "ਸਿਫਰ"
42
+
43
+ def to_cardinal(self, number):
44
+ """Convert a number to its word representation in Punjabi."""
45
+ n = str(number).strip()
46
+
47
+ if n.startswith('-'):
48
+ n = n[1:]
49
+ ret = self.negword
50
+ else:
51
+ ret = ""
52
+
53
+ if '.' in n:
54
+ left, right = n.split('.', 1)
55
+ ret += self._int_to_word(int(left)) + " " + self.pointword + " "
56
+ ret += " ".join(self._int_to_word(int(d)) for d in right)
57
+ return ret.strip()
58
+ else:
59
+ return (ret + self._int_to_word(int(n))).strip()
60
+
61
+ def _int_to_word(self, number):
62
+ """Convert an integer to its word representation in Punjabi."""
63
+ if number == 0:
64
+ return self.zero
65
+
66
+ if number < 0:
67
+ return self.negword + self._int_to_word(abs(number))
68
+ elif number < 10:
69
+ return self.ones[number]
70
+ elif number < 20:
71
+ return self.teens[number - 10]
72
+ elif number < 100:
73
+ result = self.tens[number // 10]
74
+ if number % 10:
75
+ result += " " + self.ones[number % 10]
76
+ return result
77
+ elif number < 1000:
78
+ result = self.ones[number // 100] + " " + self.hundred
79
+ remainder = number % 100
80
+ if remainder:
81
+ result += " " + self._int_to_word(remainder)
82
+ return result
83
+ elif number < 100000: # Lakh
84
+ result = self._int_to_word(number // 1000) + " " + self.thousand
85
+ remainder = number % 1000
86
+ if remainder:
87
+ result += " " + self._int_to_word(remainder)
88
+ return result
89
+ elif number < 10000000: # Crore
90
+ result = self._int_to_word(number // 100000) + " " + self.lakh
91
+ remainder = number % 100000
92
+ if remainder:
93
+ result += " " + self._int_to_word(remainder)
94
+ return result
95
+ elif number < 1000000000:
96
+ result = self._int_to_word(number // 10000000) + " " + self.crore
97
+ remainder = number % 10000000
98
+ if remainder:
99
+ result += " " + self._int_to_word(remainder)
100
+ return result
101
+ else:
102
+ return str(number) # Fallback for very large numbers
103
+
104
+ def to_ordinal(self, number):
105
+ """Convert to ordinal in Punjabi.
106
+
107
+ Punjabi ordinals use 'ਵਾਂ' suffix for masculine and 'ਵੀਂ' for feminine.
108
+ Special forms exist for first few ordinals.
109
+ """
110
+ if number == 1:
111
+ return "ਪਹਿਲਾ" # first
112
+ elif number == 2:
113
+ return "ਦੂਜਾ" # second
114
+ elif number == 3:
115
+ return "ਤੀਜਾ" # third
116
+ elif number == 4:
117
+ return "ਚੌਥਾ" # fourth
118
+ elif number == 5:
119
+ return "ਪੰਜਵਾਂ" # fifth
120
+ else:
121
+ # For other numbers, add ਵਾਂ suffix
122
+ cardinal = self.to_cardinal(number)
123
+ return cardinal + "ਵਾਂ"
124
+
125
+ def to_ordinal_num(self, number):
126
+ """Convert to abbreviated ordinal."""
127
+ return str(number) + "."
128
+
129
+ def to_year(self, val, longval=True):
130
+ """Convert to year in Punjabi."""
131
+ if val < 0:
132
+ return "BC " + self.to_cardinal(abs(val))
133
+ else:
134
+ return "" + self.to_cardinal(val)
135
+
136
+ def to_currency(self, val, currency='INR', cents=True, separator=' ', adjective=False):
137
+ """Convert to currency in Punjabi."""
138
+ is_negative = False
139
+ if val < 0:
140
+ is_negative = True
141
+ val = abs(val)
142
+
143
+ parts = str(val).split('.')
144
+ left = int(parts[0]) if parts[0] else 0
145
+ right = int(parts[1][:2].ljust(2, '0')) if len(parts) > 1 and parts[1] else 0
146
+
147
+ cr1, cr2 = self.CURRENCY_FORMS.get(currency, self.CURRENCY_FORMS['INR'])
148
+
149
+ left_str = self._int_to_word(left)
150
+ result = left_str + " " + (cr1[1] if left != 1 else cr1[0])
151
+
152
+ if cents and right:
153
+ cents_str = self._int_to_word(right)
154
+ result += separator + cents_str + " " + (cr2[1] if right != 1 else cr2[0])
155
+
156
+ if is_negative:
157
+ result = self.negword + result
158
+
159
+ return result.strip()
num2words2/lang_PL.py ADDED
@@ -0,0 +1,336 @@
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 itertools
21
+
22
+ from .base import Num2Word_Base
23
+ from .utils import get_digits, splitbyx
24
+
25
+ ZERO = ('zero',)
26
+
27
+ ONES = {
28
+ 1: ('jeden',),
29
+ 2: ('dwa',),
30
+ 3: ('trzy',),
31
+ 4: ('cztery',),
32
+ 5: ('pięć',),
33
+ 6: ('sześć',),
34
+ 7: ('siedem',),
35
+ 8: ('osiem',),
36
+ 9: ('dziewięć',),
37
+ }
38
+
39
+ ONES_ORDINALS = {
40
+ 1: ('pierwszy', "pierwszo"),
41
+ 2: ('drugi', "dwu"),
42
+ 3: ('trzeci', "trzy"),
43
+ 4: ('czwarty', "cztero"),
44
+ 5: ('piąty', "pięcio"),
45
+ 6: ('szósty', "sześcio"),
46
+ 7: ('siódmy', "siedmio"),
47
+ 8: ('ósmy', "ośmio"),
48
+ 9: ('dziewiąty', "dziewięcio"),
49
+ 10: ('dziesiąty', "dziesięcio"),
50
+ 11: ('jedenasty', "jedenasto"),
51
+ 12: ('dwunasty', "dwunasto"),
52
+ 13: ('trzynasty', "trzynasto"),
53
+ 14: ('czternasty', "czternasto"),
54
+ 15: ('piętnasty', "piętnasto"),
55
+ 16: ('szesnasty', "szesnasto"),
56
+ 17: ('siedemnasty', "siedemnasto"),
57
+ 18: ('osiemnasty', "osiemnasto"),
58
+ 19: ('dziewiętnasty', "dziewiętnasto"),
59
+ }
60
+
61
+ TENS = {
62
+ 0: ('dziesięć',),
63
+ 1: ('jedenaście',),
64
+ 2: ('dwanaście',),
65
+ 3: ('trzynaście',),
66
+ 4: ('czternaście',),
67
+ 5: ('piętnaście',),
68
+ 6: ('szesnaście',),
69
+ 7: ('siedemnaście',),
70
+ 8: ('osiemnaście',),
71
+ 9: ('dziewiętnaście',),
72
+ }
73
+
74
+
75
+ TWENTIES = {
76
+ 2: ('dwadzieścia',),
77
+ 3: ('trzydzieści',),
78
+ 4: ('czterdzieści',),
79
+ 5: ('pięćdziesiąt',),
80
+ 6: ('sześćdziesiąt',),
81
+ 7: ('siedemdziesiąt',),
82
+ 8: ('osiemdziesiąt',),
83
+ 9: ('dziewięćdziesiąt',),
84
+ }
85
+
86
+ TWENTIES_ORDINALS = {
87
+ 2: ('dwudziesty', "dwudziesto"),
88
+ 3: ('trzydziesty', "trzydiesto"),
89
+ 4: ('czterdziesty', "czterdziesto"),
90
+ 5: ('pięćdziesiąty', "pięćdziesięcio"),
91
+ 6: ('sześćdziesiąty', "sześćdziesięcio"),
92
+ 7: ('siedemdziesiąty', "siedemdziesięcio"),
93
+ 8: ('osiemdziesiąty', "osiemdziesięcio"),
94
+ 9: ('dziewięćdziesiąty', "dziewięćdziesięcio"),
95
+ }
96
+
97
+ HUNDREDS = {
98
+ 1: ('sto',),
99
+ 2: ('dwieście',),
100
+ 3: ('trzysta',),
101
+ 4: ('czterysta',),
102
+ 5: ('pięćset',),
103
+ 6: ('sześćset',),
104
+ 7: ('siedemset',),
105
+ 8: ('osiemset',),
106
+ 9: ('dziewięćset',),
107
+ }
108
+
109
+ HUNDREDS_ORDINALS = {
110
+ 1: ('setny', "stu"),
111
+ 2: ('dwusetny', "dwustu"),
112
+ 3: ('trzysetny', "trzystu"),
113
+ 4: ('czterysetny', "czterystu"),
114
+ 5: ('pięćsetny', "pięcset"),
115
+ 6: ('sześćsetny', "sześćset"),
116
+ 7: ('siedemsetny', "siedemset"),
117
+ 8: ('osiemsetny', "ośiemset"),
118
+ 9: ('dziewięćsetny', "dziewięćset"),
119
+ }
120
+
121
+ THOUSANDS = {
122
+ 1: ('tysiąc', 'tysiące', 'tysięcy'), # 10^3
123
+ }
124
+
125
+ prefixes_ordinal = {
126
+ 1: "tysięczny",
127
+ 2: "milionowy",
128
+ 3: "milairdowy"
129
+ }
130
+
131
+ prefixes = ( # 10^(6*x)
132
+ "mi", # 10^6
133
+ "bi", # 10^12
134
+ "try", # 10^18
135
+ "kwadry", # 10^24
136
+ "kwinty", # 10^30
137
+ "seksty", # 10^36
138
+ "septy", # 10^42
139
+ "okty", # 10^48
140
+ "nony", # 10^54
141
+ "decy" # 10^60
142
+ )
143
+ suffixes = ("lion", "liard") # 10^x or 10^(x+3)
144
+
145
+ for idx, (p, s) in enumerate(itertools.product(prefixes, suffixes)):
146
+ name = p + s
147
+ THOUSANDS[idx + 2] = (name, name + 'y', name + 'ów')
148
+
149
+
150
+ class Num2Word_PL(Num2Word_Base):
151
+ CURRENCY_FORMS = {
152
+ 'PLN': (
153
+ ('złoty', 'złote', 'złotych'), ('grosz', 'grosze', 'groszy')
154
+ ),
155
+ 'EUR': (
156
+ ('euro', 'euro', 'euro'), ('cent', 'centy', 'centów')
157
+ ),
158
+ 'USD': (
159
+ (
160
+ 'dolar amerykański',
161
+ 'dolary amerykańskie',
162
+ 'dolarów amerykańskich'
163
+ ),
164
+ (
165
+ 'cent',
166
+ 'centy',
167
+ 'centów'
168
+ )
169
+ ),
170
+ 'GBP': (
171
+ (
172
+ 'funt brytyjski',
173
+ 'funty brytyjskie',
174
+ 'funtów brytyjskich'
175
+ ),
176
+ (
177
+ 'pens',
178
+ 'pensy',
179
+ 'pensów'
180
+ )
181
+ ),
182
+ }
183
+
184
+ def setup(self):
185
+ self.negword = "minus"
186
+ self.pointword = "przecinek"
187
+
188
+ def to_cardinal(self, number):
189
+ n = str(number).replace(',', '.')
190
+ if '.' in n:
191
+ is_negative = n.startswith('-')
192
+ abs_n = n[1:] if is_negative else n
193
+ left, right = abs_n.split('.')
194
+
195
+ # Say each decimal digit individually
196
+ decimal_parts = []
197
+ for digit in right:
198
+ if digit == '0':
199
+ decimal_parts.append(ZERO[0])
200
+ else:
201
+ decimal_parts.append(ONES[int(digit)][0])
202
+ decimal_part = ' '.join(decimal_parts)
203
+
204
+ result = u'%s %s %s' % (
205
+ self._int2word(int(left)),
206
+ self.pointword,
207
+ decimal_part
208
+ )
209
+ if is_negative:
210
+ result = self.negword + ' ' + result
211
+ return result
212
+ else:
213
+ # Handle negative integers
214
+ is_negative = n.startswith('-')
215
+ if is_negative:
216
+ abs_n = n[1:]
217
+ result = self._int2word(int(abs_n))
218
+ return self.negword + ' ' + result
219
+ else:
220
+ return self._int2word(int(n))
221
+
222
+ def pluralize(self, n, forms):
223
+ if n == 1:
224
+ form = 0
225
+ elif 5 > n % 10 > 1 and (n % 100 < 10 or n % 100 > 20):
226
+ form = 1
227
+ else:
228
+ form = 2
229
+ return forms[form]
230
+
231
+ def last_fragment_to_ordinal(self, last, words, level):
232
+ n1, n2, n3 = get_digits(last)
233
+ last_two = n2 * 10 + n1
234
+ if last_two == 0:
235
+ words.append(HUNDREDS_ORDINALS[n3][level])
236
+ elif level == 1 and last == 1:
237
+ return
238
+ elif last_two < 20:
239
+ if n3 > 0:
240
+ words.append(HUNDREDS[n3][level])
241
+ words.append(ONES_ORDINALS[last_two][level])
242
+ elif last_two % 10 == 0:
243
+ if n3 > 0:
244
+ words.append(HUNDREDS[n3][level])
245
+ words.append(TWENTIES_ORDINALS[n2][level])
246
+ else:
247
+ if n3 > 0:
248
+ words.append(HUNDREDS[n3][0])
249
+ words.append(TWENTIES_ORDINALS[n2][0])
250
+ words.append(ONES_ORDINALS[n1][0])
251
+
252
+ def to_currency(self, val, currency='EUR', cents=True, separator=',',
253
+ adjective=False):
254
+ # Handle integers specially - just add currency name without cents
255
+ if isinstance(val, int):
256
+ try:
257
+ cr1, cr2 = self.CURRENCY_FORMS[currency]
258
+ except (KeyError, AttributeError):
259
+ # Fallback to base implementation for unknown currency
260
+ return super(Num2Word_PL, self).to_currency(
261
+ val, currency=currency, cents=cents, separator=separator,
262
+ adjective=adjective)
263
+
264
+ minus_str = self.negword if val < 0 else ""
265
+ abs_val = abs(val)
266
+ money_str = self.to_cardinal(abs_val)
267
+
268
+ # For Polish, check if number ends with "jeden" (one)
269
+ # Numbers ending in compound "jeden" (like 21, 121, 10121) get special treatment
270
+ # They use the second form (nominative plural for 2-4)
271
+ if abs_val > 1 and money_str.endswith("jeden"):
272
+ # Use nominative plural form (2-4) for compound numbers ending with "jeden"
273
+ currency_str = cr1[1] if len(cr1) > 1 else cr1[0]
274
+ else:
275
+ # Use the pluralize method for proper currency form selection
276
+ currency_str = self.pluralize(abs_val, cr1)
277
+
278
+ return (u'%s %s %s' % (minus_str, money_str, currency_str)).strip()
279
+
280
+ # For floats, use the parent class implementation
281
+ return super(Num2Word_PL, self).to_currency(
282
+ val, currency=currency, cents=cents, separator=separator,
283
+ adjective=adjective)
284
+
285
+ def to_ordinal(self, number):
286
+ if number % 1 != 0:
287
+ raise NotImplementedError()
288
+ words = []
289
+ fragments = list(splitbyx(str(number), 3))
290
+ level = 0
291
+ last = fragments[-1]
292
+ while last == 0:
293
+ level = level + 1
294
+ fragments.pop()
295
+ last = fragments[-1]
296
+ if len(fragments) > 1:
297
+ pre_part = self._int2word(number - (last * 1000**level))
298
+ words.append(pre_part)
299
+ self.last_fragment_to_ordinal(last, words, 0 if level == 0 else 1)
300
+ output = " ".join(words)
301
+ if last == 1 and level > 0 and output != "":
302
+ output = output + " "
303
+ if level > 0:
304
+ output = output + prefixes_ordinal[level]
305
+ return output
306
+
307
+ def _int2word(self, n):
308
+ if n == 0:
309
+ return ZERO[0]
310
+
311
+ words = []
312
+ chunks = list(splitbyx(str(n), 3))
313
+ i = len(chunks)
314
+ for x in chunks:
315
+ i -= 1
316
+
317
+ if x == 0:
318
+ continue
319
+
320
+ n1, n2, n3 = get_digits(x)
321
+
322
+ if n3 > 0:
323
+ words.append(HUNDREDS[n3][0])
324
+
325
+ if n2 > 1:
326
+ words.append(TWENTIES[n2][0])
327
+
328
+ if n2 == 1:
329
+ words.append(TENS[n1][0])
330
+ elif n1 > 0 and not (i > 0 and x == 1):
331
+ words.append(ONES[n1][0])
332
+
333
+ if i > 0:
334
+ words.append(self.pluralize(x, THOUSANDS[i]))
335
+
336
+ return ' '.join(words)