n2words 2.0.0 → 3.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 (335) hide show
  1. package/CHANGELOG.md +64 -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/ka.js +3 -0
  56. package/dist/languages/ka.js.map +1 -0
  57. package/dist/languages/kn.js +3 -0
  58. package/dist/languages/kn.js.map +1 -0
  59. package/dist/languages/ko.js +3 -0
  60. package/dist/languages/ko.js.map +1 -0
  61. package/dist/languages/lt.js +3 -0
  62. package/dist/languages/lt.js.map +1 -0
  63. package/dist/languages/lv.js +3 -0
  64. package/dist/languages/lv.js.map +1 -0
  65. package/dist/languages/mr.js +3 -0
  66. package/dist/languages/mr.js.map +1 -0
  67. package/dist/languages/ms.js +3 -0
  68. package/dist/languages/ms.js.map +1 -0
  69. package/dist/languages/nb.js +3 -0
  70. package/dist/languages/nb.js.map +1 -0
  71. package/dist/languages/nl.js +3 -0
  72. package/dist/languages/nl.js.map +1 -0
  73. package/dist/languages/pa.js +3 -0
  74. package/dist/languages/pa.js.map +1 -0
  75. package/dist/languages/pl.js +3 -0
  76. package/dist/languages/pl.js.map +1 -0
  77. package/dist/languages/pt.js +3 -0
  78. package/dist/languages/pt.js.map +1 -0
  79. package/dist/languages/ro.js +3 -0
  80. package/dist/languages/ro.js.map +1 -0
  81. package/dist/languages/ru.js +3 -0
  82. package/dist/languages/ru.js.map +1 -0
  83. package/dist/languages/sr-Cyrl.js +3 -0
  84. package/dist/languages/sr-Cyrl.js.map +1 -0
  85. package/dist/languages/sr-Latn.js +3 -0
  86. package/dist/languages/sr-Latn.js.map +1 -0
  87. package/dist/languages/sv.js +3 -0
  88. package/dist/languages/sv.js.map +1 -0
  89. package/dist/languages/sw.js +3 -0
  90. package/dist/languages/sw.js.map +1 -0
  91. package/dist/languages/ta.js +3 -0
  92. package/dist/languages/ta.js.map +1 -0
  93. package/dist/languages/te.js +3 -0
  94. package/dist/languages/te.js.map +1 -0
  95. package/dist/languages/th.js +3 -0
  96. package/dist/languages/th.js.map +1 -0
  97. package/dist/languages/tr.js +3 -0
  98. package/dist/languages/tr.js.map +1 -0
  99. package/dist/languages/uk.js +3 -0
  100. package/dist/languages/uk.js.map +1 -0
  101. package/dist/languages/ur.js +3 -0
  102. package/dist/languages/ur.js.map +1 -0
  103. package/dist/languages/vi.js +3 -0
  104. package/dist/languages/vi.js.map +1 -0
  105. package/dist/languages/yo.js +3 -0
  106. package/dist/languages/yo.js.map +1 -0
  107. package/dist/languages/zh-Hans.js +3 -0
  108. package/dist/languages/zh-Hans.js.map +1 -0
  109. package/dist/languages/zh-Hant.js +3 -0
  110. package/dist/languages/zh-Hant.js.map +1 -0
  111. package/dist/n2words.js +2 -2
  112. package/dist/n2words.js.map +1 -1
  113. package/lib/languages/am-Latn.d.ts +7 -0
  114. package/lib/languages/am-Latn.js +159 -0
  115. package/lib/languages/am.d.ts +7 -0
  116. package/lib/languages/am.js +159 -0
  117. package/lib/languages/ar.d.ts +14 -27
  118. package/lib/languages/ar.js +175 -129
  119. package/lib/languages/az.d.ts +4 -9
  120. package/lib/languages/az.js +166 -37
  121. package/lib/languages/bn.d.ts +4 -8
  122. package/lib/languages/bn.js +159 -124
  123. package/lib/languages/cs.d.ts +15 -85
  124. package/lib/languages/cs.js +293 -114
  125. package/lib/languages/da.d.ts +11 -12
  126. package/lib/languages/da.js +269 -101
  127. package/lib/languages/de.d.ts +14 -11
  128. package/lib/languages/de.js +305 -86
  129. package/lib/languages/el.d.ts +11 -11
  130. package/lib/languages/el.js +224 -78
  131. package/lib/languages/en.d.ts +14 -13
  132. package/lib/languages/en.js +228 -72
  133. package/lib/languages/es.d.ts +18 -12
  134. package/lib/languages/es.js +297 -103
  135. package/lib/languages/fa.d.ts +4 -44
  136. package/lib/languages/fa.js +112 -122
  137. package/lib/languages/fi.d.ts +14 -0
  138. package/lib/languages/fi.js +238 -0
  139. package/lib/languages/fil.d.ts +4 -13
  140. package/lib/languages/fil.js +196 -106
  141. package/lib/languages/fr-BE.d.ts +8 -8
  142. package/lib/languages/fr-BE.js +285 -19
  143. package/lib/languages/fr.d.ts +18 -12
  144. package/lib/languages/fr.js +339 -89
  145. package/lib/languages/gu.d.ts +4 -8
  146. package/lib/languages/gu.js +143 -125
  147. package/lib/languages/ha.d.ts +7 -0
  148. package/lib/languages/ha.js +225 -0
  149. package/lib/languages/hbo.d.ts +10 -110
  150. package/lib/languages/hbo.js +245 -214
  151. package/lib/languages/he.d.ts +10 -77
  152. package/lib/languages/he.js +231 -172
  153. package/lib/languages/hi.d.ts +4 -8
  154. package/lib/languages/hi.js +163 -124
  155. package/lib/languages/hr.d.ts +8 -77
  156. package/lib/languages/hr.js +200 -89
  157. package/lib/languages/hu.d.ts +4 -19
  158. package/lib/languages/hu.js +198 -119
  159. package/lib/languages/id.d.ts +4 -34
  160. package/lib/languages/id.js +166 -129
  161. package/lib/languages/it.d.ts +16 -34
  162. package/lib/languages/it.js +307 -97
  163. package/lib/languages/ja.d.ts +14 -14
  164. package/lib/languages/ja.js +221 -111
  165. package/lib/languages/ka.d.ts +17 -0
  166. package/lib/languages/ka.js +291 -0
  167. package/lib/languages/kn.d.ts +4 -8
  168. package/lib/languages/kn.js +143 -35
  169. package/lib/languages/ko.d.ts +11 -11
  170. package/lib/languages/ko.js +250 -49
  171. package/lib/languages/lt.d.ts +15 -67
  172. package/lib/languages/lt.js +287 -122
  173. package/lib/languages/lv.d.ts +15 -67
  174. package/lib/languages/lv.js +288 -106
  175. package/lib/languages/mr.d.ts +4 -8
  176. package/lib/languages/mr.js +143 -125
  177. package/lib/languages/ms.d.ts +4 -28
  178. package/lib/languages/ms.js +166 -116
  179. package/lib/languages/nb.d.ts +11 -9
  180. package/lib/languages/nb.js +272 -87
  181. package/lib/languages/nl.d.ts +23 -13
  182. package/lib/languages/nl.js +299 -133
  183. package/lib/languages/pa.d.ts +4 -8
  184. package/lib/languages/pa.js +151 -124
  185. package/lib/languages/pl.d.ts +19 -77
  186. package/lib/languages/pl.js +294 -87
  187. package/lib/languages/pt.d.ts +14 -26
  188. package/lib/languages/pt.js +272 -92
  189. package/lib/languages/ro.d.ts +15 -155
  190. package/lib/languages/ro.js +219 -235
  191. package/lib/languages/ru.d.ts +8 -82
  192. package/lib/languages/ru.js +239 -90
  193. package/lib/languages/sr-Cyrl.d.ts +8 -77
  194. package/lib/languages/sr-Cyrl.js +197 -89
  195. package/lib/languages/sr-Latn.d.ts +8 -77
  196. package/lib/languages/sr-Latn.js +197 -89
  197. package/lib/languages/sv.d.ts +11 -11
  198. package/lib/languages/sv.js +278 -74
  199. package/lib/languages/sw.d.ts +4 -36
  200. package/lib/languages/sw.js +133 -106
  201. package/lib/languages/ta.d.ts +4 -17
  202. package/lib/languages/ta.js +143 -202
  203. package/lib/languages/te.d.ts +4 -19
  204. package/lib/languages/te.js +133 -196
  205. package/lib/languages/th.d.ts +4 -14
  206. package/lib/languages/th.js +135 -91
  207. package/lib/languages/tr.d.ts +15 -9
  208. package/lib/languages/tr.js +245 -49
  209. package/lib/languages/uk.d.ts +8 -82
  210. package/lib/languages/uk.js +206 -78
  211. package/lib/languages/ur.d.ts +4 -8
  212. package/lib/languages/ur.js +151 -124
  213. package/lib/languages/vi.d.ts +14 -69
  214. package/lib/languages/vi.js +278 -129
  215. package/lib/languages/yo.d.ts +7 -0
  216. package/lib/languages/yo.js +303 -0
  217. package/lib/languages/zh-Hans.d.ts +8 -18
  218. package/lib/languages/zh-Hans.js +163 -92
  219. package/lib/languages/zh-Hant.d.ts +8 -18
  220. package/lib/languages/zh-Hant.js +181 -90
  221. package/lib/n2words.d.ts +55 -209
  222. package/lib/n2words.js +115 -530
  223. package/lib/utils/is-plain-object.d.ts +13 -0
  224. package/lib/utils/is-plain-object.js +17 -0
  225. package/lib/utils/parse-numeric.d.ts +17 -0
  226. package/lib/utils/parse-numeric.js +108 -0
  227. package/lib/utils/validate-options.d.ts +8 -0
  228. package/lib/utils/validate-options.js +16 -0
  229. package/package.json +26 -14
  230. package/dist/ArabicConverter.js +0 -3
  231. package/dist/ArabicConverter.js.map +0 -1
  232. package/dist/AzerbaijaniConverter.js +0 -3
  233. package/dist/AzerbaijaniConverter.js.map +0 -1
  234. package/dist/BanglaConverter.js +0 -3
  235. package/dist/BanglaConverter.js.map +0 -1
  236. package/dist/BiblicalHebrewConverter.js +0 -3
  237. package/dist/BiblicalHebrewConverter.js.map +0 -1
  238. package/dist/CroatianConverter.js +0 -3
  239. package/dist/CroatianConverter.js.map +0 -1
  240. package/dist/CzechConverter.js +0 -3
  241. package/dist/CzechConverter.js.map +0 -1
  242. package/dist/DanishConverter.js +0 -3
  243. package/dist/DanishConverter.js.map +0 -1
  244. package/dist/DutchConverter.js +0 -3
  245. package/dist/DutchConverter.js.map +0 -1
  246. package/dist/EnglishConverter.js +0 -3
  247. package/dist/EnglishConverter.js.map +0 -1
  248. package/dist/FilipinoConverter.js +0 -3
  249. package/dist/FilipinoConverter.js.map +0 -1
  250. package/dist/FrenchBelgiumConverter.js +0 -3
  251. package/dist/FrenchBelgiumConverter.js.map +0 -1
  252. package/dist/FrenchConverter.js +0 -3
  253. package/dist/FrenchConverter.js.map +0 -1
  254. package/dist/GermanConverter.js +0 -3
  255. package/dist/GermanConverter.js.map +0 -1
  256. package/dist/GreekConverter.js +0 -3
  257. package/dist/GreekConverter.js.map +0 -1
  258. package/dist/GujaratiConverter.js +0 -3
  259. package/dist/GujaratiConverter.js.map +0 -1
  260. package/dist/HebrewConverter.js +0 -3
  261. package/dist/HebrewConverter.js.map +0 -1
  262. package/dist/HindiConverter.js +0 -3
  263. package/dist/HindiConverter.js.map +0 -1
  264. package/dist/HungarianConverter.js +0 -3
  265. package/dist/HungarianConverter.js.map +0 -1
  266. package/dist/IndonesianConverter.js +0 -3
  267. package/dist/IndonesianConverter.js.map +0 -1
  268. package/dist/ItalianConverter.js +0 -3
  269. package/dist/ItalianConverter.js.map +0 -1
  270. package/dist/JapaneseConverter.js +0 -3
  271. package/dist/JapaneseConverter.js.map +0 -1
  272. package/dist/KannadaConverter.js +0 -3
  273. package/dist/KannadaConverter.js.map +0 -1
  274. package/dist/KoreanConverter.js +0 -3
  275. package/dist/KoreanConverter.js.map +0 -1
  276. package/dist/LatvianConverter.js +0 -3
  277. package/dist/LatvianConverter.js.map +0 -1
  278. package/dist/LithuanianConverter.js +0 -3
  279. package/dist/LithuanianConverter.js.map +0 -1
  280. package/dist/MalayConverter.js +0 -3
  281. package/dist/MalayConverter.js.map +0 -1
  282. package/dist/MarathiConverter.js +0 -3
  283. package/dist/MarathiConverter.js.map +0 -1
  284. package/dist/NorwegianBokmalConverter.js +0 -3
  285. package/dist/NorwegianBokmalConverter.js.map +0 -1
  286. package/dist/PersianConverter.js +0 -3
  287. package/dist/PersianConverter.js.map +0 -1
  288. package/dist/PolishConverter.js +0 -3
  289. package/dist/PolishConverter.js.map +0 -1
  290. package/dist/PortugueseConverter.js +0 -3
  291. package/dist/PortugueseConverter.js.map +0 -1
  292. package/dist/PunjabiConverter.js +0 -3
  293. package/dist/PunjabiConverter.js.map +0 -1
  294. package/dist/RomanianConverter.js +0 -3
  295. package/dist/RomanianConverter.js.map +0 -1
  296. package/dist/RussianConverter.js +0 -3
  297. package/dist/RussianConverter.js.map +0 -1
  298. package/dist/SerbianCyrillicConverter.js +0 -3
  299. package/dist/SerbianCyrillicConverter.js.map +0 -1
  300. package/dist/SerbianLatinConverter.js +0 -3
  301. package/dist/SerbianLatinConverter.js.map +0 -1
  302. package/dist/SimplifiedChineseConverter.js +0 -3
  303. package/dist/SimplifiedChineseConverter.js.map +0 -1
  304. package/dist/SpanishConverter.js +0 -3
  305. package/dist/SpanishConverter.js.map +0 -1
  306. package/dist/SwahiliConverter.js +0 -3
  307. package/dist/SwahiliConverter.js.map +0 -1
  308. package/dist/SwedishConverter.js +0 -3
  309. package/dist/SwedishConverter.js.map +0 -1
  310. package/dist/TamilConverter.js +0 -3
  311. package/dist/TamilConverter.js.map +0 -1
  312. package/dist/TeluguConverter.js +0 -3
  313. package/dist/TeluguConverter.js.map +0 -1
  314. package/dist/ThaiConverter.js +0 -3
  315. package/dist/ThaiConverter.js.map +0 -1
  316. package/dist/TraditionalChineseConverter.js +0 -3
  317. package/dist/TraditionalChineseConverter.js.map +0 -1
  318. package/dist/TurkishConverter.js +0 -3
  319. package/dist/TurkishConverter.js.map +0 -1
  320. package/dist/UkrainianConverter.js +0 -3
  321. package/dist/UkrainianConverter.js.map +0 -1
  322. package/dist/UrduConverter.js +0 -3
  323. package/dist/UrduConverter.js.map +0 -1
  324. package/dist/VietnameseConverter.js +0 -3
  325. package/dist/VietnameseConverter.js.map +0 -1
  326. package/lib/classes/abstract-language.d.ts +0 -178
  327. package/lib/classes/abstract-language.js +0 -268
  328. package/lib/classes/greedy-scale-language.d.ts +0 -109
  329. package/lib/classes/greedy-scale-language.js +0 -201
  330. package/lib/classes/slavic-language.d.ts +0 -148
  331. package/lib/classes/slavic-language.js +0 -281
  332. package/lib/classes/south-asian-language.d.ts +0 -70
  333. package/lib/classes/south-asian-language.js +0 -154
  334. package/lib/classes/turkic-language.d.ts +0 -26
  335. package/lib/classes/turkic-language.js +0 -59
@@ -1,96 +1,224 @@
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 module with its own input validation, ready for subpath exports.
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
+ // Vocabulary
18
+ // ============================================================================
19
+
20
+ const ONES_MASC = ['', 'один', 'два', 'три', 'чотири', 'п\'ять', 'шiсть', 'сiм', 'вiсiм', 'дев\'ять']
21
+ const ONES_FEM = ['', 'одна', 'двi', 'три', 'чотири', 'п\'ять', 'шiсть', 'сiм', 'вiсiм', 'дев\'ять']
22
+
23
+ const TEENS = ['десять', 'одинадцять', 'дванадцять', 'тринадцять', 'чотирнадцять', 'п\'ятнадцять', 'шiстнадцять', 'сiмнадцять', 'вiсiмнадцять', 'дев\'ятнадцять']
24
+ const TENS = ['', '', 'двадцять', 'тридцять', 'сорок', 'п\'ятдесят', 'шiстдесят', 'сiмдесят', 'вiсiмдесят', 'дев\'яносто']
25
+
26
+ // Irregular hundreds
27
+ const HUNDREDS = ['', 'сто', 'двiстi', 'триста', 'чотириста', 'п\'ятсот', 'шiстсот', 'сiмсот', 'вiсiмсот', 'дев\'ятсот']
28
+
29
+ const ZERO = 'нуль'
30
+ const NEGATIVE = 'мiнус'
31
+ const DECIMAL_SEP = 'кома'
32
+
33
+ // Scale words: [singular, few, many]
34
+ // Thousands (index 0) are feminine, rest are masculine
35
+ const SCALE_FORMS = [
36
+ ['тисяча', 'тисячi', 'тисяч'],
37
+ ['мiльйон', 'мiльйони', 'мiльйонiв'],
38
+ ['мiльярд', 'мiльярди', 'мiльярдiв'],
39
+ ['трильйон', 'трильйони', 'трильйонiв'],
40
+ ['квадрильйон', 'квадрильйони', 'квадрильйонiв'],
41
+ ['квiнтильйон', 'квiнтильйони', 'квiнтильйонiв'],
42
+ ['секстильйон', 'секстильйони', 'секстильйонiв'],
43
+ ['септильйон', 'септильйони', 'септильйонiв'],
44
+ ['октильйон', 'октильйони', 'октильйонiв']
45
+ ]
46
+
47
+ // ============================================================================
48
+ // Segment Building
49
+ // ============================================================================
50
+
51
+ function pluralize (n, forms) {
52
+ const num = typeof n === 'bigint' ? Number(n) : n
53
+ const lastDigit = num % 10
54
+ const lastTwoDigits = num % 100
55
+
56
+ if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
57
+ return forms[2]
58
+ }
59
+
60
+ if (lastDigit === 1) return forms[0]
61
+ if (lastDigit >= 2 && lastDigit <= 4) return forms[1]
62
+ return forms[2]
63
+ }
64
+
65
+ function buildSegmentMasc (n) {
66
+ if (n === 0) return ''
67
+
68
+ const onesDigit = n % 10
69
+ const tensDigit = Math.floor(n / 10) % 10
70
+ const hundredsDigit = Math.floor(n / 100)
71
+
72
+ const parts = []
73
+
74
+ if (hundredsDigit > 0) {
75
+ parts.push(HUNDREDS[hundredsDigit])
76
+ }
77
+
78
+ if (tensDigit > 1) {
79
+ parts.push(TENS[tensDigit])
80
+ }
81
+
82
+ if (tensDigit === 1) {
83
+ parts.push(TEENS[onesDigit])
84
+ } else if (onesDigit > 0) {
85
+ parts.push(ONES_MASC[onesDigit])
86
+ }
87
+
88
+ return parts.join(' ')
89
+ }
90
+
91
+ function buildSegmentFem (n) {
92
+ if (n === 0) return ''
93
+
94
+ const onesDigit = n % 10
95
+ const tensDigit = Math.floor(n / 10) % 10
96
+ const hundredsDigit = Math.floor(n / 100)
97
+
98
+ const parts = []
99
+
100
+ if (hundredsDigit > 0) {
101
+ parts.push(HUNDREDS[hundredsDigit])
102
+ }
103
+
104
+ if (tensDigit > 1) {
105
+ parts.push(TENS[tensDigit])
26
106
  }
27
107
 
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: 'дев\'ять'
108
+ if (tensDigit === 1) {
109
+ parts.push(TEENS[onesDigit])
110
+ } else if (onesDigit > 0) {
111
+ parts.push(ONES_FEM[onesDigit])
38
112
  }
39
113
 
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: 'дев\'ятнадцять'
114
+ return parts.join(' ')
115
+ }
116
+
117
+ // ============================================================================
118
+ // Conversion Functions
119
+ // ============================================================================
120
+
121
+ function integerToWords (n, options = {}) {
122
+ if (n === 0n) return ZERO
123
+
124
+ if (n < 1000n) {
125
+ return options.gender === 'feminine' ? buildSegmentFem(Number(n)) : buildSegmentMasc(Number(n))
51
126
  }
52
127
 
53
- twentiesWords = {
54
- 2: 'двадцять',
55
- 3: 'тридцять',
56
- 4: 'сорок',
57
- 5: 'п\'ятдесят',
58
- 6: 'шiстдесят',
59
- 7: 'сiмдесят',
60
- 8: 'вiсiмдесят',
61
- 9: 'дев\'яносто'
128
+ return buildLargeNumberWords(n, options)
129
+ }
130
+
131
+ function buildLargeNumberWords (n, options) {
132
+ const numStr = n.toString()
133
+ const len = numStr.length
134
+
135
+ const segments = []
136
+ const segmentSize = 3
137
+
138
+ const remainderLen = len % segmentSize
139
+ let pos = 0
140
+ if (remainderLen > 0) {
141
+ segments.push(Number(numStr.slice(0, remainderLen)))
142
+ pos = remainderLen
143
+ }
144
+ while (pos < len) {
145
+ segments.push(Number(numStr.slice(pos, pos + segmentSize)))
146
+ pos += segmentSize
62
147
  }
63
148
 
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: 'дев\'ятсот'
149
+ const parts = []
150
+ let scaleIndex = segments.length - 1
151
+
152
+ for (let i = 0; i < segments.length; i++) {
153
+ const segment = segments[i]
154
+
155
+ if (segment !== 0) {
156
+ if (scaleIndex === 0) {
157
+ parts.push(options.gender === 'feminine' ? buildSegmentFem(segment) : buildSegmentMasc(segment))
158
+ } else {
159
+ const scaleForms = SCALE_FORMS[scaleIndex - 1]
160
+ const scaleWord = pluralize(segment, scaleForms)
161
+ // Thousands (scaleIndex=1) are feminine, others masculine
162
+ const isFeminine = scaleIndex === 1
163
+ const segmentWord = isFeminine ? buildSegmentFem(segment) : buildSegmentMasc(segment)
164
+ parts.push(segmentWord + ' ' + scaleWord)
165
+ }
166
+ }
167
+
168
+ scaleIndex--
74
169
  }
75
170
 
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
171
+ return parts.join(' ')
172
+ }
173
+
174
+ function decimalPartToWords (decimalPart, options) {
175
+ let result = ''
176
+ let i = 0
177
+
178
+ while (i < decimalPart.length && decimalPart[i] === '0') {
179
+ if (result) result += ' '
180
+ result += ZERO
181
+ i++
87
182
  }
88
183
 
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
184
+ const remainder = decimalPart.slice(i)
185
+ if (remainder) {
186
+ if (result) result += ' '
187
+ result += integerToWords(BigInt(remainder), options)
95
188
  }
189
+
190
+ return result
96
191
  }
192
+
193
+ /**
194
+ * Converts a numeric value to Ukrainian words.
195
+ *
196
+ * @param {number | string | bigint} value - The numeric value to convert
197
+ * @param {Object} [options] - Optional configuration
198
+ * @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender
199
+ * @returns {string} The number in Ukrainian words
200
+ */
201
+ function toWords (value, options) {
202
+ options = validateOptions(options)
203
+ const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
204
+
205
+ let result = ''
206
+
207
+ if (isNegative) {
208
+ result = NEGATIVE + ' '
209
+ }
210
+
211
+ result += integerToWords(integerPart, options)
212
+
213
+ if (decimalPart) {
214
+ result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart, options)
215
+ }
216
+
217
+ return result
218
+ }
219
+
220
+ // ============================================================================
221
+ // Exports
222
+ // ============================================================================
223
+
224
+ 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,158 @@
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 module with its own input validation, ready for subpath exports.
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
+ // Segment Building
42
+ // ============================================================================
43
+
44
+ /**
45
+ * Builds words for a 0-999 segment.
46
+ */
47
+ function buildSegment (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
+ // Conversion Functions
62
+ // ============================================================================
63
+
64
+ /**
65
+ * Converts a non-negative integer to Urdu words.
66
+ *
67
+ * Uses BigInt modulo for segment extraction (faster than string slicing).
68
+ * South Asian 3-2-2 grouping: first 3 digits, then groups of 2.
69
+ *
70
+ * @param {bigint} n - Non-negative integer to convert
71
+ * @returns {string} Urdu words
72
+ */
73
+ function integerToWords (n) {
74
+ if (n === 0n) return ZERO
75
+
76
+ // Fast path: numbers < 1000 (direct lookup)
77
+ if (n < 1000n) {
78
+ return buildSegment(Number(n))
79
+ }
80
+
81
+ // Extract segments using BigInt modulo
82
+ const segments = []
83
+ segments.push(Number(n % 1000n))
84
+ let temp = n / 1000n
85
+
86
+ while (temp > 0n) {
87
+ segments.push(Number(temp % 100n))
88
+ temp = temp / 100n
89
+ }
90
+
91
+ // Build result string (process from most-significant to least)
92
+ const words = []
93
+ for (let i = segments.length - 1; i >= 0; i--) {
94
+ const segment = segments[i]
95
+ if (segment === 0) continue
96
+
97
+ if (i === 0) {
98
+ words.push(buildSegment(segment))
99
+ } else {
100
+ words.push(BELOW_HUNDRED[segment])
101
+ }
102
+
103
+ if (i > 0 && SCALE_WORDS[i]) {
104
+ words.push(SCALE_WORDS[i])
105
+ }
106
+ }
107
+
108
+ return words.join(' ')
109
+ }
110
+
111
+ function decimalPartToWords (decimalPart) {
112
+ let result = ''
113
+ let i = 0
114
+
115
+ while (i < decimalPart.length && decimalPart[i] === '0') {
116
+ if (result) result += ' '
117
+ result += ZERO
118
+ i++
119
+ }
120
+
121
+ const remainder = decimalPart.slice(i)
122
+ if (remainder) {
123
+ if (result) result += ' '
124
+ result += integerToWords(BigInt(remainder))
125
+ }
126
+
127
+ return result
128
+ }
129
+
130
+ /**
131
+ * Converts a numeric value to Urdu words.
132
+ *
133
+ * @param {number | string | bigint} value - The numeric value to convert
134
+ * @returns {string} The number in Urdu words
135
+ */
136
+ function toWords (value) {
137
+ const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
138
+
139
+ let result = ''
140
+
141
+ if (isNegative) {
142
+ result = NEGATIVE + ' '
143
+ }
144
+
145
+ result += integerToWords(integerPart)
146
+
147
+ if (decimalPart) {
148
+ result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)
149
+ }
150
+
151
+ return result
131
152
  }
153
+
154
+ // ============================================================================
155
+ // Exports
156
+ // ============================================================================
157
+
158
+ export { toWords }