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.
- package/CHANGELOG.md +64 -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/ka.js +3 -0
- package/dist/languages/ka.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/yo.js +3 -0
- package/dist/languages/yo.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 +159 -0
- package/lib/languages/am.d.ts +7 -0
- package/lib/languages/am.js +159 -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 +166 -37
- package/lib/languages/bn.d.ts +4 -8
- package/lib/languages/bn.js +159 -124
- package/lib/languages/cs.d.ts +15 -85
- package/lib/languages/cs.js +293 -114
- package/lib/languages/da.d.ts +11 -12
- package/lib/languages/da.js +269 -101
- package/lib/languages/de.d.ts +14 -11
- package/lib/languages/de.js +305 -86
- package/lib/languages/el.d.ts +11 -11
- package/lib/languages/el.js +224 -78
- package/lib/languages/en.d.ts +14 -13
- package/lib/languages/en.js +228 -72
- package/lib/languages/es.d.ts +18 -12
- package/lib/languages/es.js +297 -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 +238 -0
- package/lib/languages/fil.d.ts +4 -13
- package/lib/languages/fil.js +196 -106
- package/lib/languages/fr-BE.d.ts +8 -8
- package/lib/languages/fr-BE.js +285 -19
- package/lib/languages/fr.d.ts +18 -12
- package/lib/languages/fr.js +339 -89
- package/lib/languages/gu.d.ts +4 -8
- package/lib/languages/gu.js +143 -125
- package/lib/languages/ha.d.ts +7 -0
- package/lib/languages/ha.js +225 -0
- package/lib/languages/hbo.d.ts +10 -110
- package/lib/languages/hbo.js +245 -214
- package/lib/languages/he.d.ts +10 -77
- package/lib/languages/he.js +231 -172
- package/lib/languages/hi.d.ts +4 -8
- package/lib/languages/hi.js +163 -124
- package/lib/languages/hr.d.ts +8 -77
- package/lib/languages/hr.js +200 -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 +166 -129
- package/lib/languages/it.d.ts +16 -34
- package/lib/languages/it.js +307 -97
- package/lib/languages/ja.d.ts +14 -14
- package/lib/languages/ja.js +221 -111
- package/lib/languages/ka.d.ts +17 -0
- package/lib/languages/ka.js +291 -0
- package/lib/languages/kn.d.ts +4 -8
- package/lib/languages/kn.js +143 -35
- package/lib/languages/ko.d.ts +11 -11
- package/lib/languages/ko.js +250 -49
- package/lib/languages/lt.d.ts +15 -67
- package/lib/languages/lt.js +287 -122
- package/lib/languages/lv.d.ts +15 -67
- package/lib/languages/lv.js +288 -106
- package/lib/languages/mr.d.ts +4 -8
- package/lib/languages/mr.js +143 -125
- package/lib/languages/ms.d.ts +4 -28
- package/lib/languages/ms.js +166 -116
- package/lib/languages/nb.d.ts +11 -9
- package/lib/languages/nb.js +272 -87
- package/lib/languages/nl.d.ts +23 -13
- package/lib/languages/nl.js +299 -133
- package/lib/languages/pa.d.ts +4 -8
- package/lib/languages/pa.js +151 -124
- package/lib/languages/pl.d.ts +19 -77
- package/lib/languages/pl.js +294 -87
- package/lib/languages/pt.d.ts +14 -26
- package/lib/languages/pt.js +272 -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 +239 -90
- package/lib/languages/sr-Cyrl.d.ts +8 -77
- package/lib/languages/sr-Cyrl.js +197 -89
- package/lib/languages/sr-Latn.d.ts +8 -77
- package/lib/languages/sr-Latn.js +197 -89
- package/lib/languages/sv.d.ts +11 -11
- package/lib/languages/sv.js +278 -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 +143 -202
- package/lib/languages/te.d.ts +4 -19
- package/lib/languages/te.js +133 -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 +245 -49
- package/lib/languages/uk.d.ts +8 -82
- package/lib/languages/uk.js +206 -78
- package/lib/languages/ur.d.ts +4 -8
- package/lib/languages/ur.js +151 -124
- package/lib/languages/vi.d.ts +14 -69
- package/lib/languages/vi.js +278 -129
- package/lib/languages/yo.d.ts +7 -0
- package/lib/languages/yo.js +303 -0
- 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 +55 -209
- package/lib/n2words.js +115 -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/it.js
CHANGED
|
@@ -1,132 +1,342 @@
|
|
|
1
|
-
import { AbstractLanguage } from '../classes/abstract-language.js'
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
|
-
* Italian language converter
|
|
2
|
+
* Italian language converter - Functional Implementation
|
|
3
|
+
*
|
|
4
|
+
* Self-contained module with its own input validation, ready for subpath exports.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* -
|
|
8
|
-
* -
|
|
9
|
-
* -
|
|
6
|
+
* Italian-specific rules:
|
|
7
|
+
* - Concatenation without spaces within segments ("ventotto" not "venti otto")
|
|
8
|
+
* - Phonetic vowel elision: "venti" + "otto" → "ventotto"
|
|
9
|
+
* - Accent on final "tre" in compounds: "ventitré"
|
|
10
|
+
* - mille/mila alternation for thousands
|
|
11
|
+
* - Scale words: milione/milioni, miliardo/miliardi, etc.
|
|
12
|
+
* - "e" connector before simple final remainder
|
|
10
13
|
*/
|
|
11
|
-
export class Italian extends AbstractLanguage {
|
|
12
|
-
negativeWord = 'meno'
|
|
13
|
-
decimalSeparatorWord = 'virgola'
|
|
14
|
-
zeroWord = 'zero'
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
this.zeroWord, 'uno', 'due', 'tre', 'quattro', 'cinque', 'sei', 'sette', 'otto',
|
|
18
|
-
'nove', 'dieci', 'undici', 'dodici', 'tredici', 'quattordici', 'quindici',
|
|
19
|
-
'sedici', 'diciassette', 'diciotto', 'diciannove'
|
|
20
|
-
]
|
|
15
|
+
import { parseNumericValue } from '../utils/parse-numeric.js'
|
|
21
16
|
|
|
22
|
-
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// Vocabulary (module-level constants)
|
|
19
|
+
// ============================================================================
|
|
23
20
|
|
|
24
|
-
|
|
21
|
+
// Base vocabulary
|
|
22
|
+
const ONES = ['', 'uno', 'due', 'tre', 'quattro', 'cinque', 'sei', 'sette', 'otto', 'nove']
|
|
23
|
+
const TEENS = ['dieci', 'undici', 'dodici', 'tredici', 'quattordici', 'quindici', 'sedici', 'diciassette', 'diciotto', 'diciannove']
|
|
24
|
+
const TENS = ['', '', 'venti', 'trenta', 'quaranta', 'cinquanta', 'sessanta', 'settanta', 'ottanta', 'novanta']
|
|
25
|
+
const HUNDREDS = ['', 'cento', 'duecento', 'trecento', 'quattrocento', 'cinquecento', 'seicento', 'settecento', 'ottocento', 'novecento']
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
// Pre-elided tens stems (drop final vowel before 'uno'/'otto')
|
|
28
|
+
// vent- (from venti), trent- (from trenta), etc.
|
|
29
|
+
const TENS_STEM = ['', '', 'vent', 'trent', 'quarant', 'cinquant', 'sessant', 'settant', 'ottant', 'novant']
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return result.join(' ')
|
|
34
|
-
}
|
|
31
|
+
const ZERO = 'zero'
|
|
32
|
+
const NEGATIVE = 'meno'
|
|
33
|
+
const DECIMAL_SEP = 'virgola'
|
|
35
34
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
35
|
+
// Thousands
|
|
36
|
+
const THOUSAND_SINGULAR = 'mille'
|
|
37
|
+
const THOUSAND_PLURAL_SUFFIX = 'mila'
|
|
40
38
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return string.replaceAll('oo', 'o').replaceAll('ao', 'o').replaceAll('io', 'o').replaceAll('au', 'u').replaceAll('iu', 'u')
|
|
44
|
-
}
|
|
39
|
+
// Scale word generation
|
|
40
|
+
const SCALE_PREFIXES = ['m', 'b', 'tr', 'quadr', 'quint', 'sest', 'sett', 'ott', 'nov', 'dec']
|
|
45
41
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
42
|
+
// ============================================================================
|
|
43
|
+
// Segment Building
|
|
44
|
+
// ============================================================================
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Builds the segment word for a number 0-999.
|
|
48
|
+
* Handles Italian phonetic elision inline (no regex).
|
|
49
|
+
*
|
|
50
|
+
* Elision rules:
|
|
51
|
+
* - Tens ending in vowel + uno/otto → drop tens vowel: ventuno, ventotto
|
|
52
|
+
* - Hundreds cento + otto/ottanta → centotto, centottanta (drop 'o')
|
|
53
|
+
* - Final 'tre' in compounds becomes 'tré': ventitré, trentatré
|
|
54
|
+
*/
|
|
55
|
+
function buildSegment (n) {
|
|
56
|
+
if (n === 0) return ''
|
|
57
|
+
|
|
58
|
+
const ones = n % 10
|
|
59
|
+
const tens = Math.floor(n / 10) % 10
|
|
60
|
+
const hundreds = Math.floor(n / 100)
|
|
54
61
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
62
|
+
let result = ''
|
|
63
|
+
|
|
64
|
+
// Hundreds
|
|
65
|
+
if (hundreds > 0) {
|
|
66
|
+
// Elision: *cento + otto/ottanta → *centotto/centottanta (drop final 'o')
|
|
67
|
+
// Only applies when tens = 8 (ottanta) or tens = 0 and ones = 8 (otto)
|
|
68
|
+
if (tens === 8 || (tens === 0 && ones === 8)) {
|
|
69
|
+
// Remove final 'o' from hundreds: cento→cent, duecento→duecent, etc.
|
|
70
|
+
result = HUNDREDS[hundreds].slice(0, -1)
|
|
71
|
+
} else {
|
|
72
|
+
result = HUNDREDS[hundreds]
|
|
61
73
|
}
|
|
62
|
-
const postfix = this.omitIfZero(this.integerToWords(number % 100))
|
|
63
|
-
return this.phoneticContraction(prefix + postfix)
|
|
64
74
|
}
|
|
65
75
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
76
|
+
// Tens and ones
|
|
77
|
+
if (tens === 0 && ones === 0) {
|
|
78
|
+
// Nothing more (just hundreds)
|
|
79
|
+
} else if (tens === 1) {
|
|
80
|
+
// Teens: 10-19
|
|
81
|
+
result += TEENS[ones]
|
|
82
|
+
} else if (tens >= 2) {
|
|
83
|
+
// 20-99: handle elision for uno (1) and otto (8)
|
|
84
|
+
if (ones === 1 || ones === 8) {
|
|
85
|
+
// Use stem form: vent + uno = ventuno, vent + otto = ventotto
|
|
86
|
+
result += TENS_STEM[tens] + ONES[ones]
|
|
87
|
+
} else if (ones === 3) {
|
|
88
|
+
// Final tre becomes tré
|
|
89
|
+
result += TENS[tens] + 'tré'
|
|
90
|
+
} else if (ones > 0) {
|
|
91
|
+
result += TENS[tens] + ONES[ones]
|
|
92
|
+
} else {
|
|
93
|
+
result += TENS[tens]
|
|
94
|
+
}
|
|
95
|
+
} else if (ones > 0) {
|
|
96
|
+
// 1-9 (tens === 0)
|
|
97
|
+
if (ones === 3 && hundreds > 0) {
|
|
98
|
+
// centotré, duecentotré, etc.
|
|
99
|
+
result += 'tré'
|
|
100
|
+
} else {
|
|
101
|
+
result += ONES[ones]
|
|
102
|
+
}
|
|
72
103
|
}
|
|
73
104
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
105
|
+
return result
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Builds segment word with "un" for scale context (millions, billions).
|
|
110
|
+
* Same as buildSegment but returns "un" for 1 instead of "uno".
|
|
111
|
+
*/
|
|
112
|
+
function buildSegmentForScale (n) {
|
|
113
|
+
if (n === 0) return ''
|
|
114
|
+
if (n === 1) return 'un' // "un milione" not "uno milione"
|
|
115
|
+
return buildSegment(n)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Builds thousands word for 1-999 thousand.
|
|
120
|
+
* Handles elision: tre + mila = tremila (no accent), otto + mila = ottomila
|
|
121
|
+
*/
|
|
122
|
+
function buildThousands (n) {
|
|
123
|
+
if (n === 0) return ''
|
|
124
|
+
if (n === 1) return THOUSAND_SINGULAR // "mille"
|
|
125
|
+
|
|
126
|
+
// Build segment and append "mila"
|
|
127
|
+
// Note: elision of segment ending vowel + 'mila' not needed (mila starts with 'm')
|
|
128
|
+
// But we need to handle cases like "ottomila" (no double-o issue since we build directly)
|
|
129
|
+
return buildSegment(n) + THOUSAND_PLURAL_SUFFIX
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ============================================================================
|
|
133
|
+
// Scale Word Functions
|
|
134
|
+
// ============================================================================
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Gets singular scale word for index.
|
|
138
|
+
* @param {number} scaleIndex - 2=million, 3=billion, etc.
|
|
139
|
+
*/
|
|
140
|
+
function getScaleWordSingular (scaleIndex) {
|
|
141
|
+
if (scaleIndex < 2) return ''
|
|
142
|
+
const prefixIndex = Math.floor((scaleIndex - 2) / 2)
|
|
143
|
+
const isIardo = (scaleIndex - 2) % 2 === 1
|
|
144
|
+
const prefix = SCALE_PREFIXES[prefixIndex]
|
|
145
|
+
if (!prefix) return ''
|
|
146
|
+
return prefix + (isIardo ? 'iliardo' : 'ilione')
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Gets plural scale word for index.
|
|
151
|
+
* @param {number} scaleIndex - 2=million, 3=billion, etc.
|
|
152
|
+
*/
|
|
153
|
+
function getScaleWordPlural (scaleIndex) {
|
|
154
|
+
if (scaleIndex < 2) return ''
|
|
155
|
+
const prefixIndex = Math.floor((scaleIndex - 2) / 2)
|
|
156
|
+
const isIardo = (scaleIndex - 2) % 2 === 1
|
|
157
|
+
const prefix = SCALE_PREFIXES[prefixIndex]
|
|
158
|
+
if (!prefix) return ''
|
|
159
|
+
return prefix + (isIardo ? 'iliardi' : 'ilioni')
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ============================================================================
|
|
163
|
+
// Conversion Functions
|
|
164
|
+
// ============================================================================
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Converts a non-negative integer to Italian words.
|
|
168
|
+
*
|
|
169
|
+
* @param {bigint} n - Non-negative integer to convert
|
|
170
|
+
* @returns {string} Italian words
|
|
171
|
+
*/
|
|
172
|
+
function integerToWords (n) {
|
|
173
|
+
if (n === 0n) return ZERO
|
|
174
|
+
|
|
175
|
+
// Fast path: numbers < 1000
|
|
176
|
+
if (n < 1000n) {
|
|
177
|
+
return buildSegment(Number(n))
|
|
78
178
|
}
|
|
79
179
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const
|
|
180
|
+
// Fast path: numbers < 1,000,000 (thousands)
|
|
181
|
+
if (n < 1_000_000n) {
|
|
182
|
+
const thousands = Number(n / 1000n)
|
|
183
|
+
const remainder = Number(n % 1000n)
|
|
83
184
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
preDigits = 3
|
|
185
|
+
if (remainder === 0) {
|
|
186
|
+
return buildThousands(thousands)
|
|
87
187
|
}
|
|
88
188
|
|
|
89
|
-
|
|
90
|
-
|
|
189
|
+
// Concatenate thousands + remainder
|
|
190
|
+
return buildThousands(thousands) + buildSegment(remainder)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// For numbers >= 1,000,000, use scale decomposition
|
|
194
|
+
return buildLargeNumberWords(n)
|
|
195
|
+
}
|
|
91
196
|
|
|
92
|
-
|
|
93
|
-
|
|
197
|
+
/**
|
|
198
|
+
* Builds words for numbers >= 1,000,000.
|
|
199
|
+
*
|
|
200
|
+
* @param {bigint} n - Number >= 1,000,000
|
|
201
|
+
* @returns {string} Italian words
|
|
202
|
+
*/
|
|
203
|
+
function buildLargeNumberWords (n) {
|
|
204
|
+
const parts = []
|
|
205
|
+
let remaining = n
|
|
94
206
|
|
|
95
|
-
|
|
96
|
-
|
|
207
|
+
// Find the highest scale
|
|
208
|
+
let maxScale = 2
|
|
209
|
+
let testValue = 1_000_000n
|
|
210
|
+
while (testValue * 1000n <= remaining) {
|
|
211
|
+
testValue *= 1000n
|
|
212
|
+
maxScale++
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Process from highest scale down
|
|
216
|
+
for (let scaleIndex = maxScale; scaleIndex >= 0; scaleIndex--) {
|
|
217
|
+
const divisor = 1000n ** BigInt(scaleIndex)
|
|
218
|
+
const segment = remaining / divisor
|
|
219
|
+
remaining = remaining % divisor
|
|
220
|
+
|
|
221
|
+
if (segment === 0n) continue
|
|
222
|
+
|
|
223
|
+
const segNum = Number(segment)
|
|
224
|
+
|
|
225
|
+
if (scaleIndex >= 2) {
|
|
226
|
+
// Millions and above: "segment scaleWord"
|
|
227
|
+
const segmentWords = buildSegmentForScale(segNum)
|
|
228
|
+
const scaleWord = segment === 1n
|
|
229
|
+
? getScaleWordSingular(scaleIndex)
|
|
230
|
+
: getScaleWordPlural(scaleIndex)
|
|
231
|
+
parts.push(segmentWords + ' ' + scaleWord)
|
|
232
|
+
} else if (scaleIndex === 1) {
|
|
233
|
+
// Thousands
|
|
234
|
+
parts.push(buildThousands(segNum))
|
|
97
235
|
} else {
|
|
98
|
-
|
|
99
|
-
|
|
236
|
+
// Units (scaleIndex === 0): just the segment
|
|
237
|
+
parts.push(buildSegment(segNum))
|
|
100
238
|
}
|
|
239
|
+
}
|
|
101
240
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
241
|
+
return joinPartsWithConnector(parts)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Joins parts with Italian connector rules.
|
|
246
|
+
* Uses "e" before simple (non-compound) final segment.
|
|
247
|
+
*
|
|
248
|
+
* @param {string[]} parts - Parts to join
|
|
249
|
+
* @returns {string} Joined string
|
|
250
|
+
*/
|
|
251
|
+
function joinPartsWithConnector (parts) {
|
|
252
|
+
const len = parts.length
|
|
253
|
+
if (len === 0) return ''
|
|
254
|
+
if (len === 1) return parts[0]
|
|
107
255
|
|
|
108
|
-
|
|
256
|
+
// Check if last part is "simple" (no space = no scale word)
|
|
257
|
+
const lastPart = parts[len - 1]
|
|
258
|
+
if (lastPart.indexOf(' ') === -1) {
|
|
259
|
+
// Join all but last with space, then add "e" connector
|
|
260
|
+
let result = parts[0]
|
|
261
|
+
for (let i = 1; i < len - 1; i++) {
|
|
262
|
+
result += ' ' + parts[i]
|
|
109
263
|
}
|
|
264
|
+
return result + ' e ' + lastPart
|
|
265
|
+
}
|
|
110
266
|
|
|
111
|
-
|
|
267
|
+
// Join with spaces
|
|
268
|
+
let result = parts[0]
|
|
269
|
+
for (let i = 1; i < len; i++) {
|
|
270
|
+
result += ' ' + parts[i]
|
|
112
271
|
}
|
|
272
|
+
return result
|
|
273
|
+
}
|
|
113
274
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
275
|
+
/**
|
|
276
|
+
* Converts decimal digits to Italian words.
|
|
277
|
+
*
|
|
278
|
+
* @param {string} decimalPart - Decimal digits (without the point)
|
|
279
|
+
* @returns {string} Italian words for decimal part
|
|
280
|
+
*/
|
|
281
|
+
function decimalPartToWords (decimalPart) {
|
|
282
|
+
let result = ''
|
|
283
|
+
|
|
284
|
+
// Handle leading zeros
|
|
285
|
+
let i = 0
|
|
286
|
+
while (i < decimalPart.length && decimalPart[i] === '0') {
|
|
287
|
+
if (result) result += ' '
|
|
288
|
+
result += ZERO
|
|
289
|
+
i++
|
|
290
|
+
}
|
|
129
291
|
|
|
130
|
-
|
|
292
|
+
// Convert remainder as a single number
|
|
293
|
+
const remainder = decimalPart.slice(i)
|
|
294
|
+
if (remainder) {
|
|
295
|
+
if (result) result += ' '
|
|
296
|
+
result += integerToWords(BigInt(remainder))
|
|
131
297
|
}
|
|
298
|
+
|
|
299
|
+
return result
|
|
132
300
|
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Converts a numeric value to Italian words.
|
|
304
|
+
*
|
|
305
|
+
* This is the main public API. It accepts any valid numeric input
|
|
306
|
+
* (number, string, or bigint) and handles parsing internally.
|
|
307
|
+
*
|
|
308
|
+
* @param {number | string | bigint} value - The numeric value to convert
|
|
309
|
+
* @returns {string} The number in Italian words
|
|
310
|
+
* @throws {TypeError} If value is not a valid numeric type
|
|
311
|
+
* @throws {Error} If value is not a valid number format
|
|
312
|
+
*
|
|
313
|
+
* @example
|
|
314
|
+
* toWords(28) // 'ventotto'
|
|
315
|
+
* toWords(23) // 'ventitré'
|
|
316
|
+
* toWords(1000) // 'mille'
|
|
317
|
+
* toWords(2000) // 'duemila'
|
|
318
|
+
* toWords(1000000) // 'un milione'
|
|
319
|
+
*/
|
|
320
|
+
function toWords (value) {
|
|
321
|
+
const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
|
|
322
|
+
|
|
323
|
+
let result = ''
|
|
324
|
+
|
|
325
|
+
if (isNegative) {
|
|
326
|
+
result = NEGATIVE + ' '
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
result += integerToWords(integerPart)
|
|
330
|
+
|
|
331
|
+
if (decimalPart) {
|
|
332
|
+
result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return result
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// ============================================================================
|
|
339
|
+
// Public API
|
|
340
|
+
// ============================================================================
|
|
341
|
+
|
|
342
|
+
export { toWords }
|
package/lib/languages/ja.d.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Japanese
|
|
2
|
+
* Converts a numeric value to Japanese words.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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 Japanese kanji 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(42) // '四十二'
|
|
14
|
+
* toWords(10000) // '一万'
|
|
15
|
+
* toWords(100000000) // '一億'
|
|
8
16
|
*/
|
|
9
|
-
export
|
|
10
|
-
onesWords: string[];
|
|
11
|
-
scaleWords: string[];
|
|
12
|
-
/** Converts a segment of up to 4 digits to Japanese kanji with 一 omission rules. */
|
|
13
|
-
segmentToWords(num: any): string;
|
|
14
|
-
/** Converts integer part using Japanese 万-based grouping system. */
|
|
15
|
-
integerToWords(integerPart: any): string;
|
|
16
|
-
}
|
|
17
|
-
import { AbstractLanguage } from '../classes/abstract-language.js';
|
|
17
|
+
export function toWords(value: number | string | bigint): string;
|