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,100 +1,285 @@
1
- import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js'
1
+ /**
2
+ * Norwegian Bokmål language converter - Functional Implementation
3
+ *
4
+ * Self-contained module with its own input validation, ready for subpath exports.
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
+ // Segment Building
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
+ // ============================================================================
85
+ // Conversion Functions
86
+ // ============================================================================
87
+
88
+ /**
89
+ * Converts a non-negative integer to Norwegian Bokmål words.
5
90
  *
6
- * Supports:
7
- * - Hyphenation for compound numbers (tjue-en)
8
- * - "og" (and) for hundreds combinations
9
- * - Implicit '1' omission before tens and magnitudes
91
+ * @param {bigint} n - Non-negative integer to convert
92
+ * @returns {string} Norwegian words
10
93
  */
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
- }
94
+ function integerToWords (n) {
95
+ if (n === 0n) return ZERO
96
+
97
+ // Fast path: numbers < 1000
98
+ if (n < 1000n) {
99
+ return buildSegment(Number(n)).word
100
+ }
101
+
102
+ // Fast path: numbers < 1,000,000 (thousands)
103
+ if (n < 1_000_000n) {
104
+ const thousands = Number(n / 1000n)
105
+ const remainder = Number(n % 1000n)
106
+
107
+ let result = buildSegment(thousands).word + ' ' + THOUSAND
85
108
 
86
- if (precedingValue < 100n && precedingValue > followingValue) {
87
- return { [`${precedingWord}-${followingWord}`]: precedingValue + followingValue }
109
+ if (remainder > 0) {
110
+ const remainderResult = buildSegment(remainder)
111
+ // Comma before hundreds, " og " before small numbers
112
+ if (remainderResult.hasHundred) {
113
+ result += ', ' + remainderResult.word
114
+ } else {
115
+ result += ' og ' + remainderResult.word
116
+ }
88
117
  }
89
118
 
90
- if (precedingValue >= 100n && followingValue < 100n) {
91
- return { [`${precedingWord} og ${followingWord}`]: precedingValue + followingValue }
119
+ return result
120
+ }
121
+
122
+ // For numbers >= 1,000,000, use scale decomposition
123
+ return buildLargeNumberWords(n)
124
+ }
125
+
126
+ /**
127
+ * Builds words for numbers >= 1,000,000.
128
+ *
129
+ * @param {bigint} n - Number >= 1,000,000
130
+ * @returns {string} Norwegian words
131
+ */
132
+ function buildLargeNumberWords (n) {
133
+ const numStr = n.toString()
134
+ const len = numStr.length
135
+
136
+ // Build segments of 3 digits from right to left
137
+ const segments = []
138
+ const segmentSize = 3
139
+
140
+ const remainderLen = len % segmentSize
141
+ let pos = 0
142
+ if (remainderLen > 0) {
143
+ segments.push(Number(numStr.slice(0, remainderLen)))
144
+ pos = remainderLen
145
+ }
146
+ while (pos < len) {
147
+ segments.push(Number(numStr.slice(pos, pos + segmentSize)))
148
+ pos += segmentSize
149
+ }
150
+
151
+ // Convert segments to words
152
+ const parts = []
153
+ let scaleIndex = segments.length - 1
154
+
155
+ for (let i = 0; i < segments.length; i++) {
156
+ const segment = segments[i]
157
+
158
+ if (segment !== 0) {
159
+ const segmentResult = buildSegment(segment)
160
+
161
+ if (scaleIndex === 0) {
162
+ // Units segment
163
+ parts.push({ word: segmentResult.word, hasHundred: segmentResult.hasHundred, type: 'units' })
164
+ } else if (scaleIndex === 1) {
165
+ // Thousands
166
+ parts.push({ word: segmentResult.word + ' ' + THOUSAND, hasHundred: false, type: 'thousand' })
167
+ } else {
168
+ // Millions+
169
+ const scaleWord = SCALES[scaleIndex - 2]
170
+ parts.push({ word: segmentResult.word + ' ' + scaleWord, hasHundred: false, type: 'million' })
171
+ }
92
172
  }
93
173
 
94
- if (followingValue > precedingValue) {
95
- return { [`${precedingWord} ${followingWord}`]: precedingValue * followingValue }
174
+ scaleIndex--
175
+ }
176
+
177
+ // Join parts with Norwegian rules
178
+ return joinNorwegianParts(parts)
179
+ }
180
+
181
+ /**
182
+ * Joins parts with Norwegian spacing and comma rules.
183
+ *
184
+ * @param {Array} parts - Parts with type metadata
185
+ * @returns {string} Joined string
186
+ */
187
+ function joinNorwegianParts (parts) {
188
+ if (parts.length === 0) return ZERO
189
+ if (parts.length === 1) return parts[0].word
190
+
191
+ const result = []
192
+
193
+ for (let i = 0; i < parts.length; i++) {
194
+ const part = parts[i]
195
+ const nextPart = parts[i + 1]
196
+
197
+ result.push(part.word)
198
+
199
+ if (nextPart) {
200
+ if (part.type === 'thousand') {
201
+ // After thousands: comma if next has hundred, else " og "
202
+ if (nextPart.hasHundred) {
203
+ result.push(', ')
204
+ } else {
205
+ result.push(' og ')
206
+ }
207
+ } else if (part.type === 'million') {
208
+ // After millions: " og " for units without hundred, space otherwise
209
+ if (nextPart.type === 'units' && !nextPart.hasHundred) {
210
+ result.push(' og ')
211
+ } else {
212
+ result.push(' ')
213
+ }
214
+ } else {
215
+ result.push(' ')
216
+ }
96
217
  }
218
+ }
219
+
220
+ return result.join('')
221
+ }
97
222
 
98
- return { [`${precedingWord}, ${followingWord}`]: precedingValue + followingValue }
223
+ /**
224
+ * Converts decimal digits to Norwegian words.
225
+ *
226
+ * @param {string} decimalPart - Decimal digits (without the point)
227
+ * @returns {string} Norwegian words for decimal part
228
+ */
229
+ function decimalPartToWords (decimalPart) {
230
+ let result = ''
231
+
232
+ // Handle leading zeros
233
+ let i = 0
234
+ while (i < decimalPart.length && decimalPart[i] === '0') {
235
+ if (result) result += ' '
236
+ result += ZERO
237
+ i++
238
+ }
239
+
240
+ // Convert remainder as a single number
241
+ const remainder = decimalPart.slice(i)
242
+ if (remainder) {
243
+ if (result) result += ' '
244
+ result += integerToWords(BigInt(remainder))
245
+ }
246
+
247
+ return result
248
+ }
249
+
250
+ /**
251
+ * Converts a numeric value to Norwegian Bokmål words.
252
+ *
253
+ * @param {number | string | bigint} value - The numeric value to convert
254
+ * @returns {string} The number in Norwegian words
255
+ * @throws {TypeError} If value is not a valid numeric type
256
+ * @throws {Error} If value is not a valid number format
257
+ *
258
+ * @example
259
+ * toWords(21) // 'tjue-en'
260
+ * toWords(101) // 'en hundre og en'
261
+ * toWords(1000000) // 'en million'
262
+ */
263
+ function toWords (value) {
264
+ const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
265
+
266
+ let result = ''
267
+
268
+ if (isNegative) {
269
+ result = NEGATIVE + ' '
270
+ }
271
+
272
+ result += integerToWords(integerPart)
273
+
274
+ if (decimalPart) {
275
+ result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)
99
276
  }
277
+
278
+ return result
100
279
  }
280
+
281
+ // ============================================================================
282
+ // Public API
283
+ // ============================================================================
284
+
285
+ 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;