n2words 2.0.0 → 3.0.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 (327) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +86 -188
  3. package/dist/languages/am-Latn.js +3 -0
  4. package/dist/languages/am-Latn.js.map +1 -0
  5. package/dist/languages/am.js +3 -0
  6. package/dist/languages/am.js.map +1 -0
  7. package/dist/languages/ar.js +3 -0
  8. package/dist/languages/ar.js.map +1 -0
  9. package/dist/languages/az.js +3 -0
  10. package/dist/languages/az.js.map +1 -0
  11. package/dist/languages/bn.js +3 -0
  12. package/dist/languages/bn.js.map +1 -0
  13. package/dist/languages/cs.js +3 -0
  14. package/dist/languages/cs.js.map +1 -0
  15. package/dist/languages/da.js +3 -0
  16. package/dist/languages/da.js.map +1 -0
  17. package/dist/languages/de.js +3 -0
  18. package/dist/languages/de.js.map +1 -0
  19. package/dist/languages/el.js +3 -0
  20. package/dist/languages/el.js.map +1 -0
  21. package/dist/languages/en.js +3 -0
  22. package/dist/languages/en.js.map +1 -0
  23. package/dist/languages/es.js +3 -0
  24. package/dist/languages/es.js.map +1 -0
  25. package/dist/languages/fa.js +3 -0
  26. package/dist/languages/fa.js.map +1 -0
  27. package/dist/languages/fi.js +3 -0
  28. package/dist/languages/fi.js.map +1 -0
  29. package/dist/languages/fil.js +3 -0
  30. package/dist/languages/fil.js.map +1 -0
  31. package/dist/languages/fr-BE.js +3 -0
  32. package/dist/languages/fr-BE.js.map +1 -0
  33. package/dist/languages/fr.js +3 -0
  34. package/dist/languages/fr.js.map +1 -0
  35. package/dist/languages/gu.js +3 -0
  36. package/dist/languages/gu.js.map +1 -0
  37. package/dist/languages/ha.js +3 -0
  38. package/dist/languages/ha.js.map +1 -0
  39. package/dist/languages/hbo.js +3 -0
  40. package/dist/languages/hbo.js.map +1 -0
  41. package/dist/languages/he.js +3 -0
  42. package/dist/languages/he.js.map +1 -0
  43. package/dist/languages/hi.js +3 -0
  44. package/dist/languages/hi.js.map +1 -0
  45. package/dist/languages/hr.js +3 -0
  46. package/dist/languages/hr.js.map +1 -0
  47. package/dist/languages/hu.js +3 -0
  48. package/dist/languages/hu.js.map +1 -0
  49. package/dist/languages/id.js +3 -0
  50. package/dist/languages/id.js.map +1 -0
  51. package/dist/languages/it.js +3 -0
  52. package/dist/languages/it.js.map +1 -0
  53. package/dist/languages/ja.js +3 -0
  54. package/dist/languages/ja.js.map +1 -0
  55. package/dist/languages/kn.js +3 -0
  56. package/dist/languages/kn.js.map +1 -0
  57. package/dist/languages/ko.js +3 -0
  58. package/dist/languages/ko.js.map +1 -0
  59. package/dist/languages/lt.js +3 -0
  60. package/dist/languages/lt.js.map +1 -0
  61. package/dist/languages/lv.js +3 -0
  62. package/dist/languages/lv.js.map +1 -0
  63. package/dist/languages/mr.js +3 -0
  64. package/dist/languages/mr.js.map +1 -0
  65. package/dist/languages/ms.js +3 -0
  66. package/dist/languages/ms.js.map +1 -0
  67. package/dist/languages/nb.js +3 -0
  68. package/dist/languages/nb.js.map +1 -0
  69. package/dist/languages/nl.js +3 -0
  70. package/dist/languages/nl.js.map +1 -0
  71. package/dist/languages/pa.js +3 -0
  72. package/dist/languages/pa.js.map +1 -0
  73. package/dist/languages/pl.js +3 -0
  74. package/dist/languages/pl.js.map +1 -0
  75. package/dist/languages/pt.js +3 -0
  76. package/dist/languages/pt.js.map +1 -0
  77. package/dist/languages/ro.js +3 -0
  78. package/dist/languages/ro.js.map +1 -0
  79. package/dist/languages/ru.js +3 -0
  80. package/dist/languages/ru.js.map +1 -0
  81. package/dist/languages/sr-Cyrl.js +3 -0
  82. package/dist/languages/sr-Cyrl.js.map +1 -0
  83. package/dist/languages/sr-Latn.js +3 -0
  84. package/dist/languages/sr-Latn.js.map +1 -0
  85. package/dist/languages/sv.js +3 -0
  86. package/dist/languages/sv.js.map +1 -0
  87. package/dist/languages/sw.js +3 -0
  88. package/dist/languages/sw.js.map +1 -0
  89. package/dist/languages/ta.js +3 -0
  90. package/dist/languages/ta.js.map +1 -0
  91. package/dist/languages/te.js +3 -0
  92. package/dist/languages/te.js.map +1 -0
  93. package/dist/languages/th.js +3 -0
  94. package/dist/languages/th.js.map +1 -0
  95. package/dist/languages/tr.js +3 -0
  96. package/dist/languages/tr.js.map +1 -0
  97. package/dist/languages/uk.js +3 -0
  98. package/dist/languages/uk.js.map +1 -0
  99. package/dist/languages/ur.js +3 -0
  100. package/dist/languages/ur.js.map +1 -0
  101. package/dist/languages/vi.js +3 -0
  102. package/dist/languages/vi.js.map +1 -0
  103. package/dist/languages/zh-Hans.js +3 -0
  104. package/dist/languages/zh-Hans.js.map +1 -0
  105. package/dist/languages/zh-Hant.js +3 -0
  106. package/dist/languages/zh-Hant.js.map +1 -0
  107. package/dist/n2words.js +2 -2
  108. package/dist/n2words.js.map +1 -1
  109. package/lib/languages/am-Latn.d.ts +7 -0
  110. package/lib/languages/am-Latn.js +164 -0
  111. package/lib/languages/am.d.ts +7 -0
  112. package/lib/languages/am.js +164 -0
  113. package/lib/languages/ar.d.ts +14 -27
  114. package/lib/languages/ar.js +175 -129
  115. package/lib/languages/az.d.ts +4 -9
  116. package/lib/languages/az.js +171 -37
  117. package/lib/languages/bn.d.ts +4 -8
  118. package/lib/languages/bn.js +138 -124
  119. package/lib/languages/cs.d.ts +15 -85
  120. package/lib/languages/cs.js +310 -114
  121. package/lib/languages/da.d.ts +11 -12
  122. package/lib/languages/da.js +276 -101
  123. package/lib/languages/de.d.ts +14 -11
  124. package/lib/languages/de.js +317 -86
  125. package/lib/languages/el.d.ts +11 -11
  126. package/lib/languages/el.js +231 -78
  127. package/lib/languages/en.d.ts +14 -13
  128. package/lib/languages/en.js +242 -72
  129. package/lib/languages/es.d.ts +18 -12
  130. package/lib/languages/es.js +317 -103
  131. package/lib/languages/fa.d.ts +4 -44
  132. package/lib/languages/fa.js +112 -122
  133. package/lib/languages/fi.d.ts +14 -0
  134. package/lib/languages/fi.js +245 -0
  135. package/lib/languages/fil.d.ts +4 -13
  136. package/lib/languages/fil.js +207 -106
  137. package/lib/languages/fr-BE.d.ts +8 -8
  138. package/lib/languages/fr-BE.js +294 -19
  139. package/lib/languages/fr.d.ts +18 -12
  140. package/lib/languages/fr.js +352 -89
  141. package/lib/languages/gu.d.ts +4 -8
  142. package/lib/languages/gu.js +130 -125
  143. package/lib/languages/ha.d.ts +7 -0
  144. package/lib/languages/ha.js +230 -0
  145. package/lib/languages/hbo.d.ts +10 -110
  146. package/lib/languages/hbo.js +263 -214
  147. package/lib/languages/he.d.ts +10 -77
  148. package/lib/languages/he.js +242 -172
  149. package/lib/languages/hi.d.ts +4 -8
  150. package/lib/languages/hi.js +138 -124
  151. package/lib/languages/hr.d.ts +8 -77
  152. package/lib/languages/hr.js +194 -89
  153. package/lib/languages/hu.d.ts +4 -19
  154. package/lib/languages/hu.js +198 -119
  155. package/lib/languages/id.d.ts +4 -34
  156. package/lib/languages/id.js +171 -129
  157. package/lib/languages/it.d.ts +16 -34
  158. package/lib/languages/it.js +339 -94
  159. package/lib/languages/ja.d.ts +14 -14
  160. package/lib/languages/ja.js +233 -111
  161. package/lib/languages/kn.d.ts +4 -8
  162. package/lib/languages/kn.js +130 -35
  163. package/lib/languages/ko.d.ts +11 -11
  164. package/lib/languages/ko.js +257 -49
  165. package/lib/languages/lt.d.ts +15 -67
  166. package/lib/languages/lt.js +296 -122
  167. package/lib/languages/lv.d.ts +15 -67
  168. package/lib/languages/lv.js +297 -106
  169. package/lib/languages/mr.d.ts +4 -8
  170. package/lib/languages/mr.js +130 -125
  171. package/lib/languages/ms.d.ts +4 -28
  172. package/lib/languages/ms.js +171 -116
  173. package/lib/languages/nb.d.ts +11 -9
  174. package/lib/languages/nb.js +282 -87
  175. package/lib/languages/nl.d.ts +23 -13
  176. package/lib/languages/nl.js +317 -133
  177. package/lib/languages/pa.d.ts +4 -8
  178. package/lib/languages/pa.js +156 -124
  179. package/lib/languages/pl.d.ts +19 -77
  180. package/lib/languages/pl.js +307 -87
  181. package/lib/languages/pt.d.ts +14 -26
  182. package/lib/languages/pt.js +286 -92
  183. package/lib/languages/ro.d.ts +15 -155
  184. package/lib/languages/ro.js +219 -235
  185. package/lib/languages/ru.d.ts +8 -82
  186. package/lib/languages/ru.js +222 -78
  187. package/lib/languages/sr-Cyrl.d.ts +8 -77
  188. package/lib/languages/sr-Cyrl.js +191 -89
  189. package/lib/languages/sr-Latn.d.ts +8 -77
  190. package/lib/languages/sr-Latn.js +191 -89
  191. package/lib/languages/sv.d.ts +11 -11
  192. package/lib/languages/sv.js +288 -74
  193. package/lib/languages/sw.d.ts +4 -36
  194. package/lib/languages/sw.js +133 -106
  195. package/lib/languages/ta.d.ts +4 -17
  196. package/lib/languages/ta.js +129 -201
  197. package/lib/languages/te.d.ts +4 -19
  198. package/lib/languages/te.js +141 -196
  199. package/lib/languages/th.d.ts +4 -14
  200. package/lib/languages/th.js +135 -91
  201. package/lib/languages/tr.d.ts +15 -9
  202. package/lib/languages/tr.js +256 -49
  203. package/lib/languages/uk.d.ts +8 -82
  204. package/lib/languages/uk.js +200 -78
  205. package/lib/languages/ur.d.ts +4 -8
  206. package/lib/languages/ur.js +156 -124
  207. package/lib/languages/vi.d.ts +14 -69
  208. package/lib/languages/vi.js +294 -125
  209. package/lib/languages/zh-Hans.d.ts +8 -18
  210. package/lib/languages/zh-Hans.js +163 -92
  211. package/lib/languages/zh-Hant.d.ts +8 -18
  212. package/lib/languages/zh-Hant.js +181 -90
  213. package/lib/n2words.d.ts +53 -209
  214. package/lib/n2words.js +111 -530
  215. package/lib/utils/is-plain-object.d.ts +13 -0
  216. package/lib/utils/is-plain-object.js +17 -0
  217. package/lib/utils/parse-numeric.d.ts +17 -0
  218. package/lib/utils/parse-numeric.js +108 -0
  219. package/lib/utils/validate-options.d.ts +8 -0
  220. package/lib/utils/validate-options.js +16 -0
  221. package/package.json +26 -14
  222. package/dist/ArabicConverter.js +0 -3
  223. package/dist/ArabicConverter.js.map +0 -1
  224. package/dist/AzerbaijaniConverter.js +0 -3
  225. package/dist/AzerbaijaniConverter.js.map +0 -1
  226. package/dist/BanglaConverter.js +0 -3
  227. package/dist/BanglaConverter.js.map +0 -1
  228. package/dist/BiblicalHebrewConverter.js +0 -3
  229. package/dist/BiblicalHebrewConverter.js.map +0 -1
  230. package/dist/CroatianConverter.js +0 -3
  231. package/dist/CroatianConverter.js.map +0 -1
  232. package/dist/CzechConverter.js +0 -3
  233. package/dist/CzechConverter.js.map +0 -1
  234. package/dist/DanishConverter.js +0 -3
  235. package/dist/DanishConverter.js.map +0 -1
  236. package/dist/DutchConverter.js +0 -3
  237. package/dist/DutchConverter.js.map +0 -1
  238. package/dist/EnglishConverter.js +0 -3
  239. package/dist/EnglishConverter.js.map +0 -1
  240. package/dist/FilipinoConverter.js +0 -3
  241. package/dist/FilipinoConverter.js.map +0 -1
  242. package/dist/FrenchBelgiumConverter.js +0 -3
  243. package/dist/FrenchBelgiumConverter.js.map +0 -1
  244. package/dist/FrenchConverter.js +0 -3
  245. package/dist/FrenchConverter.js.map +0 -1
  246. package/dist/GermanConverter.js +0 -3
  247. package/dist/GermanConverter.js.map +0 -1
  248. package/dist/GreekConverter.js +0 -3
  249. package/dist/GreekConverter.js.map +0 -1
  250. package/dist/GujaratiConverter.js +0 -3
  251. package/dist/GujaratiConverter.js.map +0 -1
  252. package/dist/HebrewConverter.js +0 -3
  253. package/dist/HebrewConverter.js.map +0 -1
  254. package/dist/HindiConverter.js +0 -3
  255. package/dist/HindiConverter.js.map +0 -1
  256. package/dist/HungarianConverter.js +0 -3
  257. package/dist/HungarianConverter.js.map +0 -1
  258. package/dist/IndonesianConverter.js +0 -3
  259. package/dist/IndonesianConverter.js.map +0 -1
  260. package/dist/ItalianConverter.js +0 -3
  261. package/dist/ItalianConverter.js.map +0 -1
  262. package/dist/JapaneseConverter.js +0 -3
  263. package/dist/JapaneseConverter.js.map +0 -1
  264. package/dist/KannadaConverter.js +0 -3
  265. package/dist/KannadaConverter.js.map +0 -1
  266. package/dist/KoreanConverter.js +0 -3
  267. package/dist/KoreanConverter.js.map +0 -1
  268. package/dist/LatvianConverter.js +0 -3
  269. package/dist/LatvianConverter.js.map +0 -1
  270. package/dist/LithuanianConverter.js +0 -3
  271. package/dist/LithuanianConverter.js.map +0 -1
  272. package/dist/MalayConverter.js +0 -3
  273. package/dist/MalayConverter.js.map +0 -1
  274. package/dist/MarathiConverter.js +0 -3
  275. package/dist/MarathiConverter.js.map +0 -1
  276. package/dist/NorwegianBokmalConverter.js +0 -3
  277. package/dist/NorwegianBokmalConverter.js.map +0 -1
  278. package/dist/PersianConverter.js +0 -3
  279. package/dist/PersianConverter.js.map +0 -1
  280. package/dist/PolishConverter.js +0 -3
  281. package/dist/PolishConverter.js.map +0 -1
  282. package/dist/PortugueseConverter.js +0 -3
  283. package/dist/PortugueseConverter.js.map +0 -1
  284. package/dist/PunjabiConverter.js +0 -3
  285. package/dist/PunjabiConverter.js.map +0 -1
  286. package/dist/RomanianConverter.js +0 -3
  287. package/dist/RomanianConverter.js.map +0 -1
  288. package/dist/RussianConverter.js +0 -3
  289. package/dist/RussianConverter.js.map +0 -1
  290. package/dist/SerbianCyrillicConverter.js +0 -3
  291. package/dist/SerbianCyrillicConverter.js.map +0 -1
  292. package/dist/SerbianLatinConverter.js +0 -3
  293. package/dist/SerbianLatinConverter.js.map +0 -1
  294. package/dist/SimplifiedChineseConverter.js +0 -3
  295. package/dist/SimplifiedChineseConverter.js.map +0 -1
  296. package/dist/SpanishConverter.js +0 -3
  297. package/dist/SpanishConverter.js.map +0 -1
  298. package/dist/SwahiliConverter.js +0 -3
  299. package/dist/SwahiliConverter.js.map +0 -1
  300. package/dist/SwedishConverter.js +0 -3
  301. package/dist/SwedishConverter.js.map +0 -1
  302. package/dist/TamilConverter.js +0 -3
  303. package/dist/TamilConverter.js.map +0 -1
  304. package/dist/TeluguConverter.js +0 -3
  305. package/dist/TeluguConverter.js.map +0 -1
  306. package/dist/ThaiConverter.js +0 -3
  307. package/dist/ThaiConverter.js.map +0 -1
  308. package/dist/TraditionalChineseConverter.js +0 -3
  309. package/dist/TraditionalChineseConverter.js.map +0 -1
  310. package/dist/TurkishConverter.js +0 -3
  311. package/dist/TurkishConverter.js.map +0 -1
  312. package/dist/UkrainianConverter.js +0 -3
  313. package/dist/UkrainianConverter.js.map +0 -1
  314. package/dist/UrduConverter.js +0 -3
  315. package/dist/UrduConverter.js.map +0 -1
  316. package/dist/VietnameseConverter.js +0 -3
  317. package/dist/VietnameseConverter.js.map +0 -1
  318. package/lib/classes/abstract-language.d.ts +0 -178
  319. package/lib/classes/abstract-language.js +0 -268
  320. package/lib/classes/greedy-scale-language.d.ts +0 -109
  321. package/lib/classes/greedy-scale-language.js +0 -201
  322. package/lib/classes/slavic-language.d.ts +0 -148
  323. package/lib/classes/slavic-language.js +0 -281
  324. package/lib/classes/south-asian-language.d.ts +0 -70
  325. package/lib/classes/south-asian-language.js +0 -154
  326. package/lib/classes/turkic-language.d.ts +0 -26
  327. package/lib/classes/turkic-language.js +0 -59
@@ -1,120 +1,295 @@
1
- import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js'
1
+ /**
2
+ * Danish language converter - Functional Implementation
3
+ *
4
+ * A performance-optimized number-to-words converter using precomputed lookup tables.
5
+ *
6
+ * Key features:
7
+ * - Vigesimal (base-20) tens naming: halvtreds (50), treds (60), etc.
8
+ * - Units-before-tens: "enogtyve" (21) = one-and-twenty
9
+ * - Compound thousands: "ettusind", "firetusinde"
10
+ * - "og" conjunction after hundreds and thousands
11
+ * - Long scale for millions+
12
+ */
13
+
14
+ import { parseNumericValue } from '../utils/parse-numeric.js'
15
+
16
+ // ============================================================================
17
+ // Vocabulary (module-level constants)
18
+ // ============================================================================
19
+
20
+ const ONES = ['', 'et', 'to', 'tre', 'fire', 'fem', 'seks', 'syv', 'otte', 'ni']
21
+ // "en" form used in vigesimal pattern (X og Y) and before millions
22
+ const ONES_VIGESIMAL = ['', 'en', 'to', 'tre', 'fire', 'fem', 'seks', 'syv', 'otte', 'ni']
23
+
24
+ const TEENS = ['ti', 'elleve', 'tolv', 'tretten', 'fjorten', 'femten', 'seksten', 'sytten', 'atten', 'nitten']
25
+
26
+ // Danish vigesimal tens (base-20 derived names)
27
+ const TENS = ['', '', 'tyve', 'tredive', 'fyrre', 'halvtreds', 'treds', 'halvfjerds', 'firs', 'halvfems']
28
+
29
+ const HUNDRED = 'hundrede'
30
+ const THOUSAND = 'tusind'
31
+
32
+ const ZERO = 'nul'
33
+ const NEGATIVE = 'minus'
34
+ const DECIMAL_SEP = 'komma'
35
+
36
+ // Long scale: millioner, millarder, billioner, etc.
37
+ const SCALES = ['millioner', 'millarder', 'billioner', 'billarder', 'trillioner', 'trillarder', 'quadrillioner', 'quadrillarder']
38
+
39
+ // ============================================================================
40
+ // Precomputed Lookup Tables (built once at module load)
41
+ // ============================================================================
2
42
 
3
43
  /**
4
- * Danish language converter.
44
+ * Builds segment word for 0-999.
45
+ */
46
+ function buildSegment (n) {
47
+ if (n === 0) return ''
48
+
49
+ const ones = n % 10
50
+ const tens = Math.floor(n / 10) % 10
51
+ const hundreds = Math.floor(n / 100)
52
+
53
+ const parts = []
54
+
55
+ // Hundreds: "ethundrede", "tohundrede" (compound, no space)
56
+ if (hundreds > 0) {
57
+ parts.push(ONES[hundreds] + HUNDRED)
58
+ }
59
+
60
+ // Tens and ones
61
+ const tensOnes = n % 100
62
+
63
+ if (tensOnes === 0) {
64
+ // Just hundreds
65
+ } else if (tensOnes < 10) {
66
+ // Single digit
67
+ parts.push(ONES[ones])
68
+ } else if (tensOnes < 20) {
69
+ // Teens
70
+ parts.push(TEENS[ones])
71
+ } else if (ones === 0) {
72
+ // Even tens
73
+ parts.push(TENS[tens])
74
+ } else {
75
+ // Units-before-tens: "enogtyve", "treogfyrre"
76
+ parts.push(ONES_VIGESIMAL[ones] + 'og' + TENS[tens])
77
+ }
78
+
79
+ // Combine with " og " between hundreds and remainder
80
+ if (parts.length === 2) {
81
+ return parts[0] + ' og ' + parts[1]
82
+ }
83
+ return parts[0] || ''
84
+ }
85
+
86
+ // Precompute all 1000 segment words (0-999)
87
+ const SEGMENTS = new Array(1000)
88
+
89
+ for (let i = 0; i < 1000; i++) {
90
+ SEGMENTS[i] = buildSegment(i)
91
+ }
92
+
93
+ // ============================================================================
94
+ // Conversion Functions
95
+ // ============================================================================
96
+
97
+ /**
98
+ * Converts a non-negative integer to Danish words.
5
99
  *
6
- * Supports:
7
- * - Vigesimal (base-20) number system
8
- * - Units-before-tens ordering (e.g., "tre-og-tyve" = 23)
9
- * - Optional ordinal numbers via ordFlag option
100
+ * @param {bigint} n - Non-negative integer to convert
101
+ * @returns {string} Danish words
10
102
  */
11
- export class Danish extends GreedyScaleLanguage {
12
- negativeWord = 'minus'
13
- decimalSeparatorWord = 'komma'
14
- zeroWord = 'nul'
15
-
16
- scaleWords = [
17
- [1_000_000_000_000_000_000_000_000_000n, 'quadrillarder'],
18
- [1_000_000_000_000_000_000_000_000n, 'quadrillioner'],
19
- [1_000_000_000_000_000_000_000n, 'trillarder'],
20
- [1_000_000_000_000_000_000n, 'trillioner'],
21
- [1_000_000_000_000_000n, 'billarder'],
22
- [1_000_000_000_000n, 'billioner'],
23
- [1_000_000_000n, 'millarder'],
24
- [1_000_000n, 'millioner'],
25
- [1000n, 'tusind'],
26
- [100n, 'hundrede'],
27
- [90n, 'halvfems'],
28
- [80n, 'firs'],
29
- [70n, 'halvfjerds'],
30
- [60n, 'treds'],
31
- [50n, 'halvtreds'],
32
- [40n, 'fyrre'],
33
- [30n, 'tredive'],
34
- [20n, 'tyve'],
35
- [19n, 'nitten'],
36
- [18n, 'atten'],
37
- [17n, 'sytten'],
38
- [16n, 'seksten'],
39
- [15n, 'femten'],
40
- [14n, 'fjorten'],
41
- [13n, 'tretten'],
42
- [12n, 'tolv'],
43
- [11n, 'elleve'],
44
- [10n, 'ti'],
45
- [9n, 'ni'],
46
- [8n, 'otte'],
47
- [7n, 'syv'],
48
- [6n, 'seks'],
49
- [5n, 'fem'],
50
- [4n, 'fire'],
51
- [3n, 'tre'],
52
- [2n, 'to'],
53
- [1n, 'et'],
54
- [0n, 'nul']
55
- ]
56
-
57
- constructor (options = {}) {
58
- super()
59
-
60
- this.setOptions({
61
- ordFlag: false
62
- }, options)
103
+ function integerToWords (n) {
104
+ if (n === 0n) return ZERO
105
+
106
+ // Fast path: numbers < 1000 (direct lookup)
107
+ if (n < 1000n) {
108
+ return SEGMENTS[Number(n)]
63
109
  }
64
110
 
65
- /** Combines two word-sets with Danish vigesimal and reversal rules. */
66
- combineWordSets (preceding, following) {
67
- let precedingWord = Object.keys(preceding)[0]
68
- let followingWord = Object.keys(following)[0]
69
- const precedingValue = Object.values(preceding)[0] // BigInt (e.g., 1n, 100n, 1000n)
70
- const followingValue = Object.values(following)[0] // BigInt (e.g., magnitude level like 100n, 1000n)
111
+ // Fast path: numbers < 1,000,000 (thousands)
112
+ if (n < 1_000_000n) {
113
+ const thousands = Number(n / 1000n)
114
+ const remainder = Number(n % 1000n)
71
115
 
72
- // Prepend "et" to hundreds and thousands (not "en") for proper Danish form
73
- if (followingValue === 100n || followingValue === 1000n) {
74
- following = { [`et${followingWord}`]: followingValue }
75
- }
116
+ // Compound thousands: "ettusind", "firetusind"
117
+ let result = SEGMENTS[thousands] + THOUSAND
76
118
 
77
- // Implicit '1' handling: omit '1' before most magnitudes (except millions/ordinals)
78
- if (precedingValue === 1n) {
79
- if (followingValue < 1_000_000n || this.options.ordFlag) {
80
- return following // Just the magnitude word (e.g., "hundrede" not "en hundrede")
81
- }
82
- precedingWord = 'en' // Explicit "en" (one) for millions and above
119
+ if (remainder > 0) {
120
+ // Add 'e' suffix and " og " for remainder: "firetusinde og ..."
121
+ result += 'e og ' + SEGMENTS[remainder]
83
122
  }
84
123
 
85
- // Multiplication across magnitude boundaries
86
- if (followingValue > precedingValue) {
87
- // Space for million+ (e.g., "en million", "to millioner")
88
- if (followingValue >= 1_000_000n) {
89
- precedingWord += ' '
124
+ return result
125
+ }
126
+
127
+ // For numbers >= 1,000,000, use scale decomposition
128
+ return buildLargeNumberWords(n)
129
+ }
130
+
131
+ /**
132
+ * Builds words for numbers >= 1,000,000.
133
+ *
134
+ * @param {bigint} n - Number >= 1,000,000
135
+ * @returns {string} Danish words
136
+ */
137
+ function buildLargeNumberWords (n) {
138
+ const numStr = n.toString()
139
+ const len = numStr.length
140
+
141
+ // Build segments of 3 digits from right to left
142
+ const segments = []
143
+ const segmentSize = 3
144
+
145
+ const remainderLen = len % segmentSize
146
+ let pos = 0
147
+ if (remainderLen > 0) {
148
+ segments.push(Number(numStr.slice(0, remainderLen)))
149
+ pos = remainderLen
150
+ }
151
+ while (pos < len) {
152
+ segments.push(Number(numStr.slice(pos, pos + segmentSize)))
153
+ pos += segmentSize
154
+ }
155
+
156
+ // Convert segments to words with scale tracking
157
+ // scaleIndex: 0 = units, 1 = thousands, 2 = millions, etc.
158
+ const parts = []
159
+ let scaleIndex = segments.length - 1
160
+
161
+ for (let i = 0; i < segments.length; i++) {
162
+ const segment = segments[i]
163
+
164
+ if (segment !== 0) {
165
+ const segmentWord = SEGMENTS[segment]
166
+
167
+ if (scaleIndex === 0) {
168
+ // Units segment
169
+ parts.push({ word: segmentWord, type: 'units' })
170
+ } else if (scaleIndex === 1) {
171
+ // Thousands - compound form
172
+ parts.push({ word: segmentWord + THOUSAND, type: 'thousand' })
173
+ } else {
174
+ // Millions+ - space-separated, use "en" for 1
175
+ const scaleWord = SCALES[scaleIndex - 2]
176
+ let numWord = segmentWord
177
+ // "et" → "en" before millions+
178
+ if (segment === 1) {
179
+ numWord = 'en'
180
+ }
181
+ parts.push({ word: numWord + ' ' + scaleWord, type: 'million' })
90
182
  }
91
- return { [`${precedingWord}${followingWord}`]: precedingValue * followingValue }
92
183
  }
93
184
 
94
- // Addition with separator rules:
95
- // "og" (and) for hundreds + smaller numbers
96
- if (precedingValue >= 100n && precedingValue < 1000n) {
97
- precedingWord += ' og '
98
- } else if (precedingValue >= 1000n && precedingValue <= 100_000n) {
99
- // Special "e og" for thousands (e.g., "tusinde og tyve")
100
- precedingWord += 'e og '
101
- }
185
+ scaleIndex--
186
+ }
187
+
188
+ // Join parts with Danish rules
189
+ return joinDanishParts(parts)
190
+ }
102
191
 
103
- // Units-before-tens reversal (Danish vigesimal pattern):
104
- // For small units (< 10) with tens (10-99), swap order: "tre og tyve" (25)
105
- if (followingValue < 10n && precedingValue > 10n && precedingValue < 100n) {
106
- if (followingValue === 1n) {
107
- followingWord = 'en' // Convert 1 to "en" for vigesimal context
192
+ /**
193
+ * Joins parts with Danish spacing rules.
194
+ * - After thousands with remainder: "tusinde og"
195
+ * - Millions are space-separated
196
+ *
197
+ * @param {Array} parts - Parts with type metadata
198
+ * @returns {string} Joined string
199
+ */
200
+ function joinDanishParts (parts) {
201
+ if (parts.length === 0) return ZERO
202
+ if (parts.length === 1) return parts[0].word
203
+
204
+ const result = []
205
+
206
+ for (let i = 0; i < parts.length; i++) {
207
+ const part = parts[i]
208
+ const nextPart = parts[i + 1]
209
+
210
+ if (part.type === 'thousand' && nextPart && nextPart.type === 'units') {
211
+ // Thousands followed by units: add "e og"
212
+ result.push(part.word + 'e og ' + nextPart.word)
213
+ i++ // Skip the units part
214
+ } else if (part.type === 'million') {
215
+ if (result.length > 0) {
216
+ result.push(' ')
217
+ }
218
+ result.push(part.word)
219
+ if (nextPart) {
220
+ result.push(' ')
108
221
  }
109
- // Swap positions: units go after "og", tens go before
110
- const temporary = followingWord
111
- followingWord = precedingWord
112
- precedingWord = temporary + 'og'
113
- } else if (precedingValue >= 1_000_000n) {
114
- // Space for large magnitudes (millions+)
115
- precedingWord += ' '
222
+ } else {
223
+ if (result.length > 0 && !result[result.length - 1].endsWith(' ')) {
224
+ result.push(' ')
225
+ }
226
+ result.push(part.word)
116
227
  }
228
+ }
229
+
230
+ return result.join('')
231
+ }
232
+
233
+ /**
234
+ * Converts decimal digits to Danish words.
235
+ *
236
+ * @param {string} decimalPart - Decimal digits (without the point)
237
+ * @returns {string} Danish words for decimal part
238
+ */
239
+ function decimalPartToWords (decimalPart) {
240
+ let result = ''
117
241
 
118
- return { [`${precedingWord}${followingWord}`]: precedingValue + followingValue }
242
+ // Handle leading zeros
243
+ let i = 0
244
+ while (i < decimalPart.length && decimalPart[i] === '0') {
245
+ if (result) result += ' '
246
+ result += ZERO
247
+ i++
119
248
  }
249
+
250
+ // Convert remainder as a single number
251
+ const remainder = decimalPart.slice(i)
252
+ if (remainder) {
253
+ if (result) result += ' '
254
+ result += integerToWords(BigInt(remainder))
255
+ }
256
+
257
+ return result
120
258
  }
259
+
260
+ /**
261
+ * Converts a numeric value to Danish words.
262
+ *
263
+ * @param {number | string | bigint} value - The numeric value to convert
264
+ * @returns {string} The number in Danish words
265
+ * @throws {TypeError} If value is not a valid numeric type
266
+ * @throws {Error} If value is not a valid number format
267
+ *
268
+ * @example
269
+ * toWords(21) // 'enogtyve'
270
+ * toWords(1000) // 'ettusind'
271
+ * toWords(1000000) // 'en millioner'
272
+ */
273
+ function toWords (value) {
274
+ const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
275
+
276
+ let result = ''
277
+
278
+ if (isNegative) {
279
+ result = NEGATIVE + ' '
280
+ }
281
+
282
+ result += integerToWords(integerPart)
283
+
284
+ if (decimalPart) {
285
+ result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)
286
+ }
287
+
288
+ return result
289
+ }
290
+
291
+ // ============================================================================
292
+ // Public API
293
+ // ============================================================================
294
+
295
+ export { toWords }
@@ -1,14 +1,17 @@
1
1
  /**
2
- * German language converter.
2
+ * Converts a numeric value to German words.
3
3
  *
4
- * Supports:
5
- * - Compound formation (no separators between words)
6
- * - Three forms of 1 (eins/ein/eine)
7
- * - Units-before-tens ordering (einundzwanzig = one and twenty)
4
+ * This is the main public API. It accepts any valid numeric input
5
+ * (number, string, or bigint) and handles parsing internally.
6
+ *
7
+ * @param {number | string | bigint} value - The numeric value to convert
8
+ * @returns {string} The number in German words
9
+ * @throws {TypeError} If value is not a valid numeric type
10
+ * @throws {Error} If value is not a valid number format
11
+ *
12
+ * @example
13
+ * toWords(21) // 'einundzwanzig'
14
+ * toWords(1000) // 'eintausend'
15
+ * toWords(1000000) // 'eine Million'
8
16
  */
9
- export class German extends GreedyScaleLanguage {
10
- scaleWords: (string | bigint)[][];
11
- /** Combines two word-sets with German compound formation and reversal rules. */
12
- combineWordSets(preceding: any, following: any): any;
13
- }
14
- import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js';
17
+ export function toWords(value: number | string | bigint): string;