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
package/CHANGELOG.md ADDED
@@ -0,0 +1,64 @@
1
+ # Changelog
2
+
3
+ ## [3.1.0](https://github.com/forzagreen/n2words/compare/v3.0.0...v3.1.0) (2026-01-08)
4
+
5
+
6
+ ### Features
7
+
8
+ * **ka:** add Georgian language support ([#218](https://github.com/forzagreen/n2words/issues/218)) ([a7c5142](https://github.com/forzagreen/n2words/commit/a7c5142f6cc08a237e6e3f803f1b8298621c70f1))
9
+ * **yo:** add Yoruba language support ([#221](https://github.com/forzagreen/n2words/issues/221)) ([60680b0](https://github.com/forzagreen/n2words/commit/60680b09aebe8a04cb841eff9a874a08bc955c67))
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * **lang:** remove precomputed lookup tables from all languages ([#224](https://github.com/forzagreen/n2words/issues/224)) ([fbe0c7b](https://github.com/forzagreen/n2words/commit/fbe0c7b7115e5a3e63a92945e865b75ff919ebb0))
15
+
16
+ ## 3.0.0 - Functional Architecture
17
+
18
+ Complete rewrite from class-based to functional architecture with major performance improvements.
19
+
20
+ ### ⚠ BREAKING CHANGES
21
+
22
+ **Migration required** - API has changed:
23
+
24
+ #### From v1.x (default export)
25
+
26
+ > **Note:** v1 bundled all languages into a single wrapper function with runtime language selection via `{ lang: 'es' }`. v3 requires explicit language imports for tree-shaking—only the languages you import are included in your bundle.
27
+
28
+ | Context | v1 | v3 |
29
+ |---------|----|----|
30
+ | **Import (all)** | `import n2words from 'n2words'` | `import { en, es } from 'n2words'` |
31
+ | **Import (single)** | `import es from 'n2words/languages/es'` | `import { toWords } from 'n2words/es'` |
32
+ | **Usage** | `n2words(42, { lang: 'es' })` | `es(42)` or `toWords(42)` |
33
+ | **Browser** | `n2words(42, { lang: 'es' })` | `n2words.es(42)` |
34
+ | **CDN (single)** | `.../dist/languages/es.js` | `.../dist/languages/es.js` (unchanged) |
35
+
36
+ #### From v2.x (class-based)
37
+
38
+ | Context | v2 | v3 |
39
+ |---------|----|----|
40
+ | **Import (all)** | `import { EnglishConverter } from 'n2words'` | `import { en } from 'n2words'` |
41
+ | **Import (single)** | `import { EnglishConverter } from 'n2words/en'` | `import { toWords } from 'n2words/en'` |
42
+ | **Usage** | `EnglishConverter(42)` | `en(42)` or `toWords(42)` |
43
+ | **Browser** | `n2words.EnglishConverter(42)` | `n2words.en(42)` |
44
+ | **CDN (single)** | `.../dist/EnglishConverter.js` | `.../dist/languages/en.js` |
45
+
46
+ ### Highlights
47
+
48
+ - **3x-85x faster** conversion across languages
49
+ - **70-96% less memory** per conversion
50
+ - **75-92% smaller** per-language bundles
51
+ - **52 languages** - all self-contained, tree-shakeable modules
52
+
53
+ ### New Languages
54
+
55
+ `am` (Amharic), `am-Latn` (Amharic Latin), `fi` (Finnish), `ha` (Hausa), `hbo` (Biblical Hebrew), `sr-Cyrl` (Serbian Cyrillic), `zh-Hant` (Traditional Chinese)
56
+
57
+ ### Performance Improvements
58
+
59
+ - Precomputed lookup tables (en, pt, he, hbo)
60
+ - BigInt modulo instead of string slicing (ja, sw)
61
+ - Eliminated class instantiation overhead
62
+
63
+ ---
64
+ See [#206](https://github.com/forzagreen/n2words/pull/206) for full details.
package/README.md CHANGED
@@ -8,32 +8,15 @@
8
8
  [![npm downloads](https://img.shields.io/npm/dw/n2words)](https://npmjs.com/package/n2words)
9
9
  [![jsDelivr](https://img.shields.io/jsdelivr/npm/hm/n2words)](https://www.jsdelivr.com/package/npm/n2words)
10
10
 
11
- **Convert numbers to words in 48 languages with zero dependencies.**
11
+ **Convert numbers to words in 52 languages with zero dependencies.**
12
12
 
13
13
  ## Why n2words?
14
14
 
15
- - **Maximum Language Coverage** — 48 languages including European, Asian, Middle Eastern, and regional variants
16
- - **Zero Dependencies** — Pure JavaScript with no external runtime dependencies
17
- - **Universal Compatibility** — Works in Node.js, browsers (via CDN), and all modern bundlers
15
+ - **52 Languages** — European, Asian, Middle Eastern, and regional variants
16
+ - **Zero Dependencies** — Pure JavaScript, works everywhere (Node.js, browsers, bundlers)
18
17
  - **Type-Safe** — Full TypeScript support with generated `.d.ts` declarations
19
- - **Production Ready** — Comprehensive test coverage (unit, integration, browser, type checking)
20
18
  - **BigInt Support** — Handle arbitrarily large numbers without precision loss
21
- - **Flexible Input** — Accepts `number`, `bigint`, or `string` inputs
22
- - **Tree-Shakable** — Import only the languages you need (~2-5 KB gzipped per language)
23
- - **Browser Tested** — Verified in Chromium, Firefox, and WebKit via automated tests
24
-
25
- ## Contents
26
-
27
- - [Quick Start](#quick-start)
28
- - [Usage](#usage) — ESM, CommonJS, Browser (UMD)
29
- - [Type Safety](#type-safety) — TypeScript support
30
- - [Supported Languages](#supported-languages-48) — 48 languages with options
31
- - [Browser Compatibility](#browser-compatibility) — Chrome 67+, Firefox 68+, Safari 14+, Edge 79+
32
- - [Performance & Bundle Size](#performance--bundle-size) — Tree-shaking and benchmarks
33
- - [Examples](#examples) — Basic, gender agreement, language-specific features
34
- - [Documentation](#documentation) — Guides and API reference
35
- - [Contributing](#contributing) — How to contribute
36
- - [License](#license)
19
+ - **High Performance** — 1M+ ops/sec, ~1.4 KB gzipped per language
37
20
 
38
21
  ## Quick Start
39
22
 
@@ -42,11 +25,11 @@ npm install n2words
42
25
  ```
43
26
 
44
27
  ```js
45
- import { EnglishConverter, SpanishConverter, ArabicConverter } from 'n2words'
28
+ import { en, es, ar } from 'n2words'
46
29
 
47
- EnglishConverter(123) // 'one hundred and twenty-three'
48
- SpanishConverter(123) // 'ciento veintitrés'
49
- ArabicConverter(1, { gender: 'feminine' }) // 'واحدة' (with options)
30
+ en(123) // 'one hundred and twenty-three'
31
+ es(123) // 'ciento veintitrés'
32
+ ar(1, { gender: 'feminine' }) // 'واحدة' (with options)
50
33
  ```
51
34
 
52
35
  ## Usage
@@ -54,92 +37,70 @@ ArabicConverter(1, { gender: 'feminine' }) // 'واحدة' (with options)
54
37
  **ESM (Node.js, modern bundlers):**
55
38
 
56
39
  ```js
57
- import { EnglishConverter } from 'n2words'
40
+ // Named imports (tree-shakable)
41
+ import { en, es } from 'n2words'
42
+
43
+ // Subpath imports (smallest bundle, recommended for single language)
44
+ import { toWords } from 'n2words/en'
45
+ import { toWords as esWords } from 'n2words/es'
58
46
  ```
59
47
 
60
48
  **CommonJS (Node.js):**
61
49
 
62
- n2words is an ES module. For CommonJS environments, use dynamic import with Promises:
50
+ n2words is an ES module. For CommonJS environments, use dynamic import:
63
51
 
64
52
  ```js
65
53
  // Promise-based
66
- import('n2words').then(({ EnglishConverter }) => {
67
- console.log(EnglishConverter(42)) // 'forty-two'
54
+ import('n2words').then(({ en }) => {
55
+ console.log(en(42)) // 'forty-two'
68
56
  })
69
57
 
70
- // Or use async function
71
- async function convertNumber(num) {
72
- const { EnglishConverter } = await import('n2words')
73
- return EnglishConverter(num)
74
- }
58
+ // Subpath import (smallest bundle)
59
+ import('n2words/en').then(({ toWords }) => {
60
+ console.log(toWords(42)) // 'forty-two'
61
+ })
75
62
  ```
76
63
 
77
64
  **Browser (UMD via CDN):**
78
65
 
79
66
  ```html
80
- <!-- All languages (~23KB gzipped) -->
67
+ <!-- All languages -->
81
68
  <script src="https://cdn.jsdelivr.net/npm/n2words/dist/n2words.js"></script>
82
69
  <script>
83
- n2words.EnglishConverter(42) // 'forty-two'
70
+ n2words.en(42) // 'forty-two'
71
+ n2words.es(123) // 'ciento veintitrés'
84
72
  </script>
85
73
 
86
- <!-- Individual languages (~2KB gzipped each) - load only what you need -->
87
- <script src="https://cdn.jsdelivr.net/npm/n2words/dist/EnglishConverter.js"></script>
88
- <script src="https://cdn.jsdelivr.net/npm/n2words/dist/SpanishConverter.js"></script>
74
+ <!-- Single language (smallest, ~1.4 KB gzipped) -->
75
+ <script src="https://cdn.jsdelivr.net/npm/n2words/dist/languages/en.js"></script>
89
76
  <script>
90
- n2words.EnglishConverter(42) // 'forty-two'
91
- n2words.SpanishConverter(123) // 'ciento veintitrés'
77
+ n2words.en(42) // 'forty-two'
92
78
  </script>
93
- ```
94
-
95
- ## Type Safety
96
-
97
- Full TypeScript support via JSDoc annotations and generated type definitions - works in both JavaScript and TypeScript projects with IntelliSense and type checking:
98
-
99
- ```typescript
100
- import { EnglishConverter, ArabicConverter, SimplifiedChineseConverter } from 'n2words'
101
- import type { NumericValue, ArabicOptions } from 'n2words'
102
79
 
103
- // All converters accept: number | bigint | string
104
- EnglishConverter(42) // ✓ number → 'forty-two'
105
- EnglishConverter('123') // ✓ string → 'one hundred and twenty-three'
106
- EnglishConverter(100n) // ✓ BigInt → 'one hundred'
107
-
108
- // Language-specific options with type checking
109
- ArabicConverter(1, { gender: 'feminine' }) // ✓ 'واحدة' (feminine form)
110
- ArabicConverter(1, { invalid: true }) // ✗ TypeScript error: invalid property
111
-
112
- SimplifiedChineseConverter(123, { formal: false }) // ✓ '一百二十三' (common style)
113
- SimplifiedChineseConverter(123, { formal: 'yes' }) // ✗ TypeScript error: wrong type
80
+ <!-- Multiple single-language bundles (no conflicts) -->
81
+ <script src="https://cdn.jsdelivr.net/npm/n2words/dist/languages/en.js"></script>
82
+ <script src="https://cdn.jsdelivr.net/npm/n2words/dist/languages/es.js"></script>
83
+ <script>
84
+ n2words.en(42) // 'forty-two'
85
+ n2words.es(42) // 'cuarenta y dos'
86
+ </script>
114
87
  ```
115
88
 
116
- **Type Definitions:**
117
-
118
- n2words includes TypeScript declaration files (`.d.ts`) generated from JSDoc annotations:
119
-
120
- - **Source**: JSDoc annotations in JavaScript source files
121
- - **Generated**: TypeScript declarations built via `tsc` during package preparation
122
- - **Included**: Published to npm with the package (no separate `@types` package needed)
123
- - **Validated**: Comprehensive type tests ensure correctness
124
-
125
- **Exported Types:**
126
-
127
- - `NumericValue` - Accepted input types: `number | bigint | string`
128
- - Language-specific option types (e.g., `ArabicOptions`, `SimplifiedChineseOptions`, `DutchOptions`, etc.)
129
-
130
- ## Supported Languages (48)
89
+ ## Supported Languages (52)
131
90
 
132
91
  Language codes follow [IETF BCP 47](https://tools.ietf.org/html/bcp47) standards.
133
92
 
134
93
  | Code | Language | Options | Code | Language | Options |
135
94
  | --------- | ------------------- | ------- | --------- | ------------------- | ------- |
95
+ | `am` | Amharic | | `am-Latn` | Amharic Latin | |
136
96
  | `ar` | Arabic | ✓ | `az` | Azerbaijani | |
137
- | `bn` | Bengali | | `cs` | Czech ||
138
- | `da` | Danish || `de` | German | |
97
+ | `bn` | Bengali | | `cs` | Czech | |
98
+ | `da` | Danish | | `de` | German | |
139
99
  | `el` | Greek | | `en` | English | |
140
100
  | `es` | Spanish | ✓ | `fa` | Persian | |
141
- | `fil` | Filipino | | `fr` | French ||
142
- | `fr-BE` | Belgian French | ✓ | `gu` | Gujarati | |
101
+ | `fi` | Finnish | | `fil` | Filipino | |
102
+ | `fr` | French | ✓ | `fr-BE` | Belgian French | |
103
+ | `gu` | Gujarati | | `ha` | Hausa | |
143
104
  | `hbo` | Biblical Hebrew | ✓ | `he` | Modern Hebrew | ✓ |
144
105
  | `hi` | Hindi | | `hr` | Croatian | ✓ |
145
106
  | `hu` | Hungarian | | `id` | Indonesian | |
@@ -160,10 +121,10 @@ Language codes follow [IETF BCP 47](https://tools.ietf.org/html/bcp47) standards
160
121
 
161
122
  ### Language Options
162
123
 
163
- 21 languages support additional options. Common options include:
124
+ 19 languages support additional options. Common options include:
164
125
 
165
- **`gender`** (`'masculine'` | `'feminine'`) - 13 languages
166
- Arabic, Biblical Hebrew, Croatian, Czech, Latvian, Lithuanian, Polish, Romanian, Russian, Serbian (both scripts), Spanish, Ukrainian
126
+ **`gender`** (`'masculine'` | `'feminine'`) - 12 languages
127
+ Arabic, Biblical Hebrew, Croatian, Latvian, Lithuanian, Polish, Romanian, Russian, Serbian (both scripts), Spanish, Ukrainian
167
128
 
168
129
  **`formal`** (`boolean`) - 2 languages
169
130
  Simplified Chinese, Traditional Chinese - Toggle between formal/financial and common numerals
@@ -171,14 +132,11 @@ Simplified Chinese, Traditional Chinese - Toggle between formal/financial and co
171
132
  **Other options:**
172
133
 
173
134
  - Dutch: `includeOptionalAnd`, `accentOne`, `noHundredPairing`
174
- - French/French Belgium: `withHyphenSeparator`
135
+ - French/Belgian French: `withHyphenSeparator`
175
136
  - Hebrew (Modern & Biblical): `andWord`
176
137
  - Turkish: `dropSpaces`
177
- - Danish: `ordFlag` (ordinal numbers)
178
138
  - Arabic: `negativeWord` (custom negative word)
179
139
 
180
- [See complete options reference →](docs/ARCHITECTURE.md#language-specific-options)
181
-
182
140
  ## Browser Compatibility
183
141
 
184
142
  **Minimum Requirements** (due to BigInt):
@@ -200,27 +158,38 @@ Simplified Chinese, Traditional Chinese - Toggle between formal/financial and co
200
158
 
201
159
  ### Bundle Size Comparison
202
160
 
203
- | Import Strategy | Bundle Size (Minified) | Gzipped | Languages Included |
204
- | ---------------------------- | ---------------------- | -------- | ------------------ |
205
- | All languages (UMD) | ~92 KB | ~23 KB | All 48 |
206
- | Single language (UMD) | ~4-6 KB | ~2 KB | 1 |
207
- | Tree-shaken (ESM, 1 lang) | ~4-5 KB | ~2 KB | 1 |
208
- | Tree-shaken (ESM, 3 langs) | ~12-15 KB | ~4-5 KB | 3 |
209
- | Tree-shaken (ESM, 10 langs) | ~40-50 KB | ~12-15 KB| 10 |
161
+ | Import Strategy | Bundle Size | Gzipped | Languages |
162
+ | ------------------------------ | ----------- | --------- | --------- |
163
+ | All languages (UMD) | ~116 KB | ~28 KB | All 52 |
164
+ | Single language (UMD) | ~3-5 KB | ~1.4-2 KB | 1 |
165
+ | **Subpath import (ESM)** | ~3 KB | ~1.4 KB | 1 |
166
+ | Named imports (ESM, 1 lang) | ~3-5 KB | ~1.4-2 KB | 1 |
167
+ | Named imports (ESM, 3 langs) | ~9-15 KB | ~4-6 KB | 3 |
168
+ | Named imports (ESM, 10 langs) | ~30-50 KB | ~10-15 KB | 10 |
210
169
 
211
170
  ### Performance Characteristics
212
171
 
213
- - **Fast**: Sub-millisecond conversion for most numbers
214
- - **Efficient**: Zero dependencies, minimal memory footprint
215
- - **BigInt support**: Handles arbitrarily large numbers without precision loss
216
- - **Memory-efficient**: ~2 KB overhead per language when tree-shaken
172
+ - **1M+ ops/sec**: Most languages exceed 1 million conversions per second
173
+ - **Sub-millisecond**: Typical conversion takes < 1 microsecond
174
+ - **Low memory**: ~500-800 bytes per conversion (no allocations for small numbers)
175
+ - **BigInt optimized**: Uses BigInt modulo instead of string manipulation
176
+ - **Precomputed tables**: Common segments (0-999) precomputed at module load
217
177
 
218
- **Tree-shaking example:**
178
+ **Subpath imports (recommended for single language):**
219
179
 
220
180
  ```js
221
- // Import only what you need - bundler only includes used languages
222
- import { EnglishConverter, SpanishConverter } from 'n2words'
223
- // Final bundle: ~4-5 KB gzipped (only English + Spanish + core)
181
+ // Smallest possible bundle - no barrel file overhead
182
+ import { toWords } from 'n2words/en'
183
+ toWords(42) // 'forty-two'
184
+ // Final bundle: ~1.4 KB gzipped
185
+ ```
186
+
187
+ **Named imports (for multiple languages):**
188
+
189
+ ```js
190
+ // Bundler tree-shakes unused languages
191
+ import { en, es } from 'n2words'
192
+ // Final bundle: ~3-4 KB gzipped (English + Spanish)
224
193
  ```
225
194
 
226
195
  **Run benchmarks:**
@@ -232,103 +201,33 @@ npm run bench:memory # Memory usage benchmarks
232
201
 
233
202
  ## Examples
234
203
 
235
- ### Basic Conversions
236
-
237
- ```js
238
- import { EnglishConverter } from 'n2words'
239
-
240
- // Basic numbers
241
- EnglishConverter(0) // 'zero'
242
- EnglishConverter(42) // 'forty-two'
243
- EnglishConverter(1000000) // 'one million'
244
-
245
- // Decimals & negatives
246
- EnglishConverter(3.14) // 'three point one four'
247
- EnglishConverter(-42) // 'minus forty-two'
248
-
249
- // Large numbers & BigInt
250
- EnglishConverter(1234567890) // 'one billion two hundred and thirty-four million five hundred and sixty-seven thousand eight hundred and ninety'
251
- EnglishConverter(123456789012345n) // Works with arbitrarily large integers
252
- ```
253
-
254
- ### Gender Agreement
255
-
256
204
  ```js
257
- import { SpanishConverter, ArabicConverter, RussianConverter } from 'n2words'
205
+ import { en, es, ar, zhHans } from 'n2words'
258
206
 
259
- // Spanish: masculine vs feminine
260
- SpanishConverter(1) // 'uno' (masculine, default)
261
- SpanishConverter(1, { gender: 'feminine' }) // 'una'
262
- SpanishConverter(21) // 'veintiuno' (masculine)
263
- SpanishConverter(21, { gender: 'feminine' }) // 'veintiuna'
207
+ // Basic conversions
208
+ en(42) // 'forty-two'
209
+ en(3.14) // 'three point one four'
210
+ en(-1000000) // 'minus one million'
264
211
 
265
- // Arabic: rich gender system
266
- ArabicConverter(1) // 'واحد' (masculine, default)
267
- ArabicConverter(1, { gender: 'feminine' }) // 'واحدة'
212
+ // Input types: number, string, or BigInt
213
+ en('42') // 'forty-two'
214
+ en(42n) // 'forty-two'
215
+ en(999999999999999999999999n) // Works with arbitrarily large integers
268
216
 
269
- // Russian: gender for numerals
270
- RussianConverter(1) // 'один' (masculine, default)
271
- RussianConverter(1, { gender: 'feminine' }) // 'одна'
272
- ```
273
-
274
- ### Language-Specific Features
275
-
276
- ```js
277
- import {
278
- SimplifiedChineseConverter,
279
- TraditionalChineseConverter,
280
- JapaneseConverter,
281
- DutchConverter,
282
- FrenchConverter
283
- } from 'n2words'
217
+ // Gender agreement (12 languages)
218
+ es(1) // 'uno' (masculine, default)
219
+ es(1, { gender: 'feminine' }) // 'una'
220
+ ar(1, { gender: 'feminine' }) // 'واحدة'
284
221
 
285
222
  // Chinese: formal (financial) vs common numerals
286
- SimplifiedChineseConverter(123) // '壹佰贰拾叁' (formal, default)
287
- SimplifiedChineseConverter(123, { formal: false }) // '一百二十三' (common)
288
-
289
- TraditionalChineseConverter(456) // '肆佰伍拾陸' (formal, default)
290
- TraditionalChineseConverter(456, { formal: false }) // '四百五十六' (common)
291
-
292
- // Japanese: digit-by-digit decimals
293
- JapaneseConverter(3.14) // '三点一四'
294
-
295
- // Dutch: flexible formatting
296
- DutchConverter(123) // 'honderddrieëntwintig' (default compound)
297
- DutchConverter(101, { includeOptionalAnd: true }) // 'honderdeneen' (with optional "en")
298
- DutchConverter(1) // 'één' (accented, default)
299
- DutchConverter(1, { accentOne: false }) // 'een' (unaccented)
300
-
301
- // French: selective hyphens vs all hyphens
302
- FrenchConverter(123) // 'cent vingt-trois' (default)
303
- FrenchConverter(123, { withHyphenSeparator: true }) // 'cent-vingt-trois' (all hyphens)
304
- ```
305
-
306
- ### Input Flexibility
307
-
308
- ```js
309
- import { EnglishConverter } from 'n2words'
310
-
311
- // Multiple input types
312
- EnglishConverter(42) // number → 'forty-two'
313
- EnglishConverter('42') // string → 'forty-two'
314
- EnglishConverter(42n) // BigInt → 'forty-two'
315
-
316
- // Decimal strings
317
- EnglishConverter('3.14') // 'three point one four'
318
- EnglishConverter('.5') // 'zero point five'
319
-
320
- // Negative strings
321
- EnglishConverter('-42') // 'minus forty-two'
322
-
323
- // Large BigInts (no precision loss)
324
- EnglishConverter(999999999999999999999999n) // Accurate conversion
223
+ zhHans(123) // '壹佰贰拾叁' (formal, default)
224
+ zhHans(123, { formal: false }) // '一百二十三' (common)
325
225
  ```
326
226
 
327
227
  ## Documentation
328
228
 
329
229
  - **[Compatibility Guide](COMPATIBILITY.md)** - Browser and Node.js compatibility
330
230
  - **[Contributing Guide](CONTRIBUTING.md)** - How to contribute and add languages
331
- - **[Architecture Guide](docs/ARCHITECTURE.md)** - Implementation patterns and options reference
332
231
  - **[Code of Conduct](CODE_OF_CONDUCT.md)** - Community standards
333
232
 
334
233
  ## Contributing
@@ -337,7 +236,6 @@ We welcome contributions! Add a new language or improve existing ones:
337
236
 
338
237
  ```bash
339
238
  npm run lang:add <code> # Scaffold a new language (BCP 47 code)
340
- npm run lang:validate -- <code> # Validate implementation
341
239
  npm test # Run full test suite
342
240
  ```
343
241
 
@@ -0,0 +1,3 @@
1
+ /*! n2words/am-Latn v3.1.0 | MIT License | github.com/forzagreen/n2words */
2
+ var e,t;e=this,t=function(e){"use strict";function t(e){const t="-"===e[0];t&&(e=e.slice(1));const n=e.indexOf(".");if(-1===n)return{isNegative:t,integerPart:BigInt(e)};const i=e.slice(0,n)||"0",r=e.slice(n+1);return{isNegative:t,integerPart:BigInt(i),decimalPart:r}}function n(e){const[t,n]=e.toLowerCase().split("e"),i=parseInt(n,10),r=t.indexOf("."),s=-1===r?t:t.slice(0,r)+t.slice(r+1),a=(-1===r?t.length:r)+i;return a>=s.length?s+"0".repeat(a-s.length):a<=0?"0."+"0".repeat(-a)+s:s.slice(0,a)+"."+s.slice(a)}const i=["","and","hulet","sost","arat","amist","siddist","sebat","siment","zeteny"],r=["asir","asra and","asra hulet","asra sost","asra arat","asra amist","asra siddist","asra sebat","asra siment","asra zeteny"],s=["","","haya","selasa","arba","hamsa","silsa","seba","semanya","zetena"],a="zero",o=["","shi","miliyon","billiyon"];function u(e){if(0===e)return"";const t=e%10,n=Math.floor(e/10)%10,a=Math.floor(e/100),o=[];return a>0&&o.push(i[a]+" meto"),1===n?o.push(r[t]):(n>1&&o.push(s[n]),t>0&&o.push(i[t])),o.join(" ")}e.amLatn=function(e){const{isNegative:r,integerPart:s,decimalPart:c}=function(e){const i=typeof e;if("bigint"===i)return e<0n?{isNegative:!0,integerPart:-e}:{isNegative:!1,integerPart:e};if("number"===i){if(!Number.isFinite(e))throw new Error("Number must be finite (NaN and Infinity are not supported)");return Number.isSafeInteger(e)?e<0?{isNegative:!0,integerPart:BigInt(-e)}:{isNegative:!1,integerPart:BigInt(e)}:t(function(e){const t=e.toString();return t.includes("e")||t.includes("E")?n(t):t}(e))}if("string"===i)return t(function(e){const t=e.trim();if(0===t.length||Number.isNaN(Number(t)))throw new Error(`Invalid number format: "${e}"`);return t.includes("e")||t.includes("E")?n(t):t}(e));throw new TypeError(`Invalid value type: expected number, string, or bigint, received ${i}`)}(e);let l="";return r&&(l="asitegna "),l+=0n===(f=s)?a:f<1000n?u(Number(f)):function(e){const t=e.toString(),n=t.length,i=[],r=n%3;let s=0;for(r>0&&(i.push(Number(t.slice(0,r))),s=r);s<n;)i.push(Number(t.slice(s,s+3))),s+=3;const a=[];let c=i.length-1;for(let e=0;e<i.length;e++){const t=i[e];if(0!==t){const e=o[c]||"";a.push(0===c?u(t):u(t)+" "+e)}c--}return a.join(" ")}(f),c&&(l+=" netib "+function(e){const t=[];for(const n of e){const e=parseInt(n,10);t.push(0===e?a:i[e])}return t.join(" ")}(c)),l;var f}},"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).n2words=e.n2words||{});
3
+ //# sourceMappingURL=am-Latn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"am-Latn.js","sources":["../../lib/utils/parse-numeric.js","../../lib/languages/am-Latn.js"],"sourcesContent":["/**\n * Numeric value parsing utility.\n * Transforms user input (number, string, or bigint) into normalized components.\n * @module parse-numeric\n */\n\n/**\n * Parses a numeric value into its components.\n * @param {number|string|bigint} value\n * @returns {{isNegative: boolean, integerPart: bigint, decimalPart?: string}}\n * @throws {TypeError} If value is not number, string, or bigint\n * @throws {Error} If value is not a valid number format\n */\nexport function parseNumericValue (value) {\n const type = typeof value\n\n // BigInt: simplest case\n if (type === 'bigint') {\n return value < 0n\n ? { isNegative: true, integerPart: -value }\n : { isNegative: false, integerPart: value }\n }\n\n // Number: fast path for safe integers\n if (type === 'number') {\n if (!Number.isFinite(value)) {\n throw new Error('Number must be finite (NaN and Infinity are not supported)')\n }\n if (Number.isSafeInteger(value)) {\n return value < 0\n ? { isNegative: true, integerPart: BigInt(-value) }\n : { isNegative: false, integerPart: BigInt(value) }\n }\n return parseNumericString(numberToString(value))\n }\n\n // String input\n if (type === 'string') {\n return parseNumericString(normalizeString(value))\n }\n\n throw new TypeError(\n `Invalid value type: expected number, string, or bigint, received ${type}`\n )\n}\n\n/**\n * Converts a number to decimal string, expanding scientific notation if needed.\n */\nfunction numberToString (value) {\n const str = value.toString()\n return (str.includes('e') || str.includes('E'))\n ? expandScientificNotation(str)\n : str\n}\n\n/**\n * Validates and normalizes a string numeric input.\n */\nfunction normalizeString (value) {\n const trimmed = value.trim()\n if (trimmed.length === 0 || Number.isNaN(Number(trimmed))) {\n throw new Error(`Invalid number format: \"${value}\"`)\n }\n return (trimmed.includes('e') || trimmed.includes('E'))\n ? expandScientificNotation(trimmed)\n : trimmed\n}\n\n/**\n * Parses a normalized numeric string into components.\n */\nfunction parseNumericString (str) {\n const isNegative = str[0] === '-'\n if (isNegative) str = str.slice(1)\n\n const dotIndex = str.indexOf('.')\n if (dotIndex === -1) {\n return { isNegative, integerPart: BigInt(str) }\n }\n\n const integerStr = str.slice(0, dotIndex) || '0'\n const decimalPart = str.slice(dotIndex + 1)\n return { isNegative, integerPart: BigInt(integerStr), decimalPart }\n}\n\n/**\n * Expands scientific notation to decimal form (e.g., \"1e21\" → \"1000...\").\n */\nfunction expandScientificNotation (str) {\n const [mantissa, expStr] = str.toLowerCase().split('e')\n const exp = parseInt(expStr, 10)\n\n const dotIndex = mantissa.indexOf('.')\n const digits = dotIndex === -1\n ? mantissa\n : mantissa.slice(0, dotIndex) + mantissa.slice(dotIndex + 1)\n const integerLength = dotIndex === -1 ? mantissa.length : dotIndex\n const newDotPosition = integerLength + exp\n\n if (newDotPosition >= digits.length) {\n return digits + '0'.repeat(newDotPosition - digits.length)\n }\n if (newDotPosition <= 0) {\n return '0.' + '0'.repeat(-newDotPosition) + digits\n }\n return digits.slice(0, newDotPosition) + '.' + digits.slice(newDotPosition)\n}\n","/**\n * Amharic Latin language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n * Latin/ASCII romanization of Amharic numerals.\n *\n * Key features:\n * - Romanized numerals (and, hulet, sost)\n * - Teens formed with \"asra\" prefix\n * - Keeps \"one\" before hundred: \"and meto\" (100)\n * - Short scale naming\n * - Per-digit decimal reading\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ONES = ['', 'and', 'hulet', 'sost', 'arat', 'amist', 'siddist', 'sebat', 'siment', 'zeteny']\nconst TEENS = ['asir', 'asra and', 'asra hulet', 'asra sost', 'asra arat', 'asra amist', 'asra siddist', 'asra sebat', 'asra siment', 'asra zeteny']\nconst TENS = ['', '', 'haya', 'selasa', 'arba', 'hamsa', 'silsa', 'seba', 'semanya', 'zetena']\n\nconst HUNDRED = 'meto'\nconst THOUSAND = 'shi'\n\nconst ZERO = 'zero'\nconst NEGATIVE = 'asitegna'\nconst DECIMAL_SEP = 'netib'\n\n// Short scale\nconst SCALE_WORDS = ['', THOUSAND, 'miliyon', 'billiyon']\n\n// ============================================================================\n// Precomputed Lookup Table\n// ============================================================================\n\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tensDigit = Math.floor(n / 10) % 10\n const hundredsDigit = Math.floor(n / 100)\n\n const parts = []\n\n // Amharic keeps \"one\" before hundred: \"and meto\" (100)\n if (hundredsDigit > 0) {\n parts.push(ONES[hundredsDigit] + ' ' + HUNDRED)\n }\n\n if (tensDigit === 1) {\n parts.push(TEENS[ones])\n } else {\n if (tensDigit > 1) {\n parts.push(TENS[tensDigit])\n }\n if (ones > 0) {\n parts.push(ONES[ones])\n }\n }\n\n return parts.join(' ')\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n if (n < 1000n) {\n return buildSegment(Number(n))\n }\n\n return buildLargeNumberWords(n)\n}\n\nfunction buildLargeNumberWords (n) {\n const numStr = n.toString()\n const len = numStr.length\n\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n const scaleWord = SCALE_WORDS[scaleIndex] || ''\n\n if (scaleIndex === 0) {\n parts.push(buildSegment(segment))\n } else {\n parts.push(buildSegment(segment) + ' ' + scaleWord)\n }\n }\n\n scaleIndex--\n }\n\n return parts.join(' ')\n}\n\nfunction decimalPartToWords (decimalPart) {\n // Per-digit decimal reading\n const digits = []\n for (const char of decimalPart) {\n const d = parseInt(char, 10)\n digits.push(d === 0 ? ZERO : ONES[d])\n }\n return digits.join(' ')\n}\n\n/**\n * Converts a numeric value to Amharic (Latin script) words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Amharic Latin words\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n"],"names":["parseNumericString","str","isNegative","slice","dotIndex","indexOf","integerPart","BigInt","integerStr","decimalPart","expandScientificNotation","mantissa","expStr","toLowerCase","split","exp","parseInt","digits","newDotPosition","length","repeat","ONES","TEENS","TENS","ZERO","SCALE_WORDS","buildSegment","n","ones","tensDigit","Math","floor","hundredsDigit","parts","push","join","value","type","Number","isFinite","Error","isSafeInteger","toString","includes","numberToString","trimmed","trim","isNaN","normalizeString","TypeError","parseNumericValue","result","NEGATIVE","numStr","len","segments","remainderLen","pos","scaleIndex","i","segment","scaleWord","buildLargeNumberWords","char","d","decimalPartToWords"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ /*! n2words/am v3.1.0 | MIT License | github.com/forzagreen/n2words */
2
+ var e,t;e=this,t=function(e){"use strict";function t(e){const t="-"===e[0];t&&(e=e.slice(1));const n=e.indexOf(".");if(-1===n)return{isNegative:t,integerPart:BigInt(e)};const i=e.slice(0,n)||"0",r=e.slice(n+1);return{isNegative:t,integerPart:BigInt(i),decimalPart:r}}function n(e){const[t,n]=e.toLowerCase().split("e"),i=parseInt(n,10),r=t.indexOf("."),o=-1===r?t:t.slice(0,r)+t.slice(r+1),s=(-1===r?t.length:r)+i;return s>=o.length?o+"0".repeat(s-o.length):s<=0?"0."+"0".repeat(-s)+o:o.slice(0,s)+"."+o.slice(s)}const i=["","አንድ","ሁለት","ሶስት","አራት","አምስት","ስድስት","ሰባት","ስምንት","ዘጠኝ"],r=["አስር","አስራ አንድ","አስራ ሁለት","አስራ ሶስት","አስራ አራት","አስራ አምስት","አስራ ስድስት","አስራ ሰባት","አስራ ስምንት","አስራ ዘጠኝ"],o=["","","ሃያ","ሰላሳ","አርባ","ሃምሳ","ስልሳ","ሰባ","ሰማንያ","ዘጠና"],s=["","ሺ","ሚሊዮን","ቢሊዮን"];function u(e){if(0===e)return"";const t=e%10,n=Math.floor(e/10)%10,s=Math.floor(e/100),u=[];return s>0&&u.push(i[s]+" መቶ"),1===n?u.push(r[t]):(n>1&&u.push(o[n]),t>0&&u.push(i[t])),u.join(" ")}e.am=function(e){const{isNegative:r,integerPart:o,decimalPart:c}=function(e){const i=typeof e;if("bigint"===i)return e<0n?{isNegative:!0,integerPart:-e}:{isNegative:!1,integerPart:e};if("number"===i){if(!Number.isFinite(e))throw new Error("Number must be finite (NaN and Infinity are not supported)");return Number.isSafeInteger(e)?e<0?{isNegative:!0,integerPart:BigInt(-e)}:{isNegative:!1,integerPart:BigInt(e)}:t(function(e){const t=e.toString();return t.includes("e")||t.includes("E")?n(t):t}(e))}if("string"===i)return t(function(e){const t=e.trim();if(0===t.length||Number.isNaN(Number(t)))throw new Error(`Invalid number format: "${e}"`);return t.includes("e")||t.includes("E")?n(t):t}(e));throw new TypeError(`Invalid value type: expected number, string, or bigint, received ${i}`)}(e);let f="";return r&&(f="አሉታዊ "),f+=0n===(a=o)?"ዜሮ":a<1000n?u(Number(a)):function(e){const t=e.toString(),n=t.length,i=[],r=n%3;let o=0;for(r>0&&(i.push(Number(t.slice(0,r))),o=r);o<n;)i.push(Number(t.slice(o,o+3))),o+=3;const c=[];let f=i.length-1;for(let e=0;e<i.length;e++){const t=i[e];if(0!==t){const e=s[f]||"";c.push(0===f?u(t):u(t)+" "+e)}f--}return c.join(" ")}(a),c&&(f+=" ነጥብ "+function(e){const t=[];for(const n of e){const e=parseInt(n,10);t.push(0===e?"ዜሮ":i[e])}return t.join(" ")}(c)),f;var a}},"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).n2words=e.n2words||{});
3
+ //# sourceMappingURL=am.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"am.js","sources":["../../lib/utils/parse-numeric.js","../../lib/languages/am.js"],"sourcesContent":["/**\n * Numeric value parsing utility.\n * Transforms user input (number, string, or bigint) into normalized components.\n * @module parse-numeric\n */\n\n/**\n * Parses a numeric value into its components.\n * @param {number|string|bigint} value\n * @returns {{isNegative: boolean, integerPart: bigint, decimalPart?: string}}\n * @throws {TypeError} If value is not number, string, or bigint\n * @throws {Error} If value is not a valid number format\n */\nexport function parseNumericValue (value) {\n const type = typeof value\n\n // BigInt: simplest case\n if (type === 'bigint') {\n return value < 0n\n ? { isNegative: true, integerPart: -value }\n : { isNegative: false, integerPart: value }\n }\n\n // Number: fast path for safe integers\n if (type === 'number') {\n if (!Number.isFinite(value)) {\n throw new Error('Number must be finite (NaN and Infinity are not supported)')\n }\n if (Number.isSafeInteger(value)) {\n return value < 0\n ? { isNegative: true, integerPart: BigInt(-value) }\n : { isNegative: false, integerPart: BigInt(value) }\n }\n return parseNumericString(numberToString(value))\n }\n\n // String input\n if (type === 'string') {\n return parseNumericString(normalizeString(value))\n }\n\n throw new TypeError(\n `Invalid value type: expected number, string, or bigint, received ${type}`\n )\n}\n\n/**\n * Converts a number to decimal string, expanding scientific notation if needed.\n */\nfunction numberToString (value) {\n const str = value.toString()\n return (str.includes('e') || str.includes('E'))\n ? expandScientificNotation(str)\n : str\n}\n\n/**\n * Validates and normalizes a string numeric input.\n */\nfunction normalizeString (value) {\n const trimmed = value.trim()\n if (trimmed.length === 0 || Number.isNaN(Number(trimmed))) {\n throw new Error(`Invalid number format: \"${value}\"`)\n }\n return (trimmed.includes('e') || trimmed.includes('E'))\n ? expandScientificNotation(trimmed)\n : trimmed\n}\n\n/**\n * Parses a normalized numeric string into components.\n */\nfunction parseNumericString (str) {\n const isNegative = str[0] === '-'\n if (isNegative) str = str.slice(1)\n\n const dotIndex = str.indexOf('.')\n if (dotIndex === -1) {\n return { isNegative, integerPart: BigInt(str) }\n }\n\n const integerStr = str.slice(0, dotIndex) || '0'\n const decimalPart = str.slice(dotIndex + 1)\n return { isNegative, integerPart: BigInt(integerStr), decimalPart }\n}\n\n/**\n * Expands scientific notation to decimal form (e.g., \"1e21\" → \"1000...\").\n */\nfunction expandScientificNotation (str) {\n const [mantissa, expStr] = str.toLowerCase().split('e')\n const exp = parseInt(expStr, 10)\n\n const dotIndex = mantissa.indexOf('.')\n const digits = dotIndex === -1\n ? mantissa\n : mantissa.slice(0, dotIndex) + mantissa.slice(dotIndex + 1)\n const integerLength = dotIndex === -1 ? mantissa.length : dotIndex\n const newDotPosition = integerLength + exp\n\n if (newDotPosition >= digits.length) {\n return digits + '0'.repeat(newDotPosition - digits.length)\n }\n if (newDotPosition <= 0) {\n return '0.' + '0'.repeat(-newDotPosition) + digits\n }\n return digits.slice(0, newDotPosition) + '.' + digits.slice(newDotPosition)\n}\n","/**\n * Amharic language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n * Native Ge'ez script (ግዕዝ) output.\n *\n * Key features:\n * - Ge'ez/Ethiopic script numerals\n * - Teens formed with \"አስራ\" prefix\n * - Keeps \"one\" before hundred: \"አንድ መቶ\" (100)\n * - Short scale naming\n * - Per-digit decimal reading\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ONES = ['', 'አንድ', 'ሁለት', 'ሶስት', 'አራት', 'አምስት', 'ስድስት', 'ሰባት', 'ስምንት', 'ዘጠኝ']\nconst TEENS = ['አስር', 'አስራ አንድ', 'አስራ ሁለት', 'አስራ ሶስት', 'አስራ አራት', 'አስራ አምስት', 'አስራ ስድስት', 'አስራ ሰባት', 'አስራ ስምንት', 'አስራ ዘጠኝ']\nconst TENS = ['', '', 'ሃያ', 'ሰላሳ', 'አርባ', 'ሃምሳ', 'ስልሳ', 'ሰባ', 'ሰማንያ', 'ዘጠና']\n\nconst HUNDRED = 'መቶ'\nconst THOUSAND = 'ሺ'\n\nconst ZERO = 'ዜሮ'\nconst NEGATIVE = 'አሉታዊ'\nconst DECIMAL_SEP = 'ነጥብ'\n\n// Short scale\nconst SCALE_WORDS = ['', THOUSAND, 'ሚሊዮን', 'ቢሊዮን']\n\n// ============================================================================\n// Precomputed Lookup Table\n// ============================================================================\n\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tensDigit = Math.floor(n / 10) % 10\n const hundredsDigit = Math.floor(n / 100)\n\n const parts = []\n\n // Amharic keeps \"one\" before hundred: \"አንድ መቶ\" (100)\n if (hundredsDigit > 0) {\n parts.push(ONES[hundredsDigit] + ' ' + HUNDRED)\n }\n\n if (tensDigit === 1) {\n parts.push(TEENS[ones])\n } else {\n if (tensDigit > 1) {\n parts.push(TENS[tensDigit])\n }\n if (ones > 0) {\n parts.push(ONES[ones])\n }\n }\n\n return parts.join(' ')\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n if (n < 1000n) {\n return buildSegment(Number(n))\n }\n\n return buildLargeNumberWords(n)\n}\n\nfunction buildLargeNumberWords (n) {\n const numStr = n.toString()\n const len = numStr.length\n\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n const scaleWord = SCALE_WORDS[scaleIndex] || ''\n\n if (scaleIndex === 0) {\n parts.push(buildSegment(segment))\n } else {\n parts.push(buildSegment(segment) + ' ' + scaleWord)\n }\n }\n\n scaleIndex--\n }\n\n return parts.join(' ')\n}\n\nfunction decimalPartToWords (decimalPart) {\n // Per-digit decimal reading\n const digits = []\n for (const char of decimalPart) {\n const d = parseInt(char, 10)\n digits.push(d === 0 ? ZERO : ONES[d])\n }\n return digits.join(' ')\n}\n\n/**\n * Converts a numeric value to Amharic words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Amharic words\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n"],"names":["parseNumericString","str","isNegative","slice","dotIndex","indexOf","integerPart","BigInt","integerStr","decimalPart","expandScientificNotation","mantissa","expStr","toLowerCase","split","exp","parseInt","digits","newDotPosition","length","repeat","ONES","TEENS","TENS","SCALE_WORDS","buildSegment","n","ones","tensDigit","Math","floor","hundredsDigit","parts","push","join","value","type","Number","isFinite","Error","isSafeInteger","toString","includes","numberToString","trimmed","trim","isNaN","normalizeString","TypeError","parseNumericValue","result","NEGATIVE","numStr","len","segments","remainderLen","pos","scaleIndex","i","segment","scaleWord","buildLargeNumberWords","char","d","decimalPartToWords"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ /*! n2words/ar v3.1.0 | MIT License | github.com/forzagreen/n2words */
2
+ var e,t;e=this,t=function(e){"use strict";function t(e){const t="-"===e[0];t&&(e=e.slice(1));const n=e.indexOf(".");if(-1===n)return{isNegative:t,integerPart:BigInt(e)};const i=e.slice(0,n)||"0",r=e.slice(n+1);return{isNegative:t,integerPart:BigInt(i),decimalPart:r}}function n(e){const[t,n]=e.toLowerCase().split("e"),i=parseInt(n,10),r=t.indexOf("."),o=-1===r?t:t.slice(0,r)+t.slice(r+1),s=(-1===r?t.length:r)+i;return s>=o.length?o+"0".repeat(s-o.length):s<=0?"0."+"0".repeat(-s)+o:o.slice(0,s)+"."+o.slice(s)}const i=["عشرون","ثلاثون","أربعون","خمسون","ستون","سبعون","ثمانون","تسعون"],r=["","مائة","مئتان","ثلاثمائة","أربعمائة","خمسمائة","ستمائة","سبعمائة","ثمانمائة","تسعمائة"],o=["مائة","ألف","مليون","مليار","تريليون","كوادريليون","كوينتليون","سكستيليون"],s=["","ألفاً","مليوناً","ملياراً","تريليوناً","كوادريليوناً","كوينتليوناً","سكستيليوناً"],u=["","آلاف","ملايين","مليارات","تريليونات","كوادريليونات","كوينتليونات","سكستيليونات"],c=["مئتان","ألفان","مليونان","ملياران","تريليونان","كوادريليونان","كوينتليونان","سكستيليونان"],f=["مئتا","ألفا","مليونا","مليارا","تريليونا","كوادريليونا","كوينتليونا","سكستيليونا"],l=["واحد","اثنان","ثلاثة","أربعة","خمسة","ستة","سبعة","ثمانية","تسعة","عشرة","أحد عشر","اثنا عشر","ثلاثة عشر","أربعة عشر","خمسة عشر","ستة عشر","سبعة عشر","ثمانية عشر","تسعة عشر"],a=["واحدة","اثنتان","ثلاث","أربع","خمس","ست","سبع","ثمان","تسع","عشر","إحدى عشرة","اثنتا عشرة","ثلاث عشرة","أربع عشرة","خمس عشرة","ست عشرة","سبع عشرة","ثماني عشرة","تسع عشرة"];function g(e,t,n,s){const u=e%100,l=Math.trunc(e/100);let a="";if(l>0)if(0===u&&2===l)a=c[0];else{const e=r[l];e&&(a=e,0!==u&&(a+=" و"))}if(u>0)if(u<20)if(2===u&&0===l&&t>0){const i=Number(n),r=Math.trunc(Math.log10(i));r%3==0&&n===BigInt(2*Math.pow(10,r))?a+=2===e?c[t]:f[t]:a+=c[t]}else a+=1===u&&t>0?o[t]:s[u-1];else{const e=u%10,t=Math.trunc(u/10)-2;e>0&&(a+=s[e-1]+" و"),a+=i[t]}return a}function p(e,t){if(0n===e)return"صفر";const n="feminine"===(t.gender||"masculine")?a:l;let i=e,r=0;const c=[];for(;i>0n;){const t=Number(i%1000n);if(i/=1000n,t>0){const i=g(t,r,e,n);if(i){let e=i;r>0&&t>2&&(e+=1==t%100?" "+o[r]:t>=3&&t<=10?" "+u[r]:" "+(c.length>0?s[r]:o[r])),c.unshift(e)}}r++}if(1===c.length)return c[0];let f=c[0];for(let e=1;e<c.length;e++)f+=" و"+c[e];return f}e.ar=function(e,i){i=function(e){if(void 0===e)return{};if(function(e){if(null===e||"object"!=typeof e)return!1;const t=Object.getPrototypeOf(e);return null===t||t===Object.prototype}(e))return e;throw new TypeError("Invalid options: expected plain object or undefined, got "+typeof e)}(i);const{isNegative:r,integerPart:o,decimalPart:s}=function(e){const i=typeof e;if("bigint"===i)return e<0n?{isNegative:!0,integerPart:-e}:{isNegative:!1,integerPart:e};if("number"===i){if(!Number.isFinite(e))throw new Error("Number must be finite (NaN and Infinity are not supported)");return Number.isSafeInteger(e)?e<0?{isNegative:!0,integerPart:BigInt(-e)}:{isNegative:!1,integerPart:BigInt(e)}:t(function(e){const t=e.toString();return t.includes("e")||t.includes("E")?n(t):t}(e))}if("string"===i)return t(function(e){const t=e.trim();if(0===t.length||Number.isNaN(Number(t)))throw new Error(`Invalid number format: "${e}"`);return t.includes("e")||t.includes("E")?n(t):t}(e));throw new TypeError(`Invalid value type: expected number, string, or bigint, received ${i}`)}(e),u=[];return r&&u.push(i.negativeWord||"ناقص"),u.push(p(o,i)),s&&(u.push("فاصلة"),u.push(function(e,t){const n=[];let i=0;for(;i<e.length&&"0"===e[i];)n.push("صفر"),i++;const r=e.slice(i);return r&&n.push(p(BigInt(r),t)),n.join(" ")}(s,i))),u.join(" ")}},"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).n2words=e.n2words||{});
3
+ //# sourceMappingURL=ar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ar.js","sources":["../../lib/utils/parse-numeric.js","../../lib/languages/ar.js","../../lib/utils/validate-options.js","../../lib/utils/is-plain-object.js"],"sourcesContent":["/**\n * Numeric value parsing utility.\n * Transforms user input (number, string, or bigint) into normalized components.\n * @module parse-numeric\n */\n\n/**\n * Parses a numeric value into its components.\n * @param {number|string|bigint} value\n * @returns {{isNegative: boolean, integerPart: bigint, decimalPart?: string}}\n * @throws {TypeError} If value is not number, string, or bigint\n * @throws {Error} If value is not a valid number format\n */\nexport function parseNumericValue (value) {\n const type = typeof value\n\n // BigInt: simplest case\n if (type === 'bigint') {\n return value < 0n\n ? { isNegative: true, integerPart: -value }\n : { isNegative: false, integerPart: value }\n }\n\n // Number: fast path for safe integers\n if (type === 'number') {\n if (!Number.isFinite(value)) {\n throw new Error('Number must be finite (NaN and Infinity are not supported)')\n }\n if (Number.isSafeInteger(value)) {\n return value < 0\n ? { isNegative: true, integerPart: BigInt(-value) }\n : { isNegative: false, integerPart: BigInt(value) }\n }\n return parseNumericString(numberToString(value))\n }\n\n // String input\n if (type === 'string') {\n return parseNumericString(normalizeString(value))\n }\n\n throw new TypeError(\n `Invalid value type: expected number, string, or bigint, received ${type}`\n )\n}\n\n/**\n * Converts a number to decimal string, expanding scientific notation if needed.\n */\nfunction numberToString (value) {\n const str = value.toString()\n return (str.includes('e') || str.includes('E'))\n ? expandScientificNotation(str)\n : str\n}\n\n/**\n * Validates and normalizes a string numeric input.\n */\nfunction normalizeString (value) {\n const trimmed = value.trim()\n if (trimmed.length === 0 || Number.isNaN(Number(trimmed))) {\n throw new Error(`Invalid number format: \"${value}\"`)\n }\n return (trimmed.includes('e') || trimmed.includes('E'))\n ? expandScientificNotation(trimmed)\n : trimmed\n}\n\n/**\n * Parses a normalized numeric string into components.\n */\nfunction parseNumericString (str) {\n const isNegative = str[0] === '-'\n if (isNegative) str = str.slice(1)\n\n const dotIndex = str.indexOf('.')\n if (dotIndex === -1) {\n return { isNegative, integerPart: BigInt(str) }\n }\n\n const integerStr = str.slice(0, dotIndex) || '0'\n const decimalPart = str.slice(dotIndex + 1)\n return { isNegative, integerPart: BigInt(integerStr), decimalPart }\n}\n\n/**\n * Expands scientific notation to decimal form (e.g., \"1e21\" → \"1000...\").\n */\nfunction expandScientificNotation (str) {\n const [mantissa, expStr] = str.toLowerCase().split('e')\n const exp = parseInt(expStr, 10)\n\n const dotIndex = mantissa.indexOf('.')\n const digits = dotIndex === -1\n ? mantissa\n : mantissa.slice(0, dotIndex) + mantissa.slice(dotIndex + 1)\n const integerLength = dotIndex === -1 ? mantissa.length : dotIndex\n const newDotPosition = integerLength + exp\n\n if (newDotPosition >= digits.length) {\n return digits + '0'.repeat(newDotPosition - digits.length)\n }\n if (newDotPosition <= 0) {\n return '0.' + '0'.repeat(-newDotPosition) + digits\n }\n return digits.slice(0, newDotPosition) + '.' + digits.slice(newDotPosition)\n}\n","/**\n * Arabic language converter - Functional Implementation\n *\n * Self-contained converter with gender agreement and complex pluralization.\n *\n * Key features:\n * - Gender agreement (masculine/feminine forms)\n * - Complex pluralization (singular/dual/plural)\n * - Traditional Arabic number naming conventions\n * - \"و\" (and) conjunction between segments\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\nimport { validateOptions } from '../utils/validate-options.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst TENS = ['عشرون', 'ثلاثون', 'أربعون', 'خمسون', 'ستون', 'سبعون', 'ثمانون', 'تسعون']\nconst HUNDREDS = ['', 'مائة', 'مئتان', 'ثلاثمائة', 'أربعمائة', 'خمسمائة', 'ستمائة', 'سبعمائة', 'ثمانمائة', 'تسعمائة']\n\n// Magnitude words with three forms: singular, appended (tanween), plural\nconst SCALE_WORDS = ['مائة', 'ألف', 'مليون', 'مليار', 'تريليون', 'كوادريليون', 'كوينتليون', 'سكستيليون']\nconst SCALE_APPENDED = ['', 'ألفاً', 'مليوناً', 'ملياراً', 'تريليوناً', 'كوادريليوناً', 'كوينتليوناً', 'سكستيليوناً']\nconst SCALE_PLURAL = ['', 'آلاف', 'ملايين', 'مليارات', 'تريليونات', 'كوادريليونات', 'كوينتليونات', 'سكستيليونات']\n\n// Dual forms\nconst DUAL = ['مئتان', 'ألفان', 'مليونان', 'ملياران', 'تريليونان', 'كوادريليونان', 'كوينتليونان', 'سكستيليونان']\nconst DUAL_APPENDED = ['مئتا', 'ألفا', 'مليونا', 'مليارا', 'تريليونا', 'كوادريليونا', 'كوينتليونا', 'سكستيليونا']\n\n// Gender-specific forms (1-19)\nconst ONES_MASC = ['واحد', 'اثنان', 'ثلاثة', 'أربعة', 'خمسة', 'ستة', 'سبعة', 'ثمانية', 'تسعة', 'عشرة', 'أحد عشر', 'اثنا عشر', 'ثلاثة عشر', 'أربعة عشر', 'خمسة عشر', 'ستة عشر', 'سبعة عشر', 'ثمانية عشر', 'تسعة عشر']\nconst ONES_FEM = ['واحدة', 'اثنتان', 'ثلاث', 'أربع', 'خمس', 'ست', 'سبع', 'ثمان', 'تسع', 'عشر', 'إحدى عشرة', 'اثنتا عشرة', 'ثلاث عشرة', 'أربع عشرة', 'خمس عشرة', 'ست عشرة', 'سبع عشرة', 'ثماني عشرة', 'تسع عشرة']\n\nconst ZERO = 'صفر'\nconst NEGATIVE = 'ناقص'\nconst DECIMAL_SEP = 'فاصلة'\nconst AND = 'و'\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Convert a 3-digit group to words.\n * Returns a clean string with no leading/trailing spaces.\n * Arabic \"و\" (and) is attached to following word: \"مائة وخمسة\" not \"مائة و خمسة\"\n */\nfunction segmentToWords (groupNumber, groupLevel, fullNumber, ones) {\n const tensValue = groupNumber % 100\n const hundredsDigit = Math.trunc(groupNumber / 100)\n let result = ''\n\n // Process hundreds\n if (hundredsDigit > 0) {\n if (tensValue === 0 && hundredsDigit === 2) {\n result = DUAL[0]\n } else {\n const hundredsWord = HUNDREDS[hundredsDigit]\n if (hundredsWord) {\n result = hundredsWord\n if (tensValue !== 0) {\n result += ' ' + AND // \"مائة و\" - و attaches to next word\n }\n }\n }\n }\n\n // Process tens and ones\n if (tensValue > 0) {\n if (tensValue < 20) {\n if (tensValue === 2 && hundredsDigit === 0 && groupLevel > 0) {\n const numValue = Number(fullNumber)\n const pow = Math.trunc(Math.log10(numValue))\n if (pow % 3 === 0 && fullNumber === BigInt(2 * Math.pow(10, pow))) {\n result += (groupNumber === 2 ? DUAL[groupLevel] : DUAL_APPENDED[groupLevel])\n } else {\n result += DUAL[groupLevel]\n }\n } else if (tensValue === 1 && groupLevel > 0) {\n result += SCALE_WORDS[groupLevel]\n } else {\n result += ones[tensValue - 1]\n }\n } else {\n const onesDigit = tensValue % 10\n const tensIndex = Math.trunc(tensValue / 10) - 2\n\n if (onesDigit > 0) {\n result += ones[onesDigit - 1] + ' ' + AND // \"ستة و\" attaches to tens\n }\n result += TENS[tensIndex]\n }\n }\n\n return result\n}\n\nfunction integerToWords (n, options) {\n if (n === 0n) return ZERO\n\n const gender = options.gender || 'masculine'\n const ones = gender === 'feminine' ? ONES_FEM : ONES_MASC\n\n let temp = n\n let group = 0\n const groups = []\n\n while (temp > 0n) {\n const numberToProcess = Number(temp % 1000n)\n temp = temp / 1000n\n\n if (numberToProcess > 0) {\n const groupDescription = segmentToWords(numberToProcess, group, n, ones)\n\n if (groupDescription) {\n let groupText = groupDescription\n\n // Add scale word for groups > 0\n if (group > 0 && numberToProcess > 2) {\n const remainder = numberToProcess % 100\n if (remainder === 1) {\n groupText += ' ' + SCALE_WORDS[group]\n } else if (numberToProcess >= 3 && numberToProcess <= 10) {\n groupText += ' ' + SCALE_PLURAL[group]\n } else {\n groupText += ' ' + (groups.length > 0 ? SCALE_APPENDED[group] : SCALE_WORDS[group])\n }\n }\n\n groups.unshift(groupText)\n }\n }\n\n group++\n }\n\n // Join groups with و (and) - space before و, attaches to next word\n // Use simple join since segmentToWords returns clean strings\n if (groups.length === 1) return groups[0]\n\n // Build result: \"group1 وgroup2 وgroup3\"\n let result = groups[0]\n for (let i = 1; i < groups.length; i++) {\n result += ' ' + AND + groups[i]\n }\n return result\n}\n\nfunction decimalPartToWords (decimalPart, options) {\n const parts = []\n let i = 0\n\n while (i < decimalPart.length && decimalPart[i] === '0') {\n parts.push(ZERO)\n i++\n }\n\n const remainder = decimalPart.slice(i)\n if (remainder) {\n parts.push(integerToWords(BigInt(remainder), options))\n }\n\n return parts.join(' ')\n}\n\n/**\n * Converts a numeric value to Arabic words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @param {Object} [options] - Optional configuration\n * @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender\n * @param {string} [options.negativeWord] - Custom word for negative numbers\n * @returns {string} The number in Arabic words\n *\n * @example\n * toWords(1) // 'واحد'\n * toWords(1, {gender: 'feminine'}) // 'واحدة'\n */\nfunction toWords (value, options) {\n options = validateOptions(options)\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n const parts = []\n\n if (isNegative) {\n parts.push(options.negativeWord || NEGATIVE)\n }\n\n parts.push(integerToWords(integerPart, options))\n\n if (decimalPart) {\n parts.push(DECIMAL_SEP)\n parts.push(decimalPartToWords(decimalPart, options))\n }\n\n return parts.join(' ')\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","import { isPlainObject } from './is-plain-object.js'\n\n/**\n * Validates and normalizes the options parameter.\n *\n * @param {*} options The options value to validate\n * @returns {Object} A valid options object (empty object if undefined)\n * @throws {TypeError} If options is not undefined or a plain object\n */\nexport function validateOptions (options) {\n if (options === undefined) return {}\n if (isPlainObject(options)) return options\n throw new TypeError(\n `Invalid options: expected plain object or undefined, got ${typeof options}`\n )\n}\n","/**\n * Checks if a value is a plain object (not null, array, or other object types).\n *\n * A plain object is one created by:\n * - Object literal: `{}`\n * - Object.create(null): null-prototype object\n *\n * This excludes arrays, class instances, Map, Set, and other object types.\n *\n * @param {*} value Value to check\n * @returns {boolean} True if value is a plain object\n */\nexport function isPlainObject (value) {\n if (value === null || typeof value !== 'object') return false\n const proto = Object.getPrototypeOf(value)\n return proto === null || proto === Object.prototype\n}\n"],"names":["parseNumericString","str","isNegative","slice","dotIndex","indexOf","integerPart","BigInt","integerStr","decimalPart","expandScientificNotation","mantissa","expStr","toLowerCase","split","exp","parseInt","digits","newDotPosition","length","repeat","TENS","HUNDREDS","SCALE_WORDS","SCALE_APPENDED","SCALE_PLURAL","DUAL","DUAL_APPENDED","ONES_MASC","ONES_FEM","segmentToWords","groupNumber","groupLevel","fullNumber","ones","tensValue","hundredsDigit","Math","trunc","result","hundredsWord","numValue","Number","pow","log10","onesDigit","tensIndex","integerToWords","n","options","gender","temp","group","groups","numberToProcess","groupDescription","groupText","unshift","i","value","undefined","proto","Object","getPrototypeOf","prototype","isPlainObject","TypeError","validateOptions","type","isFinite","Error","isSafeInteger","toString","includes","numberToString","trimmed","trim","isNaN","normalizeString","parseNumericValue","parts","push","negativeWord","remainder","join","decimalPartToWords"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ /*! n2words/az v3.1.0 | MIT License | github.com/forzagreen/n2words */
2
+ var e,n;e=this,n=function(e){"use strict";function n(e){const n="-"===e[0];n&&(e=e.slice(1));const t=e.indexOf(".");if(-1===t)return{isNegative:n,integerPart:BigInt(e)};const i=e.slice(0,t)||"0",r=e.slice(t+1);return{isNegative:n,integerPart:BigInt(i),decimalPart:r}}function t(e){const[n,t]=e.toLowerCase().split("e"),i=parseInt(t,10),r=n.indexOf("."),o=-1===r?n:n.slice(0,r)+n.slice(r+1),s=(-1===r?n.length:r)+i;return s>=o.length?o+"0".repeat(s-o.length):s<=0?"0."+"0".repeat(-s)+o:o.slice(0,s)+"."+o.slice(s)}const i=["","bir","iki","üç","dörd","beş","altı","yeddi","səkkiz","doqquz"],r=["on","on bir","on iki","on üç","on dörd","on beş","on altı","on yeddi","on səkkiz","on doqquz"],o=["","","iyirmi","otuz","qırx","əlli","altmış","yetmiş","səksən","doxsan"],s="sıfır",u=["","min","milyon","milyar","trilyon","katrilyon","kentilyon"];function l(e){if(0===e)return"";const n=e%10,t=Math.floor(e/10)%10,s=Math.floor(e/100),u=[];return s>0&&u.push(1===s?"yüz":i[s]+" yüz"),1===t?u.push(r[n]):(t>1&&u.push(o[t]),n>0&&u.push(i[n])),u.join(" ")}function c(e){return 0n===e?s:e<1000n?l(Number(e)):function(e){const n=e.toString(),t=n.length,i=[],r=t%3;let o=0;for(r>0&&(i.push(Number(n.slice(0,r))),o=r);o<t;)i.push(Number(n.slice(o,o+3))),o+=3;const s=[];let c=i.length-1;for(let e=0;e<i.length;e++){const n=i[e];if(0!==n){const e=u[c]||"";s.push(0===c?l(n):1===c&&1===n?e:l(n)+" "+e)}c--}return s.join(" ")}(e)}e.az=function(e){const{isNegative:i,integerPart:r,decimalPart:o}=function(e){const i=typeof e;if("bigint"===i)return e<0n?{isNegative:!0,integerPart:-e}:{isNegative:!1,integerPart:e};if("number"===i){if(!Number.isFinite(e))throw new Error("Number must be finite (NaN and Infinity are not supported)");return Number.isSafeInteger(e)?e<0?{isNegative:!0,integerPart:BigInt(-e)}:{isNegative:!1,integerPart:BigInt(e)}:n(function(e){const n=e.toString();return n.includes("e")||n.includes("E")?t(n):n}(e))}if("string"===i)return n(function(e){const n=e.trim();if(0===n.length||Number.isNaN(Number(n)))throw new Error(`Invalid number format: "${e}"`);return n.includes("e")||n.includes("E")?t(n):n}(e));throw new TypeError(`Invalid value type: expected number, string, or bigint, received ${i}`)}(e);let u="";return i&&(u="mənfi "),u+=c(r),o&&(u+=" nöqtə "+function(e){let n="",t=0;for(;t<e.length&&"0"===e[t];)n&&(n+=" "),n+=s,t++;const i=e.slice(t);return i&&(n&&(n+=" "),n+=c(BigInt(i))),n}(o)),u}},"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).n2words=e.n2words||{});
3
+ //# sourceMappingURL=az.js.map