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.
- package/CHANGELOG.md +49 -0
- package/README.md +86 -188
- package/dist/languages/am-Latn.js +3 -0
- package/dist/languages/am-Latn.js.map +1 -0
- package/dist/languages/am.js +3 -0
- package/dist/languages/am.js.map +1 -0
- package/dist/languages/ar.js +3 -0
- package/dist/languages/ar.js.map +1 -0
- package/dist/languages/az.js +3 -0
- package/dist/languages/az.js.map +1 -0
- package/dist/languages/bn.js +3 -0
- package/dist/languages/bn.js.map +1 -0
- package/dist/languages/cs.js +3 -0
- package/dist/languages/cs.js.map +1 -0
- package/dist/languages/da.js +3 -0
- package/dist/languages/da.js.map +1 -0
- package/dist/languages/de.js +3 -0
- package/dist/languages/de.js.map +1 -0
- package/dist/languages/el.js +3 -0
- package/dist/languages/el.js.map +1 -0
- package/dist/languages/en.js +3 -0
- package/dist/languages/en.js.map +1 -0
- package/dist/languages/es.js +3 -0
- package/dist/languages/es.js.map +1 -0
- package/dist/languages/fa.js +3 -0
- package/dist/languages/fa.js.map +1 -0
- package/dist/languages/fi.js +3 -0
- package/dist/languages/fi.js.map +1 -0
- package/dist/languages/fil.js +3 -0
- package/dist/languages/fil.js.map +1 -0
- package/dist/languages/fr-BE.js +3 -0
- package/dist/languages/fr-BE.js.map +1 -0
- package/dist/languages/fr.js +3 -0
- package/dist/languages/fr.js.map +1 -0
- package/dist/languages/gu.js +3 -0
- package/dist/languages/gu.js.map +1 -0
- package/dist/languages/ha.js +3 -0
- package/dist/languages/ha.js.map +1 -0
- package/dist/languages/hbo.js +3 -0
- package/dist/languages/hbo.js.map +1 -0
- package/dist/languages/he.js +3 -0
- package/dist/languages/he.js.map +1 -0
- package/dist/languages/hi.js +3 -0
- package/dist/languages/hi.js.map +1 -0
- package/dist/languages/hr.js +3 -0
- package/dist/languages/hr.js.map +1 -0
- package/dist/languages/hu.js +3 -0
- package/dist/languages/hu.js.map +1 -0
- package/dist/languages/id.js +3 -0
- package/dist/languages/id.js.map +1 -0
- package/dist/languages/it.js +3 -0
- package/dist/languages/it.js.map +1 -0
- package/dist/languages/ja.js +3 -0
- package/dist/languages/ja.js.map +1 -0
- package/dist/languages/kn.js +3 -0
- package/dist/languages/kn.js.map +1 -0
- package/dist/languages/ko.js +3 -0
- package/dist/languages/ko.js.map +1 -0
- package/dist/languages/lt.js +3 -0
- package/dist/languages/lt.js.map +1 -0
- package/dist/languages/lv.js +3 -0
- package/dist/languages/lv.js.map +1 -0
- package/dist/languages/mr.js +3 -0
- package/dist/languages/mr.js.map +1 -0
- package/dist/languages/ms.js +3 -0
- package/dist/languages/ms.js.map +1 -0
- package/dist/languages/nb.js +3 -0
- package/dist/languages/nb.js.map +1 -0
- package/dist/languages/nl.js +3 -0
- package/dist/languages/nl.js.map +1 -0
- package/dist/languages/pa.js +3 -0
- package/dist/languages/pa.js.map +1 -0
- package/dist/languages/pl.js +3 -0
- package/dist/languages/pl.js.map +1 -0
- package/dist/languages/pt.js +3 -0
- package/dist/languages/pt.js.map +1 -0
- package/dist/languages/ro.js +3 -0
- package/dist/languages/ro.js.map +1 -0
- package/dist/languages/ru.js +3 -0
- package/dist/languages/ru.js.map +1 -0
- package/dist/languages/sr-Cyrl.js +3 -0
- package/dist/languages/sr-Cyrl.js.map +1 -0
- package/dist/languages/sr-Latn.js +3 -0
- package/dist/languages/sr-Latn.js.map +1 -0
- package/dist/languages/sv.js +3 -0
- package/dist/languages/sv.js.map +1 -0
- package/dist/languages/sw.js +3 -0
- package/dist/languages/sw.js.map +1 -0
- package/dist/languages/ta.js +3 -0
- package/dist/languages/ta.js.map +1 -0
- package/dist/languages/te.js +3 -0
- package/dist/languages/te.js.map +1 -0
- package/dist/languages/th.js +3 -0
- package/dist/languages/th.js.map +1 -0
- package/dist/languages/tr.js +3 -0
- package/dist/languages/tr.js.map +1 -0
- package/dist/languages/uk.js +3 -0
- package/dist/languages/uk.js.map +1 -0
- package/dist/languages/ur.js +3 -0
- package/dist/languages/ur.js.map +1 -0
- package/dist/languages/vi.js +3 -0
- package/dist/languages/vi.js.map +1 -0
- package/dist/languages/zh-Hans.js +3 -0
- package/dist/languages/zh-Hans.js.map +1 -0
- package/dist/languages/zh-Hant.js +3 -0
- package/dist/languages/zh-Hant.js.map +1 -0
- package/dist/n2words.js +2 -2
- package/dist/n2words.js.map +1 -1
- package/lib/languages/am-Latn.d.ts +7 -0
- package/lib/languages/am-Latn.js +164 -0
- package/lib/languages/am.d.ts +7 -0
- package/lib/languages/am.js +164 -0
- package/lib/languages/ar.d.ts +14 -27
- package/lib/languages/ar.js +175 -129
- package/lib/languages/az.d.ts +4 -9
- package/lib/languages/az.js +171 -37
- package/lib/languages/bn.d.ts +4 -8
- package/lib/languages/bn.js +138 -124
- package/lib/languages/cs.d.ts +15 -85
- package/lib/languages/cs.js +310 -114
- package/lib/languages/da.d.ts +11 -12
- package/lib/languages/da.js +276 -101
- package/lib/languages/de.d.ts +14 -11
- package/lib/languages/de.js +317 -86
- package/lib/languages/el.d.ts +11 -11
- package/lib/languages/el.js +231 -78
- package/lib/languages/en.d.ts +14 -13
- package/lib/languages/en.js +242 -72
- package/lib/languages/es.d.ts +18 -12
- package/lib/languages/es.js +317 -103
- package/lib/languages/fa.d.ts +4 -44
- package/lib/languages/fa.js +112 -122
- package/lib/languages/fi.d.ts +14 -0
- package/lib/languages/fi.js +245 -0
- package/lib/languages/fil.d.ts +4 -13
- package/lib/languages/fil.js +207 -106
- package/lib/languages/fr-BE.d.ts +8 -8
- package/lib/languages/fr-BE.js +294 -19
- package/lib/languages/fr.d.ts +18 -12
- package/lib/languages/fr.js +352 -89
- package/lib/languages/gu.d.ts +4 -8
- package/lib/languages/gu.js +130 -125
- package/lib/languages/ha.d.ts +7 -0
- package/lib/languages/ha.js +230 -0
- package/lib/languages/hbo.d.ts +10 -110
- package/lib/languages/hbo.js +263 -214
- package/lib/languages/he.d.ts +10 -77
- package/lib/languages/he.js +242 -172
- package/lib/languages/hi.d.ts +4 -8
- package/lib/languages/hi.js +138 -124
- package/lib/languages/hr.d.ts +8 -77
- package/lib/languages/hr.js +194 -89
- package/lib/languages/hu.d.ts +4 -19
- package/lib/languages/hu.js +198 -119
- package/lib/languages/id.d.ts +4 -34
- package/lib/languages/id.js +171 -129
- package/lib/languages/it.d.ts +16 -34
- package/lib/languages/it.js +339 -94
- package/lib/languages/ja.d.ts +14 -14
- package/lib/languages/ja.js +233 -111
- package/lib/languages/kn.d.ts +4 -8
- package/lib/languages/kn.js +130 -35
- package/lib/languages/ko.d.ts +11 -11
- package/lib/languages/ko.js +257 -49
- package/lib/languages/lt.d.ts +15 -67
- package/lib/languages/lt.js +296 -122
- package/lib/languages/lv.d.ts +15 -67
- package/lib/languages/lv.js +297 -106
- package/lib/languages/mr.d.ts +4 -8
- package/lib/languages/mr.js +130 -125
- package/lib/languages/ms.d.ts +4 -28
- package/lib/languages/ms.js +171 -116
- package/lib/languages/nb.d.ts +11 -9
- package/lib/languages/nb.js +282 -87
- package/lib/languages/nl.d.ts +23 -13
- package/lib/languages/nl.js +317 -133
- package/lib/languages/pa.d.ts +4 -8
- package/lib/languages/pa.js +156 -124
- package/lib/languages/pl.d.ts +19 -77
- package/lib/languages/pl.js +307 -87
- package/lib/languages/pt.d.ts +14 -26
- package/lib/languages/pt.js +286 -92
- package/lib/languages/ro.d.ts +15 -155
- package/lib/languages/ro.js +219 -235
- package/lib/languages/ru.d.ts +8 -82
- package/lib/languages/ru.js +222 -78
- package/lib/languages/sr-Cyrl.d.ts +8 -77
- package/lib/languages/sr-Cyrl.js +191 -89
- package/lib/languages/sr-Latn.d.ts +8 -77
- package/lib/languages/sr-Latn.js +191 -89
- package/lib/languages/sv.d.ts +11 -11
- package/lib/languages/sv.js +288 -74
- package/lib/languages/sw.d.ts +4 -36
- package/lib/languages/sw.js +133 -106
- package/lib/languages/ta.d.ts +4 -17
- package/lib/languages/ta.js +129 -201
- package/lib/languages/te.d.ts +4 -19
- package/lib/languages/te.js +141 -196
- package/lib/languages/th.d.ts +4 -14
- package/lib/languages/th.js +135 -91
- package/lib/languages/tr.d.ts +15 -9
- package/lib/languages/tr.js +256 -49
- package/lib/languages/uk.d.ts +8 -82
- package/lib/languages/uk.js +200 -78
- package/lib/languages/ur.d.ts +4 -8
- package/lib/languages/ur.js +156 -124
- package/lib/languages/vi.d.ts +14 -69
- package/lib/languages/vi.js +294 -125
- package/lib/languages/zh-Hans.d.ts +8 -18
- package/lib/languages/zh-Hans.js +163 -92
- package/lib/languages/zh-Hant.d.ts +8 -18
- package/lib/languages/zh-Hant.js +181 -90
- package/lib/n2words.d.ts +53 -209
- package/lib/n2words.js +111 -530
- package/lib/utils/is-plain-object.d.ts +13 -0
- package/lib/utils/is-plain-object.js +17 -0
- package/lib/utils/parse-numeric.d.ts +17 -0
- package/lib/utils/parse-numeric.js +108 -0
- package/lib/utils/validate-options.d.ts +8 -0
- package/lib/utils/validate-options.js +16 -0
- package/package.json +26 -14
- package/dist/ArabicConverter.js +0 -3
- package/dist/ArabicConverter.js.map +0 -1
- package/dist/AzerbaijaniConverter.js +0 -3
- package/dist/AzerbaijaniConverter.js.map +0 -1
- package/dist/BanglaConverter.js +0 -3
- package/dist/BanglaConverter.js.map +0 -1
- package/dist/BiblicalHebrewConverter.js +0 -3
- package/dist/BiblicalHebrewConverter.js.map +0 -1
- package/dist/CroatianConverter.js +0 -3
- package/dist/CroatianConverter.js.map +0 -1
- package/dist/CzechConverter.js +0 -3
- package/dist/CzechConverter.js.map +0 -1
- package/dist/DanishConverter.js +0 -3
- package/dist/DanishConverter.js.map +0 -1
- package/dist/DutchConverter.js +0 -3
- package/dist/DutchConverter.js.map +0 -1
- package/dist/EnglishConverter.js +0 -3
- package/dist/EnglishConverter.js.map +0 -1
- package/dist/FilipinoConverter.js +0 -3
- package/dist/FilipinoConverter.js.map +0 -1
- package/dist/FrenchBelgiumConverter.js +0 -3
- package/dist/FrenchBelgiumConverter.js.map +0 -1
- package/dist/FrenchConverter.js +0 -3
- package/dist/FrenchConverter.js.map +0 -1
- package/dist/GermanConverter.js +0 -3
- package/dist/GermanConverter.js.map +0 -1
- package/dist/GreekConverter.js +0 -3
- package/dist/GreekConverter.js.map +0 -1
- package/dist/GujaratiConverter.js +0 -3
- package/dist/GujaratiConverter.js.map +0 -1
- package/dist/HebrewConverter.js +0 -3
- package/dist/HebrewConverter.js.map +0 -1
- package/dist/HindiConverter.js +0 -3
- package/dist/HindiConverter.js.map +0 -1
- package/dist/HungarianConverter.js +0 -3
- package/dist/HungarianConverter.js.map +0 -1
- package/dist/IndonesianConverter.js +0 -3
- package/dist/IndonesianConverter.js.map +0 -1
- package/dist/ItalianConverter.js +0 -3
- package/dist/ItalianConverter.js.map +0 -1
- package/dist/JapaneseConverter.js +0 -3
- package/dist/JapaneseConverter.js.map +0 -1
- package/dist/KannadaConverter.js +0 -3
- package/dist/KannadaConverter.js.map +0 -1
- package/dist/KoreanConverter.js +0 -3
- package/dist/KoreanConverter.js.map +0 -1
- package/dist/LatvianConverter.js +0 -3
- package/dist/LatvianConverter.js.map +0 -1
- package/dist/LithuanianConverter.js +0 -3
- package/dist/LithuanianConverter.js.map +0 -1
- package/dist/MalayConverter.js +0 -3
- package/dist/MalayConverter.js.map +0 -1
- package/dist/MarathiConverter.js +0 -3
- package/dist/MarathiConverter.js.map +0 -1
- package/dist/NorwegianBokmalConverter.js +0 -3
- package/dist/NorwegianBokmalConverter.js.map +0 -1
- package/dist/PersianConverter.js +0 -3
- package/dist/PersianConverter.js.map +0 -1
- package/dist/PolishConverter.js +0 -3
- package/dist/PolishConverter.js.map +0 -1
- package/dist/PortugueseConverter.js +0 -3
- package/dist/PortugueseConverter.js.map +0 -1
- package/dist/PunjabiConverter.js +0 -3
- package/dist/PunjabiConverter.js.map +0 -1
- package/dist/RomanianConverter.js +0 -3
- package/dist/RomanianConverter.js.map +0 -1
- package/dist/RussianConverter.js +0 -3
- package/dist/RussianConverter.js.map +0 -1
- package/dist/SerbianCyrillicConverter.js +0 -3
- package/dist/SerbianCyrillicConverter.js.map +0 -1
- package/dist/SerbianLatinConverter.js +0 -3
- package/dist/SerbianLatinConverter.js.map +0 -1
- package/dist/SimplifiedChineseConverter.js +0 -3
- package/dist/SimplifiedChineseConverter.js.map +0 -1
- package/dist/SpanishConverter.js +0 -3
- package/dist/SpanishConverter.js.map +0 -1
- package/dist/SwahiliConverter.js +0 -3
- package/dist/SwahiliConverter.js.map +0 -1
- package/dist/SwedishConverter.js +0 -3
- package/dist/SwedishConverter.js.map +0 -1
- package/dist/TamilConverter.js +0 -3
- package/dist/TamilConverter.js.map +0 -1
- package/dist/TeluguConverter.js +0 -3
- package/dist/TeluguConverter.js.map +0 -1
- package/dist/ThaiConverter.js +0 -3
- package/dist/ThaiConverter.js.map +0 -1
- package/dist/TraditionalChineseConverter.js +0 -3
- package/dist/TraditionalChineseConverter.js.map +0 -1
- package/dist/TurkishConverter.js +0 -3
- package/dist/TurkishConverter.js.map +0 -1
- package/dist/UkrainianConverter.js +0 -3
- package/dist/UkrainianConverter.js.map +0 -1
- package/dist/UrduConverter.js +0 -3
- package/dist/UrduConverter.js.map +0 -1
- package/dist/VietnameseConverter.js +0 -3
- package/dist/VietnameseConverter.js.map +0 -1
- package/lib/classes/abstract-language.d.ts +0 -178
- package/lib/classes/abstract-language.js +0 -268
- package/lib/classes/greedy-scale-language.d.ts +0 -109
- package/lib/classes/greedy-scale-language.js +0 -201
- package/lib/classes/slavic-language.d.ts +0 -148
- package/lib/classes/slavic-language.js +0 -281
- package/lib/classes/south-asian-language.d.ts +0 -70
- package/lib/classes/south-asian-language.js +0 -154
- package/lib/classes/turkic-language.d.ts +0 -26
- package/lib/classes/turkic-language.js +0 -59
package/lib/languages/nl.js
CHANGED
|
@@ -1,155 +1,339 @@
|
|
|
1
|
-
import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js'
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
|
-
* Dutch language converter
|
|
2
|
+
* Dutch language converter - Functional Implementation
|
|
3
|
+
*
|
|
4
|
+
* A performance-optimized number-to-words converter using precomputed lookup tables.
|
|
5
|
+
* Self-contained module with its own input validation, ready for subpath exports.
|
|
6
|
+
*
|
|
7
|
+
* Key optimization: Precompute all segment values (0-999) at module load.
|
|
8
|
+
* This eliminates all per-call string manipulation for segment conversion.
|
|
5
9
|
*
|
|
6
|
-
*
|
|
7
|
-
* -
|
|
8
|
-
* -
|
|
9
|
-
* - Compound
|
|
10
|
+
* Dutch-specific rules (handled in precomputation):
|
|
11
|
+
* - Inverted tens-ones: eenentwintig (one-and-twenty)
|
|
12
|
+
* - "ën" connector when ones ends in 'e' (twee, drie)
|
|
13
|
+
* - Compound words without spaces
|
|
14
|
+
* - Hundred pairing for 1100-9999 (elfhonderd style)
|
|
15
|
+
* - "één" vs "een" (accentOne option)
|
|
16
|
+
* - Optional "en" separator (includeOptionalAnd option)
|
|
17
|
+
* - Long scale with -ard forms
|
|
10
18
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
super()
|
|
59
|
-
|
|
60
|
-
this.setOptions({
|
|
61
|
-
includeOptionalAnd: false,
|
|
62
|
-
noHundredPairing: false,
|
|
63
|
-
accentOne: true
|
|
64
|
-
}, options)
|
|
65
|
-
|
|
66
|
-
if (!this.options.accentOne) {
|
|
67
|
-
this.scaleWords[this.scaleWords.length - 2][1] = 'een'
|
|
19
|
+
|
|
20
|
+
import { parseNumericValue } from '../utils/parse-numeric.js'
|
|
21
|
+
import { validateOptions } from '../utils/validate-options.js'
|
|
22
|
+
|
|
23
|
+
// ============================================================================
|
|
24
|
+
// Vocabulary (module-level constants)
|
|
25
|
+
// ============================================================================
|
|
26
|
+
|
|
27
|
+
const ONES = ['', 'een', 'twee', 'drie', 'vier', 'vijf', 'zes', 'zeven', 'acht', 'negen']
|
|
28
|
+
const TEENS = ['tien', 'elf', 'twaalf', 'dertien', 'veertien', 'vijftien', 'zestien', 'zeventien', 'achttien', 'negentien']
|
|
29
|
+
const TENS = ['', '', 'twintig', 'dertig', 'veertig', 'vijftig', 'zestig', 'zeventig', 'tachtig', 'negentig']
|
|
30
|
+
|
|
31
|
+
const HUNDRED = 'honderd'
|
|
32
|
+
|
|
33
|
+
// Scale words (long scale with -ard forms)
|
|
34
|
+
const SCALES = ['duizend', 'miljoen', 'miljard', 'biljoen', 'biljard', 'triljoen', 'triljard', 'quadriljoen', 'quadriljard']
|
|
35
|
+
|
|
36
|
+
const ZERO = 'nul'
|
|
37
|
+
const NEGATIVE = 'min'
|
|
38
|
+
const DECIMAL_SEP = 'komma'
|
|
39
|
+
|
|
40
|
+
// ============================================================================
|
|
41
|
+
// Precomputed Lookup Tables (built once at module load)
|
|
42
|
+
// ============================================================================
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Builds segment word for 0-999.
|
|
46
|
+
* @param {number} n - Segment value
|
|
47
|
+
* @param {boolean} withAnd - Include "en" for values < 13 after hundreds
|
|
48
|
+
* @returns {string} Dutch word (compound, no spaces)
|
|
49
|
+
*/
|
|
50
|
+
function buildSegment (n, withAnd) {
|
|
51
|
+
if (n === 0) return ''
|
|
52
|
+
|
|
53
|
+
const ones = n % 10
|
|
54
|
+
const tens = Math.floor(n / 10) % 10
|
|
55
|
+
const hundreds = Math.floor(n / 100)
|
|
56
|
+
const tensOnes = n % 100
|
|
57
|
+
|
|
58
|
+
let result = ''
|
|
59
|
+
|
|
60
|
+
// Hundreds
|
|
61
|
+
if (hundreds > 0) {
|
|
62
|
+
if (hundreds === 1) {
|
|
63
|
+
result = HUNDRED
|
|
64
|
+
} else {
|
|
65
|
+
result = ONES[hundreds] + HUNDRED
|
|
68
66
|
}
|
|
69
67
|
}
|
|
70
68
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
69
|
+
// Tens and ones
|
|
70
|
+
if (tensOnes === 0) {
|
|
71
|
+
// Just hundreds
|
|
72
|
+
} else if (tensOnes < 10) {
|
|
73
|
+
// Single digit - add "en" if withAnd and after hundreds
|
|
74
|
+
if (hundreds > 0 && withAnd) {
|
|
75
|
+
result += 'en' + ONES[tensOnes]
|
|
76
|
+
} else {
|
|
77
|
+
result += ONES[tensOnes]
|
|
78
|
+
}
|
|
79
|
+
} else if (tensOnes < 20) {
|
|
80
|
+
// Teens - add "en" if withAnd and after hundreds and < 13
|
|
81
|
+
if (hundreds > 0 && withAnd && tensOnes < 13) {
|
|
82
|
+
result += 'en' + TEENS[ones]
|
|
83
|
+
} else {
|
|
84
|
+
result += TEENS[ones]
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
// 20-99: Dutch inverts with connector
|
|
88
|
+
if (ones === 0) {
|
|
89
|
+
result += TENS[tens]
|
|
90
|
+
} else {
|
|
91
|
+
// "ën" if ones ends in 'e' (twee, drie)
|
|
92
|
+
const onesWord = ONES[ones]
|
|
93
|
+
const connector = onesWord.endsWith('e') ? 'ën' : 'en'
|
|
94
|
+
result += onesWord + connector + TENS[tens]
|
|
84
95
|
}
|
|
96
|
+
}
|
|
85
97
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
98
|
+
return result
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Precompute all 1000 segment words (0-999) - standard form
|
|
102
|
+
const SEGMENTS = new Array(1000)
|
|
103
|
+
for (let i = 0; i < 1000; i++) {
|
|
104
|
+
SEGMENTS[i] = buildSegment(i, false)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Precompute all 1000 segment words (0-999) - with optional "en"
|
|
108
|
+
const SEGMENTS_WITH_AND = new Array(1000)
|
|
109
|
+
for (let i = 0; i < 1000; i++) {
|
|
110
|
+
SEGMENTS_WITH_AND[i] = buildSegment(i, true)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ============================================================================
|
|
114
|
+
// Conversion Functions
|
|
115
|
+
// ============================================================================
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Converts a non-negative integer to Dutch words.
|
|
119
|
+
*
|
|
120
|
+
* @param {bigint} n - Non-negative integer to convert
|
|
121
|
+
* @param {Object} options - Conversion options
|
|
122
|
+
* @returns {string} Dutch words
|
|
123
|
+
*/
|
|
124
|
+
function integerToWords (n, options) {
|
|
125
|
+
if (n === 0n) return ZERO
|
|
126
|
+
|
|
127
|
+
const { accentOne, includeOptionalAnd, noHundredPairing } = options
|
|
128
|
+
const segments = includeOptionalAnd ? SEGMENTS_WITH_AND : SEGMENTS
|
|
129
|
+
|
|
130
|
+
// Apply één/een replacement
|
|
131
|
+
const applyAccent = (word) => {
|
|
132
|
+
if (accentOne) {
|
|
133
|
+
return word.replace(/\been\b/g, 'één')
|
|
134
|
+
}
|
|
135
|
+
return word
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Fast path: numbers < 1000 (direct lookup)
|
|
139
|
+
if (n < 1000n) {
|
|
140
|
+
return applyAccent(segments[Number(n)])
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Hundred pairing for 1100-9999
|
|
144
|
+
if (!noHundredPairing && n >= 1100n && n < 10000n) {
|
|
145
|
+
const high = Number(n / 100n)
|
|
146
|
+
const low = Number(n % 100n)
|
|
147
|
+
|
|
148
|
+
// Only use pairing when high is not a multiple of 10
|
|
149
|
+
if (high % 10 !== 0) {
|
|
150
|
+
let result = segments[high] + HUNDRED
|
|
151
|
+
if (low > 0) {
|
|
152
|
+
const lowWord = segments[low]
|
|
153
|
+
if (includeOptionalAnd && low < 13) {
|
|
154
|
+
result += ' en ' + lowWord
|
|
155
|
+
} else {
|
|
156
|
+
result += ' ' + lowWord
|
|
157
|
+
}
|
|
102
158
|
}
|
|
103
|
-
return
|
|
159
|
+
return applyAccent(result)
|
|
104
160
|
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Fast path: numbers < 1,000,000 (thousands)
|
|
164
|
+
if (n < 1_000_000n) {
|
|
165
|
+
const thousands = Number(n / 1000n)
|
|
166
|
+
const remainder = Number(n % 1000n)
|
|
105
167
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
precedingWord = `${temporary}${andTxt}`
|
|
114
|
-
} else if (followingValue < 13n && precedingValue < 1000n && this.options.includeOptionalAnd) {
|
|
115
|
-
precedingWord = `${precedingWord}en`
|
|
116
|
-
} else if (followingValue < 13n && precedingValue >= 1000n && this.options.includeOptionalAnd) {
|
|
117
|
-
followingWord = ` en ${followingWord}`
|
|
118
|
-
hasSpace = true
|
|
119
|
-
} else if (precedingValue >= 1_000_000n) {
|
|
120
|
-
precedingWord += ' '
|
|
121
|
-
hasSpace = true
|
|
122
|
-
} else if (precedingValue === 1000n) {
|
|
123
|
-
precedingWord += ' '
|
|
124
|
-
hasSpace = true
|
|
168
|
+
let result
|
|
169
|
+
if (thousands === 1) {
|
|
170
|
+
// "duizend" not "eenduizend"
|
|
171
|
+
result = SCALES[0]
|
|
172
|
+
} else {
|
|
173
|
+
// Compound: "vijfduizend"
|
|
174
|
+
result = segments[thousands] + SCALES[0]
|
|
125
175
|
}
|
|
126
176
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
followingWord = followingWord.replace(/één/g, 'een')
|
|
177
|
+
if (remainder > 0) {
|
|
178
|
+
const remainderWord = segments[remainder]
|
|
179
|
+
if (includeOptionalAnd && remainder < 13) {
|
|
180
|
+
result += ' en ' + remainderWord
|
|
181
|
+
} else {
|
|
182
|
+
result += ' ' + remainderWord
|
|
183
|
+
}
|
|
135
184
|
}
|
|
136
185
|
|
|
137
|
-
return
|
|
186
|
+
return applyAccent(result)
|
|
138
187
|
}
|
|
139
188
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
189
|
+
// For numbers >= 1,000,000, use scale decomposition
|
|
190
|
+
return applyAccent(buildLargeNumberWords(n, options))
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Builds words for numbers >= 1,000,000.
|
|
195
|
+
* Uses BigInt division for faster segment extraction (4x faster than string slicing).
|
|
196
|
+
*
|
|
197
|
+
* @param {bigint} n - Number >= 1,000,000
|
|
198
|
+
* @param {Object} options - Conversion options
|
|
199
|
+
* @returns {string} Dutch words
|
|
200
|
+
*/
|
|
201
|
+
function buildLargeNumberWords (n, options) {
|
|
202
|
+
const { includeOptionalAnd } = options
|
|
203
|
+
const segmentLookup = includeOptionalAnd ? SEGMENTS_WITH_AND : SEGMENTS
|
|
204
|
+
|
|
205
|
+
// Extract segments using BigInt division (faster than string slicing)
|
|
206
|
+
// Segments stored least-significant first (index 0 = ones, 1 = thousands, etc.)
|
|
207
|
+
const segmentValues = []
|
|
208
|
+
let temp = n
|
|
209
|
+
while (temp > 0n) {
|
|
210
|
+
segmentValues.push(Number(temp % 1000n))
|
|
211
|
+
temp = temp / 1000n
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Build result string directly (avoids object allocation and join)
|
|
215
|
+
let result = ''
|
|
216
|
+
let prevWasScale = false
|
|
217
|
+
|
|
218
|
+
for (let i = segmentValues.length - 1; i >= 0; i--) {
|
|
219
|
+
const segment = segmentValues[i]
|
|
220
|
+
if (segment === 0) continue
|
|
221
|
+
|
|
222
|
+
if (i === 0) {
|
|
223
|
+
// Units segment
|
|
224
|
+
const word = segmentLookup[segment]
|
|
225
|
+
if (result) {
|
|
226
|
+
if (prevWasScale && includeOptionalAnd && segment < 13) {
|
|
227
|
+
result += ' en ' + word
|
|
228
|
+
} else {
|
|
229
|
+
result += ' ' + word
|
|
149
230
|
}
|
|
150
|
-
|
|
231
|
+
} else {
|
|
232
|
+
result = word
|
|
151
233
|
}
|
|
234
|
+
prevWasScale = false
|
|
235
|
+
} else if (i === 1) {
|
|
236
|
+
// Thousands - compound
|
|
237
|
+
if (result) result += ' '
|
|
238
|
+
if (segment === 1) {
|
|
239
|
+
result += SCALES[0]
|
|
240
|
+
} else {
|
|
241
|
+
result += segmentLookup[segment] + SCALES[0]
|
|
242
|
+
}
|
|
243
|
+
prevWasScale = true
|
|
244
|
+
} else {
|
|
245
|
+
// Million and above - space around scale
|
|
246
|
+
const scaleWord = SCALES[i - 1]
|
|
247
|
+
if (result) result += ' '
|
|
248
|
+
if (segment === 1) {
|
|
249
|
+
result += 'een ' + scaleWord
|
|
250
|
+
} else {
|
|
251
|
+
result += segmentLookup[segment] + ' ' + scaleWord
|
|
252
|
+
}
|
|
253
|
+
prevWasScale = true
|
|
152
254
|
}
|
|
153
|
-
return super.integerToWords(integerPart)
|
|
154
255
|
}
|
|
256
|
+
|
|
257
|
+
return result
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Converts decimal digits to Dutch words.
|
|
262
|
+
*
|
|
263
|
+
* @param {string} decimalPart - Decimal digits (without the point)
|
|
264
|
+
* @param {Object} options - Conversion options
|
|
265
|
+
* @returns {string} Dutch words for decimal part
|
|
266
|
+
*/
|
|
267
|
+
function decimalPartToWords (decimalPart, options) {
|
|
268
|
+
let result = ''
|
|
269
|
+
|
|
270
|
+
// Handle leading zeros
|
|
271
|
+
let i = 0
|
|
272
|
+
while (i < decimalPart.length && decimalPart[i] === '0') {
|
|
273
|
+
if (result) result += ' '
|
|
274
|
+
result += ZERO
|
|
275
|
+
i++
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Convert remainder as a single number
|
|
279
|
+
const remainder = decimalPart.slice(i)
|
|
280
|
+
if (remainder) {
|
|
281
|
+
if (result) result += ' '
|
|
282
|
+
const word = integerToWords(BigInt(remainder), { ...options, noHundredPairing: true })
|
|
283
|
+
result += word
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return result
|
|
155
287
|
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Converts a numeric value to Dutch words.
|
|
291
|
+
*
|
|
292
|
+
* This is the main public API. It accepts any valid numeric input
|
|
293
|
+
* (number, string, or bigint) and handles parsing internally.
|
|
294
|
+
*
|
|
295
|
+
* @param {number | string | bigint} value - The numeric value to convert
|
|
296
|
+
* @param {Object} [options] - Optional configuration
|
|
297
|
+
* @param {boolean} [options.accentOne=true] - Use "één" instead of "een"
|
|
298
|
+
* @param {boolean} [options.includeOptionalAnd=false] - Include "en" before small numbers
|
|
299
|
+
* @param {boolean} [options.noHundredPairing=false] - Disable hundred pairing (1104→duizend honderdvier)
|
|
300
|
+
* @returns {string} The number in Dutch words
|
|
301
|
+
* @throws {TypeError} If value is not a valid numeric type
|
|
302
|
+
* @throws {Error} If value is not a valid number format
|
|
303
|
+
*
|
|
304
|
+
* @example
|
|
305
|
+
* toWords(21) // 'eenentwintig'
|
|
306
|
+
* toWords(1) // 'één'
|
|
307
|
+
* toWords(1, {accentOne: false}) // 'een'
|
|
308
|
+
* toWords(1104) // 'elfhonderd vier'
|
|
309
|
+
*/
|
|
310
|
+
function toWords (value, options) {
|
|
311
|
+
options = validateOptions(options)
|
|
312
|
+
const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
|
|
313
|
+
|
|
314
|
+
const opts = {
|
|
315
|
+
accentOne: options.accentOne !== false, // default true
|
|
316
|
+
includeOptionalAnd: options.includeOptionalAnd || false,
|
|
317
|
+
noHundredPairing: options.noHundredPairing || false
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
let result = ''
|
|
321
|
+
|
|
322
|
+
if (isNegative) {
|
|
323
|
+
result = NEGATIVE + ' '
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
result += integerToWords(integerPart, opts)
|
|
327
|
+
|
|
328
|
+
if (decimalPart) {
|
|
329
|
+
result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart, opts)
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return result
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// ============================================================================
|
|
336
|
+
// Public API
|
|
337
|
+
// ============================================================================
|
|
338
|
+
|
|
339
|
+
export { toWords }
|
package/lib/languages/pa.d.ts
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Punjabi
|
|
2
|
+
* Converts a numeric value to Punjabi words.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* - Gurmukhi script
|
|
7
|
-
* - Complete word forms for 0-99
|
|
4
|
+
* @param {number | string | bigint} value - The numeric value to convert
|
|
5
|
+
* @returns {string} The number in Punjabi words
|
|
8
6
|
*/
|
|
9
|
-
export
|
|
10
|
-
}
|
|
11
|
-
import { SouthAsianLanguage } from '../classes/south-asian-language.js';
|
|
7
|
+
export function toWords(value: number | string | bigint): string;
|