n2words 2.0.0 → 3.0.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 (327) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +86 -188
  3. package/dist/languages/am-Latn.js +3 -0
  4. package/dist/languages/am-Latn.js.map +1 -0
  5. package/dist/languages/am.js +3 -0
  6. package/dist/languages/am.js.map +1 -0
  7. package/dist/languages/ar.js +3 -0
  8. package/dist/languages/ar.js.map +1 -0
  9. package/dist/languages/az.js +3 -0
  10. package/dist/languages/az.js.map +1 -0
  11. package/dist/languages/bn.js +3 -0
  12. package/dist/languages/bn.js.map +1 -0
  13. package/dist/languages/cs.js +3 -0
  14. package/dist/languages/cs.js.map +1 -0
  15. package/dist/languages/da.js +3 -0
  16. package/dist/languages/da.js.map +1 -0
  17. package/dist/languages/de.js +3 -0
  18. package/dist/languages/de.js.map +1 -0
  19. package/dist/languages/el.js +3 -0
  20. package/dist/languages/el.js.map +1 -0
  21. package/dist/languages/en.js +3 -0
  22. package/dist/languages/en.js.map +1 -0
  23. package/dist/languages/es.js +3 -0
  24. package/dist/languages/es.js.map +1 -0
  25. package/dist/languages/fa.js +3 -0
  26. package/dist/languages/fa.js.map +1 -0
  27. package/dist/languages/fi.js +3 -0
  28. package/dist/languages/fi.js.map +1 -0
  29. package/dist/languages/fil.js +3 -0
  30. package/dist/languages/fil.js.map +1 -0
  31. package/dist/languages/fr-BE.js +3 -0
  32. package/dist/languages/fr-BE.js.map +1 -0
  33. package/dist/languages/fr.js +3 -0
  34. package/dist/languages/fr.js.map +1 -0
  35. package/dist/languages/gu.js +3 -0
  36. package/dist/languages/gu.js.map +1 -0
  37. package/dist/languages/ha.js +3 -0
  38. package/dist/languages/ha.js.map +1 -0
  39. package/dist/languages/hbo.js +3 -0
  40. package/dist/languages/hbo.js.map +1 -0
  41. package/dist/languages/he.js +3 -0
  42. package/dist/languages/he.js.map +1 -0
  43. package/dist/languages/hi.js +3 -0
  44. package/dist/languages/hi.js.map +1 -0
  45. package/dist/languages/hr.js +3 -0
  46. package/dist/languages/hr.js.map +1 -0
  47. package/dist/languages/hu.js +3 -0
  48. package/dist/languages/hu.js.map +1 -0
  49. package/dist/languages/id.js +3 -0
  50. package/dist/languages/id.js.map +1 -0
  51. package/dist/languages/it.js +3 -0
  52. package/dist/languages/it.js.map +1 -0
  53. package/dist/languages/ja.js +3 -0
  54. package/dist/languages/ja.js.map +1 -0
  55. package/dist/languages/kn.js +3 -0
  56. package/dist/languages/kn.js.map +1 -0
  57. package/dist/languages/ko.js +3 -0
  58. package/dist/languages/ko.js.map +1 -0
  59. package/dist/languages/lt.js +3 -0
  60. package/dist/languages/lt.js.map +1 -0
  61. package/dist/languages/lv.js +3 -0
  62. package/dist/languages/lv.js.map +1 -0
  63. package/dist/languages/mr.js +3 -0
  64. package/dist/languages/mr.js.map +1 -0
  65. package/dist/languages/ms.js +3 -0
  66. package/dist/languages/ms.js.map +1 -0
  67. package/dist/languages/nb.js +3 -0
  68. package/dist/languages/nb.js.map +1 -0
  69. package/dist/languages/nl.js +3 -0
  70. package/dist/languages/nl.js.map +1 -0
  71. package/dist/languages/pa.js +3 -0
  72. package/dist/languages/pa.js.map +1 -0
  73. package/dist/languages/pl.js +3 -0
  74. package/dist/languages/pl.js.map +1 -0
  75. package/dist/languages/pt.js +3 -0
  76. package/dist/languages/pt.js.map +1 -0
  77. package/dist/languages/ro.js +3 -0
  78. package/dist/languages/ro.js.map +1 -0
  79. package/dist/languages/ru.js +3 -0
  80. package/dist/languages/ru.js.map +1 -0
  81. package/dist/languages/sr-Cyrl.js +3 -0
  82. package/dist/languages/sr-Cyrl.js.map +1 -0
  83. package/dist/languages/sr-Latn.js +3 -0
  84. package/dist/languages/sr-Latn.js.map +1 -0
  85. package/dist/languages/sv.js +3 -0
  86. package/dist/languages/sv.js.map +1 -0
  87. package/dist/languages/sw.js +3 -0
  88. package/dist/languages/sw.js.map +1 -0
  89. package/dist/languages/ta.js +3 -0
  90. package/dist/languages/ta.js.map +1 -0
  91. package/dist/languages/te.js +3 -0
  92. package/dist/languages/te.js.map +1 -0
  93. package/dist/languages/th.js +3 -0
  94. package/dist/languages/th.js.map +1 -0
  95. package/dist/languages/tr.js +3 -0
  96. package/dist/languages/tr.js.map +1 -0
  97. package/dist/languages/uk.js +3 -0
  98. package/dist/languages/uk.js.map +1 -0
  99. package/dist/languages/ur.js +3 -0
  100. package/dist/languages/ur.js.map +1 -0
  101. package/dist/languages/vi.js +3 -0
  102. package/dist/languages/vi.js.map +1 -0
  103. package/dist/languages/zh-Hans.js +3 -0
  104. package/dist/languages/zh-Hans.js.map +1 -0
  105. package/dist/languages/zh-Hant.js +3 -0
  106. package/dist/languages/zh-Hant.js.map +1 -0
  107. package/dist/n2words.js +2 -2
  108. package/dist/n2words.js.map +1 -1
  109. package/lib/languages/am-Latn.d.ts +7 -0
  110. package/lib/languages/am-Latn.js +164 -0
  111. package/lib/languages/am.d.ts +7 -0
  112. package/lib/languages/am.js +164 -0
  113. package/lib/languages/ar.d.ts +14 -27
  114. package/lib/languages/ar.js +175 -129
  115. package/lib/languages/az.d.ts +4 -9
  116. package/lib/languages/az.js +171 -37
  117. package/lib/languages/bn.d.ts +4 -8
  118. package/lib/languages/bn.js +138 -124
  119. package/lib/languages/cs.d.ts +15 -85
  120. package/lib/languages/cs.js +310 -114
  121. package/lib/languages/da.d.ts +11 -12
  122. package/lib/languages/da.js +276 -101
  123. package/lib/languages/de.d.ts +14 -11
  124. package/lib/languages/de.js +317 -86
  125. package/lib/languages/el.d.ts +11 -11
  126. package/lib/languages/el.js +231 -78
  127. package/lib/languages/en.d.ts +14 -13
  128. package/lib/languages/en.js +242 -72
  129. package/lib/languages/es.d.ts +18 -12
  130. package/lib/languages/es.js +317 -103
  131. package/lib/languages/fa.d.ts +4 -44
  132. package/lib/languages/fa.js +112 -122
  133. package/lib/languages/fi.d.ts +14 -0
  134. package/lib/languages/fi.js +245 -0
  135. package/lib/languages/fil.d.ts +4 -13
  136. package/lib/languages/fil.js +207 -106
  137. package/lib/languages/fr-BE.d.ts +8 -8
  138. package/lib/languages/fr-BE.js +294 -19
  139. package/lib/languages/fr.d.ts +18 -12
  140. package/lib/languages/fr.js +352 -89
  141. package/lib/languages/gu.d.ts +4 -8
  142. package/lib/languages/gu.js +130 -125
  143. package/lib/languages/ha.d.ts +7 -0
  144. package/lib/languages/ha.js +230 -0
  145. package/lib/languages/hbo.d.ts +10 -110
  146. package/lib/languages/hbo.js +263 -214
  147. package/lib/languages/he.d.ts +10 -77
  148. package/lib/languages/he.js +242 -172
  149. package/lib/languages/hi.d.ts +4 -8
  150. package/lib/languages/hi.js +138 -124
  151. package/lib/languages/hr.d.ts +8 -77
  152. package/lib/languages/hr.js +194 -89
  153. package/lib/languages/hu.d.ts +4 -19
  154. package/lib/languages/hu.js +198 -119
  155. package/lib/languages/id.d.ts +4 -34
  156. package/lib/languages/id.js +171 -129
  157. package/lib/languages/it.d.ts +16 -34
  158. package/lib/languages/it.js +339 -94
  159. package/lib/languages/ja.d.ts +14 -14
  160. package/lib/languages/ja.js +233 -111
  161. package/lib/languages/kn.d.ts +4 -8
  162. package/lib/languages/kn.js +130 -35
  163. package/lib/languages/ko.d.ts +11 -11
  164. package/lib/languages/ko.js +257 -49
  165. package/lib/languages/lt.d.ts +15 -67
  166. package/lib/languages/lt.js +296 -122
  167. package/lib/languages/lv.d.ts +15 -67
  168. package/lib/languages/lv.js +297 -106
  169. package/lib/languages/mr.d.ts +4 -8
  170. package/lib/languages/mr.js +130 -125
  171. package/lib/languages/ms.d.ts +4 -28
  172. package/lib/languages/ms.js +171 -116
  173. package/lib/languages/nb.d.ts +11 -9
  174. package/lib/languages/nb.js +282 -87
  175. package/lib/languages/nl.d.ts +23 -13
  176. package/lib/languages/nl.js +317 -133
  177. package/lib/languages/pa.d.ts +4 -8
  178. package/lib/languages/pa.js +156 -124
  179. package/lib/languages/pl.d.ts +19 -77
  180. package/lib/languages/pl.js +307 -87
  181. package/lib/languages/pt.d.ts +14 -26
  182. package/lib/languages/pt.js +286 -92
  183. package/lib/languages/ro.d.ts +15 -155
  184. package/lib/languages/ro.js +219 -235
  185. package/lib/languages/ru.d.ts +8 -82
  186. package/lib/languages/ru.js +222 -78
  187. package/lib/languages/sr-Cyrl.d.ts +8 -77
  188. package/lib/languages/sr-Cyrl.js +191 -89
  189. package/lib/languages/sr-Latn.d.ts +8 -77
  190. package/lib/languages/sr-Latn.js +191 -89
  191. package/lib/languages/sv.d.ts +11 -11
  192. package/lib/languages/sv.js +288 -74
  193. package/lib/languages/sw.d.ts +4 -36
  194. package/lib/languages/sw.js +133 -106
  195. package/lib/languages/ta.d.ts +4 -17
  196. package/lib/languages/ta.js +129 -201
  197. package/lib/languages/te.d.ts +4 -19
  198. package/lib/languages/te.js +141 -196
  199. package/lib/languages/th.d.ts +4 -14
  200. package/lib/languages/th.js +135 -91
  201. package/lib/languages/tr.d.ts +15 -9
  202. package/lib/languages/tr.js +256 -49
  203. package/lib/languages/uk.d.ts +8 -82
  204. package/lib/languages/uk.js +200 -78
  205. package/lib/languages/ur.d.ts +4 -8
  206. package/lib/languages/ur.js +156 -124
  207. package/lib/languages/vi.d.ts +14 -69
  208. package/lib/languages/vi.js +294 -125
  209. package/lib/languages/zh-Hans.d.ts +8 -18
  210. package/lib/languages/zh-Hans.js +163 -92
  211. package/lib/languages/zh-Hant.d.ts +8 -18
  212. package/lib/languages/zh-Hant.js +181 -90
  213. package/lib/n2words.d.ts +53 -209
  214. package/lib/n2words.js +111 -530
  215. package/lib/utils/is-plain-object.d.ts +13 -0
  216. package/lib/utils/is-plain-object.js +17 -0
  217. package/lib/utils/parse-numeric.d.ts +17 -0
  218. package/lib/utils/parse-numeric.js +108 -0
  219. package/lib/utils/validate-options.d.ts +8 -0
  220. package/lib/utils/validate-options.js +16 -0
  221. package/package.json +26 -14
  222. package/dist/ArabicConverter.js +0 -3
  223. package/dist/ArabicConverter.js.map +0 -1
  224. package/dist/AzerbaijaniConverter.js +0 -3
  225. package/dist/AzerbaijaniConverter.js.map +0 -1
  226. package/dist/BanglaConverter.js +0 -3
  227. package/dist/BanglaConverter.js.map +0 -1
  228. package/dist/BiblicalHebrewConverter.js +0 -3
  229. package/dist/BiblicalHebrewConverter.js.map +0 -1
  230. package/dist/CroatianConverter.js +0 -3
  231. package/dist/CroatianConverter.js.map +0 -1
  232. package/dist/CzechConverter.js +0 -3
  233. package/dist/CzechConverter.js.map +0 -1
  234. package/dist/DanishConverter.js +0 -3
  235. package/dist/DanishConverter.js.map +0 -1
  236. package/dist/DutchConverter.js +0 -3
  237. package/dist/DutchConverter.js.map +0 -1
  238. package/dist/EnglishConverter.js +0 -3
  239. package/dist/EnglishConverter.js.map +0 -1
  240. package/dist/FilipinoConverter.js +0 -3
  241. package/dist/FilipinoConverter.js.map +0 -1
  242. package/dist/FrenchBelgiumConverter.js +0 -3
  243. package/dist/FrenchBelgiumConverter.js.map +0 -1
  244. package/dist/FrenchConverter.js +0 -3
  245. package/dist/FrenchConverter.js.map +0 -1
  246. package/dist/GermanConverter.js +0 -3
  247. package/dist/GermanConverter.js.map +0 -1
  248. package/dist/GreekConverter.js +0 -3
  249. package/dist/GreekConverter.js.map +0 -1
  250. package/dist/GujaratiConverter.js +0 -3
  251. package/dist/GujaratiConverter.js.map +0 -1
  252. package/dist/HebrewConverter.js +0 -3
  253. package/dist/HebrewConverter.js.map +0 -1
  254. package/dist/HindiConverter.js +0 -3
  255. package/dist/HindiConverter.js.map +0 -1
  256. package/dist/HungarianConverter.js +0 -3
  257. package/dist/HungarianConverter.js.map +0 -1
  258. package/dist/IndonesianConverter.js +0 -3
  259. package/dist/IndonesianConverter.js.map +0 -1
  260. package/dist/ItalianConverter.js +0 -3
  261. package/dist/ItalianConverter.js.map +0 -1
  262. package/dist/JapaneseConverter.js +0 -3
  263. package/dist/JapaneseConverter.js.map +0 -1
  264. package/dist/KannadaConverter.js +0 -3
  265. package/dist/KannadaConverter.js.map +0 -1
  266. package/dist/KoreanConverter.js +0 -3
  267. package/dist/KoreanConverter.js.map +0 -1
  268. package/dist/LatvianConverter.js +0 -3
  269. package/dist/LatvianConverter.js.map +0 -1
  270. package/dist/LithuanianConverter.js +0 -3
  271. package/dist/LithuanianConverter.js.map +0 -1
  272. package/dist/MalayConverter.js +0 -3
  273. package/dist/MalayConverter.js.map +0 -1
  274. package/dist/MarathiConverter.js +0 -3
  275. package/dist/MarathiConverter.js.map +0 -1
  276. package/dist/NorwegianBokmalConverter.js +0 -3
  277. package/dist/NorwegianBokmalConverter.js.map +0 -1
  278. package/dist/PersianConverter.js +0 -3
  279. package/dist/PersianConverter.js.map +0 -1
  280. package/dist/PolishConverter.js +0 -3
  281. package/dist/PolishConverter.js.map +0 -1
  282. package/dist/PortugueseConverter.js +0 -3
  283. package/dist/PortugueseConverter.js.map +0 -1
  284. package/dist/PunjabiConverter.js +0 -3
  285. package/dist/PunjabiConverter.js.map +0 -1
  286. package/dist/RomanianConverter.js +0 -3
  287. package/dist/RomanianConverter.js.map +0 -1
  288. package/dist/RussianConverter.js +0 -3
  289. package/dist/RussianConverter.js.map +0 -1
  290. package/dist/SerbianCyrillicConverter.js +0 -3
  291. package/dist/SerbianCyrillicConverter.js.map +0 -1
  292. package/dist/SerbianLatinConverter.js +0 -3
  293. package/dist/SerbianLatinConverter.js.map +0 -1
  294. package/dist/SimplifiedChineseConverter.js +0 -3
  295. package/dist/SimplifiedChineseConverter.js.map +0 -1
  296. package/dist/SpanishConverter.js +0 -3
  297. package/dist/SpanishConverter.js.map +0 -1
  298. package/dist/SwahiliConverter.js +0 -3
  299. package/dist/SwahiliConverter.js.map +0 -1
  300. package/dist/SwedishConverter.js +0 -3
  301. package/dist/SwedishConverter.js.map +0 -1
  302. package/dist/TamilConverter.js +0 -3
  303. package/dist/TamilConverter.js.map +0 -1
  304. package/dist/TeluguConverter.js +0 -3
  305. package/dist/TeluguConverter.js.map +0 -1
  306. package/dist/ThaiConverter.js +0 -3
  307. package/dist/ThaiConverter.js.map +0 -1
  308. package/dist/TraditionalChineseConverter.js +0 -3
  309. package/dist/TraditionalChineseConverter.js.map +0 -1
  310. package/dist/TurkishConverter.js +0 -3
  311. package/dist/TurkishConverter.js.map +0 -1
  312. package/dist/UkrainianConverter.js +0 -3
  313. package/dist/UkrainianConverter.js.map +0 -1
  314. package/dist/UrduConverter.js +0 -3
  315. package/dist/UrduConverter.js.map +0 -1
  316. package/dist/VietnameseConverter.js +0 -3
  317. package/dist/VietnameseConverter.js.map +0 -1
  318. package/lib/classes/abstract-language.d.ts +0 -178
  319. package/lib/classes/abstract-language.js +0 -268
  320. package/lib/classes/greedy-scale-language.d.ts +0 -109
  321. package/lib/classes/greedy-scale-language.js +0 -201
  322. package/lib/classes/slavic-language.d.ts +0 -148
  323. package/lib/classes/slavic-language.js +0 -281
  324. package/lib/classes/south-asian-language.d.ts +0 -70
  325. package/lib/classes/south-asian-language.js +0 -154
  326. package/lib/classes/turkic-language.d.ts +0 -26
  327. package/lib/classes/turkic-language.js +0 -59
@@ -1,96 +1,218 @@
1
- import { SlavicLanguage } from '../classes/slavic-language.js'
2
-
3
1
  /**
4
- * Ukrainian language converter.
2
+ * Ukrainian language converter - Functional Implementation
3
+ *
4
+ * Self-contained converter using shared Slavic utilities.
5
5
  *
6
- * Supports:
6
+ * Key features:
7
7
  * - Three-form pluralization (one/few/many)
8
- * - Gender agreement (один/одна, два/дві)
9
- * - Ukrainian orthography and phonology
8
+ * - Gender: thousands are feminine, millions+ are masculine
9
+ * - Irregular hundreds
10
+ * - Long scale naming
10
11
  */
11
- export class Ukrainian extends SlavicLanguage {
12
- negativeWord = 'мiнус'
13
- decimalSeparatorWord = 'кома'
14
- zeroWord = 'нуль'
15
-
16
- onesWords = {
17
- 1: 'один',
18
- 2: 'два',
19
- 3: 'три',
20
- 4: 'чотири',
21
- 5: 'п\'ять',
22
- 6: 'шiсть',
23
- 7: 'сiм',
24
- 8: 'вiсiм',
25
- 9: 'дев\'ять'
12
+
13
+ import { parseNumericValue } from '../utils/parse-numeric.js'
14
+ import { validateOptions } from '../utils/validate-options.js'
15
+
16
+ // ============================================================================
17
+ // Slavic Utilities (inlined for performance)
18
+ // ============================================================================
19
+
20
+ function pluralize (n, forms) {
21
+ const num = typeof n === 'bigint' ? Number(n) : n
22
+ const lastDigit = num % 10
23
+ const lastTwoDigits = num % 100
24
+
25
+ if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
26
+ return forms[2]
27
+ }
28
+
29
+ if (lastDigit === 1) return forms[0]
30
+ if (lastDigit >= 2 && lastDigit <= 4) return forms[1]
31
+ return forms[2]
32
+ }
33
+
34
+ function buildAllSegments (onesMasc, onesFem, teens, tens, hundreds) {
35
+ const masc = new Array(1000)
36
+ const fem = new Array(1000)
37
+
38
+ for (let i = 0; i < 1000; i++) {
39
+ masc[i] = buildSegment(i, onesMasc, teens, tens, hundreds)
40
+ fem[i] = buildSegment(i, onesFem, teens, tens, hundreds)
41
+ }
42
+
43
+ return { masc, fem }
44
+ }
45
+
46
+ function buildSegment (n, ones, teens, tens, hundreds) {
47
+ if (n === 0) return ''
48
+
49
+ const onesDigit = n % 10
50
+ const tensDigit = Math.floor(n / 10) % 10
51
+ const hundredsDigit = Math.floor(n / 100)
52
+
53
+ const parts = []
54
+
55
+ if (hundredsDigit > 0) {
56
+ parts.push(hundreds[hundredsDigit])
57
+ }
58
+
59
+ if (tensDigit > 1) {
60
+ parts.push(tens[tensDigit])
61
+ }
62
+
63
+ if (tensDigit === 1) {
64
+ parts.push(teens[onesDigit])
65
+ } else if (onesDigit > 0) {
66
+ parts.push(ones[onesDigit])
67
+ }
68
+
69
+ return parts.join(' ')
70
+ }
71
+
72
+ // ============================================================================
73
+ // Vocabulary
74
+ // ============================================================================
75
+
76
+ const ONES_MASC = ['', 'один', 'два', 'три', 'чотири', 'п\'ять', 'шiсть', 'сiм', 'вiсiм', 'дев\'ять']
77
+ const ONES_FEM = ['', 'одна', 'двi', 'три', 'чотири', 'п\'ять', 'шiсть', 'сiм', 'вiсiм', 'дев\'ять']
78
+
79
+ const TEENS = ['десять', 'одинадцять', 'дванадцять', 'тринадцять', 'чотирнадцять', 'п\'ятнадцять', 'шiстнадцять', 'сiмнадцять', 'вiсiмнадцять', 'дев\'ятнадцять']
80
+ const TENS = ['', '', 'двадцять', 'тридцять', 'сорок', 'п\'ятдесят', 'шiстдесят', 'сiмдесят', 'вiсiмдесят', 'дев\'яносто']
81
+
82
+ // Irregular hundreds
83
+ const HUNDREDS = ['', 'сто', 'двiстi', 'триста', 'чотириста', 'п\'ятсот', 'шiстсот', 'сiмсот', 'вiсiмсот', 'дев\'ятсот']
84
+
85
+ const ZERO = 'нуль'
86
+ const NEGATIVE = 'мiнус'
87
+ const DECIMAL_SEP = 'кома'
88
+
89
+ // Scale words: [singular, few, many]
90
+ // Thousands (index 0) are feminine, rest are masculine
91
+ const SCALE_FORMS = [
92
+ ['тисяча', 'тисячi', 'тисяч'],
93
+ ['мiльйон', 'мiльйони', 'мiльйонiв'],
94
+ ['мiльярд', 'мiльярди', 'мiльярдiв'],
95
+ ['трильйон', 'трильйони', 'трильйонiв'],
96
+ ['квадрильйон', 'квадрильйони', 'квадрильйонiв'],
97
+ ['квiнтильйон', 'квiнтильйони', 'квiнтильйонiв'],
98
+ ['секстильйон', 'секстильйони', 'секстильйонiв'],
99
+ ['септильйон', 'септильйони', 'септильйонiв'],
100
+ ['октильйон', 'октильйони', 'октильйонiв']
101
+ ]
102
+
103
+ // ============================================================================
104
+ // Precomputed Lookup Tables
105
+ // ============================================================================
106
+
107
+ const { masc: SEGMENTS_MASC, fem: SEGMENTS_FEM } = buildAllSegments(ONES_MASC, ONES_FEM, TEENS, TENS, HUNDREDS)
108
+
109
+ // ============================================================================
110
+ // Conversion Functions
111
+ // ============================================================================
112
+
113
+ function integerToWords (n, options = {}) {
114
+ if (n === 0n) return ZERO
115
+
116
+ if (n < 1000n) {
117
+ const segments = options.gender === 'feminine' ? SEGMENTS_FEM : SEGMENTS_MASC
118
+ return segments[Number(n)]
26
119
  }
27
120
 
28
- onesFeminineWords = {
29
- 1: 'одна',
30
- 2: 'двi',
31
- 3: 'три',
32
- 4: 'чотири',
33
- 5: 'п\'ять',
34
- 6: 'шiсть',
35
- 7: 'сiм',
36
- 8: 'вiсiм',
37
- 9: 'дев\'ять'
121
+ return buildLargeNumberWords(n, options)
122
+ }
123
+
124
+ function buildLargeNumberWords (n, options) {
125
+ const numStr = n.toString()
126
+ const len = numStr.length
127
+
128
+ const segments = []
129
+ const segmentSize = 3
130
+
131
+ const remainderLen = len % segmentSize
132
+ let pos = 0
133
+ if (remainderLen > 0) {
134
+ segments.push(Number(numStr.slice(0, remainderLen)))
135
+ pos = remainderLen
136
+ }
137
+ while (pos < len) {
138
+ segments.push(Number(numStr.slice(pos, pos + segmentSize)))
139
+ pos += segmentSize
38
140
  }
39
141
 
40
- teensWords = {
41
- 0: 'десять',
42
- 1: 'одинадцять',
43
- 2: 'дванадцять',
44
- 3: 'тринадцять',
45
- 4: 'чотирнадцять',
46
- 5: 'п\'ятнадцять',
47
- 6: 'шiстнадцять',
48
- 7: 'сiмнадцять',
49
- 8: 'вiсiмнадцять',
50
- 9: 'дев\'ятнадцять'
142
+ const parts = []
143
+ let scaleIndex = segments.length - 1
144
+
145
+ for (let i = 0; i < segments.length; i++) {
146
+ const segment = segments[i]
147
+
148
+ if (segment !== 0) {
149
+ if (scaleIndex === 0) {
150
+ const segmentWords = options.gender === 'feminine' ? SEGMENTS_FEM : SEGMENTS_MASC
151
+ parts.push(segmentWords[segment])
152
+ } else {
153
+ const scaleForms = SCALE_FORMS[scaleIndex - 1]
154
+ const scaleWord = pluralize(segment, scaleForms)
155
+ // Thousands (scaleIndex=1) are feminine, others masculine
156
+ const isFeminine = scaleIndex === 1
157
+ const segmentWords = isFeminine ? SEGMENTS_FEM : SEGMENTS_MASC
158
+ parts.push(segmentWords[segment] + ' ' + scaleWord)
159
+ }
160
+ }
161
+
162
+ scaleIndex--
51
163
  }
52
164
 
53
- twentiesWords = {
54
- 2: 'двадцять',
55
- 3: 'тридцять',
56
- 4: 'сорок',
57
- 5: 'п\'ятдесят',
58
- 6: iстдесят',
59
- 7: 'сiмдесят',
60
- 8: iсiмдесят',
61
- 9: 'дев\'яносто'
165
+ return parts.join(' ')
166
+ }
167
+
168
+ function decimalPartToWords (decimalPart, options) {
169
+ let result = ''
170
+ let i = 0
171
+
172
+ while (i < decimalPart.length && decimalPart[i] === '0') {
173
+ if (result) result += ' '
174
+ result += ZERO
175
+ i++
62
176
  }
63
177
 
64
- hundredsWords = {
65
- 1: 'сто',
66
- 2: 'двiстi',
67
- 3: 'триста',
68
- 4: 'чотириста',
69
- 5: 'п\'ятсот',
70
- 6: 'шiстсот',
71
- 7: 'сiмсот',
72
- 8: 'вiсiмсот',
73
- 9: 'дев\'ятсот'
178
+ const remainder = decimalPart.slice(i)
179
+ if (remainder) {
180
+ if (result) result += ' '
181
+ result += integerToWords(BigInt(remainder), options)
74
182
  }
75
183
 
76
- pluralForms = {
77
- 1: ['тисяча', 'тисячi', 'тисяч'], // 10^ 3
78
- 2: ['мiльйон', 'мiльйони', 'мiльйонiв'], // 10^ 6
79
- 3: ['мiльярд', 'мiльярди', 'мiльярдiв'], // 10^ 9
80
- 4: ['трильйон', 'трильйони', 'трильйонiв'], // 10^ 12
81
- 5: ['квадрильйон', 'квадрильйони', 'квадрильйонiв'], // 10^ 15
82
- 6: ['квiнтильйон', 'квiнтильйони', 'квiнтильйонiв'], // 10^ 18
83
- 7: ['секстильйон', 'секстильйони', 'секстильйонiв'], // 10^ 21
84
- 8: ['септильйон', 'септильйони', 'септильйонiв'], // 10^ 24
85
- 9: ['октильйон', 'октильйони', 'октильйонiв'], // 10^ 27
86
- 10: ['нонiльйон', 'нонiльйони', 'нонiльйонiв'] // 10^ 30
184
+ return result
185
+ }
186
+
187
+ /**
188
+ * Converts a numeric value to Ukrainian words.
189
+ *
190
+ * @param {number | string | bigint} value - The numeric value to convert
191
+ * @param {Object} [options] - Optional configuration
192
+ * @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender
193
+ * @returns {string} The number in Ukrainian words
194
+ */
195
+ function toWords (value, options) {
196
+ options = validateOptions(options)
197
+ const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
198
+
199
+ let result = ''
200
+
201
+ if (isNegative) {
202
+ result = NEGATIVE + ' '
87
203
  }
88
204
 
89
- /**
90
- * Ukrainian thousands (тисяча) are feminine, requiring одна/двi forms.
91
- * Other scales (million, billion, etc.) are masculine.
92
- */
93
- scaleGenders = {
94
- 1: true // thousands are feminine
205
+ result += integerToWords(integerPart, options)
206
+
207
+ if (decimalPart) {
208
+ result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart, options)
95
209
  }
210
+
211
+ return result
96
212
  }
213
+
214
+ // ============================================================================
215
+ // Exports
216
+ // ============================================================================
217
+
218
+ export { toWords }
@@ -1,11 +1,7 @@
1
1
  /**
2
- * Urdu language converter.
2
+ * Converts a numeric value to Urdu words.
3
3
  *
4
- * Supports:
5
- * - Indian numbering system (ہزار, لاکھ, کروڑ)
6
- * - Urdu script (right-to-left)
7
- * - Complete word forms for 0-99
4
+ * @param {number | string | bigint} value - The numeric value to convert
5
+ * @returns {string} The number in Urdu words
8
6
  */
9
- export class Urdu extends SouthAsianLanguage {
10
- }
11
- import { SouthAsianLanguage } from '../classes/south-asian-language.js';
7
+ export function toWords(value: number | string | bigint): string;
@@ -1,131 +1,163 @@
1
- import { SouthAsianLanguage } from '../classes/south-asian-language.js'
2
-
3
1
  /**
4
- * Urdu language converter.
2
+ * Urdu language converter - Functional Implementation
3
+ *
4
+ * Self-contained converter for South Asian numbering.
5
5
  *
6
- * Supports:
6
+ * Key features:
7
7
  * - Indian numbering system (ہزار, لاکھ, کروڑ)
8
8
  * - Urdu script (right-to-left)
9
+ * - 3-2-2 grouping pattern (last 3 digits, then groups of 2)
9
10
  * - Complete word forms for 0-99
10
11
  */
11
- export class Urdu extends SouthAsianLanguage {
12
- negativeWord = 'منفی'
13
- decimalSeparatorWord = 'اعشاریہ'
14
- zeroWord = 'صفر'
15
- hundredWord = 'سو'
16
-
17
- belowHundredWords = [
18
- 'صفر',
19
- 'ایک',
20
- 'دو',
21
- 'تین',
22
- 'چار',
23
- 'پانچ',
24
- 'چھ',
25
- 'سات',
26
- 'آٹھ',
27
- 'نو',
28
- 'دس',
29
- 'گیارہ',
30
- 'بارہ',
31
- 'تیرہ',
32
- 'چودہ',
33
- 'پندرہ',
34
- 'سولہ',
35
- 'سترہ',
36
- 'اٹھارہ',
37
- 'انیس',
38
- 'بیس',
39
- 'اکیس',
40
- 'بائیس',
41
- 'تیئیس',
42
- 'چوبیس',
43
- 'پچیس',
44
- 'چھبیس',
45
- 'ستائیس',
46
- 'اٹھائیس',
47
- 'انتیس',
48
- 'تیس',
49
- 'اکتیس',
50
- 'بتیس',
51
- 'تینتیس',
52
- 'چونتیس',
53
- 'پینتیس',
54
- 'چھتیس',
55
- 'سینتیس',
56
- 'اڑتیس',
57
- 'انتالیس',
58
- 'چالیس',
59
- 'اکتالیس',
60
- 'بیالیس',
61
- 'تینتالیس',
62
- 'چوالیس',
63
- 'پینتالیس',
64
- 'چھالیس',
65
- 'سینتالیس',
66
- 'اڑتالیس',
67
- 'انچاس',
68
- 'پچاس',
69
- 'اکاون',
70
- 'باون',
71
- 'ترپن',
72
- 'چون',
73
- 'پچپن',
74
- 'چھپن',
75
- 'ستاون',
76
- 'اٹھاون',
77
- 'انسٹھ',
78
- 'ساٹھ',
79
- 'اکسٹھ',
80
- 'باسٹھ',
81
- 'ترسٹھ',
82
- 'چونسٹھ',
83
- 'پینسٹھ',
84
- 'چھیاسٹھ',
85
- 'سڑسٹھ',
86
- 'اڑسٹھ',
87
- 'انہتر',
88
- 'ستر',
89
- 'اکہتر',
90
- 'بہتر',
91
- 'تہتر',
92
- 'چوہتر',
93
- 'پچھتر',
94
- 'چھہتر',
95
- 'ستتر',
96
- 'اٹھہتر',
97
- 'اناسی',
98
- 'اسی',
99
- 'اکیاسی',
100
- 'بیاسی',
101
- 'تریاسی',
102
- 'چوراسی',
103
- 'پچاسی',
104
- 'چھیاسی',
105
- 'ستاسی',
106
- 'اٹھاسی',
107
- 'نواسی',
108
- 'نوے',
109
- 'اکانوے',
110
- 'بانوے',
111
- 'ترانوے',
112
- 'چورانوے',
113
- 'پچانوے',
114
- 'چھیانوے',
115
- 'ستانوے',
116
- 'اٹھانوے',
117
- 'ننانوے'
118
- ]
119
-
120
- scaleWords = [
121
- '',
122
- 'ہزار',
123
- 'لاکھ',
124
- 'کروڑ',
125
- 'ارب',
126
- 'کھرب',
127
- 'نیل',
128
- 'پدم',
129
- 'شنکھ'
130
- ]
12
+
13
+ import { parseNumericValue } from '../utils/parse-numeric.js'
14
+
15
+ // ============================================================================
16
+ // Vocabulary
17
+ // ============================================================================
18
+
19
+ const ZERO = 'صفر'
20
+ const NEGATIVE = 'منفی'
21
+ const DECIMAL_SEP = 'اعشاریہ'
22
+ const HUNDRED = 'سو'
23
+
24
+ const BELOW_HUNDRED = [
25
+ 'صفر', 'ایک', 'دو', 'تین', 'چار', 'پانچ', 'چھ', 'سات', 'آٹھ', 'نو',
26
+ 'دس', 'گیارہ', 'بارہ', 'تیرہ', 'چودہ', 'پندرہ', 'سولہ', 'سترہ', 'اٹھارہ', 'انیس',
27
+ 'بیس', 'اکیس', 'بائیس', 'تیئیس', 'چوبیس', 'پچیس', 'چھبیس', 'ستائیس', 'اٹھائیس', 'انتیس',
28
+ 'تیس', 'اکتیس', 'بتیس', 'تینتیس', 'چونتیس', 'پینتیس', 'چھتیس', 'سینتیس', 'اڑتیس', 'انتالیس',
29
+ 'چالیس', 'اکتالیس', 'بیالیس', 'تینتالیس', 'چوالیس', 'پینتالیس', 'چھالیس', 'سینتالیس', 'اڑتالیس', 'انچاس',
30
+ 'پچاس', 'اکاون', 'باون', 'ترپن', 'چون', 'پچپن', 'چھپن', 'ستاون', 'اٹھاون', 'انسٹھ',
31
+ 'ساٹھ', 'اکسٹھ', 'باسٹھ', 'ترسٹھ', 'چونسٹھ', 'پینسٹھ', 'چھیاسٹھ', 'سڑسٹھ', 'اڑسٹھ', 'انہتر',
32
+ 'ستر', 'اکہتر', 'بہتر', 'تہتر', 'چوہتر', 'پچھتر', 'چھہتر', 'ستتر', 'اٹھہتر', 'اناسی',
33
+ 'اسی', 'اکیاسی', 'بیاسی', 'تریاسی', 'چوراسی', 'پچاسی', 'چھیاسی', 'ستاسی', 'اٹھاسی', 'نواسی',
34
+ 'نوے', 'اکانوے', 'بانوے', 'ترانوے', 'چورانوے', 'پچانوے', 'چھیانوے', 'ستانوے', 'اٹھانوے', 'ننانوے'
35
+ ]
36
+
37
+ // Scale words: index 0 = units (empty), 1 = thousand, 2 = lakh, 3 = crore, etc.
38
+ const SCALE_WORDS = ['', 'ہزار', 'لاکھ', 'کروڑ', 'ارب', 'کھرب', 'نیل', 'پدم', 'شنکھ']
39
+
40
+ // ============================================================================
41
+ // Conversion Functions
42
+ // ============================================================================
43
+
44
+ /**
45
+ * Converts 0-999 to Urdu words.
46
+ */
47
+ function segmentToWords (n) {
48
+ if (n === 0) return ''
49
+ if (n < 100) return BELOW_HUNDRED[n]
50
+
51
+ const hundreds = Math.trunc(n / 100)
52
+ const remainder = n % 100
53
+
54
+ if (remainder === 0) {
55
+ return BELOW_HUNDRED[hundreds] + ' ' + HUNDRED
56
+ }
57
+ return BELOW_HUNDRED[hundreds] + ' ' + HUNDRED + ' ' + BELOW_HUNDRED[remainder]
58
+ }
59
+
60
+ /**
61
+ * Converts a non-negative integer to Urdu words.
62
+ * Uses recursive approach for Indian 3-2-2 grouping pattern.
63
+ *
64
+ * @param {bigint} n - Non-negative integer to convert
65
+ * @returns {string} Urdu words
66
+ */
67
+ function integerToWords (n) {
68
+ if (n === 0n) return ZERO
69
+
70
+ // Fast path: numbers < 1000 (direct lookup)
71
+ if (n < 1000n) {
72
+ return segmentToWords(Number(n))
73
+ }
74
+
75
+ return buildLargeNumberWords(n, 0)
131
76
  }
77
+
78
+ /**
79
+ * Recursively builds words for numbers >= 1000.
80
+ * Indian grouping: first 3 digits, then 2-digit groups.
81
+ *
82
+ * @param {bigint} n - Number to convert
83
+ * @param {number} scale - Current scale index (0=units, 1=thousands, etc.)
84
+ * @returns {string} Urdu words
85
+ */
86
+ function buildLargeNumberWords (n, scale) {
87
+ if (n === 0n) return ''
88
+
89
+ // Determine divisor: 1000 for first split, 100 for rest
90
+ const divisor = scale === 0 ? 1000n : 100n
91
+ const segment = Number(n % divisor)
92
+ const rest = n / divisor
93
+
94
+ // Build higher segments first (recursive)
95
+ let result = ''
96
+ if (rest > 0n) {
97
+ result = buildLargeNumberWords(rest, scale + 1)
98
+ }
99
+
100
+ // Add current segment
101
+ if (segment > 0) {
102
+ if (result) result += ' '
103
+
104
+ if (scale === 0) {
105
+ // Units segment (0-999)
106
+ result += segmentToWords(segment)
107
+ } else {
108
+ // Scale segments (0-99)
109
+ result += BELOW_HUNDRED[segment] + ' ' + SCALE_WORDS[scale]
110
+ }
111
+ }
112
+
113
+ return result
114
+ }
115
+
116
+ function decimalPartToWords (decimalPart) {
117
+ let result = ''
118
+ let i = 0
119
+
120
+ while (i < decimalPart.length && decimalPart[i] === '0') {
121
+ if (result) result += ' '
122
+ result += ZERO
123
+ i++
124
+ }
125
+
126
+ const remainder = decimalPart.slice(i)
127
+ if (remainder) {
128
+ if (result) result += ' '
129
+ result += integerToWords(BigInt(remainder))
130
+ }
131
+
132
+ return result
133
+ }
134
+
135
+ /**
136
+ * Converts a numeric value to Urdu words.
137
+ *
138
+ * @param {number | string | bigint} value - The numeric value to convert
139
+ * @returns {string} The number in Urdu words
140
+ */
141
+ function toWords (value) {
142
+ const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
143
+
144
+ let result = ''
145
+
146
+ if (isNegative) {
147
+ result = NEGATIVE + ' '
148
+ }
149
+
150
+ result += integerToWords(integerPart)
151
+
152
+ if (decimalPart) {
153
+ result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)
154
+ }
155
+
156
+ return result
157
+ }
158
+
159
+ // ============================================================================
160
+ // Exports
161
+ // ============================================================================
162
+
163
+ export { toWords }