n2words 1.23.0 → 1.24.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 (317) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +182 -53
  3. package/dist/languages/ar.js +2 -0
  4. package/dist/languages/ar.js.map +1 -0
  5. package/dist/languages/az.js +2 -0
  6. package/dist/languages/az.js.map +1 -0
  7. package/dist/languages/bn.js +2 -0
  8. package/dist/languages/bn.js.map +1 -0
  9. package/dist/languages/cs.js +2 -0
  10. package/dist/languages/cs.js.map +1 -0
  11. package/dist/languages/da.js +2 -0
  12. package/dist/languages/da.js.map +1 -0
  13. package/dist/languages/de.js +2 -0
  14. package/dist/languages/de.js.map +1 -0
  15. package/dist/languages/el.js +2 -0
  16. package/dist/languages/el.js.map +1 -0
  17. package/dist/languages/en.js +2 -0
  18. package/dist/languages/en.js.map +1 -0
  19. package/dist/languages/es.js +2 -0
  20. package/dist/languages/es.js.map +1 -0
  21. package/dist/languages/fa.js +2 -0
  22. package/dist/languages/fa.js.map +1 -0
  23. package/dist/languages/fil.js +2 -0
  24. package/dist/languages/fil.js.map +1 -0
  25. package/dist/languages/fr-BE.js +2 -0
  26. package/dist/languages/fr-BE.js.map +1 -0
  27. package/dist/languages/fr.js +2 -0
  28. package/dist/languages/fr.js.map +1 -0
  29. package/dist/languages/gu.js +2 -0
  30. package/dist/languages/gu.js.map +1 -0
  31. package/dist/languages/he.js +2 -0
  32. package/dist/languages/he.js.map +1 -0
  33. package/dist/languages/hi.js +2 -0
  34. package/dist/languages/hi.js.map +1 -0
  35. package/dist/languages/hr.js +2 -0
  36. package/dist/languages/hr.js.map +1 -0
  37. package/dist/languages/hu.js +2 -0
  38. package/dist/languages/hu.js.map +1 -0
  39. package/dist/languages/id.js +2 -0
  40. package/dist/languages/id.js.map +1 -0
  41. package/dist/languages/it.js +2 -0
  42. package/dist/languages/it.js.map +1 -0
  43. package/dist/languages/ja.js +2 -0
  44. package/dist/languages/ja.js.map +1 -0
  45. package/dist/languages/kn.js +2 -0
  46. package/dist/languages/kn.js.map +1 -0
  47. package/dist/languages/ko.js +2 -0
  48. package/dist/languages/ko.js.map +1 -0
  49. package/dist/languages/lt.js +2 -0
  50. package/dist/languages/lt.js.map +1 -0
  51. package/dist/languages/lv.js +2 -0
  52. package/dist/languages/lv.js.map +1 -0
  53. package/dist/languages/mr.js +2 -0
  54. package/dist/languages/mr.js.map +1 -0
  55. package/dist/languages/ms.js +2 -0
  56. package/dist/languages/ms.js.map +1 -0
  57. package/dist/languages/nb.js +2 -0
  58. package/dist/languages/nb.js.map +1 -0
  59. package/dist/languages/nl.js +2 -0
  60. package/dist/languages/nl.js.map +1 -0
  61. package/dist/languages/pa-Guru.js +2 -0
  62. package/dist/languages/pa-Guru.js.map +1 -0
  63. package/dist/languages/pl.js +2 -0
  64. package/dist/languages/pl.js.map +1 -0
  65. package/dist/languages/pt.js +2 -0
  66. package/dist/languages/pt.js.map +1 -0
  67. package/dist/languages/ro.js +2 -0
  68. package/dist/languages/ro.js.map +1 -0
  69. package/dist/languages/ru.js +2 -0
  70. package/dist/languages/ru.js.map +1 -0
  71. package/dist/languages/sr-Latn.js +2 -0
  72. package/dist/languages/sr-Latn.js.map +1 -0
  73. package/dist/languages/sv.js +2 -0
  74. package/dist/languages/sv.js.map +1 -0
  75. package/dist/languages/sw.js +2 -0
  76. package/dist/languages/sw.js.map +1 -0
  77. package/dist/languages/ta.js +2 -0
  78. package/dist/languages/ta.js.map +1 -0
  79. package/dist/languages/te.js +2 -0
  80. package/dist/languages/te.js.map +1 -0
  81. package/dist/languages/th.js +2 -0
  82. package/dist/languages/th.js.map +1 -0
  83. package/dist/languages/tr.js +2 -0
  84. package/dist/languages/tr.js.map +1 -0
  85. package/dist/languages/uk.js +2 -0
  86. package/dist/languages/uk.js.map +1 -0
  87. package/dist/languages/ur.js +2 -0
  88. package/dist/languages/ur.js.map +1 -0
  89. package/dist/languages/vi.js +2 -0
  90. package/dist/languages/vi.js.map +1 -0
  91. package/dist/languages/zh-Hans.js +2 -0
  92. package/dist/languages/zh-Hans.js.map +1 -0
  93. package/dist/n2words.js +1 -1
  94. package/dist/n2words.js.map +1 -1
  95. package/lib/classes/abstract-language.js +211 -110
  96. package/lib/classes/greedy-scale-language.js +195 -0
  97. package/lib/classes/slavic-language.js +251 -0
  98. package/lib/classes/south-asian-language.js +161 -0
  99. package/lib/classes/turkic-language.js +63 -0
  100. package/lib/languages/ar.js +243 -0
  101. package/lib/languages/az.js +58 -0
  102. package/lib/languages/bn.js +126 -0
  103. package/lib/languages/cs.js +212 -0
  104. package/lib/languages/da.js +167 -0
  105. package/lib/languages/de.js +135 -0
  106. package/lib/languages/el.js +116 -0
  107. package/lib/languages/en.js +123 -0
  108. package/lib/languages/es.js +153 -0
  109. package/lib/languages/fa.js +127 -0
  110. package/lib/languages/fil.js +162 -0
  111. package/lib/languages/fr-BE.js +61 -0
  112. package/lib/languages/fr.js +145 -0
  113. package/lib/languages/gu.js +156 -0
  114. package/lib/languages/he.js +329 -0
  115. package/lib/languages/hi.js +126 -0
  116. package/lib/languages/hr.js +157 -0
  117. package/lib/languages/hu.js +155 -0
  118. package/lib/languages/id.js +174 -0
  119. package/lib/languages/it.js +148 -0
  120. package/lib/languages/ja.js +190 -0
  121. package/lib/languages/kn.js +71 -0
  122. package/lib/languages/ko.js +83 -0
  123. package/lib/languages/lt.js +171 -0
  124. package/lib/languages/lv.js +153 -0
  125. package/lib/languages/mr.js +156 -0
  126. package/lib/languages/ms.js +146 -0
  127. package/lib/languages/nb.js +120 -0
  128. package/lib/languages/nl.js +206 -0
  129. package/lib/languages/pa-Guru.js +126 -0
  130. package/lib/languages/pl.js +189 -0
  131. package/lib/languages/pt.js +147 -0
  132. package/lib/languages/ro.js +380 -0
  133. package/lib/languages/ru.js +116 -0
  134. package/lib/languages/sr-Latn.js +157 -0
  135. package/lib/languages/sv.js +127 -0
  136. package/lib/languages/sw.js +121 -0
  137. package/lib/languages/ta.js +226 -0
  138. package/lib/languages/te.js +229 -0
  139. package/lib/languages/th.js +123 -0
  140. package/lib/languages/tr.js +83 -0
  141. package/lib/{i18n → languages}/uk.js +50 -23
  142. package/lib/languages/ur.js +126 -0
  143. package/lib/languages/vi.js +193 -0
  144. package/lib/languages/zh-Hans.js +165 -0
  145. package/lib/n2words.js +246 -75
  146. package/package.json +80 -72
  147. package/typings/classes/abstract-language.d.ts +144 -0
  148. package/typings/classes/greedy-scale-language.d.ts +148 -0
  149. package/typings/classes/slavic-language.d.ts +145 -0
  150. package/typings/classes/south-asian-language.d.ts +101 -0
  151. package/typings/classes/turkic-language.d.ts +42 -0
  152. package/typings/languages/ar.d.ts +93 -0
  153. package/typings/languages/az.d.ts +25 -0
  154. package/typings/languages/bn.d.ts +1 -0
  155. package/typings/languages/cs.d.ts +120 -0
  156. package/typings/languages/da.d.ts +53 -0
  157. package/typings/languages/de.d.ts +26 -0
  158. package/typings/languages/el.d.ts +11 -0
  159. package/typings/languages/en.d.ts +30 -0
  160. package/typings/languages/es.d.ts +43 -0
  161. package/typings/languages/fa.d.ts +81 -0
  162. package/typings/languages/fil.d.ts +12 -0
  163. package/typings/languages/fr-BE.d.ts +41 -0
  164. package/typings/languages/fr.d.ts +43 -0
  165. package/typings/languages/gu.d.ts +12 -0
  166. package/typings/languages/he.d.ts +197 -0
  167. package/typings/languages/hi.d.ts +1 -0
  168. package/typings/languages/hr.d.ts +110 -0
  169. package/typings/languages/hu.d.ts +37 -0
  170. package/typings/languages/id.d.ts +69 -0
  171. package/typings/languages/it.d.ts +51 -0
  172. package/typings/languages/ja.d.ts +58 -0
  173. package/typings/languages/kn.d.ts +11 -0
  174. package/typings/languages/ko.d.ts +25 -0
  175. package/typings/languages/lt.d.ts +110 -0
  176. package/typings/languages/lv.d.ts +99 -0
  177. package/typings/languages/mr.d.ts +12 -0
  178. package/typings/languages/ms.d.ts +37 -0
  179. package/typings/languages/nb.d.ts +27 -0
  180. package/typings/languages/nl.d.ts +65 -0
  181. package/typings/languages/pa-Guru.d.ts +1 -0
  182. package/typings/languages/pl.d.ts +116 -0
  183. package/typings/languages/pt.d.ts +39 -0
  184. package/typings/languages/ro.d.ts +229 -0
  185. package/typings/languages/ru.d.ts +108 -0
  186. package/typings/languages/sr-Latn.d.ts +98 -0
  187. package/typings/languages/sv.d.ts +30 -0
  188. package/typings/languages/sw.d.ts +1 -0
  189. package/typings/languages/ta.d.ts +1 -0
  190. package/typings/languages/te.d.ts +1 -0
  191. package/typings/languages/th.d.ts +1 -0
  192. package/typings/languages/tr.d.ts +46 -0
  193. package/typings/languages/uk.d.ts +117 -0
  194. package/typings/languages/ur.d.ts +1 -0
  195. package/typings/languages/vi.d.ts +116 -0
  196. package/typings/languages/zh-Hans.d.ts +57 -0
  197. package/typings/n2words.d.ts +177 -0
  198. package/dist/ar.js +0 -2
  199. package/dist/ar.js.map +0 -1
  200. package/dist/az.js +0 -2
  201. package/dist/az.js.map +0 -1
  202. package/dist/cz.js +0 -2
  203. package/dist/cz.js.map +0 -1
  204. package/dist/de.js +0 -2
  205. package/dist/de.js.map +0 -1
  206. package/dist/dk.js +0 -2
  207. package/dist/dk.js.map +0 -1
  208. package/dist/en.js +0 -2
  209. package/dist/en.js.map +0 -1
  210. package/dist/es.js +0 -2
  211. package/dist/es.js.map +0 -1
  212. package/dist/fa.js +0 -2
  213. package/dist/fa.js.map +0 -1
  214. package/dist/fr-BE.js +0 -2
  215. package/dist/fr-BE.js.map +0 -1
  216. package/dist/fr.js +0 -2
  217. package/dist/fr.js.map +0 -1
  218. package/dist/he.js +0 -2
  219. package/dist/he.js.map +0 -1
  220. package/dist/hr.js +0 -2
  221. package/dist/hr.js.map +0 -1
  222. package/dist/hu.js +0 -2
  223. package/dist/hu.js.map +0 -1
  224. package/dist/id.js +0 -2
  225. package/dist/id.js.map +0 -1
  226. package/dist/it.js +0 -2
  227. package/dist/it.js.map +0 -1
  228. package/dist/ko.js +0 -2
  229. package/dist/ko.js.map +0 -1
  230. package/dist/lt.js +0 -2
  231. package/dist/lt.js.map +0 -1
  232. package/dist/lv.js +0 -2
  233. package/dist/lv.js.map +0 -1
  234. package/dist/n2words.d.ts +0 -2
  235. package/dist/nl.js +0 -2
  236. package/dist/nl.js.map +0 -1
  237. package/dist/no.js +0 -2
  238. package/dist/no.js.map +0 -1
  239. package/dist/pl.js +0 -2
  240. package/dist/pl.js.map +0 -1
  241. package/dist/pt.js +0 -2
  242. package/dist/pt.js.map +0 -1
  243. package/dist/ro.js +0 -2
  244. package/dist/ro.js.map +0 -1
  245. package/dist/ru.js +0 -2
  246. package/dist/ru.js.map +0 -1
  247. package/dist/sr.js +0 -2
  248. package/dist/sr.js.map +0 -1
  249. package/dist/tr.js +0 -2
  250. package/dist/tr.js.map +0 -1
  251. package/dist/uk.js +0 -2
  252. package/dist/uk.js.map +0 -1
  253. package/dist/vi.js +0 -2
  254. package/dist/vi.js.map +0 -1
  255. package/dist/zh.js +0 -2
  256. package/dist/zh.js.map +0 -1
  257. package/lib/classes/abstract-language.d.ts +0 -54
  258. package/lib/classes/base-language.d.ts +0 -58
  259. package/lib/classes/base-language.js +0 -172
  260. package/lib/i18n/ar.d.ts +0 -41
  261. package/lib/i18n/ar.js +0 -209
  262. package/lib/i18n/az.d.ts +0 -15
  263. package/lib/i18n/az.js +0 -66
  264. package/lib/i18n/cz.d.ts +0 -68
  265. package/lib/i18n/cz.js +0 -135
  266. package/lib/i18n/de.d.ts +0 -17
  267. package/lib/i18n/de.js +0 -103
  268. package/lib/i18n/dk.d.ts +0 -14
  269. package/lib/i18n/dk.js +0 -110
  270. package/lib/i18n/en.d.ts +0 -22
  271. package/lib/i18n/en.js +0 -86
  272. package/lib/i18n/es.d.ts +0 -16
  273. package/lib/i18n/es.js +0 -110
  274. package/lib/i18n/fa.d.ts +0 -54
  275. package/lib/i18n/fa.js +0 -106
  276. package/lib/i18n/fr-BE.d.ts +0 -11
  277. package/lib/i18n/fr-BE.js +0 -20
  278. package/lib/i18n/fr.d.ts +0 -15
  279. package/lib/i18n/fr.js +0 -99
  280. package/lib/i18n/he.d.ts +0 -61
  281. package/lib/i18n/he.js +0 -132
  282. package/lib/i18n/hr.d.ts +0 -68
  283. package/lib/i18n/hr.js +0 -129
  284. package/lib/i18n/hu.d.ts +0 -17
  285. package/lib/i18n/hu.js +0 -135
  286. package/lib/i18n/id.d.ts +0 -43
  287. package/lib/i18n/id.js +0 -156
  288. package/lib/i18n/it.d.ts +0 -29
  289. package/lib/i18n/it.js +0 -137
  290. package/lib/i18n/ko.d.ts +0 -15
  291. package/lib/i18n/ko.js +0 -56
  292. package/lib/i18n/lt.d.ts +0 -68
  293. package/lib/i18n/lt.js +0 -138
  294. package/lib/i18n/lv.d.ts +0 -57
  295. package/lib/i18n/lv.js +0 -120
  296. package/lib/i18n/nl.d.ts +0 -20
  297. package/lib/i18n/nl.js +0 -125
  298. package/lib/i18n/no.d.ts +0 -15
  299. package/lib/i18n/no.js +0 -77
  300. package/lib/i18n/pl.d.ts +0 -67
  301. package/lib/i18n/pl.js +0 -126
  302. package/lib/i18n/pt.d.ts +0 -26
  303. package/lib/i18n/pt.js +0 -118
  304. package/lib/i18n/ro.d.ts +0 -109
  305. package/lib/i18n/ro.js +0 -360
  306. package/lib/i18n/ru.d.ts +0 -30
  307. package/lib/i18n/ru.js +0 -198
  308. package/lib/i18n/sr.d.ts +0 -56
  309. package/lib/i18n/sr.js +0 -127
  310. package/lib/i18n/tr.d.ts +0 -15
  311. package/lib/i18n/tr.js +0 -64
  312. package/lib/i18n/uk.d.ts +0 -78
  313. package/lib/i18n/vi.d.ts +0 -70
  314. package/lib/i18n/vi.js +0 -151
  315. package/lib/i18n/zh.d.ts +0 -18
  316. package/lib/i18n/zh.js +0 -78
  317. package/lib/n2words.d.ts +0 -9
@@ -0,0 +1,243 @@
1
+ import AbstractLanguage from '../classes/abstract-language.js'
2
+
3
+ /**
4
+ * @typedef {Object} ArabicOptions
5
+ * @property {string} [negativeWord='ناقص'] Word for negative numbers (minus).
6
+ * @property {boolean} [feminine=false] Use feminine forms for numbers.
7
+ */
8
+
9
+ /**
10
+ * Arabic language converter.
11
+ *
12
+ * Converts numbers to Arabic words with full grammatical support:
13
+ * - Gender agreement (masculine/feminine forms)
14
+ * - Complex pluralization rules (singular, dual, plural forms)
15
+ * - Special handling for hundreds, thousands, millions, etc.
16
+ * - Right-to-left text orientation
17
+ * - Traditional Arabic number naming conventions
18
+ *
19
+ * Key Features:
20
+ * - Gender-aware number forms (واحد masculine vs واحدة feminine)
21
+ * - Dual forms: اثنان/اثنتان (two masculine/feminine)
22
+ * - Complex rule system for numbers 3-10 (requiring feminine when referring to countables)
23
+ * - Group-based algorithm: splits number by powers of 1000 (ones, thousands, millions, billions)
24
+ * - Tanween (nunation) for indefinite numbers
25
+ * - Sophisticated pluralization with singular, dual, and plural forms
26
+ *
27
+ * Algorithm:
28
+ * 1. Break number into groups of 3 digits (right to left)
29
+ * 2. For each non-zero group, convert to words using gender and plural rules
30
+ * 3. Append the appropriate magnitude word (ألف/مليون/مليار) with proper plural form
31
+ * 4. Join all groups with spaces
32
+ *
33
+ * Features:
34
+ * - Support for gender-aware number forms
35
+ * - Proper handling of Arabic dual forms (اثنان/اثنتان)
36
+ * - Complex group processing for large numbers
37
+ * - Right-to-left text orientation
38
+ * - Traditional Arabic number naming conventions
39
+ */
40
+ export class Arabic extends AbstractLanguage {
41
+ decimalSeparatorWord = 'فاصلة'
42
+ zeroWord = 'صفر'
43
+ arabicTens = ['عشرون', 'ثلاثون', 'أربعون', 'خمسون', 'ستون', 'سبعون', 'ثمانون', 'تسعون']
44
+ arabicHundreds = ['', 'مائة', 'مئتان', 'ثلاثمائة', 'أربعمائة', 'خمسمائة', 'ستمائة', 'سبعمائة', 'ثمانمائة', 'تسعمائة']
45
+ arabicAppendedTwos = ['مئتا', 'ألفا', 'مليونا', 'مليارا', 'تريليونا', 'كوادريليونا', 'كوينتليونا', 'سكستيليونا']
46
+ arabicTwos = ['مئتان', 'ألفان', 'مليونان', 'ملياران', 'تريليونان', 'كوادريليونان', 'كوينتليونان', 'سكستيليونان']
47
+ arabicGroup = ['مائة', 'ألف', 'مليون', 'مليار', 'تريليون', 'كوادريليون', 'كوينتليون', 'سكستيليون']
48
+ arabicAppendedGroup = ['', 'ألفاً', 'مليوناً', 'ملياراً', 'تريليوناً', 'كوادريليوناً', 'كوينتليوناً', 'سكستيليوناً']
49
+ arabicPluralGroups = ['', 'آلاف', 'ملايين', 'مليارات', 'تريليونات', 'كوادريليونات', 'كوينتليونات', 'سكستيليونات']
50
+ ones = {
51
+ masculine: [
52
+ 'واحد',
53
+ 'اثنان',
54
+ 'ثلاثة',
55
+ 'أربعة',
56
+ 'خمسة',
57
+ 'ستة',
58
+ 'سبعة',
59
+ 'ثمانية',
60
+ 'تسعة',
61
+ 'عشرة',
62
+ 'أحد عشر',
63
+ 'اثنا عشر',
64
+ 'ثلاثة عشر',
65
+ 'أربعة عشر',
66
+ 'خمسة عشر',
67
+ 'ستة عشر',
68
+ 'سبعة عشر',
69
+ 'ثمانية عشر',
70
+ 'تسعة عشر'
71
+ ],
72
+ feminine: [
73
+ 'واحدة',
74
+ 'اثنتان',
75
+ 'ثلاث',
76
+ 'أربع',
77
+ 'خمس',
78
+ 'ست',
79
+ 'سبع',
80
+ 'ثمان',
81
+ 'تسع',
82
+ 'عشر',
83
+ 'إحدى عشرة',
84
+ 'اثنتا عشرة',
85
+ 'ثلاث عشرة',
86
+ 'أربع عشرة',
87
+ 'خمس عشرة',
88
+ 'ست عشرة',
89
+ 'سبع عشرة',
90
+ 'ثماني عشرة',
91
+ 'تسع عشرة'
92
+ ]
93
+ }
94
+
95
+ /**
96
+ * Initializes the Arabic converter with language-specific options.
97
+ *
98
+ * @param {ArabicOptions} [options={}] Configuration options.
99
+ */
100
+ constructor ({ negativeWord = 'ناقص', feminine = false } = {}) {
101
+ super()
102
+
103
+ this.feminine = feminine
104
+ this.negativeWord = negativeWord
105
+
106
+ this.selectedOnes = this.ones[this.feminine ? 'feminine' : 'masculine']
107
+ }
108
+
109
+ /**
110
+ * Returns the feminine status of a given digit (1-9).
111
+ *
112
+ * @param {number} digit - The digit to check (1-9).
113
+ * @returns {string} The word form of the digit based on feminine status.
114
+ */
115
+ digitFeminineStatus (digit) {
116
+ return this.selectedOnes[digit - 1]
117
+ }
118
+
119
+ /**
120
+ * Processes the Arabic group number and returns the corresponding Arabic representation.
121
+ * @param {number} groupNumber - The number to process. (Range: 1-999)
122
+ * @param {number} groupLevel - Group level to process. (See example)
123
+ * @returns {string} The Arabic representation of the group number.
124
+ * @example 12345678 is processed in blocks of 3: '678' (group 0), '345' (group 1), '12' (group 2).
125
+ */
126
+ processArabicGroup (groupNumber, groupLevel, fullNumber) {
127
+ const tens = groupNumber % 100
128
+ const hundredsRaw = groupNumber / 100
129
+ const hundreds = Math.trunc(hundredsRaw)
130
+ let returnValue = ''
131
+
132
+ // Process hundreds
133
+ if (hundreds > 0) {
134
+ if (tens === 0 && hundreds === 2) {
135
+ returnValue = this.arabicTwos[0]
136
+ } else {
137
+ const hundredsWord = this.arabicHundreds[hundreds]
138
+ if (hundredsWord) {
139
+ returnValue = hundredsWord
140
+ if (tens !== 0) {
141
+ returnValue += ' و'
142
+ }
143
+ }
144
+ }
145
+ }
146
+
147
+ // Process tens and ones
148
+ if (tens > 0) {
149
+ if (tens < 20) { // 1 -> 19
150
+ if (tens === 2 && hundreds === 0 && groupLevel > 0) {
151
+ // Cache expensive log10 calculation
152
+ const numValue = Number(fullNumber)
153
+ const pow = Math.trunc(Math.log10(numValue))
154
+ if (pow % 3 === 0 && fullNumber === BigInt(2 * Math.pow(10, pow))) {
155
+ returnValue = groupNumber === 2 ? this.arabicTwos[groupLevel] : this.arabicAppendedTwos[groupLevel]
156
+ } else {
157
+ returnValue = this.arabicTwos[groupLevel]
158
+ }
159
+ } else if (tens === 1 && groupLevel > 0) {
160
+ returnValue += this.arabicGroup[groupLevel]
161
+ } else {
162
+ returnValue += this.selectedOnes[tens - 1]
163
+ }
164
+ } else { // 20 -> 99
165
+ const ones = tens % 10
166
+ const tensIndex = Math.trunc(tens / 10) - 2
167
+
168
+ if (ones > 0) {
169
+ returnValue += this.selectedOnes[ones - 1]
170
+ returnValue += ' و'
171
+ }
172
+ returnValue += this.arabicTens[tensIndex]
173
+ }
174
+ }
175
+
176
+ return returnValue
177
+ }
178
+
179
+ /**
180
+ * Converts a number to its cardinal representation in Arabic.
181
+ * It process by blocks of 3 digits.
182
+ * @param {bigint} number - The number to convert.
183
+ * @returns {string} The cardinal representation of the number in Arabic.
184
+ */
185
+ convertWholePart (number) {
186
+ if (number === 0n) {
187
+ return this.zeroWord
188
+ }
189
+ let temp = number
190
+ let group = 0
191
+ let result = ''
192
+
193
+ // Process each group of 3 digits (right to left)
194
+ while (temp > 0n) {
195
+ const numberToProcess = Number(temp % 1000n)
196
+ temp = temp / 1000n
197
+
198
+ if (numberToProcess > 0) {
199
+ const groupDescription = this.processArabicGroup(numberToProcess, group, number)
200
+
201
+ if (groupDescription) {
202
+ // Add group name for thousands, millions, etc.
203
+ if (group > 0) {
204
+ if (result) {
205
+ result = ' و' + result
206
+ }
207
+
208
+ if (numberToProcess > 2) {
209
+ const remainder = numberToProcess % 100
210
+ if (remainder === 1) {
211
+ result = this.arabicGroup[group] + ' ' + result
212
+ } else if (numberToProcess >= 3 && numberToProcess <= 10) {
213
+ result = this.arabicPluralGroups[group] + ' ' + result
214
+ } else {
215
+ result = (result ? this.arabicAppendedGroup[group] : this.arabicGroup[group]) + ' ' + result
216
+ }
217
+ }
218
+ }
219
+
220
+ // Add group description (prepend for RTL)
221
+ result = groupDescription + ' ' + result
222
+ }
223
+ }
224
+
225
+ group++
226
+ }
227
+
228
+ return result.replace(/\s+/g, ' ').trim()
229
+ }
230
+ }
231
+
232
+ /**
233
+ * Converts a number to Arabic cardinal (written) form.
234
+ *
235
+ * @param {number|string|bigint} value The number to convert.
236
+ * @param {ArabicOptions} [options={}] Configuration options.
237
+ * @returns {string} The number expressed in Arabic words.
238
+ * @throws {TypeError} If value is NaN or invalid type.
239
+ * @throws {Error} If value is an invalid number string.
240
+ */
241
+ export default function convertToWords (value, options = {}) {
242
+ return new Arabic(options).convertToWords(value)
243
+ }
@@ -0,0 +1,58 @@
1
+ import TurkicLanguage from '../classes/turkic-language.js'
2
+
3
+ /**
4
+ * Azerbaijani language converter.
5
+ *
6
+ * Inherits from TurkicLanguage shared patterns:
7
+ * - Space-separated number combinations
8
+ * - Omits '1' before hundreds and thousands
9
+ * - Supports flexible word spacing configuration
10
+ */
11
+ export class Azerbaijani extends TurkicLanguage {
12
+ negativeWord = 'mənfi'
13
+ decimalSeparatorWord = 'nöqtə'
14
+ zeroWord = 'sıfır'
15
+ scaleWordPairs = [[1_000_000_000_000_000_000n, 'kentilyon'],
16
+ [1_000_000_000_000_000n, 'katrilyon'],
17
+ [1_000_000_000_000n, 'trilyon'],
18
+ [1_000_000_000n, 'milyar'],
19
+ [1_000_000n, 'milyon'],
20
+ [1000n, 'min'],
21
+ [100n, 'yüz'],
22
+ [90n, 'doxsan'],
23
+ [80n, 'səksən'],
24
+ [70n, 'yetmiş'],
25
+ [60n, 'altmış'],
26
+ [50n, 'əlli'],
27
+ [40n, 'qırx'],
28
+ [30n, 'otuz'],
29
+ [20n, 'iyirmi'],
30
+ [10n, 'on'],
31
+ [9n, 'doqquz'],
32
+ [8n, 'səkkiz'],
33
+ [7n, 'yeddi'],
34
+ [6n, 'altı'],
35
+ [5n, 'beş'],
36
+ [4n, 'dörd'],
37
+ [3n, 'üç'],
38
+ [2n, 'iki'],
39
+ [1n, 'bir'],
40
+ [0n, 'sıfır']]
41
+ }
42
+
43
+ /**
44
+ * Converts a number to Azerbaijani cardinal (written) form.
45
+ *
46
+ * @param {number|string|bigint} value The number to convert.
47
+ * @param {Object} [options] Conversion options (see AZ class).
48
+ * @returns {string} The number expressed in Azerbaijani words.
49
+ * @throws {TypeError} If value is NaN or invalid type.
50
+ * @throws {Error} If value is an invalid number string.
51
+ *
52
+ * @example
53
+ * convertToWords(42, { lang: 'az' }); // 'qırx iki'
54
+ * convertToWords(1000, { lang: 'az' }); // 'min'
55
+ */
56
+ export default function convertToWords (value, options = {}) {
57
+ return new Azerbaijani(options).convertToWords(value)
58
+ }
@@ -0,0 +1,126 @@
1
+ import SouthAsianLanguage from '../classes/south-asian-language.js'
2
+
3
+ class Bengali extends SouthAsianLanguage {
4
+ negativeWord = 'মাইনাস'
5
+ decimalSeparatorWord = 'দশমিক'
6
+ zeroWord = 'শূন্য'
7
+ hundredWord = 'শত'
8
+ belowHundred = [
9
+ 'শূন্য',
10
+ 'এক',
11
+ 'দুই',
12
+ 'তিন',
13
+ 'চার',
14
+ 'পাঁচ',
15
+ 'ছয়',
16
+ 'সাত',
17
+ 'আট',
18
+ 'নয়',
19
+ 'দশ',
20
+ 'এগারো',
21
+ 'বারো',
22
+ 'তেরো',
23
+ 'চৌদ্দ',
24
+ 'পনেরো',
25
+ 'ষোল',
26
+ 'সতেরো',
27
+ 'আঠারো',
28
+ 'উনিশ',
29
+ 'বিশ',
30
+ 'একুশ',
31
+ 'বাইশ',
32
+ 'তেইশ',
33
+ 'চব্বিশ',
34
+ 'পঁচিশ',
35
+ 'ছাব্বিশ',
36
+ 'সাতাশ',
37
+ 'আঠাশ',
38
+ 'উনত্রিশ',
39
+ 'ত্রিশ',
40
+ 'একত্রিশ',
41
+ 'বত্রিশ',
42
+ 'তেত্রিশ',
43
+ 'চৌত্রিশ',
44
+ 'পঁয়ত্রিশ',
45
+ 'ছত্রিশ',
46
+ 'সাঁইত্রিশ',
47
+ 'আটত্রিশ',
48
+ 'উনচল্লিশ',
49
+ 'চল্লিশ',
50
+ 'একচল্লিশ',
51
+ 'বেয়াল্লিশ',
52
+ 'তেতাল্লিশ',
53
+ 'চুয়াল্লিশ',
54
+ 'পঁয়তাল্লিশ',
55
+ 'ছেচল্লিশ',
56
+ 'সাতচল্লিশ',
57
+ 'আটচল্লিশ',
58
+ 'উনপঞ্চাশ',
59
+ 'পঞ্চাশ',
60
+ 'একান্ন',
61
+ 'বাহান্ন',
62
+ 'তিপ্পান্ন',
63
+ 'চুয়ান্ন',
64
+ 'পঞ্চান্ন',
65
+ 'ছাপ্পান্ন',
66
+ 'সাতান্ন',
67
+ 'আটান্ন',
68
+ 'উনষাট',
69
+ 'ষাট',
70
+ 'একষট্টি',
71
+ 'বাষট্টি',
72
+ 'তেষট্টি',
73
+ 'চৌষট্টি',
74
+ 'পঁয়ষট্টি',
75
+ 'ছেষট্টি',
76
+ 'সাতষট্টি',
77
+ 'আটষট্টি',
78
+ 'ঊনসত্তর',
79
+ 'সত্তর',
80
+ 'একাত্তর',
81
+ 'বাহাত্তর',
82
+ 'তেহাত্তর',
83
+ 'চুয়াত্তর',
84
+ 'পঁচাত্তর',
85
+ 'ছিয়াত্তর',
86
+ 'সাতাত্তর',
87
+ 'আটাত্তর',
88
+ 'উনআশি',
89
+ 'আশি',
90
+ 'একাশি',
91
+ 'বিরাশি',
92
+ 'তিরাশি',
93
+ 'চুরাশি',
94
+ 'পঁচাশি',
95
+ 'ছিয়াশি',
96
+ 'সাতাশি',
97
+ 'আটাশি',
98
+ 'উননব্বই',
99
+ 'নব্বই',
100
+ 'একানব্বই',
101
+ 'বিরানব্বই',
102
+ 'তিরানব্বই',
103
+ 'চুরানব্বই',
104
+ 'পঁচানব্বই',
105
+ 'ছিয়ানব্বই',
106
+ 'সাতানব্বই',
107
+ 'আটানব্বই',
108
+ 'নিরানব্বই'
109
+ ]
110
+
111
+ scaleWords = [
112
+ '',
113
+ 'হাজার',
114
+ 'লাখ',
115
+ 'কোটি',
116
+ 'আরব',
117
+ 'খরব',
118
+ 'নীল',
119
+ 'পদ্ম',
120
+ 'শঙ্খ'
121
+ ]
122
+ }
123
+
124
+ export default function convertToWords (value, options = {}) {
125
+ return new Bengali(options).convertToWords(value)
126
+ }
@@ -0,0 +1,212 @@
1
+ import SlavicLanguage from '../classes/slavic-language.js'
2
+
3
+ /**
4
+ * @typedef {Object} SlavicOptions
5
+ * @property {boolean} [feminine=false] Use feminine forms for numbers.
6
+ */
7
+
8
+ /**
9
+ * Czech language converter.
10
+ *
11
+ * Implements Czech number words using the Slavic language pattern:
12
+ * - Czech number words (jedna, dva, tři, čtyři, pět...)
13
+ * - Slavic three-form pluralization (tisíc/tisíce/tisíc)
14
+ * - Gender agreement for numbers 1-2
15
+ * - Czech-specific number word endings
16
+ *
17
+ * Key Features:
18
+ * - Three-form pluralization system shared across Slavic languages
19
+ * * Form 1 (singular): 1 (e.g., "tisíc")
20
+ * * Form 2 (few): 2-4, 22-24, 32-34... excluding teens (e.g., "tisíce")
21
+ * * Form 3 (many): all other numbers (e.g., "tisíc")
22
+ * - Chunk-based decomposition (splits into groups of 3 digits: ones, thousands, millions, etc.)
23
+ * - Large number handling via thousands[] array with indexed [singular, few, many] forms
24
+ *
25
+ * Inherits from SlavicLanguage:
26
+ * - Complex pluralization rules (one/few/many forms)
27
+ * - Group-based large number handling (chunk decomposition via splitByX())
28
+ * - Proper declension patterns via pluralize() method
29
+ */
30
+ export class Czech extends SlavicLanguage {
31
+ negativeWord = 'mínus'
32
+ zeroWord = 'nula'
33
+ ones = {
34
+ 1: 'jedna',
35
+ 2: 'dva',
36
+ 3: 'tři',
37
+ 4: 'čtyři',
38
+ 5: 'pět',
39
+ 6: 'šest',
40
+ 7: 'sedm',
41
+ 8: 'osm',
42
+ 9: 'devět'
43
+ }
44
+
45
+ tens = {
46
+ 0: 'deset',
47
+ 1: 'jedenáct',
48
+ 2: 'dvanáct',
49
+ 3: 'třináct',
50
+ 4: 'čtrnáct',
51
+ 5: 'patnáct',
52
+ 6: 'šestnáct',
53
+ 7: 'sedmnáct',
54
+ 8: 'osmnáct',
55
+ 9: 'devatenáct'
56
+ }
57
+
58
+ twenties = {
59
+ 2: 'dvacet',
60
+ 3: 'třicet',
61
+ 4: 'čtyřicet',
62
+ 5: 'padesát',
63
+ 6: 'šedesát',
64
+ 7: 'sedmdesát',
65
+ 8: 'osmdesát',
66
+ 9: 'devadesát'
67
+ }
68
+
69
+ hundreds = {
70
+ 1: 'sto',
71
+ 2: 'dvě stě',
72
+ 3: 'tři sta',
73
+ 4: 'čtyři sta',
74
+ 5: 'pět set',
75
+ 6: 'šest set',
76
+ 7: 'sedm set',
77
+ 8: 'osm set',
78
+ 9: 'devět set'
79
+ }
80
+
81
+ thousands = {
82
+ 1: ['tisíc', 'tisíce', 'tisíc'], // 10^ 3
83
+ 2: ['milion', 'miliony', 'milionů'], // 10^ 6
84
+ 3: ['miliarda', 'miliardy', 'miliard'], // 10^ 9
85
+ 4: ['bilion', 'biliony', 'bilionů'], // 10^ 12
86
+ 5: ['biliarda', 'biliardy', 'biliard'], // 10^ 15
87
+ 6: ['trilion', 'triliony', 'trilionů'], // 10^ 18
88
+ 7: ['triliarda', 'triliardy', 'triliard'], // 10^ 21
89
+ 8: ['kvadrilion', 'kvadriliony', 'kvadrilionů'], // 10^ 24
90
+ 9: ['kvadriliarda', 'kvadriliardy', 'kvadriliard'], // 10^ 27
91
+ 10: ['quintillion', 'quintilliony', 'quintillionů'] // 10^ 30
92
+ }
93
+
94
+ /**
95
+ * Returns the Czech word for the decimal separator based on the whole number.
96
+ *
97
+ * @returns {string} The Czech word for the decimal separator.
98
+ */
99
+ get decimalSeparatorWord () {
100
+ if (this.cachedWholeNumber === 0n || this.cachedWholeNumber === 1n) {
101
+ return 'celá'
102
+ } else if (this.cachedWholeNumber >= 2n && this.cachedWholeNumber <= 4n) {
103
+ return 'celé'
104
+ } else {
105
+ return 'celých'
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Initializes the Czech converter with language-specific options.
111
+ *
112
+ * @param {SlavicOptions} [options={}] Configuration options (inherited from SlavicLanguage).
113
+ */
114
+ constructor (options = {}) {
115
+ super(options)
116
+
117
+ // Remove the inherited decimalSeparatorWord property so our getter works
118
+ delete this.decimalSeparatorWord
119
+ }
120
+
121
+ /**
122
+ * Implements Czech-specific three-form pluralization rules.
123
+ *
124
+ * Czech three-form system:
125
+ * - Form 1 (singular): exactly n=1 (e.g., "tisíc")
126
+ * - Form 2 (few): n ends in 2-4, excluding teens (22-24, 32-34...) (e.g., "tisíce")
127
+ * - Form 3 (many): all other numbers (e.g., "tisíc" for 0, 5+)
128
+ *
129
+ * @param {bigint} n The number to classify.
130
+ * @param {Array<string>} forms Array of [singular, few, many] word forms.
131
+ * @returns {string} The appropriate form for the number n.
132
+ */
133
+ pluralize (n, forms) {
134
+ if (n === 1n) {
135
+ return forms[0]
136
+ }
137
+
138
+ // Check if n is in the "few" form range (2-4, 22-24, 32-34, etc., excluding teens)
139
+ const lastDigit = n % 10n
140
+ const lastTwoDigits = n % 100n
141
+
142
+ if (lastDigit > 1n && lastDigit < 5n && (lastTwoDigits < 10n || lastTwoDigits > 20n)) {
143
+ return forms[1]
144
+ }
145
+
146
+ return forms[2]
147
+ }
148
+
149
+ /**
150
+ * Converts a whole number to Czech cardinal form.
151
+ *
152
+ * Algorithm (chunk-based decomposition):
153
+ * 1. Split number into chunks of 3 digits (right-to-left): ones, thousands, millions, etc.
154
+ * 2. For each non-zero chunk:
155
+ * a. Extract hundreds digit (n3), tens digit (n2), ones digit (n1)
156
+ * b. Add hundreds word (e.g., "sto", "dvě stě")
157
+ * c. Add tens/ones (handles teens 10-19 separately from compound tens)
158
+ * d. Add pluralized magnitude word (e.g., "tisíce", "miliony")
159
+ * 3. Join all words with spaces
160
+ *
161
+ * Example: 1234 → chunks [1, 234] → "jeden tisíc dvěstěčtyřicet"
162
+ * - Chunk 1 (index=1, thousands): "jeden" + "tisíc"
163
+ * - Chunk 234 (index=0): "dvě stě" + "třicet" + "čtyři"
164
+ *
165
+ * Special case: When chunk=1 at thousands+ levels, omit the ones word to avoid
166
+ * redundancy with the pluralized magnitude (e.g., just "tisíc" not "jeden tisíc").
167
+ *
168
+ * @param {bigint} number The whole number to convert.
169
+ * @returns {string} The number expressed in Czech words.
170
+ */
171
+ convertWholePart (number) {
172
+ if (number === 0n) {
173
+ return this.zeroWord
174
+ }
175
+ const words = []
176
+ const chunks = this.splitByX(number.toString(), 3)
177
+ let index = chunks.length
178
+ for (const x of chunks) {
179
+ index--
180
+ if (x === 0n) continue
181
+ const [n1, n2, n3] = this.getDigits(x)
182
+ if (n3 > 0n) {
183
+ words.push(this.hundreds[n3])
184
+ }
185
+ if (n2 > 1n) {
186
+ words.push(this.twenties[n2])
187
+ }
188
+ if (n2 === 1n) {
189
+ words.push(this.tens[n1])
190
+ } else if (n1 > 0n && !(index > 0 && x === 1n)) {
191
+ words.push(this.ones[n1])
192
+ }
193
+ if (index > 0) {
194
+ words.push(this.pluralize(x, this.thousands[index]))
195
+ }
196
+ }
197
+ return words.join(' ')
198
+ }
199
+ }
200
+
201
+ /**
202
+ * Converts a number to Czech cardinal (written) form.
203
+ *
204
+ * @param {number|string|bigint} value The number to convert.
205
+ * @param {SlavicOptions} [options={}] Configuration options.
206
+ * @returns {string} The number expressed in Czech words.
207
+ * @throws {TypeError} If value is NaN or invalid type.
208
+ * @throws {Error} If value is an invalid number string.
209
+ */
210
+ export default function convertToWords (value, options = {}) {
211
+ return new Czech(options).convertToWords(value)
212
+ }