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,132 +1,150 @@
1
- import { SouthAsianLanguage } from '../classes/south-asian-language.js'
2
-
3
1
  /**
4
- * Marathi language converter.
2
+ * Marathi 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
  * - Devanagari script
9
+ * - 3-2-2 grouping pattern (last 3 digits, then groups of 2)
9
10
  * - Complete word forms for 0-99
11
+ * - Per-digit decimal reading
12
+ */
13
+
14
+ import { parseNumericValue } from '../utils/parse-numeric.js'
15
+
16
+ // ============================================================================
17
+ // Vocabulary
18
+ // ============================================================================
19
+
20
+ const ZERO = 'शून्य'
21
+ const NEGATIVE = 'उणे'
22
+ const DECIMAL_SEP = 'दशांश'
23
+ const HUNDRED = 'शंभर'
24
+
25
+ const BELOW_HUNDRED = [
26
+ 'शून्य', 'एक', 'दोन', 'तीन', 'चार', 'पाच', 'सहा', 'सात', 'आठ', 'नऊ',
27
+ 'दहा', 'अकरा', 'बारा', 'तेरा', 'चौदा', 'पंधरा', 'सोळा', 'सतरा', 'अठरा', 'एकोणीस',
28
+ 'वीस', 'एकवीस', 'बावीस', 'तेवीस', 'चोवीस', 'पंचवीस', 'सव्वीस', 'सत्तावीस', 'अठ्ठावीस', 'एकोणतीस',
29
+ 'तीस', 'एकतीस', 'बत्तीस', 'तेहेतीस', 'चौतीस', 'पस्तीस', 'छत्तीस', 'सदतीस', 'अडतीस', 'एकोणचाळीस',
30
+ 'चाळीस', 'एकेचाळीस', 'बेचाळीस', 'त्रेचाळीस', 'चव्वेचाळीस', 'पंचेचाळीस', 'सेहेचाळीस', 'सत्तेचाळीस', 'अठ्ठेचाळीस', 'एकोणपन्नास',
31
+ 'पन्नास', 'एक्काव्वन', 'बावन्न', 'त्रेपन्न', 'चोपन्न', 'पंचाव्वन', 'छप्पन्न', 'सत्तावन्न', 'अठ्ठावन्न', 'एकोणसाठ',
32
+ 'साठ', 'एकसष्ठ', 'बासष्ठ', 'त्रेसष्ठ', 'चौसष्ठ', 'पासष्ठ', 'सहासष्ठ', 'सदुसष्ठ', 'अडुसष्ठ', 'एकोणसत्तर',
33
+ 'सत्तर', 'एकाहत्तर', 'बाहत्तर', 'त्र्याहत्तर', 'चौऱ्याहत्तर', 'पंच्याहत्तर', 'शहात्तर', 'सत्याहत्तर', 'अठ्ठ्याहत्तर', 'एकोणऐंशी',
34
+ 'ऐंशी', 'एक्याऐंशी', 'ब्याऐंशी', 'त्र्याऐंशी', 'चौऱ्याऐंशी', 'पंच्याऐंशी', 'शहाऐंशी', 'सत्याऐंशी', 'अठ्ठ्याऐंशी', 'एकोणनव्वद',
35
+ 'नव्वद', 'एक्याण्णव', 'ब्याण्णव', 'त्र्याण्णव', 'चौऱ्याण्णव', 'पंच्याण्णव', 'शहाण्णव', 'सत्याण्णव', 'अठ्ठ्याण्णव', 'नव्याण्णव'
36
+ ]
37
+
38
+ // Scale words: index 0 = units (empty), 1 = thousand, 2 = lakh, 3 = crore, etc.
39
+ const SCALE_WORDS = ['', 'हजार', 'लाख', 'कोटी', 'अब्ज', 'खर्व', 'निखर्व', 'महापद्म', 'शंकू']
40
+
41
+ // ============================================================================
42
+ // Segment Building
43
+ // ============================================================================
44
+
45
+ /**
46
+ * Builds words for a 0-999 segment.
10
47
  */
11
- export class Marathi extends SouthAsianLanguage {
12
- negativeWord = 'उणे'
13
- decimalSeparatorWord = 'दशांश'
14
- zeroWord = 'शून्य'
15
- hundredWord = 'शंभर'
16
- usePerDigitDecimals = true
17
-
18
- belowHundredWords = [
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
-
121
- scaleWords = [
122
- '',
123
- 'हजार',
124
- 'लाख',
125
- 'कोटी',
126
- 'अब्ज',
127
- 'खर्व',
128
- 'निखर्व',
129
- 'महापद्म',
130
- 'शंकू'
131
- ]
48
+ function buildSegment (n) {
49
+ if (n === 0) return ''
50
+ if (n < 100) return BELOW_HUNDRED[n]
51
+
52
+ const hundreds = Math.trunc(n / 100)
53
+ const remainder = n % 100
54
+
55
+ if (remainder === 0) {
56
+ return BELOW_HUNDRED[hundreds] + ' ' + HUNDRED
57
+ }
58
+ return BELOW_HUNDRED[hundreds] + ' ' + HUNDRED + ' ' + BELOW_HUNDRED[remainder]
132
59
  }
60
+
61
+ // ============================================================================
62
+ // Conversion Functions
63
+ // ============================================================================
64
+
65
+ /**
66
+ * Converts a non-negative integer to Marathi words.
67
+ *
68
+ * Uses BigInt modulo for segment extraction (faster than string slicing).
69
+ * South Asian 3-2-2 grouping: first 3 digits, then groups of 2.
70
+ *
71
+ * @param {bigint} n - Non-negative integer to convert
72
+ * @returns {string} Marathi words
73
+ */
74
+ function integerToWords (n) {
75
+ if (n === 0n) return ZERO
76
+
77
+ // Fast path: numbers < 1000 (direct lookup)
78
+ if (n < 1000n) {
79
+ return buildSegment(Number(n))
80
+ }
81
+
82
+ // Extract segments using BigInt modulo
83
+ const segments = []
84
+ segments.push(Number(n % 1000n))
85
+ let temp = n / 1000n
86
+
87
+ while (temp > 0n) {
88
+ segments.push(Number(temp % 100n))
89
+ temp = temp / 100n
90
+ }
91
+
92
+ // Build result string (process from most-significant to least)
93
+ const words = []
94
+ for (let i = segments.length - 1; i >= 0; i--) {
95
+ const segment = segments[i]
96
+ if (segment === 0) continue
97
+
98
+ if (i === 0) {
99
+ words.push(buildSegment(segment))
100
+ } else {
101
+ words.push(BELOW_HUNDRED[segment])
102
+ }
103
+
104
+ if (i > 0 && SCALE_WORDS[i]) {
105
+ words.push(SCALE_WORDS[i])
106
+ }
107
+ }
108
+
109
+ return words.join(' ')
110
+ }
111
+
112
+ function decimalPartToWords (decimalPart) {
113
+ // Per-digit decimal reading
114
+ const digits = []
115
+ for (const char of decimalPart) {
116
+ const d = parseInt(char, 10)
117
+ digits.push(d === 0 ? ZERO : BELOW_HUNDRED[d])
118
+ }
119
+ return digits.join(' ')
120
+ }
121
+
122
+ /**
123
+ * Converts a numeric value to Marathi words.
124
+ *
125
+ * @param {number | string | bigint} value - The numeric value to convert
126
+ * @returns {string} The number in Marathi words
127
+ */
128
+ function toWords (value) {
129
+ const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
130
+
131
+ let result = ''
132
+
133
+ if (isNegative) {
134
+ result = NEGATIVE + ' '
135
+ }
136
+
137
+ result += integerToWords(integerPart)
138
+
139
+ if (decimalPart) {
140
+ result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)
141
+ }
142
+
143
+ return result
144
+ }
145
+
146
+ // ============================================================================
147
+ // Exports
148
+ // ============================================================================
149
+
150
+ export { toWords }
@@ -1,31 +1,7 @@
1
1
  /**
2
- * Malay (Bahasa Melayu) language converter.
2
+ * Converts a numeric value to Malay words.
3
3
  *
4
- * Supports:
5
- * - "Se-" prefix for singular units (seratus, seribu, sejuta)
6
- * - Regular patterns (puluh for tens, ratus for hundreds)
7
- * - Space-separated number components
4
+ * @param {number | string | bigint} value - The numeric value to convert
5
+ * @returns {string} The number in Malay words
8
6
  */
9
- export class Malay extends AbstractLanguage {
10
- /**
11
- * Word forms for digits 0-9.
12
- * @type {Object.<number, string[]>}
13
- */
14
- digitWords: {
15
- [x: number]: string[];
16
- };
17
- /**
18
- * Scale magnitude words keyed by exponent (10^n).
19
- * @type {Object.<number, string>}
20
- */
21
- scaleWords: {
22
- [x: number]: string;
23
- };
24
- splitBy3(number: any): any[][];
25
- spell(blocks: any): any[];
26
- getHundreds(number: any): string[];
27
- getTens(number: any): string[];
28
- join(wordBlocks: any): string;
29
- integerToWords(integerPart: any): string;
30
- }
31
- import { AbstractLanguage } from '../classes/abstract-language.js';
7
+ export function toWords(value: number | string | bigint): string;
@@ -1,150 +1,200 @@
1
- import { AbstractLanguage } from '../classes/abstract-language.js'
2
-
3
1
  /**
4
- * Malay (Bahasa Melayu) language converter.
2
+ * Malay (Bahasa Melayu) language converter - Functional Implementation
3
+ *
4
+ * Self-contained module with its own input validation, ready for subpath exports.
5
5
  *
6
- * Supports:
7
- * - "Se-" prefix for singular units (seratus, seribu, sejuta)
6
+ * Key features:
7
+ * - "Se-" prefix for ALL singular scale units (seratus, seribu, sejuta, sebilion)
8
8
  * - Regular patterns (puluh for tens, ratus for hundreds)
9
- * - Space-separated number components
9
+ * - Teens with "belas" suffix
10
+ * - Note: "lapan" (8) differs from Indonesian "delapan"
10
11
  */
11
- export class Malay extends AbstractLanguage {
12
- negativeWord = 'minus'
13
- decimalSeparatorWord = 'perpuluhan'
14
- zeroWord = 'sifar'
15
-
16
- /**
17
- * Word forms for digits 0-9.
18
- * @type {Object.<number, string[]>}
19
- */
20
- digitWords = {
21
- 0: [],
22
- 1: ['satu'],
23
- 2: ['dua'],
24
- 3: ['tiga'],
25
- 4: ['empat'],
26
- 5: ['lima'],
27
- 6: ['enam'],
28
- 7: ['tujuh'],
29
- 8: ['lapan'],
30
- 9: ['sembilan']
12
+
13
+ import { parseNumericValue } from '../utils/parse-numeric.js'
14
+
15
+ // ============================================================================
16
+ // Vocabulary
17
+ // ============================================================================
18
+
19
+ const ONES = ['', 'satu', 'dua', 'tiga', 'empat', 'lima', 'enam', 'tujuh', 'lapan', 'sembilan']
20
+ const TEENS = ['sepuluh', 'sebelas', 'dua belas', 'tiga belas', 'empat belas', 'lima belas', 'enam belas', 'tujuh belas', 'lapan belas', 'sembilan belas']
21
+ const TENS = ['', '', 'dua puluh', 'tiga puluh', 'empat puluh', 'lima puluh', 'enam puluh', 'tujuh puluh', 'lapan puluh', 'sembilan puluh']
22
+
23
+ const HUNDRED_WORD = 'ratus'
24
+ const THOUSAND_WORD = 'ribu'
25
+ const SCALE_WORDS = ['juta', 'bilion', 'trilion']
26
+
27
+ const ZERO = 'sifar'
28
+ const NEGATIVE = 'minus'
29
+ const DECIMAL_SEP = 'perpuluhan'
30
+
31
+ // ============================================================================
32
+ // Segment Building
33
+ // ============================================================================
34
+
35
+ function buildSegment (n) {
36
+ if (n === 0) return ''
37
+
38
+ const onesDigit = n % 10
39
+ const tensDigit = Math.floor(n / 10) % 10
40
+ const hundredsDigit = Math.floor(n / 100)
41
+
42
+ const parts = []
43
+
44
+ // Hundreds: seratus (100) or N ratus (200-900)
45
+ if (hundredsDigit > 0) {
46
+ if (hundredsDigit === 1) {
47
+ parts.push('se' + HUNDRED_WORD)
48
+ } else {
49
+ parts.push(ONES[hundredsDigit] + ' ' + HUNDRED_WORD)
50
+ }
31
51
  }
32
52
 
33
- /**
34
- * Scale magnitude words keyed by exponent (10^n).
35
- * @type {Object.<number, string>}
36
- */
37
- scaleWords = {
38
- 3: 'ribu',
39
- 6: 'juta',
40
- 9: 'bilion',
41
- 12: 'trilion'
53
+ // Tens and ones
54
+ const tensOnes = n % 100
55
+
56
+ if (tensOnes === 0) {
57
+ // Just hundreds
58
+ } else if (tensOnes < 10) {
59
+ parts.push(ONES[tensOnes])
60
+ } else if (tensOnes < 20) {
61
+ parts.push(TEENS[tensOnes - 10])
62
+ } else if (onesDigit === 0) {
63
+ parts.push(TENS[tensDigit])
64
+ } else {
65
+ parts.push(TENS[tensDigit] + ' ' + ONES[onesDigit])
42
66
  }
43
67
 
44
- splitBy3 (number) {
45
- const blocks = []
46
- const stringNumber = number.toString()
47
- const length = stringNumber.length
48
- let firstBlock
68
+ return parts.join(' ')
69
+ }
49
70
 
50
- if (length < 3) {
51
- blocks.push([stringNumber])
52
- } else {
53
- const firstBlockLength = length % 3
71
+ // ============================================================================
72
+ // Conversion Functions
73
+ // ============================================================================
54
74
 
55
- if (firstBlockLength > 0) {
56
- firstBlock = [stringNumber.slice(0, firstBlockLength)]
57
- blocks.push(firstBlock)
58
- }
75
+ function integerToWords (n) {
76
+ if (n === 0n) return ZERO
59
77
 
60
- for (let index = firstBlockLength; index < length; index += 3) {
61
- const nextBlock = [stringNumber.slice(index, index + 3)]
62
- blocks.push(nextBlock)
63
- }
64
- }
65
- return blocks
78
+ if (n < 1000n) {
79
+ return buildSegment(Number(n))
66
80
  }
67
81
 
68
- spell (blocks) {
69
- let wordBlocks = []
70
- let spelling
71
- const firstBlock = blocks[0]
72
- if (firstBlock[0].length === 1) {
73
- spelling = firstBlock[0] === '0' ? ['sifar'] : this.digitWords[Math.trunc(firstBlock[0])]
74
- } else if (firstBlock[0].length === 2) {
75
- spelling = this.getTens(firstBlock[0])
82
+ if (n < 1_000_000n) {
83
+ const thousands = Number(n / 1000n)
84
+ const remainder = Number(n % 1000n)
85
+
86
+ let result
87
+ if (thousands === 1) {
88
+ result = 'se' + THOUSAND_WORD
76
89
  } else {
77
- spelling = [...this.getHundreds(firstBlock[0][0]), ...this.getTens(firstBlock[0].slice(1, 3))]
90
+ result = buildSegment(thousands) + ' ' + THOUSAND_WORD
78
91
  }
79
- wordBlocks = [...wordBlocks, [firstBlock[0], spelling]]
80
- for (let index = 1; index < blocks.length; index++) {
81
- let block = blocks[index]
82
- spelling = [...this.getHundreds(block[0][0]), ...this.getTens(block[0].slice(1, 3))]
83
- block = [...block, spelling]
84
- wordBlocks = [...wordBlocks, block]
92
+
93
+ if (remainder > 0) {
94
+ result += ' ' + buildSegment(remainder)
85
95
  }
86
- return wordBlocks
96
+
97
+ return result
87
98
  }
88
99
 
89
- getHundreds (number) { // 'ratus'
90
- if (number === '1') {
91
- return ['seratus']
92
- } else if (number === '0') {
93
- return []
94
- } else {
95
- return [...this.digitWords[Math.trunc(number)], 'ratus']
96
- }
100
+ return buildLargeNumberWords(n)
101
+ }
102
+
103
+ function buildLargeNumberWords (n) {
104
+ const numStr = n.toString()
105
+ const len = numStr.length
106
+
107
+ const segments = []
108
+ const segmentSize = 3
109
+
110
+ const remainderLen = len % segmentSize
111
+ let pos = 0
112
+ if (remainderLen > 0) {
113
+ segments.push(Number(numStr.slice(0, remainderLen)))
114
+ pos = remainderLen
115
+ }
116
+ while (pos < len) {
117
+ segments.push(Number(numStr.slice(pos, pos + segmentSize)))
118
+ pos += segmentSize
97
119
  }
98
120
 
99
- getTens (number) { // 'puluh' and 'belas'
100
- if (number[0] === '1') {
101
- if (number[1] === '0') {
102
- return ['sepuluh']
103
- } else if (number[1] === '1') {
104
- return ['sebelas']
121
+ const parts = []
122
+ let scaleIndex = segments.length - 1
123
+
124
+ for (let i = 0; i < segments.length; i++) {
125
+ const segment = segments[i]
126
+
127
+ if (segment !== 0) {
128
+ if (scaleIndex === 0) {
129
+ parts.push(buildSegment(segment))
130
+ } else if (scaleIndex === 1) {
131
+ if (segment === 1) {
132
+ parts.push('se' + THOUSAND_WORD)
133
+ } else {
134
+ parts.push(buildSegment(segment) + ' ' + THOUSAND_WORD)
135
+ }
136
+ } else {
137
+ // Malay: "se-" prefix for ALL scale words when segment is 1
138
+ const scaleWord = SCALE_WORDS[scaleIndex - 2]
139
+ if (segment === 1) {
140
+ parts.push('se' + scaleWord)
141
+ } else {
142
+ parts.push(buildSegment(segment) + ' ' + scaleWord)
143
+ }
105
144
  }
106
- return [...this.digitWords[Math.trunc(number[1])], 'belas']
107
- }
108
-
109
- if (number[0] === '0') {
110
- return this.digitWords[Math.trunc(number[1])]
111
145
  }
112
146
 
113
- return [...this.digitWords[Math.trunc(number[0])], 'puluh', ...this.digitWords[Math.trunc(number[1])]]
147
+ scaleIndex--
114
148
  }
115
149
 
116
- join (wordBlocks) {
117
- const wordList = []
118
- const length = wordBlocks.length - 1
150
+ return parts.join(' ')
151
+ }
119
152
 
120
- for (let index = 0; index <= length; index++) {
121
- const words = wordBlocks[index][1]
122
- const isLast = index === length
123
- const scaleWord = isLast ? null : this.scaleWords[(length - index) * 3]
153
+ function decimalPartToWords (decimalPart) {
154
+ let result = ''
124
155
 
125
- if (!isLast && words.length === 1 && words[0] === 'satu') {
126
- // Use se- prefix for singular scale units: seribu, sejuta, sebilion, setrilion
127
- wordList.push('se' + scaleWord)
128
- continue
129
- }
156
+ let i = 0
157
+ while (i < decimalPart.length && decimalPart[i] === '0') {
158
+ if (result) result += ' '
159
+ result += ZERO
160
+ i++
161
+ }
130
162
 
131
- // Add current block words
132
- for (const w of words) wordList.push(w)
163
+ const remainder = decimalPart.slice(i)
164
+ if (remainder) {
165
+ if (result) result += ' '
166
+ result += integerToWords(BigInt(remainder))
167
+ }
133
168
 
134
- // Append scale word if applicable and current block contributed words
135
- if (!isLast && words.length > 0) {
136
- wordList.push(scaleWord)
137
- }
138
- }
169
+ return result
170
+ }
171
+
172
+ /**
173
+ * Converts a numeric value to Malay words.
174
+ *
175
+ * @param {number | string | bigint} value - The numeric value to convert
176
+ * @returns {string} The number in Malay words
177
+ */
178
+ function toWords (value) {
179
+ const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
139
180
 
140
- return wordList.join(' ')
181
+ let result = ''
182
+
183
+ if (isNegative) {
184
+ result = NEGATIVE + ' '
141
185
  }
142
186
 
143
- integerToWords (integerPart) {
144
- return this.join(
145
- this.spell(
146
- this.splitBy3(integerPart)
147
- )
148
- ).trim()
187
+ result += integerToWords(integerPart)
188
+
189
+ if (decimalPart) {
190
+ result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)
149
191
  }
192
+
193
+ return result
150
194
  }
195
+
196
+ // ============================================================================
197
+ // Exports
198
+ // ============================================================================
199
+
200
+ export { toWords }
@@ -1,12 +1,14 @@
1
1
  /**
2
- * Norwegian Bokmål language converter.
2
+ * Converts a numeric value to Norwegian Bokmål words.
3
3
  *
4
- * Supports:
5
- * - Hyphenation for compound numbers (tjue-en)
6
- * - "og" (and) for hundreds combinations
7
- * - Implicit '1' omission before tens and magnitudes
4
+ * @param {number | string | bigint} value - The numeric value to convert
5
+ * @returns {string} The number in Norwegian 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(21) // 'tjue-en'
11
+ * toWords(101) // 'en hundre og en'
12
+ * toWords(1000000) // 'en million'
8
13
  */
9
- export class NorwegianBokmal extends GreedyScaleLanguage {
10
- scaleWords: (string | bigint)[][];
11
- }
12
- import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js';
14
+ export function toWords(value: number | string | bigint): string;