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,113 +1,221 @@
1
- import { SlavicLanguage } from '../classes/slavic-language.js'
2
-
3
1
  /**
4
- * Serbian Latin language converter.
2
+ * Serbian Latin 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 (jedan/jedna, dva/dve)
9
- * - Latin script representation
8
+ * - Gender: thousands are feminine, millions+ are masculine
9
+ * - Irregular hundreds (dvesta, trista, etc.)
10
+ * - Long scale naming with -ard forms
11
+ * - Latin script
10
12
  */
11
- export class SerbianLatin extends SlavicLanguage {
12
- negativeWord = 'minus'
13
- decimalSeparatorWord = 'zapeta'
14
- zeroWord = 'nula'
15
-
16
- onesWords = {
17
- 1: 'jedan',
18
- 2: 'dva',
19
- 3: 'tri',
20
- 4: 'četiri',
21
- 5: 'pet',
22
- 6: 'šest',
23
- 7: 'sedam',
24
- 8: 'osam',
25
- 9: 'devet'
13
+
14
+ import { parseNumericValue } from '../utils/parse-numeric.js'
15
+ import { validateOptions } from '../utils/validate-options.js'
16
+
17
+ // ============================================================================
18
+ // Vocabulary
19
+ // ============================================================================
20
+
21
+ const ONES_MASC = ['', 'jedan', 'dva', 'tri', 'četiri', 'pet', 'šest', 'sedam', 'osam', 'devet']
22
+ const ONES_FEM = ['', 'jedna', 'dve', 'tri', 'četiri', 'pet', 'šest', 'sedam', 'osam', 'devet']
23
+ const TEENS = ['deset', 'jedanaest', 'dvanaest', 'trinaest', 'četrnaest', 'petnaest', 'šesnaest', 'sedamnaest', 'osamnaest', 'devetnaest']
24
+ const TENS = ['', '', 'dvadeset', 'trideset', 'četrdeset', 'pedeset', 'šezdeset', 'sedamdeset', 'osamdeset', 'devedeset']
25
+ const HUNDREDS = ['', 'sto', 'dvesta', 'trista', 'četiristo', 'petsto', 'šesto', 'sedamsto', 'osamsto', 'devetsto']
26
+
27
+ const ZERO = 'nula'
28
+ const NEGATIVE = 'minus'
29
+ const DECIMAL_SEP = 'zapeta'
30
+
31
+ // Scale words: [singular, few, many]
32
+ const SCALE_FORMS = [
33
+ ['hiljada', 'hiljade', 'hiljada'],
34
+ ['milion', 'miliona', 'miliona'],
35
+ ['milijarda', 'milijarde', 'milijarda'],
36
+ ['bilion', 'biliona', 'biliona'],
37
+ ['bilijarda', 'bilijarde', 'bilijarda'],
38
+ ['trilion', 'triliona', 'triliona'],
39
+ ['trilijarda', 'trilijarde', 'trilijarda'],
40
+ ['kvadrilion', 'kvadriliona', 'kvadriliona'],
41
+ ['kvadrilijarda', 'kvadrilijarde', 'kvadrilijarda']
42
+ ]
43
+
44
+ // ============================================================================
45
+ // Segment Building
46
+ // ============================================================================
47
+
48
+ function pluralize (n, forms) {
49
+ const num = typeof n === 'bigint' ? Number(n) : n
50
+ const lastDigit = num % 10
51
+ const lastTwoDigits = num % 100
52
+
53
+ if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
54
+ return forms[2]
26
55
  }
27
56
 
28
- onesFeminineWords = {
29
- 1: 'jedna',
30
- 2: 'dve',
31
- 3: 'tri',
32
- 4: 'četiri',
33
- 5: 'pet',
34
- 6: 'šest',
35
- 7: 'sedam',
36
- 8: 'osam',
37
- 9: 'devet'
57
+ if (lastDigit === 1) return forms[0]
58
+ if (lastDigit >= 2 && lastDigit <= 4) return forms[1]
59
+ return forms[2]
60
+ }
61
+
62
+ function buildSegmentMasc (n) {
63
+ if (n === 0) return ''
64
+
65
+ const onesDigit = n % 10
66
+ const tensDigit = Math.floor(n / 10) % 10
67
+ const hundredsDigit = Math.floor(n / 100)
68
+
69
+ const parts = []
70
+
71
+ if (hundredsDigit > 0) {
72
+ parts.push(HUNDREDS[hundredsDigit])
73
+ }
74
+
75
+ if (tensDigit > 1) {
76
+ parts.push(TENS[tensDigit])
38
77
  }
39
78
 
40
- teensWords = {
41
- 0: 'deset',
42
- 1: 'jedanaest',
43
- 2: 'dvanaest',
44
- 3: 'trinaest',
45
- 4: 'četrnaest',
46
- 5: 'petnaest',
47
- 6: 'šesnaest',
48
- 7: 'sedamnaest',
49
- 8: 'osamnaest',
50
- 9: 'devetnaest'
79
+ if (tensDigit === 1) {
80
+ parts.push(TEENS[onesDigit])
81
+ } else if (onesDigit > 0) {
82
+ parts.push(ONES_MASC[onesDigit])
51
83
  }
52
84
 
53
- twentiesWords = {
54
- 2: 'dvadeset',
55
- 3: 'trideset',
56
- 4: 'četrdeset',
57
- 5: 'pedeset',
58
- 6: 'šezdeset',
59
- 7: 'sedamdeset',
60
- 8: 'osamdeset',
61
- 9: 'devedeset'
85
+ return parts.join(' ')
86
+ }
87
+
88
+ function buildSegmentFem (n) {
89
+ if (n === 0) return ''
90
+
91
+ const onesDigit = n % 10
92
+ const tensDigit = Math.floor(n / 10) % 10
93
+ const hundredsDigit = Math.floor(n / 100)
94
+
95
+ const parts = []
96
+
97
+ if (hundredsDigit > 0) {
98
+ parts.push(HUNDREDS[hundredsDigit])
62
99
  }
63
100
 
64
- hundredsWords = {
65
- 1: 'sto',
66
- 2: 'dvesta',
67
- 3: 'trista',
68
- 4: 'četiristo',
69
- 5: 'petsto',
70
- 6: 'šesto',
71
- 7: 'sedamsto',
72
- 8: 'osamsto',
73
- 9: 'devetsto'
101
+ if (tensDigit > 1) {
102
+ parts.push(TENS[tensDigit])
74
103
  }
75
104
 
76
- pluralForms = {
77
- 1: ['hiljada', 'hiljade', 'hiljada'], // 10 ^ 3
78
- 2: ['milion', 'miliona', 'miliona'], // 10 ^ 6
79
- 3: ['milijarda', 'milijarde', 'milijarda'], // 10 ^ 9
80
- 4: ['bilion', 'biliona', 'biliona'], // 10 ^ 12
81
- 5: ['bilijarda', 'bilijarde', 'bilijarda'], // 10 ^ 15
82
- 6: ['trilion', 'triliona', 'triliona'], // 10 ^ 18
83
- 7: ['trilijarda', 'trilijarde', 'trilijarda'], // 10 ^ 21
84
- 8: ['kvadrilion', 'kvadriliona', 'kvadriliona'], // 10 ^ 24
85
- 9: ['kvadrilijarda', 'kvadrilijarde', 'kvadrilijarda'], // 10 ^ 27
86
- 10: ['kvintilion', 'kvintiliona', 'kvintiliona'] // 10 ^ 30
105
+ if (tensDigit === 1) {
106
+ parts.push(TEENS[onesDigit])
107
+ } else if (onesDigit > 0) {
108
+ parts.push(ONES_FEM[onesDigit])
87
109
  }
88
110
 
89
- /**
90
- * Maps segment indices to whether they are grammatically feminine.
91
- * In Serbian, thousands (index 1) are feminine, others are masculine.
92
- * @type {Object.<number, boolean>}
93
- */
94
- scaleGenders = {
95
- 1: true // thousands are feminine (others default to false)
111
+ return parts.join(' ')
112
+ }
113
+
114
+ // ============================================================================
115
+ // Conversion Functions
116
+ // ============================================================================
117
+
118
+ function integerToWords (n, options = {}) {
119
+ if (n === 0n) return ZERO
120
+
121
+ if (n < 1000n) {
122
+ return options.gender === 'feminine' ? buildSegmentFem(Number(n)) : buildSegmentMasc(Number(n))
96
123
  }
97
124
 
98
- /** Selects Serbian plural form: 1 = singular, 2-4 = few, else = many. */
99
- pluralize (n, forms) {
100
- const lastDigit = n % 10n
101
- const lastTwoDigits = n % 100n
125
+ return buildLargeNumberWords(n, options)
126
+ }
102
127
 
103
- if ((lastTwoDigits < 10n || lastTwoDigits > 20n) && lastDigit === 1n) {
104
- return forms[0]
105
- }
128
+ function buildLargeNumberWords (n, options) {
129
+ const numStr = n.toString()
130
+ const len = numStr.length
106
131
 
107
- if ((lastTwoDigits < 10n || lastTwoDigits > 20n) && lastDigit > 1n && lastDigit < 5n) {
108
- return forms[1]
132
+ const segments = []
133
+ const segmentSize = 3
134
+
135
+ const remainderLen = len % segmentSize
136
+ let pos = 0
137
+ if (remainderLen > 0) {
138
+ segments.push(Number(numStr.slice(0, remainderLen)))
139
+ pos = remainderLen
140
+ }
141
+ while (pos < len) {
142
+ segments.push(Number(numStr.slice(pos, pos + segmentSize)))
143
+ pos += segmentSize
144
+ }
145
+
146
+ const parts = []
147
+ let scaleIndex = segments.length - 1
148
+
149
+ for (let i = 0; i < segments.length; i++) {
150
+ const segment = segments[i]
151
+
152
+ if (segment !== 0) {
153
+ if (scaleIndex === 0) {
154
+ parts.push(options.gender === 'feminine' ? buildSegmentFem(segment) : buildSegmentMasc(segment))
155
+ } else {
156
+ const scaleForms = SCALE_FORMS[scaleIndex - 1]
157
+ const scaleWord = pluralize(segment, scaleForms)
158
+ // Thousands (scaleIndex=1) are feminine, others masculine
159
+ const isFeminine = scaleIndex === 1
160
+ const segmentWord = isFeminine ? buildSegmentFem(segment) : buildSegmentMasc(segment)
161
+ parts.push(segmentWord + ' ' + scaleWord)
162
+ }
109
163
  }
110
164
 
111
- return forms[2]
165
+ scaleIndex--
112
166
  }
167
+
168
+ return parts.join(' ')
169
+ }
170
+
171
+ function decimalPartToWords (decimalPart, options) {
172
+ let result = ''
173
+ let i = 0
174
+
175
+ while (i < decimalPart.length && decimalPart[i] === '0') {
176
+ if (result) result += ' '
177
+ result += ZERO
178
+ i++
179
+ }
180
+
181
+ const remainder = decimalPart.slice(i)
182
+ if (remainder) {
183
+ if (result) result += ' '
184
+ result += integerToWords(BigInt(remainder), options)
185
+ }
186
+
187
+ return result
113
188
  }
189
+
190
+ /**
191
+ * Converts a numeric value to Serbian (Latin) words.
192
+ *
193
+ * @param {number | string | bigint} value - The numeric value to convert
194
+ * @param {Object} [options] - Optional configuration
195
+ * @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender
196
+ * @returns {string} The number in Serbian Latin words
197
+ */
198
+ function toWords (value, options) {
199
+ options = validateOptions(options)
200
+ const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
201
+
202
+ let result = ''
203
+
204
+ if (isNegative) {
205
+ result = NEGATIVE + ' '
206
+ }
207
+
208
+ result += integerToWords(integerPart, options)
209
+
210
+ if (decimalPart) {
211
+ result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart, options)
212
+ }
213
+
214
+ return result
215
+ }
216
+
217
+ // ============================================================================
218
+ // Exports
219
+ // ============================================================================
220
+
221
+ export { toWords }
@@ -1,14 +1,14 @@
1
1
  /**
2
- * Swedish language converter.
2
+ * Converts a numeric value to Swedish words.
3
3
  *
4
- * Supports:
5
- * - Hyphenation for compound tens (tjugo-tre)
6
- * - "och" (and) after hundreds
7
- * - Space-separated larger composites
4
+ * @param {number | string | bigint} value - The numeric value to convert
5
+ * @returns {string} The number in Swedish words
6
+ * @throws {TypeError} If value is not a valid numeric type
7
+ * @throws {Error} If value is not a valid number format
8
+ *
9
+ * @example
10
+ * toWords(42) // 'fyrtio-två'
11
+ * toWords(101) // 'hundra och ett'
12
+ * toWords(1000000) // 'en miljon'
8
13
  */
9
- export class Swedish extends GreedyScaleLanguage {
10
- scaleWords: (string | bigint)[][];
11
- /** Combines two word-sets according to Swedish grammar rules. */
12
- combineWordSets(preceding: any, following: any): any;
13
- }
14
- import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js';
14
+ export function toWords(value: number | string | bigint): string;