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,240 @@
1
- import { SlavicLanguage } from '../classes/slavic-language.js'
2
-
3
1
  /**
4
- * Russian language converter.
2
+ * Russian language converter - Functional Implementation
3
+ *
4
+ * Self-contained converter using shared Slavic utilities.
5
5
  *
6
- * Supports:
7
- * - Gender agreement (masculine/feminine forms)
6
+ * Key features:
8
7
  * - Three-form pluralization (one/few/many)
9
- * - Large numbers up to nonillions
8
+ * - Gender: thousands are feminine, millions+ are masculine
9
+ * - Irregular hundreds (двести, триста, etc.)
10
+ * - Long scale naming
10
11
  */
11
- export class Russian extends SlavicLanguage {
12
- negativeWord = 'минус'
13
- decimalSeparatorWord = 'запятая'
14
- zeroWord = 'ноль'
15
-
16
- onesWords = {
17
- 1: 'один',
18
- 2: 'два',
19
- 3: 'три',
20
- 4: 'четыре',
21
- 5: 'пять',
22
- 6: 'шесть',
23
- 7: 'семь',
24
- 8: 'восемь',
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]
26
27
  }
27
28
 
28
- onesFeminineWords = {
29
- 1: 'одна',
30
- 2: 'две',
31
- 3: 'три',
32
- 4: 'четыре',
33
- 5: 'пять',
34
- 6: 'шесть',
35
- 7: 'семь',
36
- 8: 'восемь',
37
- 9: 'девять'
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)
38
41
  }
39
42
 
40
- teensWords = {
41
- 0: 'десять',
42
- 1: 'одиннадцать',
43
- 2: 'двенадцать',
44
- 3: 'тринадцать',
45
- 4: 'четырнадцать',
46
- 5: 'пятнадцать',
47
- 6: 'шестнадцать',
48
- 7: 'семнадцать',
49
- 8: 'восемнадцать',
50
- 9: 'девятнадцать'
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])
51
57
  }
52
58
 
53
- twentiesWords = {
54
- 2: 'двадцать',
55
- 3: 'тридцать',
56
- 4: 'сорок',
57
- 5: 'пятьдесят',
58
- 6: 'шестьдесят',
59
- 7: 'семьдесят',
60
- 8: 'восемьдесят',
61
- 9: 'девяносто'
59
+ if (tensDigit > 1) {
60
+ parts.push(tens[tensDigit])
62
61
  }
63
62
 
64
- hundredsWords = {
65
- 1: 'сто',
66
- 2: 'двести',
67
- 3: 'триста',
68
- 4: 'четыреста',
69
- 5: 'пятьсот',
70
- 6: 'шестьсот',
71
- 7: 'семьсот',
72
- 8: 'восемьсот',
73
- 9: 'девятьсот'
63
+ if (tensDigit === 1) {
64
+ parts.push(teens[onesDigit])
65
+ } else if (onesDigit > 0) {
66
+ parts.push(ones[onesDigit])
74
67
  }
75
68
 
76
- pluralForms = {
77
- 1: ['тысяча', 'тысячи', 'тысяч'], // 10^ 3
78
- 2: ['миллион', 'миллиона', 'миллионов'], // 10^ 6
79
- 3: ['миллиард', 'миллиарда', 'миллиардов'], // 10^ 9
80
- 4: ['триллион', 'триллиона', 'триллионов'], // 10^ 12
81
- 5: ['квадриллион', 'квадриллиона', 'квадриллионов'], // 10^ 15
82
- 6: ['квинтиллион', 'квинтиллиона', 'квинтиллионов'], // 10^ 18
83
- 7: ['секстиллион', 'секстиллиона', 'секстиллионов'], // 10^ 21
84
- 8: ['септиллион', 'септиллиона', 'септиллионов'], // 10^ 24
85
- 9: ['октиллион', 'октиллиона', 'октиллионов'], // 10^ 27
86
- 10: ['нониллион', 'нониллиона', 'нониллионов'] // 10^ 30
69
+ return parts.join(' ')
70
+ }
71
+
72
+ // ============================================================================
73
+ // Vocabulary
74
+ // ============================================================================
75
+
76
+ const ONES_MASC = ['', 'один', 'два', 'три', 'четыре', 'пять', 'шесть', 'семь', 'восемь', 'девять']
77
+ const ONES_FEM = ['', 'одна', 'две', 'три', 'четыре', 'пять', 'шесть', 'семь', 'восемь', 'девять']
78
+
79
+ const TEENS = ['десять', 'одиннадцать', 'двенадцать', 'тринадцать', 'четырнадцать', 'пятнадцать', 'шестнадцать', 'семнадцать', 'восемнадцать', 'девятнадцать']
80
+ const TENS = ['', '', 'двадцать', 'тридцать', 'сорок', 'пятьдесят', 'шестьдесят', 'семьдесят', 'восемьдесят', 'девяносто']
81
+
82
+ // Irregular hundreds
83
+ const HUNDREDS = ['', 'сто', 'двести', 'триста', 'четыреста', 'пятьсот', 'шестьсот', 'семьсот', 'восемьсот', 'девятьсот']
84
+
85
+ const ZERO = 'ноль'
86
+ const NEGATIVE = 'минус'
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
+ ['тысяча', 'тысячи', 'тысяч'],
93
+ ['миллион', 'миллиона', 'миллионов'],
94
+ ['миллиард', 'миллиарда', 'миллиардов'],
95
+ ['триллион', 'триллиона', 'триллионов'],
96
+ ['квадриллион', 'квадриллиона', 'квадриллионов'],
97
+ ['квинтиллион', 'квинтиллиона', 'квинтиллионов'],
98
+ ['секстиллион', 'секстиллиона', 'секстиллионов'],
99
+ ['септиллион', 'септиллиона', 'септиллионов'],
100
+ ['октиллион', 'октиллиона', 'октиллионов'],
101
+ ['нониллион', 'нониллиона', 'нониллионов']
102
+ ]
103
+
104
+ // ============================================================================
105
+ // Precomputed Lookup Tables
106
+ // ============================================================================
107
+
108
+ const { masc: SEGMENTS_MASC, fem: SEGMENTS_FEM } = buildAllSegments(ONES_MASC, ONES_FEM, TEENS, TENS, HUNDREDS)
109
+
110
+ // ============================================================================
111
+ // Conversion Functions
112
+ // ============================================================================
113
+
114
+ function integerToWords (n, options = {}) {
115
+ if (n === 0n) return ZERO
116
+
117
+ const feminine = options.gender === 'feminine'
118
+
119
+ if (n < 1000n) {
120
+ const segments = feminine ? SEGMENTS_FEM : SEGMENTS_MASC
121
+ return segments[Number(n)]
87
122
  }
88
123
 
89
- /**
90
- * Russian thousands (тысяча) are feminine, requiring одна/две forms.
91
- * Other scales (million, billion, etc.) are masculine.
92
- */
93
- scaleGenders = {
94
- 1: true // thousands are feminine
124
+ if (n < 1_000_000n) {
125
+ const thousands = Number(n / 1000n)
126
+ const remainder = Number(n % 1000n)
127
+
128
+ // Thousands are always feminine in Russian
129
+ const thousandsWord = SEGMENTS_FEM[thousands]
130
+ const scaleWord = pluralize(thousands, SCALE_FORMS[0])
131
+
132
+ let result = thousandsWord + ' ' + scaleWord
133
+
134
+ if (remainder > 0) {
135
+ const segments = feminine ? SEGMENTS_FEM : SEGMENTS_MASC
136
+ result += ' ' + segments[remainder]
137
+ }
138
+
139
+ return result
95
140
  }
141
+
142
+ return buildLargeNumberWords(n, options)
96
143
  }
144
+
145
+ function buildLargeNumberWords (n, options) {
146
+ const feminine = options.gender === 'feminine'
147
+ const numStr = n.toString()
148
+ const len = numStr.length
149
+
150
+ const segments = []
151
+ const segmentSize = 3
152
+
153
+ const remainderLen = len % segmentSize
154
+ let pos = 0
155
+ if (remainderLen > 0) {
156
+ segments.push(Number(numStr.slice(0, remainderLen)))
157
+ pos = remainderLen
158
+ }
159
+ while (pos < len) {
160
+ segments.push(Number(numStr.slice(pos, pos + segmentSize)))
161
+ pos += segmentSize
162
+ }
163
+
164
+ const parts = []
165
+ let scaleIndex = segments.length - 1
166
+
167
+ for (let i = 0; i < segments.length; i++) {
168
+ const segment = segments[i]
169
+
170
+ if (segment !== 0) {
171
+ if (scaleIndex === 0) {
172
+ const segmentWords = feminine ? SEGMENTS_FEM : SEGMENTS_MASC
173
+ parts.push(segmentWords[segment])
174
+ } else {
175
+ const scaleForms = SCALE_FORMS[scaleIndex - 1]
176
+ const scaleWord = pluralize(segment, scaleForms)
177
+ // Thousands (scaleIndex=1) are feminine, others masculine
178
+ const isFeminine = scaleIndex === 1
179
+ const segmentWords = isFeminine ? SEGMENTS_FEM : SEGMENTS_MASC
180
+ parts.push(segmentWords[segment] + ' ' + scaleWord)
181
+ }
182
+ }
183
+
184
+ scaleIndex--
185
+ }
186
+
187
+ return parts.join(' ')
188
+ }
189
+
190
+ function decimalPartToWords (decimalPart, options) {
191
+ let result = ''
192
+ let i = 0
193
+
194
+ while (i < decimalPart.length && decimalPart[i] === '0') {
195
+ if (result) result += ' '
196
+ result += ZERO
197
+ i++
198
+ }
199
+
200
+ const remainder = decimalPart.slice(i)
201
+ if (remainder) {
202
+ if (result) result += ' '
203
+ result += integerToWords(BigInt(remainder), options)
204
+ }
205
+
206
+ return result
207
+ }
208
+
209
+ /**
210
+ * Converts a numeric value to Russian words.
211
+ *
212
+ * @param {number | string | bigint} value - The numeric value to convert
213
+ * @param {Object} [options] - Optional configuration
214
+ * @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender
215
+ * @returns {string} The number in Russian words
216
+ */
217
+ function toWords (value, options) {
218
+ options = validateOptions(options)
219
+ const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
220
+
221
+ let result = ''
222
+
223
+ if (isNegative) {
224
+ result = NEGATIVE + ' '
225
+ }
226
+
227
+ result += integerToWords(integerPart, options)
228
+
229
+ if (decimalPart) {
230
+ result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart, options)
231
+ }
232
+
233
+ return result
234
+ }
235
+
236
+ // ============================================================================
237
+ // Exports
238
+ // ============================================================================
239
+
240
+ export { toWords }
@@ -1,80 +1,11 @@
1
1
  /**
2
- * Serbian language converter (Cyrillic script).
2
+ * Converts a numeric value to Serbian (Cyrillic) words.
3
3
  *
4
- * Supports:
5
- * - Three-form pluralization (one/few/many)
6
- * - Gender agreement (један/једна, два/две)
7
- * - Cyrillic script representation
4
+ * @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
7
+ * @returns {string} The number in Serbian Cyrillic words
8
8
  */
9
- export class SerbianCyrillic extends SlavicLanguage {
10
- onesWords: {
11
- 1: string;
12
- 2: string;
13
- 3: string;
14
- 4: string;
15
- 5: string;
16
- 6: string;
17
- 7: string;
18
- 8: string;
19
- 9: string;
20
- };
21
- onesFeminineWords: {
22
- 1: string;
23
- 2: string;
24
- 3: string;
25
- 4: string;
26
- 5: string;
27
- 6: string;
28
- 7: string;
29
- 8: string;
30
- 9: string;
31
- };
32
- teensWords: {
33
- 0: string;
34
- 1: string;
35
- 2: string;
36
- 3: string;
37
- 4: string;
38
- 5: string;
39
- 6: string;
40
- 7: string;
41
- 8: string;
42
- 9: string;
43
- };
44
- twentiesWords: {
45
- 2: string;
46
- 3: string;
47
- 4: string;
48
- 5: string;
49
- 6: string;
50
- 7: string;
51
- 8: string;
52
- 9: string;
53
- };
54
- hundredsWords: {
55
- 1: string;
56
- 2: string;
57
- 3: string;
58
- 4: string;
59
- 5: string;
60
- 6: string;
61
- 7: string;
62
- 8: string;
63
- 9: string;
64
- };
65
- pluralForms: {
66
- 1: string[];
67
- 2: string[];
68
- 3: string[];
69
- 4: string[];
70
- 5: string[];
71
- 6: string[];
72
- 7: string[];
73
- 8: string[];
74
- 9: string[];
75
- 10: string[];
76
- };
77
- /** Selects Serbian plural form: 1 = singular, 2-4 = few, else = many. */
78
- pluralize(n: any, forms: any): any;
79
- }
80
- import { SlavicLanguage } from '../classes/slavic-language.js';
9
+ export function toWords(value: number | string | bigint, options?: {
10
+ gender?: "masculine" | "feminine" | undefined;
11
+ }): string;