n2words 5.0.0 → 5.1.0

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 (309) hide show
  1. package/CHANGELOG.md +128 -42
  2. package/README.md +6 -4
  3. package/dist/am-ET.js +2 -2
  4. package/dist/am-ET.umd.js +2 -2
  5. package/dist/am-Latn-ET.js +2 -2
  6. package/dist/am-Latn-ET.umd.js +2 -2
  7. package/dist/ar-SA.js +2 -2
  8. package/dist/ar-SA.umd.js +2 -2
  9. package/dist/az-AZ.js +2 -2
  10. package/dist/az-AZ.umd.js +2 -2
  11. package/dist/bn-BD.js +2 -2
  12. package/dist/bn-BD.umd.js +2 -2
  13. package/dist/cs-CZ.js +2 -2
  14. package/dist/cs-CZ.umd.js +2 -2
  15. package/dist/da-DK.js +2 -2
  16. package/dist/da-DK.umd.js +2 -2
  17. package/dist/de-DE.js +2 -2
  18. package/dist/de-DE.umd.js +2 -2
  19. package/dist/el-GR.js +2 -2
  20. package/dist/el-GR.umd.js +2 -2
  21. package/dist/en-AU.js +2 -2
  22. package/dist/en-AU.umd.js +2 -2
  23. package/dist/en-BD.js +2 -2
  24. package/dist/en-BD.umd.js +2 -2
  25. package/dist/en-CA.js +2 -2
  26. package/dist/en-CA.umd.js +2 -2
  27. package/dist/en-GB.js +2 -2
  28. package/dist/en-GB.umd.js +2 -2
  29. package/dist/en-GH.js +2 -2
  30. package/dist/en-GH.umd.js +2 -2
  31. package/dist/en-IE.js +2 -2
  32. package/dist/en-IE.umd.js +2 -2
  33. package/dist/en-IN.js +2 -2
  34. package/dist/en-IN.umd.js +2 -2
  35. package/dist/en-KE.js +2 -2
  36. package/dist/en-KE.umd.js +2 -2
  37. package/dist/en-MY.js +2 -2
  38. package/dist/en-MY.umd.js +2 -2
  39. package/dist/en-NG.js +2 -2
  40. package/dist/en-NG.umd.js +2 -2
  41. package/dist/en-NZ.js +2 -2
  42. package/dist/en-NZ.umd.js +2 -2
  43. package/dist/en-PH.js +2 -2
  44. package/dist/en-PH.umd.js +2 -2
  45. package/dist/en-PK.js +2 -2
  46. package/dist/en-PK.umd.js +2 -2
  47. package/dist/en-SG.js +2 -2
  48. package/dist/en-SG.umd.js +2 -2
  49. package/dist/en-US.js +2 -2
  50. package/dist/en-US.umd.js +2 -2
  51. package/dist/en-ZA.js +2 -2
  52. package/dist/en-ZA.umd.js +2 -2
  53. package/dist/es-ES.js +2 -2
  54. package/dist/es-ES.umd.js +2 -2
  55. package/dist/es-MX.js +2 -2
  56. package/dist/es-MX.umd.js +2 -2
  57. package/dist/es-US.js +2 -2
  58. package/dist/es-US.umd.js +2 -2
  59. package/dist/fa-IR.js +2 -2
  60. package/dist/fa-IR.umd.js +2 -2
  61. package/dist/fi-FI.js +2 -2
  62. package/dist/fi-FI.umd.js +2 -2
  63. package/dist/fil-PH.js +2 -2
  64. package/dist/fil-PH.umd.js +2 -2
  65. package/dist/fr-BE.js +2 -2
  66. package/dist/fr-BE.umd.js +2 -2
  67. package/dist/fr-FR.js +2 -2
  68. package/dist/fr-FR.umd.js +2 -2
  69. package/dist/gu-IN.js +2 -2
  70. package/dist/gu-IN.umd.js +2 -2
  71. package/dist/ha-NG.js +2 -2
  72. package/dist/ha-NG.umd.js +2 -2
  73. package/dist/hbo-IL.js +2 -2
  74. package/dist/hbo-IL.umd.js +2 -2
  75. package/dist/he-IL.js +2 -2
  76. package/dist/he-IL.umd.js +2 -2
  77. package/dist/hi-IN.js +2 -2
  78. package/dist/hi-IN.umd.js +2 -2
  79. package/dist/hr-HR.js +2 -2
  80. package/dist/hr-HR.umd.js +2 -2
  81. package/dist/hu-HU.js +2 -2
  82. package/dist/hu-HU.umd.js +2 -2
  83. package/dist/id-ID.js +2 -2
  84. package/dist/id-ID.umd.js +2 -2
  85. package/dist/it-IT.js +2 -2
  86. package/dist/it-IT.umd.js +2 -2
  87. package/dist/ja-JP.js +2 -2
  88. package/dist/ja-JP.umd.js +2 -2
  89. package/dist/ka-GE.js +2 -2
  90. package/dist/ka-GE.umd.js +2 -2
  91. package/dist/kn-IN.js +2 -2
  92. package/dist/kn-IN.umd.js +2 -2
  93. package/dist/ko-KR.js +2 -2
  94. package/dist/ko-KR.umd.js +2 -2
  95. package/dist/lt-LT.js +2 -2
  96. package/dist/lt-LT.umd.js +2 -2
  97. package/dist/lv-LV.js +2 -2
  98. package/dist/lv-LV.umd.js +2 -2
  99. package/dist/mr-IN.js +2 -2
  100. package/dist/mr-IN.umd.js +2 -2
  101. package/dist/ms-MY.js +2 -2
  102. package/dist/ms-MY.umd.js +2 -2
  103. package/dist/nb-NO.js +2 -2
  104. package/dist/nb-NO.umd.js +2 -2
  105. package/dist/nl-NL.js +2 -2
  106. package/dist/nl-NL.umd.js +2 -2
  107. package/dist/pa-IN.js +2 -2
  108. package/dist/pa-IN.umd.js +2 -2
  109. package/dist/pl-PL.js +2 -2
  110. package/dist/pl-PL.umd.js +2 -2
  111. package/dist/pt-BR.js +2 -2
  112. package/dist/pt-BR.umd.js +2 -2
  113. package/dist/pt-PT.js +2 -2
  114. package/dist/pt-PT.umd.js +2 -2
  115. package/dist/ro-RO.js +2 -2
  116. package/dist/ro-RO.umd.js +2 -2
  117. package/dist/ru-RU.js +2 -2
  118. package/dist/ru-RU.umd.js +2 -2
  119. package/dist/sr-Cyrl-RS.js +2 -2
  120. package/dist/sr-Cyrl-RS.umd.js +2 -2
  121. package/dist/sr-Latn-RS.js +2 -2
  122. package/dist/sr-Latn-RS.umd.js +2 -2
  123. package/dist/sv-SE.js +2 -2
  124. package/dist/sv-SE.umd.js +2 -2
  125. package/dist/sw-KE.js +2 -2
  126. package/dist/sw-KE.umd.js +2 -2
  127. package/dist/ta-IN.js +2 -2
  128. package/dist/ta-IN.umd.js +2 -2
  129. package/dist/te-IN.js +2 -2
  130. package/dist/te-IN.umd.js +2 -2
  131. package/dist/th-TH.js +2 -2
  132. package/dist/th-TH.umd.js +2 -2
  133. package/dist/tr-TR.js +2 -2
  134. package/dist/tr-TR.umd.js +2 -2
  135. package/dist/uk-UA.js +2 -2
  136. package/dist/uk-UA.umd.js +2 -2
  137. package/dist/ur-PK.js +2 -2
  138. package/dist/ur-PK.umd.js +2 -2
  139. package/dist/vi-VN.js +2 -2
  140. package/dist/vi-VN.umd.js +2 -2
  141. package/dist/yo-NG.js +2 -2
  142. package/dist/yo-NG.umd.js +2 -2
  143. package/dist/zh-Hans-CN.js +2 -2
  144. package/dist/zh-Hans-CN.umd.js +2 -2
  145. package/dist/zh-Hant-TW.js +2 -2
  146. package/dist/zh-Hant-TW.umd.js +2 -2
  147. package/package.json +31 -22
  148. package/src/am-ET.d.ts +3 -5
  149. package/src/am-ET.js +41 -16
  150. package/src/am-Latn-ET.d.ts +3 -5
  151. package/src/am-Latn-ET.js +45 -16
  152. package/src/ar-SA.d.ts +44 -18
  153. package/src/ar-SA.js +93 -40
  154. package/src/az-AZ.d.ts +3 -5
  155. package/src/az-AZ.js +58 -20
  156. package/src/bn-BD.d.ts +3 -5
  157. package/src/bn-BD.js +32 -16
  158. package/src/cs-CZ.d.ts +3 -6
  159. package/src/cs-CZ.js +66 -42
  160. package/src/da-DK.d.ts +3 -6
  161. package/src/da-DK.js +53 -48
  162. package/src/de-DE.d.ts +17 -11
  163. package/src/de-DE.js +88 -57
  164. package/src/el-GR.d.ts +3 -6
  165. package/src/el-GR.js +45 -32
  166. package/src/en-AU.d.ts +17 -11
  167. package/src/en-AU.js +56 -41
  168. package/src/en-BD.d.ts +17 -11
  169. package/src/en-BD.js +60 -41
  170. package/src/en-CA.d.ts +36 -18
  171. package/src/en-CA.js +67 -46
  172. package/src/en-GB.d.ts +17 -11
  173. package/src/en-GB.js +56 -41
  174. package/src/en-GH.d.ts +32 -3
  175. package/src/en-GH.js +104 -26
  176. package/src/en-IE.d.ts +17 -11
  177. package/src/en-IE.js +56 -41
  178. package/src/en-IN.d.ts +17 -11
  179. package/src/en-IN.js +60 -41
  180. package/src/en-KE.d.ts +28 -3
  181. package/src/en-KE.js +93 -26
  182. package/src/en-MY.d.ts +26 -3
  183. package/src/en-MY.js +91 -26
  184. package/src/en-NG.d.ts +17 -11
  185. package/src/en-NG.js +56 -41
  186. package/src/en-NZ.d.ts +32 -3
  187. package/src/en-NZ.js +85 -31
  188. package/src/en-PH.d.ts +32 -3
  189. package/src/en-PH.js +97 -26
  190. package/src/en-PK.d.ts +17 -11
  191. package/src/en-PK.js +60 -41
  192. package/src/en-SG.d.ts +28 -3
  193. package/src/en-SG.js +93 -26
  194. package/src/en-US.d.ts +36 -18
  195. package/src/en-US.js +70 -47
  196. package/src/en-ZA.d.ts +17 -11
  197. package/src/en-ZA.js +56 -41
  198. package/src/es-ES.d.ts +53 -21
  199. package/src/es-ES.js +104 -56
  200. package/src/es-MX.d.ts +53 -21
  201. package/src/es-MX.js +104 -56
  202. package/src/es-US.d.ts +53 -21
  203. package/src/es-US.js +92 -51
  204. package/src/fa-IR.d.ts +3 -5
  205. package/src/fa-IR.js +28 -13
  206. package/src/fi-FI.d.ts +3 -6
  207. package/src/fi-FI.js +47 -29
  208. package/src/fil-PH.d.ts +3 -5
  209. package/src/fil-PH.js +61 -28
  210. package/src/fr-BE.d.ts +31 -15
  211. package/src/fr-BE.js +128 -57
  212. package/src/fr-FR.d.ts +31 -16
  213. package/src/fr-FR.js +97 -60
  214. package/src/gu-IN.d.ts +3 -5
  215. package/src/gu-IN.js +31 -16
  216. package/src/ha-NG.d.ts +3 -5
  217. package/src/ha-NG.js +55 -27
  218. package/src/hbo-IL.d.ts +26 -12
  219. package/src/hbo-IL.js +92 -51
  220. package/src/he-IL.d.ts +17 -10
  221. package/src/he-IL.js +92 -50
  222. package/src/hi-IN.d.ts +3 -5
  223. package/src/hi-IN.js +30 -17
  224. package/src/hr-HR.d.ts +21 -10
  225. package/src/hr-HR.js +89 -33
  226. package/src/hu-HU.d.ts +3 -5
  227. package/src/hu-HU.js +57 -23
  228. package/src/id-ID.d.ts +3 -5
  229. package/src/id-ID.js +56 -23
  230. package/src/it-IT.d.ts +17 -11
  231. package/src/it-IT.js +74 -43
  232. package/src/ja-JP.d.ts +3 -6
  233. package/src/ja-JP.js +39 -26
  234. package/src/ka-GE.d.ts +3 -6
  235. package/src/ka-GE.js +38 -26
  236. package/src/kn-IN.d.ts +3 -5
  237. package/src/kn-IN.js +31 -16
  238. package/src/ko-KR.d.ts +3 -6
  239. package/src/ko-KR.js +34 -26
  240. package/src/lt-LT.d.ts +21 -11
  241. package/src/lt-LT.js +64 -42
  242. package/src/lv-LV.d.ts +21 -11
  243. package/src/lv-LV.js +79 -51
  244. package/src/mr-IN.d.ts +3 -5
  245. package/src/mr-IN.js +31 -16
  246. package/src/ms-MY.d.ts +3 -5
  247. package/src/ms-MY.js +58 -24
  248. package/src/nb-NO.d.ts +3 -6
  249. package/src/nb-NO.js +54 -34
  250. package/src/nl-NL.d.ts +41 -20
  251. package/src/nl-NL.js +111 -69
  252. package/src/pa-IN.d.ts +3 -5
  253. package/src/pa-IN.js +32 -16
  254. package/src/pl-PL.d.ts +21 -11
  255. package/src/pl-PL.js +69 -45
  256. package/src/pt-BR.d.ts +22 -11
  257. package/src/pt-BR.js +93 -53
  258. package/src/pt-PT.d.ts +17 -11
  259. package/src/pt-PT.js +80 -48
  260. package/src/ro-RO.d.ts +21 -11
  261. package/src/ro-RO.js +77 -39
  262. package/src/ru-RU.d.ts +35 -15
  263. package/src/ru-RU.js +100 -38
  264. package/src/sr-Cyrl-RS.d.ts +35 -15
  265. package/src/sr-Cyrl-RS.js +100 -38
  266. package/src/sr-Latn-RS.d.ts +35 -15
  267. package/src/sr-Latn-RS.js +100 -38
  268. package/src/sv-SE.d.ts +3 -6
  269. package/src/sv-SE.js +53 -34
  270. package/src/sw-KE.d.ts +3 -5
  271. package/src/sw-KE.js +50 -20
  272. package/src/ta-IN.d.ts +3 -5
  273. package/src/ta-IN.js +29 -17
  274. package/src/te-IN.d.ts +3 -5
  275. package/src/te-IN.js +31 -16
  276. package/src/th-TH.d.ts +3 -5
  277. package/src/th-TH.js +42 -19
  278. package/src/tr-TR.d.ts +17 -11
  279. package/src/tr-TR.js +63 -37
  280. package/src/uk-UA.d.ts +21 -10
  281. package/src/uk-UA.js +89 -33
  282. package/src/ur-PK.d.ts +3 -5
  283. package/src/ur-PK.js +32 -16
  284. package/src/utils/check-max.d.ts +26 -0
  285. package/src/utils/check-max.js +33 -0
  286. package/src/utils/expand-scientific.d.ts +0 -4
  287. package/src/utils/expand-scientific.js +7 -9
  288. package/src/utils/is-plain-object.d.ts +3 -4
  289. package/src/utils/is-plain-object.js +3 -4
  290. package/src/utils/parse-cardinal.d.ts +1 -2
  291. package/src/utils/parse-cardinal.js +12 -9
  292. package/src/utils/parse-currency.d.ts +1 -2
  293. package/src/utils/parse-currency.js +9 -11
  294. package/src/utils/parse-ordinal.d.ts +0 -1
  295. package/src/utils/parse-ordinal.js +9 -10
  296. package/src/utils/resolve-options.d.ts +17 -0
  297. package/src/utils/resolve-options.js +56 -0
  298. package/src/utils/scale.d.ts +49 -0
  299. package/src/utils/scale.js +65 -0
  300. package/src/vi-VN.d.ts +3 -6
  301. package/src/vi-VN.js +41 -28
  302. package/src/yo-NG.d.ts +3 -5
  303. package/src/yo-NG.js +49 -33
  304. package/src/zh-Hans-CN.d.ts +45 -20
  305. package/src/zh-Hans-CN.js +84 -31
  306. package/src/zh-Hant-TW.d.ts +45 -20
  307. package/src/zh-Hant-TW.js +85 -34
  308. package/src/utils/validate-options.d.ts +0 -8
  309. package/src/utils/validate-options.js +0 -16
package/src/pl-PL.js CHANGED
@@ -14,7 +14,9 @@
14
14
  import { parseCardinalValue } from './utils/parse-cardinal.js'
15
15
  import { parseCurrencyValue } from './utils/parse-currency.js'
16
16
  import { parseOrdinalValue } from './utils/parse-ordinal.js'
17
- import { validateOptions } from './utils/validate-options.js'
17
+ import { checkMax } from './utils/check-max.js'
18
+ import { western } from './utils/scale.js'
19
+ import { resolveOptions } from './utils/resolve-options.js'
18
20
 
19
21
  // ============================================================================
20
22
  // Vocabulary (module-level constants)
@@ -31,6 +33,7 @@ const TENS = ['', '', 'dwadzieścia', 'trzydzieści', 'czterdzieści', 'pięćdz
31
33
  const HUNDREDS = ['', 'sto', 'dwieście', 'trzysta', 'czterysta', 'pięćset', 'sześćset', 'siedemset', 'osiemset', 'dziewięćset']
32
34
 
33
35
  // Scale words: [singular, few (2-4), many (5+)]
36
+ /** @type {Record<number, string[]>} */
34
37
  const PLURAL_FORMS = {
35
38
  1: ['tysiąc', 'tysiące', 'tysięcy'],
36
39
  2: ['milion', 'miliony', 'milionów'],
@@ -41,7 +44,7 @@ const PLURAL_FORMS = {
41
44
  7: ['tryliard', 'tryliardy', 'tryliardów'],
42
45
  8: ['kwadrylion', 'kwadryliony', 'kwadrylionów'],
43
46
  9: ['kwaryliard', 'kwadryliardy', 'kwadryliardów'],
44
- 10: ['kwintylion', 'kwintyliony', 'kwintylionów']
47
+ 10: ['kwintylion', 'kwintyliony', 'kwintylionów'],
45
48
  }
46
49
 
47
50
  const ZERO = 'zero'
@@ -86,7 +89,7 @@ const GROSZ_FORMS = ['grosz', 'grosze', 'groszy']
86
89
  * @param {number} n - Segment value
87
90
  * @returns {string} Polish word
88
91
  */
89
- function buildSegment (n) {
92
+ function buildSegment(n) {
90
93
  if (n === 0) return ''
91
94
 
92
95
  const ones = n % 10
@@ -104,7 +107,8 @@ function buildSegment (n) {
104
107
  if (tens === 1) {
105
108
  // Teens (10-19)
106
109
  parts.push(TEENS[ones])
107
- } else {
110
+ }
111
+ else {
108
112
  if (tens >= 2) {
109
113
  parts.push(TENS[tens])
110
114
  }
@@ -121,7 +125,7 @@ function buildSegment (n) {
121
125
  * @param {number} n - Segment value
122
126
  * @returns {string} Polish word
123
127
  */
124
- function buildSegmentFeminine (n) {
128
+ function buildSegmentFeminine(n) {
125
129
  if (n === 0) return ''
126
130
 
127
131
  const ones = n % 10
@@ -138,7 +142,8 @@ function buildSegmentFeminine (n) {
138
142
  // Tens and ones - feminine for ones only
139
143
  if (tens === 1) {
140
144
  parts.push(TEENS[ones])
141
- } else {
145
+ }
146
+ else {
142
147
  if (tens >= 2) {
143
148
  parts.push(TENS[tens])
144
149
  }
@@ -157,12 +162,11 @@ function buildSegmentFeminine (n) {
157
162
  /**
158
163
  * Polish pluralization: 1 = singular, 2-4 = few, else = many.
159
164
  * Special case: 11-19 always use many form.
160
- *
161
165
  * @param {bigint} n - Number to pluralize
162
166
  * @param {string[]} forms - [singular, few, many]
163
167
  * @returns {string} Correct plural form
164
168
  */
165
- function pluralize (n, forms) {
169
+ function pluralize(n, forms) {
166
170
  if (n === 1n) {
167
171
  return forms[0]
168
172
  }
@@ -185,12 +189,11 @@ function pluralize (n, forms) {
185
189
 
186
190
  /**
187
191
  * Converts a non-negative integer to Polish words.
188
- *
189
192
  * @param {bigint} n - Non-negative integer to convert
190
- * @param {Object} options - Conversion options
193
+ * @param {string} gender - Gender for numbers < 1000 ('masculine' or 'feminine')
191
194
  * @returns {string} Polish words
192
195
  */
193
- function integerToWords (n, gender) {
196
+ function integerToWords(n, gender) {
194
197
  if (n === 0n) return ZERO
195
198
 
196
199
  // Fast path: numbers < 1000
@@ -209,7 +212,8 @@ function integerToWords (n, gender) {
209
212
  if (thousands === 1) {
210
213
  // Omit "jeden" before tysiąc
211
214
  result = scaleWord
212
- } else {
215
+ }
216
+ else {
213
217
  result = buildSegment(thousands) + ' ' + scaleWord
214
218
  }
215
219
 
@@ -221,18 +225,16 @@ function integerToWords (n, gender) {
221
225
  }
222
226
 
223
227
  // For numbers >= 1,000,000, use scale decomposition
224
- return buildLargeNumberWords(n, gender)
228
+ return buildLargeNumberWords(n)
225
229
  }
226
230
 
227
231
  /**
228
232
  * Builds words for numbers >= 1,000,000.
229
233
  * Uses BigInt division for faster segment extraction.
230
- *
231
234
  * @param {bigint} n - Number >= 1,000,000
232
- * @param {Object} options - Conversion options
233
235
  * @returns {string} Polish words
234
236
  */
235
- function buildLargeNumberWords (n, gender) {
237
+ function buildLargeNumberWords(n) {
236
238
  // Extract segments using BigInt division (faster than string slicing)
237
239
  // Segments stored least-significant first (index 0 = ones, 1 = thousands, etc.)
238
240
  const segmentValues = []
@@ -256,7 +258,8 @@ function buildLargeNumberWords (n, gender) {
256
258
  if (i === 0) {
257
259
  // Units segment
258
260
  result += segmentWord
259
- } else {
261
+ }
262
+ else {
260
263
  // Scale word needed
261
264
  const forms = PLURAL_FORMS[i]
262
265
  if (forms) {
@@ -265,7 +268,8 @@ function buildLargeNumberWords (n, gender) {
265
268
  if (segment === 1n) {
266
269
  // Omit "jeden" before scale words
267
270
  result += scaleWord
268
- } else {
271
+ }
272
+ else {
269
273
  result += segmentWord + ' ' + scaleWord
270
274
  }
271
275
  }
@@ -277,12 +281,11 @@ function buildLargeNumberWords (n, gender) {
277
281
 
278
282
  /**
279
283
  * Converts decimal digits to Polish words.
280
- *
281
284
  * @param {string} decimalPart - Decimal digits (without the point)
282
- * @param {Object} options - Conversion options
285
+ * @param {string} gender - Gender for numbers < 1000 ('masculine' or 'feminine')
283
286
  * @returns {string} Polish words for decimal part
284
287
  */
285
- function decimalPartToWords (decimalPart, gender) {
288
+ function decimalPartToWords(decimalPart, gender) {
286
289
  let result = ''
287
290
 
288
291
  // Handle leading zeros
@@ -303,31 +306,52 @@ function decimalPartToWords (decimalPart, gender) {
303
306
  return result
304
307
  }
305
308
 
309
+ // Supported magnitude ceilings (checked at the public entry points). PLURAL_FORMS
310
+ // is keyed by scale level (1 = thousands, …), units separate, so cardinal/
311
+ // currency reach 10^((maxScaleKey + 1) * 3) = 10^33; the shorter ORDINAL_SCALES
312
+ // bounds ordinals at 10^((length + 1) * 3) = 10^24. (Past the cardinal ceiling
313
+ // the fraction would spell silently-wrong, so the decimal is guarded too.)
314
+ // Derive from the max numeric key (robust to gaps / non-scale keys).
315
+ const MAX_SCALE_KEY = Math.max(...Object.keys(PLURAL_FORMS).map(Number))
316
+ export const cardinalMax = western(MAX_SCALE_KEY)
317
+ export const ordinalMax = western(ORDINAL_SCALES.length)
318
+ export const currencyMax = western(MAX_SCALE_KEY)
319
+
320
+ /**
321
+ * @typedef {object} CardinalOptions
322
+ * @property {('masculine'|'feminine')} [gender] - Gender for numbers < 1000
323
+ */
324
+
325
+ /** @type {Required<CardinalOptions>} */
326
+ export const cardinalDefaults = { gender: 'masculine' }
327
+
328
+ /** @type {{ gender: ReadonlyArray<Required<CardinalOptions>['gender']> }} */
329
+ export const cardinalValues = { gender: ['masculine', 'feminine'] }
330
+
306
331
  /**
307
332
  * Converts a numeric value to Polish words.
308
333
  *
309
334
  * This is the main public API. It accepts any valid numeric input
310
335
  * (number, string, or bigint) and handles parsing internally.
311
- *
312
336
  * @param {number | string | bigint} value - The numeric value to convert
313
- * @param {Object} [options] - Conversion options
314
- * @param {string} [options.gender='masculine'] - Gender for numbers < 1000
337
+ * @param {CardinalOptions} [options] - Conversion options
315
338
  * @returns {string} The number in Polish words
316
339
  * @throws {TypeError} If value is not a valid numeric type
317
340
  * @throws {Error} If value is not a valid number format
318
- *
319
341
  * @example
320
342
  * toCardinal(1) // 'jeden'
321
343
  * toCardinal(1, { gender: 'feminine' }) // 'jedna'
322
344
  * toCardinal(1000) // 'tysiąc'
323
345
  * toCardinal(2000) // 'dwa tysiące'
324
346
  */
325
- function toCardinal (value, options) {
326
- options = validateOptions(options)
347
+ function toCardinal(value, options) {
327
348
  const { isNegative, integerPart, decimalPart } = parseCardinalValue(value)
349
+ // Both the integer part and the decimal's significant digits are spelled via
350
+ // the scale builder, so both must clear the ceiling.
351
+ checkMax(integerPart, cardinalMax, decimalPart)
328
352
 
329
353
  // Apply option defaults
330
- const { gender = 'masculine' } = options
354
+ const { gender } = resolveOptions(options, cardinalDefaults, cardinalValues)
331
355
 
332
356
  let result = ''
333
357
 
@@ -351,11 +375,10 @@ function toCardinal (value, options) {
351
375
  /**
352
376
  * Builds ordinal for a 0-99 segment when it's the final (ordinal) part.
353
377
  * Returns ordinal form: "pierwszy", "dwudziesty pierwszy", etc.
354
- *
355
378
  * @param {number} n - Number 0-99
356
379
  * @returns {string} Ordinal words
357
380
  */
358
- function buildOrdinalTensOnes (n) {
381
+ function buildOrdinalTensOnes(n) {
359
382
  if (n === 0) return ''
360
383
 
361
384
  const onesDigit = n % 10
@@ -386,11 +409,10 @@ function buildOrdinalTensOnes (n) {
386
409
  *
387
410
  * In Polish ordinals, only the LAST component becomes ordinal.
388
411
  * E.g., 121 = "sto dwudziesty pierwszy" (one hundred twenty-first)
389
- *
390
412
  * @param {bigint} n - Positive integer to convert
391
413
  * @returns {string} Ordinal Polish words
392
414
  */
393
- function integerToOrdinal (n) {
415
+ function integerToOrdinal(n) {
394
416
  // Fast path: numbers < 100
395
417
  if (n < 100n) {
396
418
  return buildOrdinalTensOnes(Number(n))
@@ -438,11 +460,10 @@ function integerToOrdinal (n) {
438
460
  /**
439
461
  * Builds ordinal words for numbers >= 1,000,000.
440
462
  * All segments except the final one are cardinal; final segment is ordinal.
441
- *
442
463
  * @param {bigint} n - Number >= 1,000,000
443
464
  * @returns {string} Ordinal Polish words
444
465
  */
445
- function buildLargeOrdinal (n) {
466
+ function buildLargeOrdinal(n) {
446
467
  const numStr = n.toString()
447
468
  const len = numStr.length
448
469
 
@@ -480,26 +501,31 @@ function buildLargeOrdinal (n) {
480
501
  // Units position (no scale)
481
502
  if (isLastNonZero) {
482
503
  parts.push(integerToOrdinal(BigInt(segment)))
483
- } else {
504
+ }
505
+ else {
484
506
  parts.push(buildSegment(segment))
485
507
  }
486
- } else {
508
+ }
509
+ else {
487
510
  // Has scale word
488
511
  if (isLastNonZero) {
489
512
  // This scale position is the final ordinal
490
513
  if (segment === 1) {
491
514
  parts.push(ORDINAL_SCALES[scaleIndex - 1])
492
- } else {
515
+ }
516
+ else {
493
517
  // Use cardinal segment + ordinal scale
494
518
  parts.push(buildSegment(segment) + ' ' + ORDINAL_SCALES[scaleIndex - 1])
495
519
  }
496
- } else {
520
+ }
521
+ else {
497
522
  // Not the final segment: use cardinal
498
523
  const forms = PLURAL_FORMS[scaleIndex]
499
524
  const scaleWord = forms ? pluralize(BigInt(segment), forms) : ''
500
525
  if (segment === 1) {
501
526
  parts.push(scaleWord)
502
- } else {
527
+ }
528
+ else {
503
529
  parts.push(buildSegment(segment) + ' ' + scaleWord)
504
530
  }
505
531
  }
@@ -514,12 +540,10 @@ function buildLargeOrdinal (n) {
514
540
 
515
541
  /**
516
542
  * Converts a numeric value to Polish ordinal words (masculine nominative).
517
- *
518
543
  * @param {number | string | bigint} value - The numeric value to convert (must be a positive integer)
519
544
  * @returns {string} The number as ordinal words (e.g., "pierwszy", "czterdziesty drugi")
520
545
  * @throws {TypeError} If value is not a valid numeric type
521
546
  * @throws {RangeError} If value is negative, zero, or has a decimal part
522
- *
523
547
  * @example
524
548
  * toOrdinal(1) // 'pierwszy'
525
549
  * toOrdinal(2) // 'drugi'
@@ -529,8 +553,9 @@ function buildLargeOrdinal (n) {
529
553
  * toOrdinal(100) // 'setny'
530
554
  * toOrdinal(1000) // 'tysięczny'
531
555
  */
532
- function toOrdinal (value) {
556
+ function toOrdinal(value) {
533
557
  const integerPart = parseOrdinalValue(value)
558
+ checkMax(integerPart, ordinalMax)
534
559
  return integerToOrdinal(integerPart)
535
560
  }
536
561
 
@@ -540,20 +565,19 @@ function toOrdinal (value) {
540
565
 
541
566
  /**
542
567
  * Converts a numeric value to Polish currency words (Polish Złoty).
543
- *
544
568
  * @param {number | string | bigint} value - The currency amount to convert
545
569
  * @returns {string} The amount in Polish currency words
546
570
  * @throws {TypeError} If value is not a valid numeric type
547
571
  * @throws {Error} If value is not a valid number format
548
- *
549
572
  * @example
550
573
  * toCurrency(42) // 'czterdzieści dwa złote'
551
574
  * toCurrency(1) // 'jeden złoty'
552
575
  * toCurrency(1.50) // 'jeden złoty pięćdziesiąt groszy'
553
576
  * toCurrency(-5) // 'minus pięć złotych'
554
577
  */
555
- function toCurrency (value) {
578
+ function toCurrency(value) {
556
579
  const { isNegative, dollars: zloty, cents: grosze } = parseCurrencyValue(value)
580
+ checkMax(zloty, currencyMax)
557
581
 
558
582
  let result = ''
559
583
  if (isNegative) {
package/src/pt-BR.d.ts CHANGED
@@ -1,31 +1,42 @@
1
+ export const cardinalMax: bigint;
2
+ export const ordinalMax: bigint;
3
+ export const currencyMax: bigint;
4
+ /**
5
+ * @typedef {object} CurrencyOptions
6
+ * @property {boolean} [and] - Include "e" between major and minor units
7
+ * @property {string} [currency] - Currency code (e.g., 'BRL', 'USD'); empty means auto-detect for pt-BR
8
+ */
9
+ /** @type {Required<CurrencyOptions>} */
10
+ export const currencyDefaults: Required<CurrencyOptions>;
11
+ export type CurrencyOptions = {
12
+ /**
13
+ * - Include "e" between major and minor units
14
+ */
15
+ and?: boolean | undefined;
16
+ /**
17
+ * - Currency code (e.g., 'BRL', 'USD'); empty means auto-detect for pt-BR
18
+ */
19
+ currency?: string | undefined;
20
+ };
1
21
  /**
2
22
  * Converts a numeric value to Portuguese words.
3
- *
4
23
  * @param {number | string | bigint} value - The numeric value to convert
5
24
  * @returns {string} The number in Portuguese words
6
25
  */
7
26
  export function toCardinal(value: number | string | bigint): string;
8
27
  /**
9
28
  * Converts a number to Portuguese ordinal words.
10
- *
11
29
  * @param {number | string | bigint} value - The number to convert
12
30
  * @returns {string} Portuguese ordinal words
13
31
  */
14
32
  export function toOrdinal(value: number | string | bigint): string;
15
33
  /**
16
34
  * Converts a number to Brazilian Portuguese currency words.
17
- *
18
35
  * @param {number | string | bigint} value - The amount to convert
19
- * @param {Object} [options]
20
- * @param {boolean} [options.and=true] - Include "e" between major and minor units
21
- * @param {string} [options.currency] - Currency code (e.g., 'BRL', 'USD')
36
+ * @param {CurrencyOptions} [options] Currency formatting options
22
37
  * @returns {string} Brazilian Portuguese currency words
23
- *
24
38
  * @example
25
39
  * toCurrency(42.50) // 'quarenta e dois reais e cinquenta centavos'
26
40
  * toCurrency(42.50, {currency: 'USD'}) // 'quarenta e dois dólares e cinquenta centavos'
27
41
  */
28
- export function toCurrency(value: number | string | bigint, options?: {
29
- and?: boolean | undefined;
30
- currency?: string | undefined;
31
- }): string;
42
+ export function toCurrency(value: number | string | bigint, options?: CurrencyOptions): string;