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,100 +1,295 @@
1
- import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js'
1
+ /**
2
+ * Norwegian Bokmål language converter - Functional Implementation
3
+ *
4
+ * A performance-optimized number-to-words converter using precomputed lookup tables.
5
+ *
6
+ * Key features:
7
+ * - Hyphenated tens+ones: "tjue-en" (21)
8
+ * - "og" conjunction after hundreds
9
+ * - Comma separator after thousands before hundreds
10
+ * - Short scale: million, milliard, billion, etc.
11
+ */
12
+
13
+ import { parseNumericValue } from '../utils/parse-numeric.js'
14
+
15
+ // ============================================================================
16
+ // Vocabulary (module-level constants)
17
+ // ============================================================================
18
+
19
+ const ONES = ['', 'en', 'to', 'tre', 'fire', 'fem', 'seks', 'syv', 'åtte', 'ni']
20
+
21
+ const TEENS = ['ti', 'elleve', 'tolv', 'tretten', 'fjorten', 'femten', 'seksten', 'sytten', 'atten', 'nitten']
22
+ const TENS = ['', '', 'tjue', 'tretti', 'førti', 'femti', 'seksti', 'sytti', 'åtti', 'nitti']
23
+
24
+ const HUNDRED = 'hundre'
25
+ const THOUSAND = 'tusen'
26
+
27
+ const ZERO = 'null'
28
+ const NEGATIVE = 'minus'
29
+ const DECIMAL_SEP = 'komma'
30
+
31
+ // Short scale: million, milliard, billion, etc.
32
+ const SCALES = ['million', 'milliard', 'billion', 'billiard', 'kvintillion', 'sekstillion', 'septillion', 'oktillion']
33
+
34
+ // ============================================================================
35
+ // Precomputed Lookup Tables (built once at module load)
36
+ // ============================================================================
2
37
 
3
38
  /**
4
- * Norwegian Bokmål language converter.
39
+ * Builds segment word for 0-999.
40
+ * Returns object with word and hasHundred flag.
41
+ */
42
+ function buildSegment (n) {
43
+ if (n === 0) return { word: '', hasHundred: false }
44
+
45
+ const ones = n % 10
46
+ const tens = Math.floor(n / 10) % 10
47
+ const hundreds = Math.floor(n / 100)
48
+
49
+ const parts = []
50
+ let hasHundred = false
51
+
52
+ // Hundreds: "en hundre", "to hundre"
53
+ if (hundreds > 0) {
54
+ hasHundred = true
55
+ parts.push(ONES[hundreds] + ' ' + HUNDRED)
56
+ }
57
+
58
+ // Tens and ones
59
+ const tensOnes = n % 100
60
+
61
+ if (tensOnes === 0) {
62
+ // Just hundreds
63
+ } else if (tensOnes < 10) {
64
+ // Single digit
65
+ parts.push(ONES[ones])
66
+ } else if (tensOnes < 20) {
67
+ // Teens
68
+ parts.push(TEENS[ones])
69
+ } else if (ones === 0) {
70
+ // Even tens
71
+ parts.push(TENS[tens])
72
+ } else {
73
+ // Hyphenated: "tjue-en"
74
+ parts.push(TENS[tens] + '-' + ONES[ones])
75
+ }
76
+
77
+ // Combine with " og " between hundreds and remainder
78
+ if (parts.length === 2) {
79
+ return { word: parts[0] + ' og ' + parts[1], hasHundred: true }
80
+ }
81
+ return { word: parts[0] || '', hasHundred }
82
+ }
83
+
84
+ // Precompute all 1000 segment words (0-999)
85
+ const SEGMENTS = new Array(1000)
86
+ const SEGMENTS_HAS_HUNDRED = new Array(1000)
87
+
88
+ for (let i = 0; i < 1000; i++) {
89
+ const result = buildSegment(i)
90
+ SEGMENTS[i] = result.word
91
+ SEGMENTS_HAS_HUNDRED[i] = result.hasHundred
92
+ }
93
+
94
+ // ============================================================================
95
+ // Conversion Functions
96
+ // ============================================================================
97
+
98
+ /**
99
+ * Converts a non-negative integer to Norwegian Bokmål words.
5
100
  *
6
- * Supports:
7
- * - Hyphenation for compound numbers (tjue-en)
8
- * - "og" (and) for hundreds combinations
9
- * - Implicit '1' omission before tens and magnitudes
101
+ * @param {bigint} n - Non-negative integer to convert
102
+ * @returns {string} Norwegian words
10
103
  */
11
- export class NorwegianBokmal extends GreedyScaleLanguage {
12
- negativeWord = 'minus'
13
- decimalSeparatorWord = 'komma'
14
- zeroWord = 'null'
15
-
16
- scaleWords = [
17
- [1_000_000_000_000_000_000_000_000_000n, 'oktillion'],
18
- [1_000_000_000_000_000_000_000_000n, 'septillion'],
19
- [1_000_000_000_000_000_000_000n, 'sekstillion'],
20
- [1_000_000_000_000_000_000n, 'kvintillion'],
21
- [1_000_000_000_000_000n, 'kvadrillion'],
22
- [1_000_000_000_000n, 'billion'],
23
- [1_000_000_000n, 'milliard'],
24
- [1_000_000n, 'million'],
25
- [1000n, 'tusen'],
26
- [100n, 'hundre'],
27
- [90n, 'nitti'],
28
- [80n, 'åtti'],
29
- [70n, 'sytti'],
30
- [60n, 'seksti'],
31
- [50n, 'femti'],
32
- [40n, 'førti'],
33
- [30n, 'tretti'],
34
- [20n, 'tjue'],
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, 'åtte'],
47
- [7n, 'syv'],
48
- [6n, 'seks'],
49
- [5n, 'fem'],
50
- [4n, 'fire'],
51
- [3n, 'tre'],
52
- [2n, 'to'],
53
- [1n, 'en'],
54
- [0n, 'null']
55
- ]
56
-
57
- /**
58
- * Merges two adjacent word-sets according to Norwegian grammar rules.
59
- *
60
- * Norwegian-specific rules:
61
- * - Implicit "en": `combineWordSets({ 'en': 1n }, { 'hundre': 100n })` → `{ 'hundre': 100n }`
62
- * - Hyphenation for compound tens: `combineWordSets({ 'tjue': 20n }, { 'en': 1n })` → `{ 'tjue-en': 21n }`
63
- * - "og" (and) after hundreds: `combineWordSets({ 'hundre': 100n }, { 'en': 1n })` → `{ 'hundre og en': 101n }`
64
- * - Space-separated for large numbers (thousands+)
65
- * - Comma separator for non-magnitude additions (e.g., \"tusen, en\")
66
- *
67
- * @param {Object} preceding The preceding word-set as `{ word: BigInt }`.
68
- * @param {Object} following The following word-set as `{ word: BigInt }`.
69
- * @returns {Object} Merged pair with combined word and resulting numeric value.
70
- *
71
- * @example
72
- * combineWordSets({ 'en': 1n }, { 'hundre': 100n }); // { 'hundre': 100n }
73
- * combineWordSets({ 'tjue': 20n }, { 'tre': 3n }); // { 'tjue-tre': 23n }
74
- */
75
- // Norwegian merge rules mirror the former Scandinavian base logic
76
- combineWordSets (preceding, following) {
77
- const precedingWord = Object.keys(preceding)[0]
78
- const followingWord = Object.keys(following)[0]
79
- const precedingValue = Object.values(preceding)[0]
80
- const followingValue = Object.values(following)[0]
81
-
82
- if (precedingValue === 1n && followingValue < 100n) {
83
- return following
84
- }
104
+ function integerToWords (n) {
105
+ if (n === 0n) return ZERO
106
+
107
+ // Fast path: numbers < 1000 (direct lookup)
108
+ if (n < 1000n) {
109
+ return SEGMENTS[Number(n)]
110
+ }
111
+
112
+ // Fast path: numbers < 1,000,000 (thousands)
113
+ if (n < 1_000_000n) {
114
+ const thousands = Number(n / 1000n)
115
+ const remainder = Number(n % 1000n)
85
116
 
86
- if (precedingValue < 100n && precedingValue > followingValue) {
87
- return { [`${precedingWord}-${followingWord}`]: precedingValue + followingValue }
117
+ let result = SEGMENTS[thousands] + ' ' + THOUSAND
118
+
119
+ if (remainder > 0) {
120
+ // Comma before hundreds, " og " before small numbers
121
+ if (SEGMENTS_HAS_HUNDRED[remainder]) {
122
+ result += ', ' + SEGMENTS[remainder]
123
+ } else {
124
+ result += ' og ' + SEGMENTS[remainder]
125
+ }
88
126
  }
89
127
 
90
- if (precedingValue >= 100n && followingValue < 100n) {
91
- return { [`${precedingWord} og ${followingWord}`]: precedingValue + followingValue }
128
+ return result
129
+ }
130
+
131
+ // For numbers >= 1,000,000, use scale decomposition
132
+ return buildLargeNumberWords(n)
133
+ }
134
+
135
+ /**
136
+ * Builds words for numbers >= 1,000,000.
137
+ *
138
+ * @param {bigint} n - Number >= 1,000,000
139
+ * @returns {string} Norwegian words
140
+ */
141
+ function buildLargeNumberWords (n) {
142
+ const numStr = n.toString()
143
+ const len = numStr.length
144
+
145
+ // Build segments of 3 digits from right to left
146
+ const segments = []
147
+ const segmentSize = 3
148
+
149
+ const remainderLen = len % segmentSize
150
+ let pos = 0
151
+ if (remainderLen > 0) {
152
+ segments.push(Number(numStr.slice(0, remainderLen)))
153
+ pos = remainderLen
154
+ }
155
+ while (pos < len) {
156
+ segments.push(Number(numStr.slice(pos, pos + segmentSize)))
157
+ pos += segmentSize
158
+ }
159
+
160
+ // Convert segments to words
161
+ const parts = []
162
+ let scaleIndex = segments.length - 1
163
+
164
+ for (let i = 0; i < segments.length; i++) {
165
+ const segment = segments[i]
166
+
167
+ if (segment !== 0) {
168
+ const segmentWord = SEGMENTS[segment]
169
+ const hasHundred = SEGMENTS_HAS_HUNDRED[segment]
170
+
171
+ if (scaleIndex === 0) {
172
+ // Units segment
173
+ parts.push({ word: segmentWord, hasHundred, type: 'units' })
174
+ } else if (scaleIndex === 1) {
175
+ // Thousands
176
+ parts.push({ word: segmentWord + ' ' + THOUSAND, hasHundred: false, type: 'thousand' })
177
+ } else {
178
+ // Millions+
179
+ const scaleWord = SCALES[scaleIndex - 2]
180
+ parts.push({ word: segmentWord + ' ' + scaleWord, hasHundred: false, type: 'million' })
181
+ }
92
182
  }
93
183
 
94
- if (followingValue > precedingValue) {
95
- return { [`${precedingWord} ${followingWord}`]: precedingValue * followingValue }
184
+ scaleIndex--
185
+ }
186
+
187
+ // Join parts with Norwegian rules
188
+ return joinNorwegianParts(parts)
189
+ }
190
+
191
+ /**
192
+ * Joins parts with Norwegian spacing and comma rules.
193
+ *
194
+ * @param {Array} parts - Parts with type metadata
195
+ * @returns {string} Joined string
196
+ */
197
+ function joinNorwegianParts (parts) {
198
+ if (parts.length === 0) return ZERO
199
+ if (parts.length === 1) return parts[0].word
200
+
201
+ const result = []
202
+
203
+ for (let i = 0; i < parts.length; i++) {
204
+ const part = parts[i]
205
+ const nextPart = parts[i + 1]
206
+
207
+ result.push(part.word)
208
+
209
+ if (nextPart) {
210
+ if (part.type === 'thousand') {
211
+ // After thousands: comma if next has hundred, else " og "
212
+ if (nextPart.hasHundred) {
213
+ result.push(', ')
214
+ } else {
215
+ result.push(' og ')
216
+ }
217
+ } else if (part.type === 'million') {
218
+ // After millions: " og " for units without hundred, space otherwise
219
+ if (nextPart.type === 'units' && !nextPart.hasHundred) {
220
+ result.push(' og ')
221
+ } else {
222
+ result.push(' ')
223
+ }
224
+ } else {
225
+ result.push(' ')
226
+ }
96
227
  }
228
+ }
229
+
230
+ return result.join('')
231
+ }
232
+
233
+ /**
234
+ * Converts decimal digits to Norwegian words.
235
+ *
236
+ * @param {string} decimalPart - Decimal digits (without the point)
237
+ * @returns {string} Norwegian words for decimal part
238
+ */
239
+ function decimalPartToWords (decimalPart) {
240
+ let result = ''
97
241
 
98
- 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++
99
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
100
258
  }
259
+
260
+ /**
261
+ * Converts a numeric value to Norwegian Bokmål words.
262
+ *
263
+ * @param {number | string | bigint} value - The numeric value to convert
264
+ * @returns {string} The number in Norwegian 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) // 'tjue-en'
270
+ * toWords(101) // 'en hundre og en'
271
+ * toWords(1000000) // 'en million'
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,16 +1,26 @@
1
1
  /**
2
- * Dutch language converter.
2
+ * Converts a numeric value to Dutch words.
3
3
  *
4
- * Supports:
5
- * - Optional "en" (and) separator for tens
6
- * - Optional comma before hundreds
7
- * - Compound word formation without hyphenation
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
+ * @param {Object} [options] - Optional configuration
9
+ * @param {boolean} [options.accentOne=true] - Use "één" instead of "een"
10
+ * @param {boolean} [options.includeOptionalAnd=false] - Include "en" before small numbers
11
+ * @param {boolean} [options.noHundredPairing=false] - Disable hundred pairing (1104→duizend honderdvier)
12
+ * @returns {string} The number in Dutch words
13
+ * @throws {TypeError} If value is not a valid numeric type
14
+ * @throws {Error} If value is not a valid number format
15
+ *
16
+ * @example
17
+ * toWords(21) // 'eenentwintig'
18
+ * toWords(1) // 'één'
19
+ * toWords(1, {accentOne: false}) // 'een'
20
+ * toWords(1104) // 'elfhonderd vier'
8
21
  */
9
- export class Dutch extends GreedyScaleLanguage {
10
- constructor(options?: {});
11
- scaleWords: (string | bigint)[][];
12
- /** Combines two word-sets according to Dutch grammar rules. */
13
- combineWordSets(preceding: any, following: any): any;
14
- integerToWords(integerPart: any): string;
15
- }
16
- import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js';
22
+ export function toWords(value: number | string | bigint, options?: {
23
+ accentOne?: boolean | undefined;
24
+ includeOptionalAnd?: boolean | undefined;
25
+ noHundredPairing?: boolean | undefined;
26
+ }): string;