n2words 5.0.0 → 5.1.1

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 +133 -40
  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 +33 -24
  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 +106 -43
  266. package/src/sr-Latn-RS.d.ts +35 -15
  267. package/src/sr-Latn-RS.js +106 -43
  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/th-TH.js CHANGED
@@ -14,6 +14,14 @@
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 { UNBOUNDED } from './utils/scale.js'
18
+
19
+ // Thai spells large numbers by repeating ล้าน (10^6), so every form is
20
+ // magnitude-preserving with no fixed ceiling. Declared per form so the gate
21
+ // switches to its injectivity check and the docs read "no fixed limit".
22
+ export const cardinalMax = UNBOUNDED
23
+ export const ordinalMax = UNBOUNDED
24
+ export const currencyMax = UNBOUNDED
17
25
 
18
26
  // ============================================================================
19
27
  // Vocabulary
@@ -43,7 +51,11 @@ const BAHT_ONLY = 'ถ้วน' // "exactly" suffix when no satang
43
51
  // Conversion Functions
44
52
  // ============================================================================
45
53
 
46
- function convertBelowMillion (n) {
54
+ /**
55
+ * @param {number} n A value below one million to convert.
56
+ * @returns {string} The number in Thai words.
57
+ */
58
+ function convertBelowMillion(n) {
47
59
  if (n === 0) return ''
48
60
 
49
61
  let value = n
@@ -67,7 +79,8 @@ function convertBelowMillion (n) {
67
79
  if (tenThousands > 0) {
68
80
  if (tenThousands === 1) {
69
81
  parts.push('หนึ่งหมื่น')
70
- } else {
82
+ }
83
+ else {
71
84
  parts.push(ONES[tenThousands - 1] + 'หมื่น')
72
85
  }
73
86
  }
@@ -83,9 +96,11 @@ function convertBelowMillion (n) {
83
96
  if (tens > 0) {
84
97
  if (tens === 1) {
85
98
  parts.push('สิบ')
86
- } else if (tens === 2) {
99
+ }
100
+ else if (tens === 2) {
87
101
  parts.push('ยี่สิบ')
88
- } else {
102
+ }
103
+ else {
89
104
  parts.push(ONES[tens - 1] + 'สิบ')
90
105
  }
91
106
  }
@@ -94,7 +109,8 @@ function convertBelowMillion (n) {
94
109
  const hasHigher = hundredThousands > 0 || tenThousands > 0 || thousands > 0 || hundreds > 0 || tens > 0
95
110
  if (ones === 1 && (tens > 0 || hasHigher)) {
96
111
  parts.push('เอ็ด')
97
- } else {
112
+ }
113
+ else {
98
114
  parts.push(ONES[ones - 1])
99
115
  }
100
116
  }
@@ -102,7 +118,11 @@ function convertBelowMillion (n) {
102
118
  return parts.join('')
103
119
  }
104
120
 
105
- function splitMillionGroups (n) {
121
+ /**
122
+ * @param {bigint} n The integer to split into million-based groups.
123
+ * @returns {number[]} The groups in most-significant-first order.
124
+ */
125
+ function splitMillionGroups(n) {
106
126
  const groups = []
107
127
  let remaining = n
108
128
 
@@ -116,7 +136,11 @@ function splitMillionGroups (n) {
116
136
  return groups
117
137
  }
118
138
 
119
- function integerToWords (n) {
139
+ /**
140
+ * @param {bigint} n The integer to convert.
141
+ * @returns {string} The integer in Thai words.
142
+ */
143
+ function integerToWords(n) {
120
144
  if (n === 0n) return ZERO
121
145
 
122
146
  const groups = splitMillionGroups(n)
@@ -136,7 +160,11 @@ function integerToWords (n) {
136
160
  return parts.join('')
137
161
  }
138
162
 
139
- function decimalPartToWords (decimalPart) {
163
+ /**
164
+ * @param {string} decimalPart The fractional digits to read.
165
+ * @returns {string} The decimal digits in Thai words.
166
+ */
167
+ function decimalPartToWords(decimalPart) {
140
168
  // Per-digit decimal reading
141
169
  const digits = []
142
170
  for (const char of decimalPart) {
@@ -148,11 +176,10 @@ function decimalPartToWords (decimalPart) {
148
176
 
149
177
  /**
150
178
  * Converts a numeric value to Thai words.
151
- *
152
179
  * @param {number | string | bigint} value - The numeric value to convert
153
180
  * @returns {string} The number in Thai words
154
181
  */
155
- function toCardinal (value) {
182
+ function toCardinal(value) {
156
183
  const { isNegative, integerPart, decimalPart } = parseCardinalValue(value)
157
184
 
158
185
  let result = ''
@@ -178,28 +205,25 @@ function toCardinal (value) {
178
205
  * Converts a non-negative integer to Thai ordinal words.
179
206
  *
180
207
  * Thai ordinals use "ที่" prefix + cardinal number.
181
- *
182
208
  * @param {bigint} n - Positive integer to convert
183
209
  * @returns {string} Thai ordinal words
184
210
  */
185
- function integerToOrdinal (n) {
211
+ function integerToOrdinal(n) {
186
212
  return ORDINAL_PREFIX + integerToWords(n)
187
213
  }
188
214
 
189
215
  /**
190
216
  * Converts a numeric value to Thai ordinal words.
191
- *
192
217
  * @param {number | string | bigint} value - The numeric value to convert (positive integer)
193
218
  * @returns {string} The number as ordinal words
194
219
  * @throws {TypeError} If value is not a valid numeric type
195
220
  * @throws {RangeError} If value is negative, zero, or has a decimal part
196
- *
197
221
  * @example
198
222
  * toOrdinal(1) // 'ที่หนึ่ง'
199
223
  * toOrdinal(2) // 'ที่สอง'
200
224
  * toOrdinal(10) // 'ที่สิบ'
201
225
  */
202
- function toOrdinal (value) {
226
+ function toOrdinal(value) {
203
227
  const integerPart = parseOrdinalValue(value)
204
228
  return integerToOrdinal(integerPart)
205
229
  }
@@ -213,18 +237,16 @@ function toOrdinal (value) {
213
237
  *
214
238
  * Thai Baht uses satang as subunit (100 satang = 1 baht).
215
239
  * When whole amounts, adds "ถ้วน" (exactly) suffix.
216
- *
217
240
  * @param {number | string | bigint} value - The currency amount to convert
218
241
  * @returns {string} The amount in Thai currency words
219
242
  * @throws {TypeError} If value is not a valid numeric type
220
243
  * @throws {Error} If value is not a valid number format
221
- *
222
244
  * @example
223
245
  * toCurrency(42) // 'สี่สิบสองบาทถ้วน'
224
246
  * toCurrency(1.50) // 'หนึ่งบาทห้าสิบสตางค์'
225
247
  * toCurrency(-5) // 'ลบห้าบาทถ้วน'
226
248
  */
227
- function toCurrency (value) {
249
+ function toCurrency(value) {
228
250
  const { isNegative, dollars: baht, cents: satang } = parseCurrencyValue(value)
229
251
 
230
252
  let result = ''
@@ -240,7 +262,8 @@ function toCurrency (value) {
240
262
  if (satang > 0n) {
241
263
  result += integerToWords(satang)
242
264
  result += SATANG
243
- } else {
265
+ }
266
+ else {
244
267
  result += BAHT_ONLY
245
268
  }
246
269
 
package/src/tr-TR.d.ts CHANGED
@@ -1,29 +1,37 @@
1
+ export const cardinalMax: bigint;
2
+ export const ordinalMax: bigint;
3
+ export const currencyMax: bigint;
4
+ /**
5
+ * @typedef {object} CardinalOptions
6
+ * @property {boolean} [dropSpaces] - Remove spaces for compound form
7
+ */
8
+ /** @type {Required<CardinalOptions>} */
9
+ export const cardinalDefaults: Required<CardinalOptions>;
10
+ export type CardinalOptions = {
11
+ /**
12
+ * - Remove spaces for compound form
13
+ */
14
+ dropSpaces?: boolean | undefined;
15
+ };
1
16
  /**
2
17
  * Converts a numeric value to Turkish words.
3
- *
4
18
  * @param {number | string | bigint} value - The numeric value to convert
5
- * @param {Object} [options] - Conversion options
6
- * @param {boolean} [options.dropSpaces=false] - Remove spaces for compound form
19
+ * @param {CardinalOptions} [options] - Conversion options
7
20
  * @returns {string} The number in Turkish words
8
21
  * @throws {TypeError} If value is not a valid numeric type
9
22
  * @throws {Error} If value is not a valid number format
10
- *
11
23
  * @example
12
24
  * toCardinal(21) // 'yirmi bir'
13
25
  * toCardinal(21, { dropSpaces: true }) // 'yirmibir'
14
26
  * toCardinal(1000) // 'bin'
15
27
  */
16
- export function toCardinal(value: number | string | bigint, options?: {
17
- dropSpaces?: boolean | undefined;
18
- }): string;
28
+ export function toCardinal(value: number | string | bigint, options?: CardinalOptions): string;
19
29
  /**
20
30
  * Converts a numeric value to Turkish ordinal words.
21
- *
22
31
  * @param {number | string | bigint} value - The numeric value to convert (positive integer)
23
32
  * @returns {string} The number as ordinal words
24
33
  * @throws {TypeError} If value is not a valid numeric type
25
34
  * @throws {RangeError} If value is negative, zero, or has a decimal part
26
- *
27
35
  * @example
28
36
  * toOrdinal(1) // 'birinci'
29
37
  * toOrdinal(2) // 'ikinci'
@@ -34,12 +42,10 @@ export function toOrdinal(value: number | string | bigint): string;
34
42
  * Converts a numeric value to Turkish currency words (Turkish Lira).
35
43
  *
36
44
  * Uses lira and kuruş (100 kuruş = 1 lira).
37
- *
38
45
  * @param {number | string | bigint} value - The currency amount to convert
39
46
  * @returns {string} The amount in Turkish currency words
40
47
  * @throws {TypeError} If value is not a valid numeric type
41
48
  * @throws {Error} If value is not a valid number format
42
- *
43
49
  * @example
44
50
  * toCurrency(42) // 'kırk iki lira'
45
51
  * toCurrency(1.50) // 'bir lira elli kuruş'
package/src/tr-TR.js CHANGED
@@ -12,7 +12,9 @@
12
12
  import { parseCardinalValue } from './utils/parse-cardinal.js'
13
13
  import { parseCurrencyValue } from './utils/parse-currency.js'
14
14
  import { parseOrdinalValue } from './utils/parse-ordinal.js'
15
- import { validateOptions } from './utils/validate-options.js'
15
+ import { checkMax } from './utils/check-max.js'
16
+ import { western } from './utils/scale.js'
17
+ import { resolveOptions } from './utils/resolve-options.js'
16
18
 
17
19
  // ============================================================================
18
20
  // Vocabulary (module-level constants)
@@ -33,12 +35,23 @@ const DECIMAL_SEP = 'virgül'
33
35
  // Short scale
34
36
  const SCALES = ['milyon', 'milyar', 'trilyon', 'katrilyon', 'kentilyon']
35
37
 
38
+ // Supported magnitude ceiling (checked at the public entry points). SCALES is
39
+ // indexed [scaleIndex - 2] (units and thousands are separate), so segments are
40
+ // [units, thousands, then one per SCALES entry] -> the ceiling is
41
+ // 10^((SCALES.length + 2) * 3) = 10^21. Ordinal (integerToWords + suffix),
42
+ // currency, and the decimal all route through the cardinal builder, so all
43
+ // share the ceiling.
44
+ export const cardinalMax = western(SCALES.length + 1)
45
+ export const ordinalMax = western(SCALES.length + 1)
46
+ export const currencyMax = western(SCALES.length + 1)
47
+
36
48
  // ============================================================================
37
49
  // Ordinal Vocabulary
38
50
  // ============================================================================
39
51
 
40
52
  // Turkish ordinals use -(i/ı/u/ü)nci/ncı/ncu/ncü suffix with vowel harmony
41
53
  // Special forms for 1-10
54
+ /** @type {Record<number, string>} */
42
55
  const ORDINAL_SPECIAL = {
43
56
  1: 'birinci',
44
57
  2: 'ikinci',
@@ -49,7 +62,7 @@ const ORDINAL_SPECIAL = {
49
62
  7: 'yedinci',
50
63
  8: 'sekizinci',
51
64
  9: 'dokuzuncu',
52
- 10: 'onuncu'
65
+ 10: 'onuncu',
53
66
  }
54
67
 
55
68
  // ============================================================================
@@ -66,8 +79,11 @@ const KURUS = 'kuruş' // subunit (100 kuruş = 1 lira)
66
79
  /**
67
80
  * Builds segment word for 0-999.
68
81
  * Omits "bir" before "yüz" (hundred).
82
+ * @param {number} n - Segment value (0-999)
83
+ * @param {string} [separator] - Separator between words
84
+ * @returns {string} Segment words
69
85
  */
70
- function buildSegment (n, separator = ' ') {
86
+ function buildSegment(n, separator = ' ') {
71
87
  if (n === 0) return ''
72
88
 
73
89
  const ones = n % 10
@@ -80,7 +96,8 @@ function buildSegment (n, separator = ' ') {
80
96
  if (hundreds > 0) {
81
97
  if (hundreds === 1) {
82
98
  parts.push(HUNDRED)
83
- } else {
99
+ }
100
+ else {
84
101
  parts.push(ONES[hundreds] + separator + HUNDRED)
85
102
  }
86
103
  }
@@ -90,13 +107,17 @@ function buildSegment (n, separator = ' ') {
90
107
 
91
108
  if (tensOnes === 0) {
92
109
  // Just hundreds
93
- } else if (tensOnes < 10) {
110
+ }
111
+ else if (tensOnes < 10) {
94
112
  parts.push(ONES[ones])
95
- } else if (tensOnes < 20) {
113
+ }
114
+ else if (tensOnes < 20) {
96
115
  parts.push(TEENS[ones].replace(' ', separator))
97
- } else if (ones === 0) {
116
+ }
117
+ else if (ones === 0) {
98
118
  parts.push(TENS[tens])
99
- } else {
119
+ }
120
+ else {
100
121
  parts.push(TENS[tens] + separator + ONES[ones])
101
122
  }
102
123
 
@@ -109,12 +130,11 @@ function buildSegment (n, separator = ' ') {
109
130
 
110
131
  /**
111
132
  * Converts a non-negative integer to Turkish words.
112
- *
113
133
  * @param {bigint} n - Non-negative integer to convert
114
- * @param {Object} options - Conversion options
134
+ * @param {boolean} dropSpaces - Remove spaces for compound form
115
135
  * @returns {string} Turkish words
116
136
  */
117
- function integerToWords (n, dropSpaces) {
137
+ function integerToWords(n, dropSpaces) {
118
138
  if (n === 0n) return ZERO
119
139
 
120
140
  const sep = dropSpaces ? '' : ' '
@@ -133,7 +153,8 @@ function integerToWords (n, dropSpaces) {
133
153
  let result
134
154
  if (thousands === 1) {
135
155
  result = THOUSAND
136
- } else {
156
+ }
157
+ else {
137
158
  result = buildSegment(thousands, sep) + sep + THOUSAND
138
159
  }
139
160
 
@@ -150,12 +171,11 @@ function integerToWords (n, dropSpaces) {
150
171
 
151
172
  /**
152
173
  * Builds words for numbers >= 1,000,000.
153
- *
154
174
  * @param {bigint} n - Number >= 1,000,000
155
- * @param {Object} options - Conversion options
175
+ * @param {boolean} dropSpaces - Remove spaces for compound form
156
176
  * @returns {string} Turkish words
157
177
  */
158
- function buildLargeNumberWords (n, dropSpaces) {
178
+ function buildLargeNumberWords(n, dropSpaces) {
159
179
  const sep = dropSpaces ? '' : ' '
160
180
 
161
181
  const numStr = n.toString()
@@ -189,14 +209,17 @@ function buildLargeNumberWords (n, dropSpaces) {
189
209
  if (scaleIndex === 0) {
190
210
  // Units segment
191
211
  parts.push(segmentWord)
192
- } else if (scaleIndex === 1) {
212
+ }
213
+ else if (scaleIndex === 1) {
193
214
  // Thousands - omit "bir" before bin
194
215
  if (segment === 1) {
195
216
  parts.push(THOUSAND)
196
- } else {
217
+ }
218
+ else {
197
219
  parts.push(segmentWord + sep + THOUSAND)
198
220
  }
199
- } else {
221
+ }
222
+ else {
200
223
  // Millions+ - "bir" is kept before scale words
201
224
  const scaleWord = SCALES[scaleIndex - 2]
202
225
  parts.push(segmentWord + sep + scaleWord)
@@ -211,12 +234,11 @@ function buildLargeNumberWords (n, dropSpaces) {
211
234
 
212
235
  /**
213
236
  * Converts decimal digits to Turkish words.
214
- *
215
237
  * @param {string} decimalPart - Decimal digits (without the point)
216
- * @param {Object} options - Conversion options
238
+ * @param {boolean} dropSpaces - Remove spaces for compound form
217
239
  * @returns {string} Turkish words for decimal part
218
240
  */
219
- function decimalPartToWords (decimalPart, dropSpaces) {
241
+ function decimalPartToWords(decimalPart, dropSpaces) {
220
242
  const sep = dropSpaces ? '' : ' '
221
243
  let result = ''
222
244
 
@@ -238,27 +260,34 @@ function decimalPartToWords (decimalPart, dropSpaces) {
238
260
  return result
239
261
  }
240
262
 
263
+ /**
264
+ * @typedef {object} CardinalOptions
265
+ * @property {boolean} [dropSpaces] - Remove spaces for compound form
266
+ */
267
+
268
+ /** @type {Required<CardinalOptions>} */
269
+ export const cardinalDefaults = { dropSpaces: false }
270
+
241
271
  /**
242
272
  * Converts a numeric value to Turkish words.
243
- *
244
273
  * @param {number | string | bigint} value - The numeric value to convert
245
- * @param {Object} [options] - Conversion options
246
- * @param {boolean} [options.dropSpaces=false] - Remove spaces for compound form
274
+ * @param {CardinalOptions} [options] - Conversion options
247
275
  * @returns {string} The number in Turkish words
248
276
  * @throws {TypeError} If value is not a valid numeric type
249
277
  * @throws {Error} If value is not a valid number format
250
- *
251
278
  * @example
252
279
  * toCardinal(21) // 'yirmi bir'
253
280
  * toCardinal(21, { dropSpaces: true }) // 'yirmibir'
254
281
  * toCardinal(1000) // 'bin'
255
282
  */
256
- function toCardinal (value, options) {
257
- options = validateOptions(options)
283
+ function toCardinal(value, options) {
258
284
  const { isNegative, integerPart, decimalPart } = parseCardinalValue(value)
285
+ // Both the integer part and the decimal's significant digits are spelled via
286
+ // the scale builder, so both must clear the ceiling.
287
+ checkMax(integerPart, cardinalMax, decimalPart)
259
288
 
260
289
  // Apply option defaults
261
- const { dropSpaces = false } = options
290
+ const { dropSpaces } = resolveOptions(options, cardinalDefaults)
262
291
 
263
292
  const sep = dropSpaces ? '' : ' '
264
293
  let result = ''
@@ -285,7 +314,7 @@ function toCardinal (value, options) {
285
314
  * @param {string} word - The cardinal word
286
315
  * @returns {string} The appropriate suffix
287
316
  */
288
- function getOrdinalSuffix (word) {
317
+ function getOrdinalSuffix(word) {
289
318
  // Turkish vowel harmony: back vowels (a,ı,o,u) vs front vowels (e,i,ö,ü)
290
319
  // Find last vowel to determine suffix
291
320
  const backVowels = 'aıou'
@@ -313,11 +342,10 @@ function getOrdinalSuffix (word) {
313
342
  *
314
343
  * Turkish ordinals: birinci (1st), ikinci (2nd), üçüncü (3rd), etc.
315
344
  * Uses vowel harmony for suffix selection.
316
- *
317
345
  * @param {bigint} n - Positive integer to convert
318
346
  * @returns {string} Turkish ordinal words
319
347
  */
320
- function integerToOrdinal (n) {
348
+ function integerToOrdinal(n) {
321
349
  // Special forms for 1-10
322
350
  if (n >= 1n && n <= 10n) {
323
351
  return ORDINAL_SPECIAL[Number(n)]
@@ -331,19 +359,18 @@ function integerToOrdinal (n) {
331
359
 
332
360
  /**
333
361
  * Converts a numeric value to Turkish ordinal words.
334
- *
335
362
  * @param {number | string | bigint} value - The numeric value to convert (positive integer)
336
363
  * @returns {string} The number as ordinal words
337
364
  * @throws {TypeError} If value is not a valid numeric type
338
365
  * @throws {RangeError} If value is negative, zero, or has a decimal part
339
- *
340
366
  * @example
341
367
  * toOrdinal(1) // 'birinci'
342
368
  * toOrdinal(2) // 'ikinci'
343
369
  * toOrdinal(21) // 'yirmibirinci'
344
370
  */
345
- function toOrdinal (value) {
371
+ function toOrdinal(value) {
346
372
  const integerPart = parseOrdinalValue(value)
373
+ checkMax(integerPart, ordinalMax)
347
374
  return integerToOrdinal(integerPart)
348
375
  }
349
376
 
@@ -355,19 +382,18 @@ function toOrdinal (value) {
355
382
  * Converts a numeric value to Turkish currency words (Turkish Lira).
356
383
  *
357
384
  * Uses lira and kuruş (100 kuruş = 1 lira).
358
- *
359
385
  * @param {number | string | bigint} value - The currency amount to convert
360
386
  * @returns {string} The amount in Turkish currency words
361
387
  * @throws {TypeError} If value is not a valid numeric type
362
388
  * @throws {Error} If value is not a valid number format
363
- *
364
389
  * @example
365
390
  * toCurrency(42) // 'kırk iki lira'
366
391
  * toCurrency(1.50) // 'bir lira elli kuruş'
367
392
  * toCurrency(-5) // 'eksi beş lira'
368
393
  */
369
- function toCurrency (value) {
394
+ function toCurrency(value) {
370
395
  const { isNegative, dollars: lira, cents: kurus } = parseCurrencyValue(value)
396
+ checkMax(lira, currencyMax)
371
397
 
372
398
  let result = ''
373
399
  if (isNegative) {
package/src/uk-UA.d.ts CHANGED
@@ -1,22 +1,35 @@
1
+ export const cardinalMax: bigint;
2
+ export const ordinalMax: bigint;
3
+ export const currencyMax: bigint;
4
+ /**
5
+ * @typedef {object} CardinalOptions
6
+ * @property {('masculine'|'feminine')} [gender] - Grammatical gender
7
+ */
8
+ /** @type {Required<CardinalOptions>} */
9
+ export const cardinalDefaults: Required<CardinalOptions>;
10
+ /** @type {{ gender: ReadonlyArray<Required<CardinalOptions>['gender']> }} */
11
+ export const cardinalValues: {
12
+ gender: ReadonlyArray<Required<CardinalOptions>["gender"]>;
13
+ };
14
+ export type CardinalOptions = {
15
+ /**
16
+ * - Grammatical gender
17
+ */
18
+ gender?: "feminine" | "masculine" | undefined;
19
+ };
1
20
  /**
2
21
  * Converts a numeric value to Ukrainian words.
3
- *
4
22
  * @param {number | string | bigint} value - The numeric value to convert
5
- * @param {Object} [options] - Optional configuration
6
- * @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender
23
+ * @param {CardinalOptions} [options] - Optional configuration
7
24
  * @returns {string} The number in Ukrainian words
8
25
  */
9
- export function toCardinal(value: number | string | bigint, options?: {
10
- gender?: "masculine" | "feminine" | undefined;
11
- }): string;
26
+ export function toCardinal(value: number | string | bigint, options?: CardinalOptions): string;
12
27
  /**
13
28
  * Converts a numeric value to Ukrainian ordinal words (masculine nominative).
14
- *
15
29
  * @param {number | string | bigint} value - The numeric value to convert (must be a positive integer)
16
30
  * @returns {string} The number as ordinal words
17
31
  * @throws {TypeError} If value is not a valid numeric type
18
32
  * @throws {RangeError} If value is negative, zero, or has a decimal part
19
- *
20
33
  * @example
21
34
  * toOrdinal(1) // 'перший'
22
35
  * toOrdinal(2) // 'другий'
@@ -27,12 +40,10 @@ export function toCardinal(value: number | string | bigint, options?: {
27
40
  export function toOrdinal(value: number | string | bigint): string;
28
41
  /**
29
42
  * Converts a numeric value to Ukrainian currency words (Hryvnia).
30
- *
31
43
  * @param {number | string | bigint} value - The currency amount to convert
32
44
  * @returns {string} The amount in Ukrainian currency words
33
45
  * @throws {TypeError} If value is not a valid numeric type
34
46
  * @throws {Error} If value is not a valid number format
35
- *
36
47
  * @example
37
48
  * toCurrency(42) // 'сорок двi гривнi'
38
49
  * toCurrency(1) // 'одна гривня'