n2words 2.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (335) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/README.md +86 -188
  3. package/dist/languages/am-Latn.js +3 -0
  4. package/dist/languages/am-Latn.js.map +1 -0
  5. package/dist/languages/am.js +3 -0
  6. package/dist/languages/am.js.map +1 -0
  7. package/dist/languages/ar.js +3 -0
  8. package/dist/languages/ar.js.map +1 -0
  9. package/dist/languages/az.js +3 -0
  10. package/dist/languages/az.js.map +1 -0
  11. package/dist/languages/bn.js +3 -0
  12. package/dist/languages/bn.js.map +1 -0
  13. package/dist/languages/cs.js +3 -0
  14. package/dist/languages/cs.js.map +1 -0
  15. package/dist/languages/da.js +3 -0
  16. package/dist/languages/da.js.map +1 -0
  17. package/dist/languages/de.js +3 -0
  18. package/dist/languages/de.js.map +1 -0
  19. package/dist/languages/el.js +3 -0
  20. package/dist/languages/el.js.map +1 -0
  21. package/dist/languages/en.js +3 -0
  22. package/dist/languages/en.js.map +1 -0
  23. package/dist/languages/es.js +3 -0
  24. package/dist/languages/es.js.map +1 -0
  25. package/dist/languages/fa.js +3 -0
  26. package/dist/languages/fa.js.map +1 -0
  27. package/dist/languages/fi.js +3 -0
  28. package/dist/languages/fi.js.map +1 -0
  29. package/dist/languages/fil.js +3 -0
  30. package/dist/languages/fil.js.map +1 -0
  31. package/dist/languages/fr-BE.js +3 -0
  32. package/dist/languages/fr-BE.js.map +1 -0
  33. package/dist/languages/fr.js +3 -0
  34. package/dist/languages/fr.js.map +1 -0
  35. package/dist/languages/gu.js +3 -0
  36. package/dist/languages/gu.js.map +1 -0
  37. package/dist/languages/ha.js +3 -0
  38. package/dist/languages/ha.js.map +1 -0
  39. package/dist/languages/hbo.js +3 -0
  40. package/dist/languages/hbo.js.map +1 -0
  41. package/dist/languages/he.js +3 -0
  42. package/dist/languages/he.js.map +1 -0
  43. package/dist/languages/hi.js +3 -0
  44. package/dist/languages/hi.js.map +1 -0
  45. package/dist/languages/hr.js +3 -0
  46. package/dist/languages/hr.js.map +1 -0
  47. package/dist/languages/hu.js +3 -0
  48. package/dist/languages/hu.js.map +1 -0
  49. package/dist/languages/id.js +3 -0
  50. package/dist/languages/id.js.map +1 -0
  51. package/dist/languages/it.js +3 -0
  52. package/dist/languages/it.js.map +1 -0
  53. package/dist/languages/ja.js +3 -0
  54. package/dist/languages/ja.js.map +1 -0
  55. package/dist/languages/ka.js +3 -0
  56. package/dist/languages/ka.js.map +1 -0
  57. package/dist/languages/kn.js +3 -0
  58. package/dist/languages/kn.js.map +1 -0
  59. package/dist/languages/ko.js +3 -0
  60. package/dist/languages/ko.js.map +1 -0
  61. package/dist/languages/lt.js +3 -0
  62. package/dist/languages/lt.js.map +1 -0
  63. package/dist/languages/lv.js +3 -0
  64. package/dist/languages/lv.js.map +1 -0
  65. package/dist/languages/mr.js +3 -0
  66. package/dist/languages/mr.js.map +1 -0
  67. package/dist/languages/ms.js +3 -0
  68. package/dist/languages/ms.js.map +1 -0
  69. package/dist/languages/nb.js +3 -0
  70. package/dist/languages/nb.js.map +1 -0
  71. package/dist/languages/nl.js +3 -0
  72. package/dist/languages/nl.js.map +1 -0
  73. package/dist/languages/pa.js +3 -0
  74. package/dist/languages/pa.js.map +1 -0
  75. package/dist/languages/pl.js +3 -0
  76. package/dist/languages/pl.js.map +1 -0
  77. package/dist/languages/pt.js +3 -0
  78. package/dist/languages/pt.js.map +1 -0
  79. package/dist/languages/ro.js +3 -0
  80. package/dist/languages/ro.js.map +1 -0
  81. package/dist/languages/ru.js +3 -0
  82. package/dist/languages/ru.js.map +1 -0
  83. package/dist/languages/sr-Cyrl.js +3 -0
  84. package/dist/languages/sr-Cyrl.js.map +1 -0
  85. package/dist/languages/sr-Latn.js +3 -0
  86. package/dist/languages/sr-Latn.js.map +1 -0
  87. package/dist/languages/sv.js +3 -0
  88. package/dist/languages/sv.js.map +1 -0
  89. package/dist/languages/sw.js +3 -0
  90. package/dist/languages/sw.js.map +1 -0
  91. package/dist/languages/ta.js +3 -0
  92. package/dist/languages/ta.js.map +1 -0
  93. package/dist/languages/te.js +3 -0
  94. package/dist/languages/te.js.map +1 -0
  95. package/dist/languages/th.js +3 -0
  96. package/dist/languages/th.js.map +1 -0
  97. package/dist/languages/tr.js +3 -0
  98. package/dist/languages/tr.js.map +1 -0
  99. package/dist/languages/uk.js +3 -0
  100. package/dist/languages/uk.js.map +1 -0
  101. package/dist/languages/ur.js +3 -0
  102. package/dist/languages/ur.js.map +1 -0
  103. package/dist/languages/vi.js +3 -0
  104. package/dist/languages/vi.js.map +1 -0
  105. package/dist/languages/yo.js +3 -0
  106. package/dist/languages/yo.js.map +1 -0
  107. package/dist/languages/zh-Hans.js +3 -0
  108. package/dist/languages/zh-Hans.js.map +1 -0
  109. package/dist/languages/zh-Hant.js +3 -0
  110. package/dist/languages/zh-Hant.js.map +1 -0
  111. package/dist/n2words.js +2 -2
  112. package/dist/n2words.js.map +1 -1
  113. package/lib/languages/am-Latn.d.ts +7 -0
  114. package/lib/languages/am-Latn.js +159 -0
  115. package/lib/languages/am.d.ts +7 -0
  116. package/lib/languages/am.js +159 -0
  117. package/lib/languages/ar.d.ts +14 -27
  118. package/lib/languages/ar.js +175 -129
  119. package/lib/languages/az.d.ts +4 -9
  120. package/lib/languages/az.js +166 -37
  121. package/lib/languages/bn.d.ts +4 -8
  122. package/lib/languages/bn.js +159 -124
  123. package/lib/languages/cs.d.ts +15 -85
  124. package/lib/languages/cs.js +293 -114
  125. package/lib/languages/da.d.ts +11 -12
  126. package/lib/languages/da.js +269 -101
  127. package/lib/languages/de.d.ts +14 -11
  128. package/lib/languages/de.js +305 -86
  129. package/lib/languages/el.d.ts +11 -11
  130. package/lib/languages/el.js +224 -78
  131. package/lib/languages/en.d.ts +14 -13
  132. package/lib/languages/en.js +228 -72
  133. package/lib/languages/es.d.ts +18 -12
  134. package/lib/languages/es.js +297 -103
  135. package/lib/languages/fa.d.ts +4 -44
  136. package/lib/languages/fa.js +112 -122
  137. package/lib/languages/fi.d.ts +14 -0
  138. package/lib/languages/fi.js +238 -0
  139. package/lib/languages/fil.d.ts +4 -13
  140. package/lib/languages/fil.js +196 -106
  141. package/lib/languages/fr-BE.d.ts +8 -8
  142. package/lib/languages/fr-BE.js +285 -19
  143. package/lib/languages/fr.d.ts +18 -12
  144. package/lib/languages/fr.js +339 -89
  145. package/lib/languages/gu.d.ts +4 -8
  146. package/lib/languages/gu.js +143 -125
  147. package/lib/languages/ha.d.ts +7 -0
  148. package/lib/languages/ha.js +225 -0
  149. package/lib/languages/hbo.d.ts +10 -110
  150. package/lib/languages/hbo.js +245 -214
  151. package/lib/languages/he.d.ts +10 -77
  152. package/lib/languages/he.js +231 -172
  153. package/lib/languages/hi.d.ts +4 -8
  154. package/lib/languages/hi.js +163 -124
  155. package/lib/languages/hr.d.ts +8 -77
  156. package/lib/languages/hr.js +200 -89
  157. package/lib/languages/hu.d.ts +4 -19
  158. package/lib/languages/hu.js +198 -119
  159. package/lib/languages/id.d.ts +4 -34
  160. package/lib/languages/id.js +166 -129
  161. package/lib/languages/it.d.ts +16 -34
  162. package/lib/languages/it.js +307 -97
  163. package/lib/languages/ja.d.ts +14 -14
  164. package/lib/languages/ja.js +221 -111
  165. package/lib/languages/ka.d.ts +17 -0
  166. package/lib/languages/ka.js +291 -0
  167. package/lib/languages/kn.d.ts +4 -8
  168. package/lib/languages/kn.js +143 -35
  169. package/lib/languages/ko.d.ts +11 -11
  170. package/lib/languages/ko.js +250 -49
  171. package/lib/languages/lt.d.ts +15 -67
  172. package/lib/languages/lt.js +287 -122
  173. package/lib/languages/lv.d.ts +15 -67
  174. package/lib/languages/lv.js +288 -106
  175. package/lib/languages/mr.d.ts +4 -8
  176. package/lib/languages/mr.js +143 -125
  177. package/lib/languages/ms.d.ts +4 -28
  178. package/lib/languages/ms.js +166 -116
  179. package/lib/languages/nb.d.ts +11 -9
  180. package/lib/languages/nb.js +272 -87
  181. package/lib/languages/nl.d.ts +23 -13
  182. package/lib/languages/nl.js +299 -133
  183. package/lib/languages/pa.d.ts +4 -8
  184. package/lib/languages/pa.js +151 -124
  185. package/lib/languages/pl.d.ts +19 -77
  186. package/lib/languages/pl.js +294 -87
  187. package/lib/languages/pt.d.ts +14 -26
  188. package/lib/languages/pt.js +272 -92
  189. package/lib/languages/ro.d.ts +15 -155
  190. package/lib/languages/ro.js +219 -235
  191. package/lib/languages/ru.d.ts +8 -82
  192. package/lib/languages/ru.js +239 -90
  193. package/lib/languages/sr-Cyrl.d.ts +8 -77
  194. package/lib/languages/sr-Cyrl.js +197 -89
  195. package/lib/languages/sr-Latn.d.ts +8 -77
  196. package/lib/languages/sr-Latn.js +197 -89
  197. package/lib/languages/sv.d.ts +11 -11
  198. package/lib/languages/sv.js +278 -74
  199. package/lib/languages/sw.d.ts +4 -36
  200. package/lib/languages/sw.js +133 -106
  201. package/lib/languages/ta.d.ts +4 -17
  202. package/lib/languages/ta.js +143 -202
  203. package/lib/languages/te.d.ts +4 -19
  204. package/lib/languages/te.js +133 -196
  205. package/lib/languages/th.d.ts +4 -14
  206. package/lib/languages/th.js +135 -91
  207. package/lib/languages/tr.d.ts +15 -9
  208. package/lib/languages/tr.js +245 -49
  209. package/lib/languages/uk.d.ts +8 -82
  210. package/lib/languages/uk.js +206 -78
  211. package/lib/languages/ur.d.ts +4 -8
  212. package/lib/languages/ur.js +151 -124
  213. package/lib/languages/vi.d.ts +14 -69
  214. package/lib/languages/vi.js +278 -129
  215. package/lib/languages/yo.d.ts +7 -0
  216. package/lib/languages/yo.js +303 -0
  217. package/lib/languages/zh-Hans.d.ts +8 -18
  218. package/lib/languages/zh-Hans.js +163 -92
  219. package/lib/languages/zh-Hant.d.ts +8 -18
  220. package/lib/languages/zh-Hant.js +181 -90
  221. package/lib/n2words.d.ts +55 -209
  222. package/lib/n2words.js +115 -530
  223. package/lib/utils/is-plain-object.d.ts +13 -0
  224. package/lib/utils/is-plain-object.js +17 -0
  225. package/lib/utils/parse-numeric.d.ts +17 -0
  226. package/lib/utils/parse-numeric.js +108 -0
  227. package/lib/utils/validate-options.d.ts +8 -0
  228. package/lib/utils/validate-options.js +16 -0
  229. package/package.json +26 -14
  230. package/dist/ArabicConverter.js +0 -3
  231. package/dist/ArabicConverter.js.map +0 -1
  232. package/dist/AzerbaijaniConverter.js +0 -3
  233. package/dist/AzerbaijaniConverter.js.map +0 -1
  234. package/dist/BanglaConverter.js +0 -3
  235. package/dist/BanglaConverter.js.map +0 -1
  236. package/dist/BiblicalHebrewConverter.js +0 -3
  237. package/dist/BiblicalHebrewConverter.js.map +0 -1
  238. package/dist/CroatianConverter.js +0 -3
  239. package/dist/CroatianConverter.js.map +0 -1
  240. package/dist/CzechConverter.js +0 -3
  241. package/dist/CzechConverter.js.map +0 -1
  242. package/dist/DanishConverter.js +0 -3
  243. package/dist/DanishConverter.js.map +0 -1
  244. package/dist/DutchConverter.js +0 -3
  245. package/dist/DutchConverter.js.map +0 -1
  246. package/dist/EnglishConverter.js +0 -3
  247. package/dist/EnglishConverter.js.map +0 -1
  248. package/dist/FilipinoConverter.js +0 -3
  249. package/dist/FilipinoConverter.js.map +0 -1
  250. package/dist/FrenchBelgiumConverter.js +0 -3
  251. package/dist/FrenchBelgiumConverter.js.map +0 -1
  252. package/dist/FrenchConverter.js +0 -3
  253. package/dist/FrenchConverter.js.map +0 -1
  254. package/dist/GermanConverter.js +0 -3
  255. package/dist/GermanConverter.js.map +0 -1
  256. package/dist/GreekConverter.js +0 -3
  257. package/dist/GreekConverter.js.map +0 -1
  258. package/dist/GujaratiConverter.js +0 -3
  259. package/dist/GujaratiConverter.js.map +0 -1
  260. package/dist/HebrewConverter.js +0 -3
  261. package/dist/HebrewConverter.js.map +0 -1
  262. package/dist/HindiConverter.js +0 -3
  263. package/dist/HindiConverter.js.map +0 -1
  264. package/dist/HungarianConverter.js +0 -3
  265. package/dist/HungarianConverter.js.map +0 -1
  266. package/dist/IndonesianConverter.js +0 -3
  267. package/dist/IndonesianConverter.js.map +0 -1
  268. package/dist/ItalianConverter.js +0 -3
  269. package/dist/ItalianConverter.js.map +0 -1
  270. package/dist/JapaneseConverter.js +0 -3
  271. package/dist/JapaneseConverter.js.map +0 -1
  272. package/dist/KannadaConverter.js +0 -3
  273. package/dist/KannadaConverter.js.map +0 -1
  274. package/dist/KoreanConverter.js +0 -3
  275. package/dist/KoreanConverter.js.map +0 -1
  276. package/dist/LatvianConverter.js +0 -3
  277. package/dist/LatvianConverter.js.map +0 -1
  278. package/dist/LithuanianConverter.js +0 -3
  279. package/dist/LithuanianConverter.js.map +0 -1
  280. package/dist/MalayConverter.js +0 -3
  281. package/dist/MalayConverter.js.map +0 -1
  282. package/dist/MarathiConverter.js +0 -3
  283. package/dist/MarathiConverter.js.map +0 -1
  284. package/dist/NorwegianBokmalConverter.js +0 -3
  285. package/dist/NorwegianBokmalConverter.js.map +0 -1
  286. package/dist/PersianConverter.js +0 -3
  287. package/dist/PersianConverter.js.map +0 -1
  288. package/dist/PolishConverter.js +0 -3
  289. package/dist/PolishConverter.js.map +0 -1
  290. package/dist/PortugueseConverter.js +0 -3
  291. package/dist/PortugueseConverter.js.map +0 -1
  292. package/dist/PunjabiConverter.js +0 -3
  293. package/dist/PunjabiConverter.js.map +0 -1
  294. package/dist/RomanianConverter.js +0 -3
  295. package/dist/RomanianConverter.js.map +0 -1
  296. package/dist/RussianConverter.js +0 -3
  297. package/dist/RussianConverter.js.map +0 -1
  298. package/dist/SerbianCyrillicConverter.js +0 -3
  299. package/dist/SerbianCyrillicConverter.js.map +0 -1
  300. package/dist/SerbianLatinConverter.js +0 -3
  301. package/dist/SerbianLatinConverter.js.map +0 -1
  302. package/dist/SimplifiedChineseConverter.js +0 -3
  303. package/dist/SimplifiedChineseConverter.js.map +0 -1
  304. package/dist/SpanishConverter.js +0 -3
  305. package/dist/SpanishConverter.js.map +0 -1
  306. package/dist/SwahiliConverter.js +0 -3
  307. package/dist/SwahiliConverter.js.map +0 -1
  308. package/dist/SwedishConverter.js +0 -3
  309. package/dist/SwedishConverter.js.map +0 -1
  310. package/dist/TamilConverter.js +0 -3
  311. package/dist/TamilConverter.js.map +0 -1
  312. package/dist/TeluguConverter.js +0 -3
  313. package/dist/TeluguConverter.js.map +0 -1
  314. package/dist/ThaiConverter.js +0 -3
  315. package/dist/ThaiConverter.js.map +0 -1
  316. package/dist/TraditionalChineseConverter.js +0 -3
  317. package/dist/TraditionalChineseConverter.js.map +0 -1
  318. package/dist/TurkishConverter.js +0 -3
  319. package/dist/TurkishConverter.js.map +0 -1
  320. package/dist/UkrainianConverter.js +0 -3
  321. package/dist/UkrainianConverter.js.map +0 -1
  322. package/dist/UrduConverter.js +0 -3
  323. package/dist/UrduConverter.js.map +0 -1
  324. package/dist/VietnameseConverter.js +0 -3
  325. package/dist/VietnameseConverter.js.map +0 -1
  326. package/lib/classes/abstract-language.d.ts +0 -178
  327. package/lib/classes/abstract-language.js +0 -268
  328. package/lib/classes/greedy-scale-language.d.ts +0 -109
  329. package/lib/classes/greedy-scale-language.js +0 -201
  330. package/lib/classes/slavic-language.d.ts +0 -148
  331. package/lib/classes/slavic-language.js +0 -281
  332. package/lib/classes/south-asian-language.d.ts +0 -70
  333. package/lib/classes/south-asian-language.js +0 -154
  334. package/lib/classes/turkic-language.d.ts +0 -26
  335. package/lib/classes/turkic-language.js +0 -59
@@ -1,120 +1,288 @@
1
- import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js'
1
+ /**
2
+ * Danish language converter - Functional Implementation
3
+ *
4
+ * Self-contained module with its own input validation, ready for subpath exports.
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
+ // Segment Building
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
+ // ============================================================================
87
+ // Conversion Functions
88
+ // ============================================================================
89
+
90
+ /**
91
+ * Converts a non-negative integer to Danish words.
5
92
  *
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
93
+ * @param {bigint} n - Non-negative integer to convert
94
+ * @returns {string} Danish words
10
95
  */
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)
96
+ function integerToWords (n) {
97
+ if (n === 0n) return ZERO
98
+
99
+ // Fast path: numbers < 1000 (direct lookup)
100
+ if (n < 1000n) {
101
+ return buildSegment(Number(n))
63
102
  }
64
103
 
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)
104
+ // Fast path: numbers < 1,000,000 (thousands)
105
+ if (n < 1_000_000n) {
106
+ const thousands = Number(n / 1000n)
107
+ const remainder = Number(n % 1000n)
71
108
 
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
- }
109
+ // Compound thousands: "ettusind", "firetusind"
110
+ let result = buildSegment(thousands) + THOUSAND
76
111
 
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
112
+ if (remainder > 0) {
113
+ // Add 'e' suffix and " og " for remainder: "firetusinde og ..."
114
+ result += 'e og ' + buildSegment(remainder)
83
115
  }
84
116
 
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 += ' '
117
+ return result
118
+ }
119
+
120
+ // For numbers >= 1,000,000, use scale decomposition
121
+ return buildLargeNumberWords(n)
122
+ }
123
+
124
+ /**
125
+ * Builds words for numbers >= 1,000,000.
126
+ *
127
+ * @param {bigint} n - Number >= 1,000,000
128
+ * @returns {string} Danish words
129
+ */
130
+ function buildLargeNumberWords (n) {
131
+ const numStr = n.toString()
132
+ const len = numStr.length
133
+
134
+ // Build segments of 3 digits from right to left
135
+ const segments = []
136
+ const segmentSize = 3
137
+
138
+ const remainderLen = len % segmentSize
139
+ let pos = 0
140
+ if (remainderLen > 0) {
141
+ segments.push(Number(numStr.slice(0, remainderLen)))
142
+ pos = remainderLen
143
+ }
144
+ while (pos < len) {
145
+ segments.push(Number(numStr.slice(pos, pos + segmentSize)))
146
+ pos += segmentSize
147
+ }
148
+
149
+ // Convert segments to words with scale tracking
150
+ // scaleIndex: 0 = units, 1 = thousands, 2 = millions, etc.
151
+ const parts = []
152
+ let scaleIndex = segments.length - 1
153
+
154
+ for (let i = 0; i < segments.length; i++) {
155
+ const segment = segments[i]
156
+
157
+ if (segment !== 0) {
158
+ const segmentWord = buildSegment(segment)
159
+
160
+ if (scaleIndex === 0) {
161
+ // Units segment
162
+ parts.push({ word: segmentWord, type: 'units' })
163
+ } else if (scaleIndex === 1) {
164
+ // Thousands - compound form
165
+ parts.push({ word: segmentWord + THOUSAND, type: 'thousand' })
166
+ } else {
167
+ // Millions+ - space-separated, use "en" for 1
168
+ const scaleWord = SCALES[scaleIndex - 2]
169
+ let numWord = segmentWord
170
+ // "et" → "en" before millions+
171
+ if (segment === 1) {
172
+ numWord = 'en'
173
+ }
174
+ parts.push({ word: numWord + ' ' + scaleWord, type: 'million' })
90
175
  }
91
- return { [`${precedingWord}${followingWord}`]: precedingValue * followingValue }
92
176
  }
93
177
 
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
- }
178
+ scaleIndex--
179
+ }
102
180
 
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
181
+ // Join parts with Danish rules
182
+ return joinDanishParts(parts)
183
+ }
184
+
185
+ /**
186
+ * Joins parts with Danish spacing rules.
187
+ * - After thousands with remainder: "tusinde og"
188
+ * - Millions are space-separated
189
+ *
190
+ * @param {Array} parts - Parts with type metadata
191
+ * @returns {string} Joined string
192
+ */
193
+ function joinDanishParts (parts) {
194
+ if (parts.length === 0) return ZERO
195
+ if (parts.length === 1) return parts[0].word
196
+
197
+ const result = []
198
+
199
+ for (let i = 0; i < parts.length; i++) {
200
+ const part = parts[i]
201
+ const nextPart = parts[i + 1]
202
+
203
+ if (part.type === 'thousand' && nextPart && nextPart.type === 'units') {
204
+ // Thousands followed by units: add "e og"
205
+ result.push(part.word + 'e og ' + nextPart.word)
206
+ i++ // Skip the units part
207
+ } else if (part.type === 'million') {
208
+ if (result.length > 0) {
209
+ result.push(' ')
210
+ }
211
+ result.push(part.word)
212
+ if (nextPart) {
213
+ result.push(' ')
214
+ }
215
+ } else {
216
+ if (result.length > 0 && !result[result.length - 1].endsWith(' ')) {
217
+ result.push(' ')
108
218
  }
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 += ' '
219
+ result.push(part.word)
116
220
  }
221
+ }
222
+
223
+ return result.join('')
224
+ }
117
225
 
118
- return { [`${precedingWord}${followingWord}`]: precedingValue + followingValue }
226
+ /**
227
+ * Converts decimal digits to Danish words.
228
+ *
229
+ * @param {string} decimalPart - Decimal digits (without the point)
230
+ * @returns {string} Danish words for decimal part
231
+ */
232
+ function decimalPartToWords (decimalPart) {
233
+ let result = ''
234
+
235
+ // Handle leading zeros
236
+ let i = 0
237
+ while (i < decimalPart.length && decimalPart[i] === '0') {
238
+ if (result) result += ' '
239
+ result += ZERO
240
+ i++
241
+ }
242
+
243
+ // Convert remainder as a single number
244
+ const remainder = decimalPart.slice(i)
245
+ if (remainder) {
246
+ if (result) result += ' '
247
+ result += integerToWords(BigInt(remainder))
248
+ }
249
+
250
+ return result
251
+ }
252
+
253
+ /**
254
+ * Converts a numeric value to Danish words.
255
+ *
256
+ * @param {number | string | bigint} value - The numeric value to convert
257
+ * @returns {string} The number in Danish words
258
+ * @throws {TypeError} If value is not a valid numeric type
259
+ * @throws {Error} If value is not a valid number format
260
+ *
261
+ * @example
262
+ * toWords(21) // 'enogtyve'
263
+ * toWords(1000) // 'ettusind'
264
+ * toWords(1000000) // 'en millioner'
265
+ */
266
+ function toWords (value) {
267
+ const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
268
+
269
+ let result = ''
270
+
271
+ if (isNegative) {
272
+ result = NEGATIVE + ' '
273
+ }
274
+
275
+ result += integerToWords(integerPart)
276
+
277
+ if (decimalPart) {
278
+ result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)
119
279
  }
280
+
281
+ return result
120
282
  }
283
+
284
+ // ============================================================================
285
+ // Public API
286
+ // ============================================================================
287
+
288
+ 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;