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,146 @@
1
+ import AbstractLanguage from '../classes/abstract-language.js'
2
+
3
+ /**
4
+ * Malay (Bahasa Melayu) language converter.
5
+ *
6
+ * Conventions:
7
+ * - Base-10 structure
8
+ * - "Se-" prefix for singular units (seratus, seribu, sejuta, sebilion, setrilion)
9
+ * - Space-separated components (no conjunction like "dan" for tens/ones)
10
+ * - Grouping by thousands (ribu, juta, bilion, trilion)
11
+ */
12
+ export class Malay extends AbstractLanguage {
13
+ negativeWord = 'minus'
14
+ decimalSeparatorWord = 'perpuluhan'
15
+ zeroWord = 'sifar'
16
+ base = {
17
+ 0: [],
18
+ 1: ['satu'],
19
+ 2: ['dua'],
20
+ 3: ['tiga'],
21
+ 4: ['empat'],
22
+ 5: ['lima'],
23
+ 6: ['enam'],
24
+ 7: ['tujuh'],
25
+ 8: ['lapan'],
26
+ 9: ['sembilan']
27
+ }
28
+
29
+ thousands = {
30
+ 3: 'ribu',
31
+ 6: 'juta',
32
+ 9: 'bilion',
33
+ 12: 'trilion'
34
+ }
35
+
36
+ splitBy3 (number) {
37
+ const blocks = []
38
+ const stringNumber = number.toString()
39
+ const length = stringNumber.length
40
+ let firstBlock
41
+
42
+ if (length < 3) {
43
+ blocks.push([stringNumber])
44
+ } else {
45
+ const firstBlockLength = length % 3
46
+
47
+ if (firstBlockLength > 0) {
48
+ firstBlock = [stringNumber.slice(0, firstBlockLength)]
49
+ blocks.push(firstBlock)
50
+ }
51
+
52
+ for (let index = firstBlockLength; index < length; index += 3) {
53
+ const nextBlock = [stringNumber.slice(index, index + 3)]
54
+ blocks.push(nextBlock)
55
+ }
56
+ }
57
+ return blocks
58
+ }
59
+
60
+ spell (blocks) {
61
+ let wordBlocks = []
62
+ let spelling
63
+ const firstBlock = blocks[0]
64
+ if (firstBlock[0].length === 1) {
65
+ spelling = firstBlock[0] === '0' ? ['sifar'] : this.base[Math.trunc(firstBlock[0])]
66
+ } else if (firstBlock[0].length === 2) {
67
+ spelling = this.getTens(firstBlock[0])
68
+ } else {
69
+ spelling = [...this.getHundreds(firstBlock[0][0]), ...this.getTens(firstBlock[0].slice(1, 3))]
70
+ }
71
+ wordBlocks = [...wordBlocks, [firstBlock[0], spelling]]
72
+ for (let index = 1; index < blocks.length; index++) {
73
+ let block = blocks[index]
74
+ spelling = [...this.getHundreds(block[0][0]), ...this.getTens(block[0].slice(1, 3))]
75
+ block = [...block, spelling]
76
+ wordBlocks = [...wordBlocks, block]
77
+ }
78
+ return wordBlocks
79
+ }
80
+
81
+ getHundreds (number) { // 'ratus'
82
+ if (number === '1') {
83
+ return ['seratus']
84
+ } else if (number === '0') {
85
+ return []
86
+ } else {
87
+ return [...this.base[Math.trunc(number)], 'ratus']
88
+ }
89
+ }
90
+
91
+ getTens (number) { // 'puluh' and 'belas'
92
+ if (number[0] === '1') {
93
+ if (number[1] === '0') {
94
+ return ['sepuluh']
95
+ } else if (number[1] === '1') {
96
+ return ['sebelas']
97
+ }
98
+ return [...this.base[Math.trunc(number[1])], 'belas']
99
+ }
100
+
101
+ if (number[0] === '0') {
102
+ return this.base[Math.trunc(number[1])]
103
+ }
104
+
105
+ return [...this.base[Math.trunc(number[0])], 'puluh', ...this.base[Math.trunc(number[1])]]
106
+ }
107
+
108
+ join (wordBlocks) {
109
+ const wordList = []
110
+ const length = wordBlocks.length - 1
111
+
112
+ for (let index = 0; index <= length; index++) {
113
+ const words = wordBlocks[index][1]
114
+ const isLast = index === length
115
+ const scaleWord = isLast ? null : this.thousands[(length - index) * 3]
116
+
117
+ if (!isLast && words.length === 1 && words[0] === 'satu') {
118
+ // Use se- prefix for singular scale units: seribu, sejuta, sebilion, setrilion
119
+ wordList.push('se' + scaleWord)
120
+ continue
121
+ }
122
+
123
+ // Add current block words
124
+ for (const w of words) wordList.push(w)
125
+
126
+ // Append scale word if applicable and current block contributed words
127
+ if (!isLast && words.length > 0) {
128
+ wordList.push(scaleWord)
129
+ }
130
+ }
131
+
132
+ return wordList.join(' ')
133
+ }
134
+
135
+ convertWholePart (number) {
136
+ return this.join(
137
+ this.spell(
138
+ this.splitBy3(number)
139
+ )
140
+ ).trim()
141
+ }
142
+ }
143
+
144
+ export default function convertToWords (value, options = {}) {
145
+ return new Malay(options).convertToWords(value)
146
+ }
@@ -0,0 +1,120 @@
1
+ import GreedyScaleLanguage from '../classes/greedy-scale-language.js'
2
+
3
+ /**
4
+ * Norwegian language converter.
5
+ *
6
+ * GreedyScaleLanguage with inline Norwegian merge rules:
7
+ * - Hyphenation for compound numbers (e.g., "tjueen")
8
+ * - "og" (and) for hundreds combinations
9
+ * - Comma separation for non-magnitude additions
10
+ * - Implicit '1' before tens and magnitudes
11
+ * - Space separators for large numbers
12
+ */
13
+ export class Norwegian extends GreedyScaleLanguage {
14
+ negativeWord = 'minus'
15
+ decimalSeparatorWord = 'komma'
16
+ zeroWord = 'null'
17
+ scaleWordPairs = [
18
+ [1_000_000_000_000_000_000_000_000_000_000_000n, 'quintillard'],
19
+ [1_000_000_000_000_000_000_000_000_000_000n, 'quintillion'],
20
+ [1_000_000_000_000_000_000_000_000_000n, 'quadrillard'],
21
+ [1_000_000_000_000_000_000_000_000n, 'quadrillion'],
22
+ [1_000_000_000_000_000_000_000n, 'trillard'],
23
+ [1_000_000_000_000_000_000n, 'trillion'],
24
+ [1_000_000_000_000_000n, 'billard'],
25
+ [1_000_000_000_000n, 'billion'],
26
+ [1_000_000_000n, 'millard'],
27
+ [1_000_000n, 'million'],
28
+ [1000n, 'tusen'],
29
+ [100n, 'hundre'],
30
+ [90n, 'nitti'],
31
+ [80n, 'åtti'],
32
+ [70n, 'sytti'],
33
+ [60n, 'seksti'],
34
+ [50n, 'femti'],
35
+ [40n, 'førti'],
36
+ [30n, 'tretti'],
37
+ [20n, 'tjue'],
38
+ [19n, 'nitten'],
39
+ [18n, 'atten'],
40
+ [17n, 'sytten'],
41
+ [16n, 'seksten'],
42
+ [15n, 'femten'],
43
+ [14n, 'fjorten'],
44
+ [13n, 'tretten'],
45
+ [12n, 'tolv'],
46
+ [11n, 'elleve'],
47
+ [10n, 'ti'],
48
+ [9n, 'ni'],
49
+ [8n, 'åtte'],
50
+ [7n, 'syv'],
51
+ [6n, 'seks'],
52
+ [5n, 'fem'],
53
+ [4n, 'fire'],
54
+ [3n, 'tre'],
55
+ [2n, 'to'],
56
+ [1n, 'en'],
57
+ [0n, 'null']
58
+ ]
59
+
60
+ /**
61
+ * Merges two adjacent word-number pairs according to Norwegian grammar rules.
62
+ *
63
+ * Norwegian-specific rules:
64
+ * - Implicit \"en\": `mergeScales({ 'en': 1n }, { 'hundre': 100n })` → `{ 'hundre': 100n }`
65
+ * - Hyphenation for compound tens: `mergeScales({ 'tjue': 20n }, { 'en': 1n })` → `{ 'tjue-en': 21n }`
66
+ * - \"og\" (and) after hundreds: `mergeScales({ 'hundre': 100n }, { 'en': 1n })` → `{ 'hundre og en': 101n }`
67
+ * - Space-separated for large numbers (thousands+)
68
+ * - Comma separator for non-magnitude additions (e.g., \"tusen, en\")
69
+ *
70
+ * @param {Object} leftPair The left operand as `{ word: BigInt }`.
71
+ * @param {Object} rightPair The right operand as `{ word: BigInt }`.
72
+ * @returns {Object} Merged pair with combined word and resulting numeric value.
73
+ *
74
+ * @example
75
+ * mergeScales({ 'en': 1n }, { 'hundre': 100n }); // { 'hundre': 100n }
76
+ * mergeScales({ 'tjue': 20n }, { 'tre': 3n }); // { 'tjue-tre': 23n }
77
+ */
78
+ // Norwegian merge rules mirror the former Scandinavian base logic
79
+ mergeScales (leftPair, rightPair) {
80
+ const leftWord = Object.keys(leftPair)[0]
81
+ const rightWord = Object.keys(rightPair)[0]
82
+ const leftNumber = Object.values(leftPair)[0]
83
+ const rightNumber = Object.values(rightPair)[0]
84
+
85
+ if (leftNumber === 1n && rightNumber < 100n) {
86
+ return rightPair
87
+ }
88
+
89
+ if (leftNumber < 100n && leftNumber > rightNumber) {
90
+ return { [`${leftWord}-${rightWord}`]: leftNumber + rightNumber }
91
+ }
92
+
93
+ if (leftNumber >= 100n && rightNumber < 100n) {
94
+ return { [`${leftWord} og ${rightWord}`]: leftNumber + rightNumber }
95
+ }
96
+
97
+ if (rightNumber > leftNumber) {
98
+ return { [`${leftWord} ${rightWord}`]: leftNumber * rightNumber }
99
+ }
100
+
101
+ return { [`${leftWord}, ${rightWord}`]: leftNumber + rightNumber }
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Converts a number to Norwegian cardinal (written) form.
107
+ *
108
+ * @param {number|string|bigint} value The number to convert.
109
+ * @param {Object} [options] Conversion options (see Norwegian class options).
110
+ * @returns {string} The number expressed in Norwegian words.
111
+ * @throws {TypeError} If value is NaN or invalid type.
112
+ * @throws {Error} If value is an invalid number string.
113
+ *
114
+ * @example
115
+ * convertToWords(42); // 'førti-to'
116
+ * convertToWords('1.5'); // 'en komma fem'
117
+ */
118
+ export default function convertToWords (value, options = {}) {
119
+ return new Norwegian(options).convertToWords(value)
120
+ }
@@ -0,0 +1,206 @@
1
+ import GreedyScaleLanguage from '../classes/greedy-scale-language.js'
2
+
3
+ /**
4
+ * @typedef {Object} DutchOptions
5
+ * @property {boolean} [includeOptionalAnd=false] Include optional "en" separator.
6
+ * @property {boolean} [noHundredPairs=false] Disable comma before hundreds.
7
+ * @property {boolean} [accentOne=true] Use accented "één" for one.
8
+ */
9
+
10
+ /**
11
+ * Dutch language converter.
12
+ *
13
+ * Features:
14
+ * - Optional "en" (and) separator for tens (includeOptionalAnd)
15
+ * - Optional comma before hundreds (noHundredPairs)
16
+ * - Compound word formation without hyphenation
17
+ */
18
+ export class Dutch extends GreedyScaleLanguage {
19
+ negativeWord = 'min'
20
+ decimalSeparatorWord = 'komma'
21
+ zeroWord = 'nul'
22
+ scaleWordPairs = [
23
+ [1_000_000_000_000_000_000_000_000_000n, 'quadriljard'],
24
+ [1_000_000_000_000_000_000_000_000n, 'quadriljoen'],
25
+ [1_000_000_000_000_000_000_000n, 'triljard'],
26
+ [1_000_000_000_000_000_000n, 'triljoen'],
27
+ [1_000_000_000_000_000n, 'biljard'],
28
+ [1_000_000_000_000n, 'biljoen'],
29
+ [1_000_000_000n, 'miljard'],
30
+ [1_000_000n, 'miljoen'],
31
+ [1000n, 'duizend'],
32
+ [100n, 'honderd'],
33
+ [90n, 'negentig'],
34
+ [80n, 'tachtig'],
35
+ [70n, 'zeventig'],
36
+ [60n, 'zestig'],
37
+ [50n, 'vijftig'],
38
+ [40n, 'veertig'],
39
+ [30n, 'dertig'],
40
+ [20n, 'twintig'],
41
+ [19n, 'negentien'],
42
+ [18n, 'achttien'],
43
+ [17n, 'zeventien'],
44
+ [16n, 'zestien'],
45
+ [15n, 'vijftien'],
46
+ [14n, 'veertien'],
47
+ [13n, 'dertien'],
48
+ [12n, 'twaalf'],
49
+ [11n, 'elf'],
50
+ [10n, 'tien'],
51
+ [9n, 'negen'],
52
+ [8n, 'acht'],
53
+ [7n, 'zeven'],
54
+ [6n, 'zes'],
55
+ [5n, 'vijf'],
56
+ [4n, 'vier'],
57
+ [3n, 'drie'],
58
+ [2n, 'twee'],
59
+ [1n, 'één'],
60
+ [0n, 'nul']
61
+ ]
62
+
63
+ /**
64
+ * Initializes the Dutch converter with language-specific options.
65
+ *
66
+ * @param {DutchOptions} [options={}] Configuration options.
67
+ */
68
+ constructor ({ includeOptionalAnd = false, noHundredPairs = false, accentOne = true } = {}) {
69
+ super()
70
+
71
+ this.includeOptionalAnd = includeOptionalAnd
72
+
73
+ this.noHundredPairs = noHundredPairs
74
+
75
+ this.accentOne = accentOne
76
+ if (!this.accentOne) {
77
+ this.scaleWordPairs[this.scaleWordPairs.length - 2][1] = 'een'
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Merges two adjacent word-number pairs according to Dutch grammar rules.
83
+ *
84
+ * Dutch-specific rules:
85
+ * - Implicit "één": `mergeScales({ 'één': 1n }, { 'duizend': 1000n })` → `{ 'duizend': 1000n }`
86
+ * - Reversed digit order for tens + units: `mergeScales({ 'twintig': 20n }, { 'één': 1n })` → `{ 'ééntwintig': 21n }`
87
+ * - Optional "en" separator based on includeOptionalAnd option
88
+ * - Compound words without separators for most combinations
89
+ * - Converts 'één' to 'een' in compound words (no accent in compounds)
90
+ * - Space separators for large numbers (millions+)
91
+ *
92
+ * @param {Object} current The left operand as `{ word: BigInt }`.
93
+ * @param {Object} next The right operand as `{ word: BigInt }`.
94
+ * @returns {Object} Merged pair with combined word and resulting numeric value.
95
+ *
96
+ * @example
97
+ * mergeScales({ 'één': 1n }, { 'duizend': 1000n }); // { 'duizend': 1000n }
98
+ * mergeScales({ 'twintig': 20n }, { 'drie': 3n }); // { 'drieentwintig': 23n }
99
+ */
100
+ mergeScales (current, next) {
101
+ let cText = Object.keys(current)[0]
102
+ let nText = Object.keys(next)[0]
103
+ const cNumber = Object.values(current)[0] // BigInt
104
+ const nNumber = Object.values(next)[0] // BigInt
105
+
106
+ // Implicit "een": omit before large multipliers ("miljoen" not "een miljoen")
107
+ if (cNumber === 1n) {
108
+ if (nNumber < 1_000_000n) {
109
+ return next
110
+ }
111
+ cText = this.accentOne ? 'één' : 'een'
112
+ }
113
+
114
+ // Handle multiplication and spacing
115
+ if (nNumber > cNumber) {
116
+ let hadSpace = false
117
+ // Large scale words (millions+): add space before multiplier
118
+ if (nNumber >= 1_000_000n) {
119
+ cText += ' '
120
+ hadSpace = true
121
+ } else if (nNumber > 100n) {
122
+ // Hundreds and above: add space after multiplier
123
+ nText += ' '
124
+ hadSpace = true
125
+ }
126
+ // Convert 'één' to 'een' in compounds (when no space or accentOne disabled)
127
+ if (!hadSpace || !this.accentOne) {
128
+ cText = cText.replace(/één/g, 'een')
129
+ nText = nText.replace(/één/g, 'een')
130
+ }
131
+ return { [`${cText}${nText}`]: cNumber * nNumber }
132
+ }
133
+
134
+ // Track if we're adding a space (which keeps words separate)
135
+ let hasSpace = false
136
+
137
+ if (nNumber < 10n && cNumber > 10n && cNumber < 100n) {
138
+ const temporary = nText
139
+ nText = cText
140
+ const andTxt = temporary.endsWith('e') ? 'ën' : 'en'
141
+ cText = `${temporary}${andTxt}`
142
+ } else if (nNumber < 13n && cNumber < 1000n && this.includeOptionalAnd) {
143
+ cText = `${cText}en`
144
+ } else if (nNumber < 13n && cNumber >= 1000n && this.includeOptionalAnd) {
145
+ nText = ` en ${nText}`
146
+ hasSpace = true
147
+ } else if (cNumber >= 1_000_000n) {
148
+ cText += ' '
149
+ hasSpace = true
150
+ } else if (cNumber === 1000n) {
151
+ cText += ' '
152
+ hasSpace = true
153
+ }
154
+
155
+ // Convert 'één' to 'een' in direct compounds (no space)
156
+ // Keep 'één' only if there's a space AND accentOne=true
157
+ if (!hasSpace) {
158
+ cText = cText.replace(/één/g, 'een')
159
+ nText = nText.replace(/één/g, 'een')
160
+ } else if (!this.accentOne) {
161
+ cText = cText.replace(/één/g, 'een')
162
+ nText = nText.replace(/één/g, 'een')
163
+ }
164
+
165
+ return { [`${cText}${nText}`]: cNumber + nNumber }
166
+ }
167
+
168
+ convertWholePart (value) {
169
+ if (value >= 1100n && value < 10_000n && !this.noHundredPairs) {
170
+ const high = value / 100n
171
+ const low = value % 100n
172
+ if (high % 10n !== 0n) {
173
+ let result = super.convertWholePart(high) + 'honderd'
174
+ if (low) {
175
+ result +=
176
+ (this.includeOptionalAnd ? ' en ' : ' ') + super.convertWholePart(low)
177
+ }
178
+ return result
179
+ }
180
+ }
181
+ return super.convertWholePart(value)
182
+ }
183
+ }
184
+
185
+ /**
186
+ * Converts a number to Dutch cardinal (written) form.
187
+ *
188
+ * @param {number|string|bigint} value The number to convert.
189
+ * @param {Object} [options] Conversion options (see Dutch class options).
190
+ * @param {boolean} [options.includeOptionalAnd=false] Include optional 'en' (and) separator.
191
+ * @param {boolean} [options.noHundredPairs=false] Don't combine hundreds with tens/units.
192
+ * @param {boolean} [options.accentOne=true] Use accented 'één' for standalone 1.
193
+ * @returns {string} The number expressed in Dutch words.
194
+ * @throws {TypeError} If value is NaN or invalid type.
195
+ * @throws {Error} If value is an invalid number string.
196
+ *
197
+ * @example
198
+ * convertToWords(1); // 'één' (default accent)
199
+ * convertToWords(1, { accentOne: false }); // 'een'
200
+ * convertToWords(21); // 'eenentwintig' (no accent in compounds)
201
+ * convertToWords(42); // 'tweeenveertig'
202
+ * convertToWords('1.5'); // 'één komma vijf'
203
+ */
204
+ export default function convertToWords (value, options = {}) {
205
+ return new Dutch(options).convertToWords(value)
206
+ }
@@ -0,0 +1,126 @@
1
+ import SouthAsianLanguage from '../classes/south-asian-language.js'
2
+
3
+ class Punjabi 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 Punjabi(options).convertToWords(value)
126
+ }