n2words 1.23.1 → 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 +181 -52
  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,167 @@
1
+ import GreedyScaleLanguage from '../classes/greedy-scale-language.js'
2
+
3
+ /**
4
+ * @typedef {Object} DanishOptions
5
+ * @property {boolean} [ordFlag=false] Enable ordinal number conversion.
6
+ */
7
+
8
+ /**
9
+ * Danish language converter.
10
+ *
11
+ * GreedyScaleLanguage with Danish-specific extensions:
12
+ * - Unique vigesimal (base-20) number system for 50-90
13
+ * - Special composition rules ("og" for "and" between units and tens)
14
+ * - Reverse digit order (e.g., "fem-og-tyve" = five-and-twenty = 25)
15
+ * - Support for ordinal numbers via ordFlag option
16
+ *
17
+ * Key Features:
18
+ * - Vigesimal tens: halvtreds (50), treds (60), halvfjerds (70), firs (80), halvfems (90)
19
+ * - Units-before-tens pattern (e.g., "tre-og-tyve" = 23)
20
+ * - "et" prefix for hundreds/thousands (not "en")
21
+ * - Optional ordinal number conversion via ordFlag option
22
+ * - Inline merge logic tailored for Danish ordering
23
+ */
24
+ export class Danish extends GreedyScaleLanguage {
25
+ negativeWord = 'minus'
26
+ decimalSeparatorWord = 'komma'
27
+ zeroWord = 'nul'
28
+ scaleWordPairs = [
29
+ [1_000_000_000_000_000_000_000_000_000n, 'quadrillarder'],
30
+ [1_000_000_000_000_000_000_000_000n, 'quadrillioner'],
31
+ [1_000_000_000_000_000_000_000n, 'trillarder'],
32
+ [1_000_000_000_000_000_000n, 'trillioner'],
33
+ [1_000_000_000_000_000n, 'billarder'],
34
+ [1_000_000_000_000n, 'billioner'],
35
+ [1_000_000_000n, 'millarder'],
36
+ [1_000_000n, 'millioner'],
37
+ [1000n, 'tusind'],
38
+ [100n, 'hundrede'],
39
+ [90n, 'halvfems'],
40
+ [80n, 'firs'],
41
+ [70n, 'halvfjerds'],
42
+ [60n, 'treds'],
43
+ [50n, 'halvtreds'],
44
+ [40n, 'fyrre'],
45
+ [30n, 'tredive'],
46
+ [20n, 'tyve'],
47
+ [19n, 'nitten'],
48
+ [18n, 'atten'],
49
+ [17n, 'sytten'],
50
+ [16n, 'seksten'],
51
+ [15n, 'femten'],
52
+ [14n, 'fjorten'],
53
+ [13n, 'tretten'],
54
+ [12n, 'tolv'],
55
+ [11n, 'elleve'],
56
+ [10n, 'ti'],
57
+ [9n, 'ni'],
58
+ [8n, 'otte'],
59
+ [7n, 'syv'],
60
+ [6n, 'seks'],
61
+ [5n, 'fem'],
62
+ [4n, 'fire'],
63
+ [3n, 'tre'],
64
+ [2n, 'to'],
65
+ [1n, 'et'],
66
+ [0n, 'nul']
67
+ ]
68
+
69
+ /**
70
+ * Initializes the Danish converter with language-specific options.
71
+ *
72
+ * @param {DanishOptions} [options={}] Configuration options.
73
+ */
74
+ constructor ({ ordFlag = false } = {}) {
75
+ super()
76
+
77
+ this.ordFlag = ordFlag
78
+ }
79
+
80
+ /**
81
+ * Merges two adjacent word-number pairs according to Danish grammar rules.
82
+ * Danish uses complex vigesimal (base-20) patterns and reverse digit ordering.
83
+ *
84
+ * Key Danish patterns:
85
+ * - Vigesimal tens: halvtreds(50), treds(60), halvfjerds(70), firs(80), halvfems(90)
86
+ * - Units-before-tens order with "og" (and): "tre-og-tyve" (3 and 20 = 25)
87
+ * - "et" prefix for hundreds/thousands (not "en")
88
+ * - Space separators for large magnitudes (≥ millions)
89
+ * - Ordinal support via this.ordFlag
90
+ *
91
+ * @param {Object} current The left operand as `{ word: bigint }`.
92
+ * @param {Object} next The right operand as `{ word: bigint }`.
93
+ * @returns {Object} Merged pair with combined word and resulting number (bigint).
94
+ */
95
+ mergeScales (current, next) {
96
+ let cText = Object.keys(current)[0]
97
+ let nText = Object.keys(next)[0]
98
+ const cNumber = Object.values(current)[0] // BigInt (e.g., 1n, 100n, 1000n)
99
+ const nNumber = Object.values(next)[0] // BigInt (e.g., magnitude level like 100n, 1000n)
100
+
101
+ // Prepend "et" to hundreds and thousands (not "en") for proper Danish form
102
+ if (nNumber === 100n || nNumber === 1000n) {
103
+ next = { [`et${nText}`]: nNumber }
104
+ }
105
+
106
+ // Implicit '1' handling: omit '1' before most magnitudes (except millions/ordinals)
107
+ if (cNumber === 1n) {
108
+ if (nNumber < 1_000_000n || this.ordFlag) {
109
+ return next // Just the magnitude word (e.g., "hundrede" not "en hundrede")
110
+ }
111
+ cText = 'en' // Explicit "en" (one) for millions and above
112
+ }
113
+
114
+ // Multiplication across magnitude boundaries
115
+ if (nNumber > cNumber) {
116
+ // Space for million+ (e.g., "en million", "to millioner")
117
+ if (nNumber >= 1_000_000n) {
118
+ cText += ' '
119
+ }
120
+ return { [`${cText}${nText}`]: cNumber * nNumber }
121
+ }
122
+
123
+ // Addition with separator rules:
124
+ // "og" (and) for hundreds + smaller numbers
125
+ if (cNumber >= 100n && cNumber < 1000n) {
126
+ cText += ' og '
127
+ } else if (cNumber >= 1000n && cNumber <= 100_000n) {
128
+ // Special "e og" for thousands (e.g., "tusinde og tyve")
129
+ cText += 'e og '
130
+ }
131
+
132
+ // Units-before-tens reversal (Danish vigesimal pattern):
133
+ // For small units (< 10) with tens (10-99), swap order: "tre og tyve" (25)
134
+ if (nNumber < 10n && cNumber > 10n && cNumber < 100n) {
135
+ if (nNumber === 1n) {
136
+ nText = 'en' // Convert 1 to "en" for vigesimal context
137
+ }
138
+ // Swap positions: units go after "og", tens go before
139
+ const temporary = nText
140
+ nText = cText
141
+ cText = temporary + 'og'
142
+ } else if (cNumber >= 1_000_000n) {
143
+ // Space for large magnitudes (millions+)
144
+ cText += ' '
145
+ }
146
+
147
+ return { [`${cText}${nText}`]: cNumber + nNumber }
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Converts a number to Danish cardinal (written) form.
153
+ *
154
+ * @param {number|string|bigint} value The number to convert.
155
+ * @param {Object} [options] Conversion options (see Danish class options).
156
+ * @param {boolean} [options.ordFlag=false] Enable ordinal number conversion.
157
+ * @returns {string} The number expressed in Danish words.
158
+ * @throws {TypeError} If value is NaN or invalid type.
159
+ * @throws {Error} If value is an invalid number string.
160
+ *
161
+ * @example
162
+ * convertToWords(25); // 'femogtyve' (five-and-twenty)
163
+ * convertToWords(50); // 'halvtreds' (half-third-times-twenty)
164
+ */
165
+ export default function convertToWords (value, options = {}) {
166
+ return new Danish(options).convertToWords(value)
167
+ }
@@ -0,0 +1,135 @@
1
+ import GreedyScaleLanguage from '../classes/greedy-scale-language.js'
2
+
3
+ /**
4
+ * German language converter.
5
+ *
6
+ * Handles German grammatical features:
7
+ * - "eins" vs "ein" and "eine" forms for 1
8
+ * - Compound words without separators (e.g., "einundzwanzig" = 21)
9
+ * - Million/Billion pluralization
10
+ * - Special characters (e.g., "ü", "ö", "ß")
11
+ */
12
+ export class German extends GreedyScaleLanguage {
13
+ negativeWord = 'minus'
14
+ decimalSeparatorWord = 'komma'
15
+ zeroWord = 'null'
16
+ scaleWordPairs = [
17
+ [1_000_000_000_000_000_000_000_000_000n, 'Quadrilliarde'],
18
+ [1_000_000_000_000_000_000_000_000n, 'Quadrillion'],
19
+ [1_000_000_000_000_000_000_000n, 'Trilliarde'],
20
+ [1_000_000_000_000_000_000n, 'Trillion'],
21
+ [1_000_000_000_000_000n, 'Billiarde'],
22
+ [1_000_000_000_000n, 'Billion'],
23
+ [1_000_000_000n, 'Milliarde'],
24
+ [1_000_000n, 'Million'],
25
+ [1000n, 'tausend'],
26
+ [100n, 'hundert'],
27
+ [90n, 'neunzig'],
28
+ [80n, 'achtzig'],
29
+ [70n, 'siebzig'],
30
+ [60n, 'sechzig'],
31
+ [50n, 'fünfzig'],
32
+ [40n, 'vierzig'],
33
+ [30n, 'dreißig'],
34
+ [20n, 'zwanzig'],
35
+ [19n, 'neunzehn'],
36
+ [18n, 'achtzehn'],
37
+ [17n, 'siebzehn'],
38
+ [16n, 'sechzehn'],
39
+ [15n, 'fünfzehn'],
40
+ [14n, 'vierzehn'],
41
+ [13n, 'dreizehn'],
42
+ [12n, 'zwölf'],
43
+ [11n, 'elf'],
44
+ [10n, 'zehn'],
45
+ [9n, 'neun'],
46
+ [8n, 'acht'],
47
+ [7n, 'sieben'],
48
+ [6n, 'sechs'],
49
+ [5n, 'fünf'],
50
+ [4n, 'vier'],
51
+ [3n, 'drei'],
52
+ [2n, 'zwei'],
53
+ [1n, 'eins'],
54
+ [0n, 'null']
55
+ ]
56
+
57
+ /**
58
+ * Merges two adjacent word-number pairs according to German grammar rules.
59
+ *
60
+ * German-specific rules:
61
+ * - Implicit "eins": `mergeScales({ 'eins': 1n }, { 'hundert': 100n })` → `{ 'einhundert': 100n }`
62
+ * - Compound words without separators (e.g., "einundzwanzig" = 21)
63
+ * - Special handling for forms of 1 (eins/ein/eine) depending on context
64
+ * - Pluralization of millions and higher: "Millionen", "Milliarden"
65
+ * - Reordering of tens and units: `mergeScales({ 'zwanzig': 20n }, { 'eins': 1n })` → `{ 'einundzwanzig': 21n }`
66
+ *
67
+ * @param {Object} currentPair The left operand as `{ word: BigInt }`.
68
+ * @param {Object} nextPair The right operand as `{ word: BigInt }`.
69
+ * @returns {Object} Merged pair with combined word and resulting numeric value.
70
+ *
71
+ * @example
72
+ * mergeScales({ 'eins': 1n }, { 'hundert': 100n }); // { 'einhundert': 100n }
73
+ * mergeScales({ 'zwanzig': 20n }, { 'drei': 3n }); // { 'dreiundzwanzig': 23n }
74
+ */
75
+ mergeScales (currentPair, nextPair) {
76
+ let currentWord = Object.keys(currentPair)[0]
77
+ let nextWord = Object.keys(nextPair)[0]
78
+ const currentNumber = Object.values(currentPair)[0]
79
+ const nextNumber = Object.values(nextPair)[0]
80
+
81
+ // Handle form of 1: "eins" → "ein(e)" in certain contexts
82
+ if (currentNumber === 1n) {
83
+ if (nextNumber === 100n || nextNumber === 1000n) {
84
+ return { [`ein${nextWord}`]: nextNumber }
85
+ }
86
+ if (nextNumber < 1_000_000n) {
87
+ return nextPair
88
+ }
89
+ currentWord = 'eine'
90
+ }
91
+
92
+ if (nextNumber > currentNumber) {
93
+ // Multiply: apply pluralization rules for millions
94
+ if (nextNumber >= 1_000_000n) {
95
+ if (currentNumber > 1n) {
96
+ nextWord += nextWord.at(-1) === 'e' ? 'n' : 'en'
97
+ }
98
+ currentWord += ' '
99
+ }
100
+ return { [`${currentWord}${nextWord}`]: currentNumber * nextNumber }
101
+ }
102
+
103
+ // Add: handle special case of tens + units
104
+ if (nextNumber < 10n && currentNumber > 10n && currentNumber < 100n) {
105
+ // German reverses tens and units (einundzwanzig = one and twenty)
106
+ if (nextNumber === 1n) {
107
+ nextWord = 'ein'
108
+ }
109
+ const temp = nextWord
110
+ nextWord = currentWord
111
+ currentWord = `${temp}und`
112
+ } else if (currentNumber >= 1_000_000n) {
113
+ currentWord += ' '
114
+ }
115
+
116
+ return { [`${currentWord}${nextWord}`]: currentNumber + nextNumber }
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Converts a number to German cardinal (written) form.
122
+ *
123
+ * @param {number|string|bigint} value The number to convert.
124
+ * @param {Object} [options] Conversion options (see German class options).
125
+ * @returns {string} The number expressed in German words.
126
+ * @throws {TypeError} If value is NaN or invalid type.
127
+ * @throws {Error} If value is an invalid number string.
128
+ *
129
+ * @example
130
+ * convertToWords(42); // 'zweiundvierzig'
131
+ * convertToWords('1.5'); // 'eins komma fünf'
132
+ */
133
+ export default function convertToWords (value, options = {}) {
134
+ return new German(options).convertToWords(value)
135
+ }
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Converts numbers to their word representation in Greek (Ελληνικά).
3
+ * @module languages/el
4
+ */
5
+
6
+ import GreedyScaleLanguage from '../classes/greedy-scale-language.js'
7
+
8
+ /**
9
+ * Greek language implementation using scale-based number conversion.
10
+ * @extends GreedyScaleLanguage
11
+ */
12
+ class GreekLanguage extends GreedyScaleLanguage {
13
+ negativeWord = 'μείον'
14
+ decimalSeparatorWord = 'κόμμα'
15
+ zeroWord = 'μηδέν'
16
+ convertDecimalsPerDigit = true
17
+
18
+ scaleWordPairs = [
19
+ // Large numbers (limited set for now)
20
+ [1_000_000_000n, 'δισεκατομμύριο'],
21
+ [1_000_000n, 'εκατομμύριο'],
22
+ [1000n, 'χίλια'],
23
+
24
+ // Hundreds
25
+ [900n, 'εννιακόσια'],
26
+ [800n, 'οκτακόσια'],
27
+ [700n, 'επτακόσια'],
28
+ [600n, 'εξακόσια'],
29
+ [500n, 'πεντακόσια'],
30
+ [400n, 'τετρακόσια'],
31
+ [300n, 'τριακόσια'],
32
+ [200n, 'διακόσια'],
33
+ [100n, 'εκατό'],
34
+
35
+ // Tens
36
+ [90n, 'ενενήντα'],
37
+ [80n, 'ογδόντα'],
38
+ [70n, 'εβδομήντα'],
39
+ [60n, 'εξήντα'],
40
+ [50n, 'πενήντα'],
41
+ [40n, 'σαράντα'],
42
+ [30n, 'τριάντα'],
43
+ [20n, 'είκοσι'],
44
+ [19n, 'δεκαεννέα'],
45
+ [18n, 'δεκαοκτώ'],
46
+ [17n, 'δεκαεπτά'],
47
+ [16n, 'δεκαέξι'],
48
+ [15n, 'δεκαπέντε'],
49
+ [14n, 'δεκατέσσερα'],
50
+ [13n, 'δεκατρία'],
51
+ [12n, 'δώδεκα'],
52
+ [11n, 'έντεκα'],
53
+ [10n, 'δέκα'],
54
+
55
+ // Singles
56
+ [9n, 'εννέα'],
57
+ [8n, 'οκτώ'],
58
+ [7n, 'επτά'],
59
+ [6n, 'έξι'],
60
+ [5n, 'πέντε'],
61
+ [4n, 'τέσσερα'],
62
+ [3n, 'τρία'],
63
+ [2n, 'δύο'],
64
+ [1n, 'ένα'],
65
+ [0n, 'μηδέν']
66
+ ]
67
+
68
+ /**
69
+ * Merges two adjacent word-number pairs according to Greek grammar rules.
70
+ *
71
+ * Greek-specific rules:
72
+ * - Implicit "one": χίλια (thousand), not ένα χίλια
73
+ * - Space-separated composites
74
+ * - Multiplication for scales (e.g., δύο χίλια = two thousand)
75
+ *
76
+ * @param {Object} leftPair - Left word-number pair (e.g., { 'δύο': 2n })
77
+ * @param {Object} rightPair - Right word-number pair (e.g., { 'χίλια': 1000n })
78
+ * @returns {Object} Merged word-number pair
79
+ */
80
+ mergeScales (leftPair, rightPair) {
81
+ const leftWord = Object.keys(leftPair)[0]
82
+ const leftNumber = Object.values(leftPair)[0]
83
+ const rightWord = Object.keys(rightPair)[0]
84
+ const rightNumber = Object.values(rightPair)[0]
85
+
86
+ // Implicit one: omit "ένα" before any following value (> 0)
87
+ if (leftNumber === 1n) {
88
+ return rightPair
89
+ }
90
+
91
+ // No special handling needed for trailing 'ένα';
92
+ // nested merge will first collapse {1, 'ένα'} -> 'ένα'.
93
+
94
+ // Multiplication: larger right scale multiplied by left number
95
+ if (rightNumber > leftNumber) {
96
+ return { [`${leftWord} ${rightWord}`]: leftNumber * rightNumber }
97
+ }
98
+
99
+ // Addition: smaller numbers added together
100
+ return { [`${leftWord} ${rightWord}`]: leftNumber + rightNumber }
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Converts a number to its word representation in Greek.
106
+ * @param {number|string|bigint} value - The number to convert
107
+ * @param {Object} [options={}] - Conversion options
108
+ * @returns {string} The word representation of the number
109
+ * @example
110
+ * convertToWords(42) // 'σαράντα δύο'
111
+ * convertToWords(1000) // 'χίλια'
112
+ * convertToWords(2000) // 'δύο χίλια'
113
+ */
114
+ export default function convertToWords (value, options = {}) {
115
+ return new GreekLanguage(options).convertToWords(value)
116
+ }
@@ -0,0 +1,123 @@
1
+ import GreedyScaleLanguage from '../classes/greedy-scale-language.js'
2
+
3
+ /**
4
+ * English language converter.
5
+ *
6
+ * Converts numbers to English words, supporting:
7
+ * - Negative numbers (prepended with "minus")
8
+ * - Decimal numbers (word "point" between whole and fractional parts)
9
+ * - Numbers up to octillions
10
+ *
11
+ * Merge rules:
12
+ * - Hyphenated for compound tens (e.g., "twenty-three")
13
+ * - "and" after hundreds (e.g., "one hundred and one")
14
+ * - Space-separated for larger composites (e.g., "one thousand twenty-three")
15
+ */
16
+ export class English extends GreedyScaleLanguage {
17
+ negativeWord = 'minus'
18
+ decimalSeparatorWord = 'point'
19
+ zeroWord = 'zero'
20
+ scaleWordPairs = [
21
+ [1_000_000_000_000_000_000_000_000_000n, 'octillion'],
22
+ [1_000_000_000_000_000_000_000_000n, 'septillion'],
23
+ [1_000_000_000_000_000_000_000n, 'sextillion'],
24
+ [1_000_000_000_000_000_000n, 'quintillion'],
25
+ [1_000_000_000_000_000n, 'quadrillion'],
26
+ [1_000_000_000_000n, 'trillion'],
27
+ [1_000_000_000n, 'billion'],
28
+ [1_000_000n, 'million'],
29
+ [1000n, 'thousand'],
30
+ [100n, 'hundred'],
31
+ [90n, 'ninety'],
32
+ [80n, 'eighty'],
33
+ [70n, 'seventy'],
34
+ [60n, 'sixty'],
35
+ [50n, 'fifty'],
36
+ [40n, 'forty'],
37
+ [30n, 'thirty'],
38
+ [20n, 'twenty'],
39
+ [19n, 'nineteen'],
40
+ [18n, 'eighteen'],
41
+ [17n, 'seventeen'],
42
+ [16n, 'sixteen'],
43
+ [15n, 'fifteen'],
44
+ [14n, 'fourteen'],
45
+ [13n, 'thirteen'],
46
+ [12n, 'twelve'],
47
+ [11n, 'eleven'],
48
+ [10n, 'ten'],
49
+ [9n, 'nine'],
50
+ [8n, 'eight'],
51
+ [7n, 'seven'],
52
+ [6n, 'six'],
53
+ [5n, 'five'],
54
+ [4n, 'four'],
55
+ [3n, 'three'],
56
+ [2n, 'two'],
57
+ [1n, 'one'],
58
+ [0n, 'zero']
59
+ ]
60
+
61
+ /**
62
+ * Merges two adjacent word-number pairs according to English grammar rules.
63
+ *
64
+ * English-specific rules:
65
+ * - Implicit "one": `mergeScales({ 'one': 1n }, { 'hundred': 100n })` → `{ 'one hundred': 100n }`
66
+ * - Hyphenated compounds: `mergeScales({ 'twenty': 20n }, { 'three': 3n })` → `{ 'twenty-three': 23n }`
67
+ * - "and" after hundreds: `mergeScales({ 'one hundred': 100n }, { 'one': 1n })` → `{ 'one hundred and one': 101n }`
68
+ * - Multiplication: `mergeScales({ 'one': 1n }, { 'thousand': 1000n })` → `{ 'one thousand': 1000n }`
69
+ *
70
+ * @param {Object} leftPair Left word-set as `{ word: BigInt }`.
71
+ * @param {Object} rightPair Right word-set as `{ word: BigInt }`.
72
+ * @returns {Object} Merged pair with combined word and resulting numeric value.
73
+ *
74
+ * @example
75
+ * mergeScales({ 'one': 1n }, { 'hundred': 100n }); // { 'one hundred': 100n }
76
+ * mergeScales({ 'twenty': 20n }, { 'three': 3n }); // { 'twenty-three': 23n }
77
+ */
78
+ mergeScales (leftPair, rightPair) {
79
+ const leftWord = Object.keys(leftPair)[0]
80
+ const leftNumber = Object.values(leftPair)[0]
81
+ const rightWord = Object.keys(rightPair)[0]
82
+ const rightNumber = Object.values(rightPair)[0]
83
+
84
+ // Rule 1: Implicit "one" - omit when multiplying ("one hundred" → "hundred")
85
+ if (leftNumber === 1n && rightNumber < 100n) {
86
+ return { [rightWord]: rightNumber }
87
+ }
88
+
89
+ // Rule 2: Hyphenate compounds under 100 ("twenty-three")
90
+ if (leftNumber < 100n && leftNumber > rightNumber) {
91
+ return { [`${leftWord}-${rightWord}`]: leftNumber + rightNumber }
92
+ }
93
+
94
+ // Rule 3: Add "and" before units after hundreds ("one hundred and one")
95
+ if (leftNumber >= 100n && rightNumber < 100n) {
96
+ return { [`${leftWord} and ${rightWord}`]: leftNumber + rightNumber }
97
+ }
98
+
99
+ // Rule 4: Multiply when right > left ("one thousand")
100
+ if (rightNumber > leftNumber) {
101
+ return { [`${leftWord} ${rightWord}`]: leftNumber * rightNumber }
102
+ }
103
+
104
+ return { [`${leftWord} ${rightWord}`]: leftNumber + rightNumber }
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Converts a number to English cardinal (written) form.
110
+ *
111
+ * @param {number|string|bigint} value The number to convert.
112
+ * @param {Object} [options] Conversion options (see English class options).
113
+ * @returns {string} The number expressed in English words.
114
+ * @throws {TypeError} If value is NaN or invalid type.
115
+ * @throws {Error} If value is an invalid number string.
116
+ *
117
+ * @example
118
+ * convertToWords(42); // 'forty-two'
119
+ * convertToWords('1.5'); // 'one point five'
120
+ */
121
+ export default function convertToWords (value, options = {}) {
122
+ return new English(options).convertToWords(value)
123
+ }
@@ -0,0 +1,153 @@
1
+ import GreedyScaleLanguage from '../classes/greedy-scale-language.js'
2
+
3
+ /**
4
+ * @typedef {Object} SpanishOptions
5
+ * @property {string} [genderStem='o'] Masculine 'o' or feminine 'a' ending.
6
+ */
7
+
8
+ /**
9
+ * Spanish language converter.
10
+ *
11
+ * Handles Spanish grammatical features:
12
+ * - Gender agreement for numbers (masculine by default, feminine via `genderStem`)
13
+ * - "y" (and) between tens and units (e.g., "veinte y uno")
14
+ * - Special forms for hundreds (e.g., "cien", "ciento", "doscientos")
15
+ * - Million pluralization
16
+ */
17
+ export class Spanish extends GreedyScaleLanguage {
18
+ negativeWord = 'menos'
19
+ decimalSeparatorWord = 'punto'
20
+ zeroWord = 'cero'
21
+ scaleWordPairs = [
22
+ [1_000_000_000_000_000_000_000_000n, 'cuatrillón'],
23
+ [1_000_000_000_000_000_000n, 'trillón'],
24
+ [1_000_000_000_000n, 'billón'],
25
+ [1_000_000n, 'millón'],
26
+ [1000n, 'mil'],
27
+ [100n, 'cien'],
28
+ [90n, 'noventa'],
29
+ [80n, 'ochenta'],
30
+ [70n, 'setenta'],
31
+ [60n, 'sesenta'],
32
+ [50n, 'cincuenta'],
33
+ [40n, 'cuarenta'],
34
+ [30n, 'treinta'],
35
+ [29n, 'veintinueve'],
36
+ [28n, 'veintiocho'],
37
+ [27n, 'veintisiete'],
38
+ [26n, 'veintiséis'],
39
+ [25n, 'veinticinco'],
40
+ [24n, 'veinticuatro'],
41
+ [23n, 'veintitrés'],
42
+ [22n, 'veintidós'],
43
+ [21n, 'veintiuno'],
44
+ [20n, 'veinte'],
45
+ [19n, 'diecinueve'],
46
+ [18n, 'dieciocho'],
47
+ [17n, 'diecisiete'],
48
+ [16n, 'dieciseis'],
49
+ [15n, 'quince'],
50
+ [14n, 'catorce'],
51
+ [13n, 'trece'],
52
+ [12n, 'doce'],
53
+ [11n, 'once'],
54
+ [10n, 'diez'],
55
+ [9n, 'nueve'],
56
+ [8n, 'ocho'],
57
+ [7n, 'siete'],
58
+ [6n, 'seis'],
59
+ [5n, 'cinco'],
60
+ [4n, 'cuatro'],
61
+ [3n, 'tres'],
62
+ [2n, 'dos'],
63
+ [1n, 'uno'],
64
+ [0n, 'cero']
65
+ ]
66
+
67
+ /**
68
+ * Initializes the Spanish converter.
69
+ *
70
+ * @param {SpanishOptions} [options={}] Configuration options.
71
+ */
72
+ constructor ({ genderStem = 'o' } = {}) {
73
+ super()
74
+
75
+ this.genderStem = genderStem
76
+ }
77
+
78
+ /**
79
+ * Merges two adjacent word-number pairs according to Spanish grammar rules.
80
+ *
81
+ * Spanish-specific rules:
82
+ * - Implicit "uno": `mergeScales({ 'uno': 1n }, { 'mil': 1000n })` → `{ 'mil': 1000n }`
83
+ * - "y" (and) between tens and units: `mergeScales({ 'veinte': 20n }, { 'uno': 1n })` → `{ 'veinte y uno': 21n }`
84
+ * - Gender agreement for hundreds: "cien" + suffix based on genderStem
85
+ * - Special forms for hundreds (cien/ciento/quinientos/setecientos/novecientos)
86
+ * - Million pluralization when coefficient > 1: "millones" instead of "millón"
87
+ *
88
+ * @param {Object} currentPair The left operand as `{ word: BigInt }`.
89
+ * @param {Object} nextPair The right operand as `{ word: BigInt }`.
90
+ * @returns {Object} Merged pair with combined word and resulting numeric value.
91
+ *
92
+ * @example
93
+ * mergeScales({ 'uno': 1n }, { 'mil': 1000n }); // { 'mil': 1000n }
94
+ * mergeScales({ 'veinte': 20n }, { 'tres': 3n }); // { 'veinte y tres': 23n }
95
+ */
96
+ mergeScales (currentPair, nextPair) {
97
+ let currentWord = Object.keys(currentPair)[0]
98
+ let nextWord = Object.keys(nextPair)[0]
99
+ const currentNumber = Object.values(currentPair)[0]
100
+ const nextNumber = Object.values(nextPair)[0]
101
+
102
+ if (currentNumber === 1n) {
103
+ if (nextNumber < 1_000_000n) return nextPair
104
+ currentWord = 'un'
105
+ } else if (currentNumber === 100n && nextNumber % 1000n !== 0n) {
106
+ currentWord += 't' + this.genderStem
107
+ }
108
+
109
+ if (nextNumber < currentNumber) {
110
+ if (currentNumber < 100n) {
111
+ return { [`${currentWord} y ${nextWord}`]: currentNumber + nextNumber }
112
+ }
113
+ return { [`${currentWord} ${nextWord}`]: currentNumber + nextNumber }
114
+ }
115
+
116
+ if (nextNumber % 1_000_000n === 0n && currentNumber > 1n) {
117
+ nextWord = nextWord.slice(0, -3) + 'lones'
118
+ }
119
+
120
+ if (nextNumber === 100n) {
121
+ if (currentNumber === 5n) {
122
+ currentWord = 'quinien'
123
+ nextWord = ''
124
+ } else if (currentNumber === 7n) {
125
+ currentWord = 'sete'
126
+ } else if (currentNumber === 9n) {
127
+ currentWord = 'nove'
128
+ }
129
+ nextWord += 't' + this.genderStem + 's'
130
+ } else {
131
+ nextWord = ' ' + nextWord
132
+ }
133
+
134
+ return { [`${currentWord}${nextWord}`]: currentNumber * nextNumber }
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Converts a number to Spanish cardinal (written) form.
140
+ *
141
+ * @param {number|string|bigint} value The number to convert.
142
+ * @param {Object} [options] Conversion options (see ES class).
143
+ * @returns {string} The number expressed in Spanish words.
144
+ * @throws {TypeError} If value is NaN or invalid type.
145
+ * @throws {Error} If value is an invalid number string.
146
+ *
147
+ * @example
148
+ * convertToWords(42, { lang: 'es' }); // 'cuarenta y dos'
149
+ * convertToWords(100, { lang: 'es' }); // 'cien'
150
+ */
151
+ export default function convertToWords (value, options = {}) {
152
+ return new Spanish(options).convertToWords(value)
153
+ }