n2words 1.23.1 → 1.24.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/LICENSE +1 -1
- package/README.md +181 -52
- package/dist/languages/ar.js +2 -0
- package/dist/languages/ar.js.map +1 -0
- package/dist/languages/az.js +2 -0
- package/dist/languages/az.js.map +1 -0
- package/dist/languages/bn.js +2 -0
- package/dist/languages/bn.js.map +1 -0
- package/dist/languages/cs.js +2 -0
- package/dist/languages/cs.js.map +1 -0
- package/dist/languages/da.js +2 -0
- package/dist/languages/da.js.map +1 -0
- package/dist/languages/de.js +2 -0
- package/dist/languages/de.js.map +1 -0
- package/dist/languages/el.js +2 -0
- package/dist/languages/el.js.map +1 -0
- package/dist/languages/en.js +2 -0
- package/dist/languages/en.js.map +1 -0
- package/dist/languages/es.js +2 -0
- package/dist/languages/es.js.map +1 -0
- package/dist/languages/fa.js +2 -0
- package/dist/languages/fa.js.map +1 -0
- package/dist/languages/fil.js +2 -0
- package/dist/languages/fil.js.map +1 -0
- package/dist/languages/fr-BE.js +2 -0
- package/dist/languages/fr-BE.js.map +1 -0
- package/dist/languages/fr.js +2 -0
- package/dist/languages/fr.js.map +1 -0
- package/dist/languages/gu.js +2 -0
- package/dist/languages/gu.js.map +1 -0
- package/dist/languages/he.js +2 -0
- package/dist/languages/he.js.map +1 -0
- package/dist/languages/hi.js +2 -0
- package/dist/languages/hi.js.map +1 -0
- package/dist/languages/hr.js +2 -0
- package/dist/languages/hr.js.map +1 -0
- package/dist/languages/hu.js +2 -0
- package/dist/languages/hu.js.map +1 -0
- package/dist/languages/id.js +2 -0
- package/dist/languages/id.js.map +1 -0
- package/dist/languages/it.js +2 -0
- package/dist/languages/it.js.map +1 -0
- package/dist/languages/ja.js +2 -0
- package/dist/languages/ja.js.map +1 -0
- package/dist/languages/kn.js +2 -0
- package/dist/languages/kn.js.map +1 -0
- package/dist/languages/ko.js +2 -0
- package/dist/languages/ko.js.map +1 -0
- package/dist/languages/lt.js +2 -0
- package/dist/languages/lt.js.map +1 -0
- package/dist/languages/lv.js +2 -0
- package/dist/languages/lv.js.map +1 -0
- package/dist/languages/mr.js +2 -0
- package/dist/languages/mr.js.map +1 -0
- package/dist/languages/ms.js +2 -0
- package/dist/languages/ms.js.map +1 -0
- package/dist/languages/nb.js +2 -0
- package/dist/languages/nb.js.map +1 -0
- package/dist/languages/nl.js +2 -0
- package/dist/languages/nl.js.map +1 -0
- package/dist/languages/pa-Guru.js +2 -0
- package/dist/languages/pa-Guru.js.map +1 -0
- package/dist/languages/pl.js +2 -0
- package/dist/languages/pl.js.map +1 -0
- package/dist/languages/pt.js +2 -0
- package/dist/languages/pt.js.map +1 -0
- package/dist/languages/ro.js +2 -0
- package/dist/languages/ro.js.map +1 -0
- package/dist/languages/ru.js +2 -0
- package/dist/languages/ru.js.map +1 -0
- package/dist/languages/sr-Latn.js +2 -0
- package/dist/languages/sr-Latn.js.map +1 -0
- package/dist/languages/sv.js +2 -0
- package/dist/languages/sv.js.map +1 -0
- package/dist/languages/sw.js +2 -0
- package/dist/languages/sw.js.map +1 -0
- package/dist/languages/ta.js +2 -0
- package/dist/languages/ta.js.map +1 -0
- package/dist/languages/te.js +2 -0
- package/dist/languages/te.js.map +1 -0
- package/dist/languages/th.js +2 -0
- package/dist/languages/th.js.map +1 -0
- package/dist/languages/tr.js +2 -0
- package/dist/languages/tr.js.map +1 -0
- package/dist/languages/uk.js +2 -0
- package/dist/languages/uk.js.map +1 -0
- package/dist/languages/ur.js +2 -0
- package/dist/languages/ur.js.map +1 -0
- package/dist/languages/vi.js +2 -0
- package/dist/languages/vi.js.map +1 -0
- package/dist/languages/zh-Hans.js +2 -0
- package/dist/languages/zh-Hans.js.map +1 -0
- package/dist/n2words.js +1 -1
- package/dist/n2words.js.map +1 -1
- package/lib/classes/abstract-language.js +211 -110
- package/lib/classes/greedy-scale-language.js +195 -0
- package/lib/classes/slavic-language.js +251 -0
- package/lib/classes/south-asian-language.js +161 -0
- package/lib/classes/turkic-language.js +63 -0
- package/lib/languages/ar.js +243 -0
- package/lib/languages/az.js +58 -0
- package/lib/languages/bn.js +126 -0
- package/lib/languages/cs.js +212 -0
- package/lib/languages/da.js +167 -0
- package/lib/languages/de.js +135 -0
- package/lib/languages/el.js +116 -0
- package/lib/languages/en.js +123 -0
- package/lib/languages/es.js +153 -0
- package/lib/languages/fa.js +127 -0
- package/lib/languages/fil.js +162 -0
- package/lib/languages/fr-BE.js +61 -0
- package/lib/languages/fr.js +145 -0
- package/lib/languages/gu.js +156 -0
- package/lib/languages/he.js +329 -0
- package/lib/languages/hi.js +126 -0
- package/lib/languages/hr.js +157 -0
- package/lib/languages/hu.js +155 -0
- package/lib/languages/id.js +174 -0
- package/lib/languages/it.js +148 -0
- package/lib/languages/ja.js +190 -0
- package/lib/languages/kn.js +71 -0
- package/lib/languages/ko.js +83 -0
- package/lib/languages/lt.js +171 -0
- package/lib/languages/lv.js +153 -0
- package/lib/languages/mr.js +156 -0
- package/lib/languages/ms.js +146 -0
- package/lib/languages/nb.js +120 -0
- package/lib/languages/nl.js +206 -0
- package/lib/languages/pa-Guru.js +126 -0
- package/lib/languages/pl.js +189 -0
- package/lib/languages/pt.js +147 -0
- package/lib/languages/ro.js +380 -0
- package/lib/languages/ru.js +116 -0
- package/lib/languages/sr-Latn.js +157 -0
- package/lib/languages/sv.js +127 -0
- package/lib/languages/sw.js +121 -0
- package/lib/languages/ta.js +226 -0
- package/lib/languages/te.js +229 -0
- package/lib/languages/th.js +123 -0
- package/lib/languages/tr.js +83 -0
- package/lib/{i18n → languages}/uk.js +50 -23
- package/lib/languages/ur.js +126 -0
- package/lib/languages/vi.js +193 -0
- package/lib/languages/zh-Hans.js +165 -0
- package/lib/n2words.js +246 -75
- package/package.json +80 -72
- package/typings/classes/abstract-language.d.ts +144 -0
- package/typings/classes/greedy-scale-language.d.ts +148 -0
- package/typings/classes/slavic-language.d.ts +145 -0
- package/typings/classes/south-asian-language.d.ts +101 -0
- package/typings/classes/turkic-language.d.ts +42 -0
- package/typings/languages/ar.d.ts +93 -0
- package/typings/languages/az.d.ts +25 -0
- package/typings/languages/bn.d.ts +1 -0
- package/typings/languages/cs.d.ts +120 -0
- package/typings/languages/da.d.ts +53 -0
- package/typings/languages/de.d.ts +26 -0
- package/typings/languages/el.d.ts +11 -0
- package/typings/languages/en.d.ts +30 -0
- package/typings/languages/es.d.ts +43 -0
- package/typings/languages/fa.d.ts +81 -0
- package/typings/languages/fil.d.ts +12 -0
- package/typings/languages/fr-BE.d.ts +41 -0
- package/typings/languages/fr.d.ts +43 -0
- package/typings/languages/gu.d.ts +12 -0
- package/typings/languages/he.d.ts +197 -0
- package/typings/languages/hi.d.ts +1 -0
- package/typings/languages/hr.d.ts +110 -0
- package/typings/languages/hu.d.ts +37 -0
- package/typings/languages/id.d.ts +69 -0
- package/typings/languages/it.d.ts +51 -0
- package/typings/languages/ja.d.ts +58 -0
- package/typings/languages/kn.d.ts +11 -0
- package/typings/languages/ko.d.ts +25 -0
- package/typings/languages/lt.d.ts +110 -0
- package/typings/languages/lv.d.ts +99 -0
- package/typings/languages/mr.d.ts +12 -0
- package/typings/languages/ms.d.ts +37 -0
- package/typings/languages/nb.d.ts +27 -0
- package/typings/languages/nl.d.ts +65 -0
- package/typings/languages/pa-Guru.d.ts +1 -0
- package/typings/languages/pl.d.ts +116 -0
- package/typings/languages/pt.d.ts +39 -0
- package/typings/languages/ro.d.ts +229 -0
- package/typings/languages/ru.d.ts +108 -0
- package/typings/languages/sr-Latn.d.ts +98 -0
- package/typings/languages/sv.d.ts +30 -0
- package/typings/languages/sw.d.ts +1 -0
- package/typings/languages/ta.d.ts +1 -0
- package/typings/languages/te.d.ts +1 -0
- package/typings/languages/th.d.ts +1 -0
- package/typings/languages/tr.d.ts +46 -0
- package/typings/languages/uk.d.ts +117 -0
- package/typings/languages/ur.d.ts +1 -0
- package/typings/languages/vi.d.ts +116 -0
- package/typings/languages/zh-Hans.d.ts +57 -0
- package/typings/n2words.d.ts +177 -0
- package/dist/ar.js +0 -2
- package/dist/ar.js.map +0 -1
- package/dist/az.js +0 -2
- package/dist/az.js.map +0 -1
- package/dist/cz.js +0 -2
- package/dist/cz.js.map +0 -1
- package/dist/de.js +0 -2
- package/dist/de.js.map +0 -1
- package/dist/dk.js +0 -2
- package/dist/dk.js.map +0 -1
- package/dist/en.js +0 -2
- package/dist/en.js.map +0 -1
- package/dist/es.js +0 -2
- package/dist/es.js.map +0 -1
- package/dist/fa.js +0 -2
- package/dist/fa.js.map +0 -1
- package/dist/fr-BE.js +0 -2
- package/dist/fr-BE.js.map +0 -1
- package/dist/fr.js +0 -2
- package/dist/fr.js.map +0 -1
- package/dist/he.js +0 -2
- package/dist/he.js.map +0 -1
- package/dist/hr.js +0 -2
- package/dist/hr.js.map +0 -1
- package/dist/hu.js +0 -2
- package/dist/hu.js.map +0 -1
- package/dist/id.js +0 -2
- package/dist/id.js.map +0 -1
- package/dist/it.js +0 -2
- package/dist/it.js.map +0 -1
- package/dist/ko.js +0 -2
- package/dist/ko.js.map +0 -1
- package/dist/lt.js +0 -2
- package/dist/lt.js.map +0 -1
- package/dist/lv.js +0 -2
- package/dist/lv.js.map +0 -1
- package/dist/n2words.d.ts +0 -2
- package/dist/nl.js +0 -2
- package/dist/nl.js.map +0 -1
- package/dist/no.js +0 -2
- package/dist/no.js.map +0 -1
- package/dist/pl.js +0 -2
- package/dist/pl.js.map +0 -1
- package/dist/pt.js +0 -2
- package/dist/pt.js.map +0 -1
- package/dist/ro.js +0 -2
- package/dist/ro.js.map +0 -1
- package/dist/ru.js +0 -2
- package/dist/ru.js.map +0 -1
- package/dist/sr.js +0 -2
- package/dist/sr.js.map +0 -1
- package/dist/tr.js +0 -2
- package/dist/tr.js.map +0 -1
- package/dist/uk.js +0 -2
- package/dist/uk.js.map +0 -1
- package/dist/vi.js +0 -2
- package/dist/vi.js.map +0 -1
- package/dist/zh.js +0 -2
- package/dist/zh.js.map +0 -1
- package/lib/classes/abstract-language.d.ts +0 -54
- package/lib/classes/base-language.d.ts +0 -58
- package/lib/classes/base-language.js +0 -172
- package/lib/i18n/ar.d.ts +0 -41
- package/lib/i18n/ar.js +0 -209
- package/lib/i18n/az.d.ts +0 -15
- package/lib/i18n/az.js +0 -66
- package/lib/i18n/cz.d.ts +0 -68
- package/lib/i18n/cz.js +0 -135
- package/lib/i18n/de.d.ts +0 -17
- package/lib/i18n/de.js +0 -103
- package/lib/i18n/dk.d.ts +0 -14
- package/lib/i18n/dk.js +0 -110
- package/lib/i18n/en.d.ts +0 -22
- package/lib/i18n/en.js +0 -86
- package/lib/i18n/es.d.ts +0 -16
- package/lib/i18n/es.js +0 -110
- package/lib/i18n/fa.d.ts +0 -54
- package/lib/i18n/fa.js +0 -106
- package/lib/i18n/fr-BE.d.ts +0 -11
- package/lib/i18n/fr-BE.js +0 -20
- package/lib/i18n/fr.d.ts +0 -15
- package/lib/i18n/fr.js +0 -99
- package/lib/i18n/he.d.ts +0 -61
- package/lib/i18n/he.js +0 -132
- package/lib/i18n/hr.d.ts +0 -68
- package/lib/i18n/hr.js +0 -129
- package/lib/i18n/hu.d.ts +0 -17
- package/lib/i18n/hu.js +0 -135
- package/lib/i18n/id.d.ts +0 -43
- package/lib/i18n/id.js +0 -156
- package/lib/i18n/it.d.ts +0 -29
- package/lib/i18n/it.js +0 -137
- package/lib/i18n/ko.d.ts +0 -15
- package/lib/i18n/ko.js +0 -56
- package/lib/i18n/lt.d.ts +0 -68
- package/lib/i18n/lt.js +0 -138
- package/lib/i18n/lv.d.ts +0 -57
- package/lib/i18n/lv.js +0 -120
- package/lib/i18n/nl.d.ts +0 -20
- package/lib/i18n/nl.js +0 -125
- package/lib/i18n/no.d.ts +0 -15
- package/lib/i18n/no.js +0 -77
- package/lib/i18n/pl.d.ts +0 -67
- package/lib/i18n/pl.js +0 -126
- package/lib/i18n/pt.d.ts +0 -26
- package/lib/i18n/pt.js +0 -118
- package/lib/i18n/ro.d.ts +0 -109
- package/lib/i18n/ro.js +0 -360
- package/lib/i18n/ru.d.ts +0 -30
- package/lib/i18n/ru.js +0 -198
- package/lib/i18n/sr.d.ts +0 -56
- package/lib/i18n/sr.js +0 -127
- package/lib/i18n/tr.d.ts +0 -15
- package/lib/i18n/tr.js +0 -64
- package/lib/i18n/uk.d.ts +0 -78
- package/lib/i18n/vi.d.ts +0 -70
- package/lib/i18n/vi.js +0 -151
- package/lib/i18n/zh.d.ts +0 -18
- package/lib/i18n/zh.js +0 -78
- package/lib/n2words.d.ts +0 -9
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import GreedyScaleLanguage from '../classes/greedy-scale-language.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Hungarian language converter.
|
|
5
|
+
*
|
|
6
|
+
* Converts numbers to Hungarian words following Hungarian conventions:
|
|
7
|
+
* - Agglutinative structure for compound numbers
|
|
8
|
+
* - No spaces between tens and units (e.g., "huszonegy" = twenty-one)
|
|
9
|
+
* - Special handling for "egy" (one) - often omitted as multiplier
|
|
10
|
+
* - Vowel harmony in compound words
|
|
11
|
+
*
|
|
12
|
+
* Features:
|
|
13
|
+
* - Compact number representations (húsz, harminc, negyven)
|
|
14
|
+
* - Pre-composed twenties (huszonegy through huszonkilenc)
|
|
15
|
+
* - "egész" as decimal separator (meaning "whole")
|
|
16
|
+
* - Support for very large numbers (up to quadrilliards)
|
|
17
|
+
*/
|
|
18
|
+
export class Hungarian extends GreedyScaleLanguage {
|
|
19
|
+
negativeWord = 'mínusz'
|
|
20
|
+
decimalSeparatorWord = 'egész'
|
|
21
|
+
zeroWord = 'nulla'
|
|
22
|
+
scaleWordPairs = [
|
|
23
|
+
[1_000_000_000_000_000_000_000_000_000n, 'quadrilliárd'],
|
|
24
|
+
[1_000_000_000_000_000_000_000_000n, 'quadrillió'],
|
|
25
|
+
[1_000_000_000_000_000_000_000n, 'trilliárd'],
|
|
26
|
+
[1_000_000_000_000_000_000n, 'trillió'],
|
|
27
|
+
[1_000_000_000_000_000n, 'billiárd'],
|
|
28
|
+
[1_000_000_000_000n, 'billió'],
|
|
29
|
+
[1_000_000_000n, 'milliárd'],
|
|
30
|
+
[1_000_000n, 'millió'],
|
|
31
|
+
[1000n, 'ezer'],
|
|
32
|
+
[100n, 'száz'],
|
|
33
|
+
[90n, 'kilencven'],
|
|
34
|
+
[80n, 'nyolcvan'],
|
|
35
|
+
[70n, 'hetven'],
|
|
36
|
+
[60n, 'hatvan'],
|
|
37
|
+
[50n, 'ötven'],
|
|
38
|
+
[40n, 'negyven'],
|
|
39
|
+
[30n, 'harminc'],
|
|
40
|
+
[29n, 'huszonkilenc'],
|
|
41
|
+
[28n, 'huszonnyolc'],
|
|
42
|
+
[27n, 'huszonhét'],
|
|
43
|
+
[26n, 'huszonhat'],
|
|
44
|
+
[25n, 'huszonöt'],
|
|
45
|
+
[24n, 'huszonnégy'],
|
|
46
|
+
[23n, 'huszonhárom'],
|
|
47
|
+
[22n, 'huszonkettő'],
|
|
48
|
+
[21n, 'huszonegy'],
|
|
49
|
+
[20n, 'húsz'],
|
|
50
|
+
[19n, 'tizenkilenc'],
|
|
51
|
+
[18n, 'tizennyolc'],
|
|
52
|
+
[17n, 'tizenhét'],
|
|
53
|
+
[16n, 'tizenhat'],
|
|
54
|
+
[15n, 'tizenöt'],
|
|
55
|
+
[14n, 'tizennégy'],
|
|
56
|
+
[13n, 'tizenhárom'],
|
|
57
|
+
[12n, 'tizenkettő'],
|
|
58
|
+
[11n, 'tizenegy'],
|
|
59
|
+
[10n, 'tíz'],
|
|
60
|
+
[9n, 'kilenc'],
|
|
61
|
+
[8n, 'nyolc'],
|
|
62
|
+
[7n, 'hét'],
|
|
63
|
+
[6n, 'hat'],
|
|
64
|
+
[5n, 'öt'],
|
|
65
|
+
[4n, 'négy'],
|
|
66
|
+
[3n, 'három'],
|
|
67
|
+
[2n, 'kettő'],
|
|
68
|
+
[1n, 'egy'],
|
|
69
|
+
[0n, 'nulla']
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
tensToCardinal (number) {
|
|
73
|
+
// Expecting `number` as bigint when called from convertWholePart
|
|
74
|
+
if (this.getScaleWord(number)) {
|
|
75
|
+
return this.getScaleWord(number)
|
|
76
|
+
} else {
|
|
77
|
+
const tens = number / 10n
|
|
78
|
+
const units = number % 10n
|
|
79
|
+
return this.getScaleWord(tens * 10n) + this.convertWholePart(units)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
hundredsToCardinal (number) {
|
|
84
|
+
const hundreds = number / 100n
|
|
85
|
+
let prefix = 'száz'
|
|
86
|
+
if (hundreds !== 1n) {
|
|
87
|
+
prefix = this.convertWholePart(hundreds, '') + prefix
|
|
88
|
+
}
|
|
89
|
+
const postfix = this.convertWholePart(number % 100n, '')
|
|
90
|
+
return prefix + postfix
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
thousandsToCardinal (number) {
|
|
94
|
+
const thousands = number / 1000n
|
|
95
|
+
let prefix = 'ezer'
|
|
96
|
+
if (thousands !== 1n) {
|
|
97
|
+
prefix = this.convertWholePart(thousands, '') + prefix
|
|
98
|
+
}
|
|
99
|
+
const postfix = this.convertWholePart(number % 1000n, '')
|
|
100
|
+
const middle = (number <= 2000n || postfix === '') ? '' : '-'
|
|
101
|
+
return prefix + middle + postfix
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
bigNumberToCardinal (number) {
|
|
105
|
+
const numberLength = number.toString().length
|
|
106
|
+
const digits = (numberLength % 3 === 0) ? numberLength - 2 : numberLength
|
|
107
|
+
const exp = 10 ** (Math.floor(digits / 3) * 3)
|
|
108
|
+
const prefix = this.convertWholePart(number / BigInt(exp), '')
|
|
109
|
+
const rest = this.convertWholePart(number % BigInt(exp), '')
|
|
110
|
+
const postfix = (rest === '') ? '' : ('-' + rest)
|
|
111
|
+
return prefix + this.getScaleWord(BigInt(exp)) + postfix
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
convertWholePart (number, zeroWord = this.zeroWord) {
|
|
115
|
+
let words = ''
|
|
116
|
+
|
|
117
|
+
// Normalize to BigInt for consistent comparisons
|
|
118
|
+
if (typeof number !== 'bigint') number = BigInt(number)
|
|
119
|
+
|
|
120
|
+
if (number === 0n) {
|
|
121
|
+
words = zeroWord
|
|
122
|
+
} else if (zeroWord === '' && number === 2n) {
|
|
123
|
+
words = 'két'
|
|
124
|
+
} else if (number < 30n) {
|
|
125
|
+
words = this.getScaleWord(number)
|
|
126
|
+
} else if (number < 100n) {
|
|
127
|
+
words = this.tensToCardinal(number)
|
|
128
|
+
} else if (number < 1000n) {
|
|
129
|
+
words = this.hundredsToCardinal(number)
|
|
130
|
+
} else if (number < 1_000_000n) {
|
|
131
|
+
words = this.thousandsToCardinal(number)
|
|
132
|
+
} else {
|
|
133
|
+
words = this.bigNumberToCardinal(number)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return words
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Converts a number to Hungarian cardinal (written) form.
|
|
142
|
+
*
|
|
143
|
+
* @param {number|string|bigint} value The number to convert.
|
|
144
|
+
* @param {Object} [options] Conversion options (see Hungarian class options).
|
|
145
|
+
* @returns {string} The number expressed in Hungarian words.
|
|
146
|
+
* @throws {TypeError} If value is NaN or invalid type.
|
|
147
|
+
* @throws {Error} If value is an invalid number string.
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* convertToWords(42); // 'negyvenkettő'
|
|
151
|
+
* convertToWords(21); // 'huszonegy'
|
|
152
|
+
*/
|
|
153
|
+
export default function convertToWords (value, options = {}) {
|
|
154
|
+
return new Hungarian(options).convertToWords(value)
|
|
155
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import AbstractLanguage from '../classes/abstract-language.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Indonesian language converter.
|
|
5
|
+
*
|
|
6
|
+
* Converts numbers to Indonesian words following Indonesian conventions:
|
|
7
|
+
* - Simple base-10 structure
|
|
8
|
+
* - "Se-" prefix for one (e.g., "seratus" = one hundred, "seribu" = one thousand)
|
|
9
|
+
* - Space-separated number components
|
|
10
|
+
* - Straightforward grouping by thousands
|
|
11
|
+
*
|
|
12
|
+
* Key Features:
|
|
13
|
+
* - Base number mapping (base) for single digits 0-9
|
|
14
|
+
* - Magnitude scale (thousands) mapping powers of 10 to Indonesian words
|
|
15
|
+
* - Group-based algorithm:
|
|
16
|
+
* 1. Split number into groups of 3 digits
|
|
17
|
+
* 2. For each group, convert ones/tens/hundreds using base and naming rules
|
|
18
|
+
* 3. Apply "se-" prefix for 1 (seratus, seribu, sejuta)
|
|
19
|
+
* 4. Combine with magnitude words
|
|
20
|
+
* 5. Join all parts with spaces
|
|
21
|
+
* - Regular patterns (puluh for tens, ratus for hundreds, ribu for thousands)
|
|
22
|
+
* - Clear grouping: ribu (10³), juta (10⁶), miliar (10⁹), triliun (10¹²)
|
|
23
|
+
*
|
|
24
|
+
* Features:
|
|
25
|
+
* - "Se-" prefix usage for singular units
|
|
26
|
+
* - Support for very large numbers (up to decillions)
|
|
27
|
+
*/
|
|
28
|
+
export class Indonesian extends AbstractLanguage {
|
|
29
|
+
negativeWord = 'min'
|
|
30
|
+
decimalSeparatorWord = 'koma'
|
|
31
|
+
zeroWord = 'nol'
|
|
32
|
+
base = {
|
|
33
|
+
0: [],
|
|
34
|
+
1: ['satu'],
|
|
35
|
+
2: ['dua'],
|
|
36
|
+
3: ['tiga'],
|
|
37
|
+
4: ['empat'],
|
|
38
|
+
5: ['lima'],
|
|
39
|
+
6: ['enam'],
|
|
40
|
+
7: ['tujuh'],
|
|
41
|
+
8: ['delapan'],
|
|
42
|
+
9: ['sembilan']
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
thousands = {
|
|
46
|
+
3: 'ribu', // 10^3
|
|
47
|
+
6: 'juta', // 10^6
|
|
48
|
+
9: 'miliar', // 10^9
|
|
49
|
+
12: 'triliun',
|
|
50
|
+
15: 'kuadriliun',
|
|
51
|
+
18: 'kuantiliun',
|
|
52
|
+
21: 'sekstiliun',
|
|
53
|
+
24: 'septiliun',
|
|
54
|
+
27: 'oktiliun',
|
|
55
|
+
30: 'noniliun',
|
|
56
|
+
33: 'desiliun'
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
splitBy3 (number) {
|
|
60
|
+
// Split to groups of 3 numbers: 1234567 -> [['1'], ['234'], ['567']]
|
|
61
|
+
const blocks = []
|
|
62
|
+
const stringNumber = number.toString()
|
|
63
|
+
const length = stringNumber.length
|
|
64
|
+
let firstBlock
|
|
65
|
+
|
|
66
|
+
if (length < 3) {
|
|
67
|
+
blocks.push([stringNumber])
|
|
68
|
+
} else {
|
|
69
|
+
const firstBlockLength = length % 3
|
|
70
|
+
|
|
71
|
+
if (firstBlockLength > 0) {
|
|
72
|
+
firstBlock = [stringNumber.slice(0, firstBlockLength)]
|
|
73
|
+
blocks.push(firstBlock)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
for (let index = firstBlockLength; index < length; index += 3) {
|
|
77
|
+
const nextBlock = [stringNumber.slice(index, index + 3)]
|
|
78
|
+
blocks.push(nextBlock)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return blocks
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
spell (blocks) {
|
|
85
|
+
let wordBlocks = []
|
|
86
|
+
let spelling
|
|
87
|
+
const firstBlock = blocks[0]
|
|
88
|
+
if (firstBlock[0].length === 1) {
|
|
89
|
+
spelling = firstBlock[0] === '0' ? ['nol'] : this.base[Math.trunc(firstBlock[0])]
|
|
90
|
+
} else if (firstBlock[0].length === 2) {
|
|
91
|
+
spelling = this.getTens(firstBlock[0])
|
|
92
|
+
} else {
|
|
93
|
+
spelling = [...this.getHundreds(firstBlock[0][0]), ...this.getTens(firstBlock[0].slice(1, 3))]
|
|
94
|
+
}
|
|
95
|
+
wordBlocks = [...wordBlocks, [firstBlock[0], spelling]]
|
|
96
|
+
for (let index = 1; index < blocks.length; index++) {
|
|
97
|
+
let block = blocks[index]
|
|
98
|
+
spelling = [...this.getHundreds(block[0][0]), ...this.getTens(block[0].slice(1, 3))]
|
|
99
|
+
block = [...block, spelling]
|
|
100
|
+
wordBlocks = [...wordBlocks, block]
|
|
101
|
+
}
|
|
102
|
+
return wordBlocks
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
getHundreds (number) {
|
|
106
|
+
if (number === '1') {
|
|
107
|
+
return ['seratus']
|
|
108
|
+
} else if (number === '0') {
|
|
109
|
+
return []
|
|
110
|
+
} else {
|
|
111
|
+
return [...this.base[Math.trunc(number)], 'ratus']
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
getTens (number) {
|
|
116
|
+
if (number[0] === '1') {
|
|
117
|
+
if (number[1] === '0') {
|
|
118
|
+
return ['sepuluh']
|
|
119
|
+
} else if (number[1] === '1') {
|
|
120
|
+
return ['sebelas']
|
|
121
|
+
}
|
|
122
|
+
return [...this.base[Math.trunc(number[1])], 'belas']
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (number[0] === '0') {
|
|
126
|
+
return this.base[Math.trunc(number[1])]
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return [...this.base[Math.trunc(number[0])], 'puluh', ...this.base[Math.trunc(number[1])]]
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
join (wordBlocks) {
|
|
133
|
+
let wordList = []
|
|
134
|
+
const length = wordBlocks.length - 1
|
|
135
|
+
const firstBlock = [wordBlocks[0]]
|
|
136
|
+
let start = 0
|
|
137
|
+
if (length === 1 && firstBlock[0][0] === '1') {
|
|
138
|
+
wordList.push('seribu')
|
|
139
|
+
start = 1
|
|
140
|
+
}
|
|
141
|
+
for (let index = start; index < length + 1; index++) {
|
|
142
|
+
wordList = [...wordList, ...wordBlocks[index][1]]
|
|
143
|
+
if (wordBlocks[index][1].length === 0) {
|
|
144
|
+
continue
|
|
145
|
+
}
|
|
146
|
+
if (index === length) {
|
|
147
|
+
break
|
|
148
|
+
}
|
|
149
|
+
wordList = [...wordList, this.thousands[(length - index) * 3]]
|
|
150
|
+
}
|
|
151
|
+
return wordList.join(' ')
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
convertWholePart (number) {
|
|
155
|
+
return this.join(
|
|
156
|
+
this.spell(
|
|
157
|
+
this.splitBy3(number)
|
|
158
|
+
)
|
|
159
|
+
).trim()
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Converts a number to Indonesian cardinal (written) form.
|
|
165
|
+
*
|
|
166
|
+
* @param {number|string|bigint} value The number to convert.
|
|
167
|
+
* @param {Object} [options={}] Configuration options.
|
|
168
|
+
* @returns {string} The number expressed in Indonesian words.
|
|
169
|
+
* @throws {TypeError} If value is NaN or invalid type.
|
|
170
|
+
* @throws {Error} If value is an invalid number string.
|
|
171
|
+
*/
|
|
172
|
+
export default function convertToWords (value, options = {}) {
|
|
173
|
+
return new Indonesian(options).convertToWords(value)
|
|
174
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import GreedyScaleLanguage from '../classes/greedy-scale-language.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Italian language converter.
|
|
5
|
+
*
|
|
6
|
+
* Converts numbers to Italian words following Italian conventions:
|
|
7
|
+
* - Phonetic contractions (removes duplicate vowels: "ventotto" not "ventiotto")
|
|
8
|
+
* - Accentuation rules for "tre" in compounds ("ventitré" not "ventitre")
|
|
9
|
+
* - Special handling for "uno" and vowel agreement
|
|
10
|
+
* - Complex composition patterns for large numbers
|
|
11
|
+
*
|
|
12
|
+
* Architecture Note:
|
|
13
|
+
* Unlike other GreedyScaleLanguage subclasses, Italian uses a custom algorithm
|
|
14
|
+
* rather than the standard highest-matching-scale approach. This is necessary
|
|
15
|
+
* because Italian's word formation rules are irregular and context-dependent.
|
|
16
|
+
* See tensToCardinal(), hundredsToCardinal(), and bigNumberToCardinal().
|
|
17
|
+
*
|
|
18
|
+
* Features:
|
|
19
|
+
* - Vowel elision (e.g., "ventotto" not "ventiotto")
|
|
20
|
+
* - Accentuation of final "tre" (ventitré, trentacinque - note: accent on compound tres)
|
|
21
|
+
* - Exponent-based large number naming (milione, miliardo, trilione)
|
|
22
|
+
* - Custom word construction for hundreds and thousands
|
|
23
|
+
*/
|
|
24
|
+
export class Italian extends GreedyScaleLanguage {
|
|
25
|
+
negativeWord = 'meno'
|
|
26
|
+
decimalSeparatorWord = 'virgola'
|
|
27
|
+
zeroWord = 'zero'
|
|
28
|
+
cardinalWords = [
|
|
29
|
+
this.zeroWord, 'uno', 'due', 'tre', 'quattro', 'cinque', 'sei', 'sette', 'otto',
|
|
30
|
+
'nove', 'dieci', 'undici', 'dodici', 'tredici', 'quattordici', 'quindici',
|
|
31
|
+
'sedici', 'diciassette', 'diciotto', 'diciannove'
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
strTens = { 2: 'venti', 3: 'trenta', 4: 'quaranta', 6: 'sessanta' }
|
|
35
|
+
|
|
36
|
+
exponentPrefixes = [this.zeroWord, 'm', 'b', 'tr', 'quadr', 'quint', 'sest', 'sett', 'ott', 'nov', 'dec']
|
|
37
|
+
|
|
38
|
+
accentuate (string) {
|
|
39
|
+
const splittedString = string.split(' ')
|
|
40
|
+
|
|
41
|
+
const result = splittedString.map(word => {
|
|
42
|
+
return word.slice(-3) === 'tre' && word.length > 3 ? word.replaceAll('tré', 'tre').slice(0, -3) + 'tré' : word.replaceAll('tré', 'tre')
|
|
43
|
+
})
|
|
44
|
+
return result.join(' ')
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
omitIfZero (numberToString) {
|
|
48
|
+
return numberToString === this.zeroWord ? '' : numberToString
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
phoneticContraction (string) {
|
|
52
|
+
return string.replaceAll('oo', 'o').replaceAll('ao', 'o').replaceAll('io', 'o').replaceAll('au', 'u').replaceAll('iu', 'u')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
tensToCardinal (number) {
|
|
56
|
+
const tens = Math.floor(number / 10)
|
|
57
|
+
const units = number % 10
|
|
58
|
+
const prefix = Object.prototype.hasOwnProperty.call(this.strTens, tens) ? this.strTens[tens] : this.cardinalWords[tens].slice(0, -1) + 'anta'
|
|
59
|
+
const postfix = this.omitIfZero(this.cardinalWords[units])
|
|
60
|
+
return this.phoneticContraction(prefix + postfix)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
hundredsToCardinal (number) {
|
|
64
|
+
const hundreds = Math.floor(number / 100)
|
|
65
|
+
let prefix = 'cento'
|
|
66
|
+
if (hundreds !== 1) {
|
|
67
|
+
prefix = this.cardinalWords[hundreds] + prefix
|
|
68
|
+
}
|
|
69
|
+
const postfix = this.omitIfZero(this.convertWholePart(number % 100))
|
|
70
|
+
return this.phoneticContraction(prefix + postfix)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
thousandsToCardinal (number) {
|
|
74
|
+
const thousands = Math.floor(number / 1000)
|
|
75
|
+
const prefix = thousands === 1 ? 'mille' : this.convertWholePart(thousands) + 'mila'
|
|
76
|
+
const postfix = this.omitIfZero(this.convertWholePart(number % 1000))
|
|
77
|
+
return prefix + postfix
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
exponentLengthToString (exponentLength) {
|
|
81
|
+
const prefix = this.exponentPrefixes[Math.floor(exponentLength / 6)]
|
|
82
|
+
return exponentLength % 6 === 0 ? prefix + 'ilione' : prefix + 'iliardo'
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
bigNumberToCardinal (number) {
|
|
86
|
+
const digits = [...number.toString()]
|
|
87
|
+
|
|
88
|
+
let preDigits = digits.length % 3
|
|
89
|
+
if (preDigits === 0) {
|
|
90
|
+
preDigits = 3
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const multiplier = digits.slice(0, preDigits) // first `preDigits` elements
|
|
94
|
+
const exponent = digits.slice(preDigits) // without the first `preDigits` elements
|
|
95
|
+
|
|
96
|
+
let prefix, postfix
|
|
97
|
+
let infix = this.exponentLengthToString(exponent.length)
|
|
98
|
+
|
|
99
|
+
if (multiplier.join('') === '1') {
|
|
100
|
+
prefix = 'un '
|
|
101
|
+
} else {
|
|
102
|
+
prefix = this.convertWholePart(Math.trunc(Number(multiplier.join(''))))
|
|
103
|
+
infix = ' ' + infix.slice(0, -1) + 'i' // without last element
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const isSetsEqual = (a, b) => a.size === b.size && [...a].every(value => b.has(value))
|
|
107
|
+
if (isSetsEqual(new Set(exponent), new Set(['0']))) {
|
|
108
|
+
postfix = ''
|
|
109
|
+
} else {
|
|
110
|
+
postfix = this.convertWholePart(Math.trunc(exponent.join('')))
|
|
111
|
+
|
|
112
|
+
infix += (postfix.includes(' e ') ? ', ' : ' e ')
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return prefix + infix + postfix
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
convertWholePart (number) {
|
|
119
|
+
let words = ''
|
|
120
|
+
|
|
121
|
+
if (number < 20) {
|
|
122
|
+
words = this.cardinalWords[number]
|
|
123
|
+
} else if (number < 100) {
|
|
124
|
+
words = this.tensToCardinal(Number(number))
|
|
125
|
+
} else if (number < 1000) {
|
|
126
|
+
words = this.hundredsToCardinal(Number(number))
|
|
127
|
+
} else if (number < 1_000_000) {
|
|
128
|
+
words = this.thousandsToCardinal(Number(number))
|
|
129
|
+
} else {
|
|
130
|
+
words = this.bigNumberToCardinal(number)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return this.accentuate(words)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Converts a number to Italian cardinal (written) form.
|
|
139
|
+
*
|
|
140
|
+
* @param {number|string|bigint} value The number to convert.
|
|
141
|
+
* @param {Object} [options={}] Configuration options.
|
|
142
|
+
* @returns {string} The number expressed in Italian words.
|
|
143
|
+
* @throws {TypeError} If value is NaN or invalid type.
|
|
144
|
+
* @throws {Error} If value is an invalid number string.
|
|
145
|
+
*/
|
|
146
|
+
export default function convertToWords (value, options = {}) {
|
|
147
|
+
return new Italian(options).convertToWords(value)
|
|
148
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import AbstractLanguage from '../classes/abstract-language.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Japanese language converter.
|
|
5
|
+
*
|
|
6
|
+
* Converts numbers to Japanese kanji numerals using the Sino-Japanese system:
|
|
7
|
+
* - Uses kanji characters (一, 二, 三, etc.)
|
|
8
|
+
* - Grouping by 万 (man, 10,000) and 億 (oku, 100,000,000)
|
|
9
|
+
* - Unique scale units: 兆 (chō, trillion), 京 (kei, 10^16)
|
|
10
|
+
* - Special rules for 1: omitted before 十 (10), 百 (100), 千 (1000), but kept for 万 and above
|
|
11
|
+
*
|
|
12
|
+
* Key Features:
|
|
13
|
+
* - Sino-Japanese number system (standard for general counting)
|
|
14
|
+
* - Grouping by powers of 10,000 (万-based system, not 1,000)
|
|
15
|
+
* - Scale units: 万 (10^4), 億 (10^8), 兆 (10^12), 京 (10^16)
|
|
16
|
+
* - Special handling of 一 (one) prefix
|
|
17
|
+
* - Support for very large numbers up to 無量大数 (10^68)
|
|
18
|
+
*
|
|
19
|
+
* Algorithm:
|
|
20
|
+
* 1. Split number into groups of 4 digits (万-based, not 3-digit thousand-based)
|
|
21
|
+
* 2. Convert each group to kanji using special rules
|
|
22
|
+
* 3. Append appropriate scale word (万, 億, 兆, etc.)
|
|
23
|
+
* 4. Join all groups
|
|
24
|
+
*
|
|
25
|
+
* Special Rules:
|
|
26
|
+
* - 10, 100, 1000: Don't use 一 prefix (十, 百, 千 not 一十, 一百, 一千)
|
|
27
|
+
* - 10,000+: Keep 一 prefix (一万, 一億, 一兆)
|
|
28
|
+
* - Zero: 零 or 〇 (both acceptable, 零 is more formal)
|
|
29
|
+
*/
|
|
30
|
+
export class Japanese extends AbstractLanguage {
|
|
31
|
+
negativeWord = 'マイナス'
|
|
32
|
+
decimalSeparatorWord = '点'
|
|
33
|
+
zeroWord = '零'
|
|
34
|
+
wordSeparator = '' // Japanese doesn't use spaces between characters
|
|
35
|
+
convertDecimalsPerDigit = true // Enable digit-by-digit decimal conversion
|
|
36
|
+
|
|
37
|
+
// Digits used for group conversion (1-9)
|
|
38
|
+
digits = ['一', '二', '三', '四', '五', '六', '七', '八', '九']
|
|
39
|
+
|
|
40
|
+
// Scale words for grouping by 10^4
|
|
41
|
+
scales = [
|
|
42
|
+
'', // 10^0 (ones)
|
|
43
|
+
'万', // 10^4 (man)
|
|
44
|
+
'億', // 10^8 (oku)
|
|
45
|
+
'兆', // 10^12 (chō)
|
|
46
|
+
'京', // 10^16 (kei)
|
|
47
|
+
'垓', // 10^20 (gai)
|
|
48
|
+
'秭', // 10^24 (jo/shi)
|
|
49
|
+
'穣', // 10^28 (jō)
|
|
50
|
+
'溝', // 10^32 (kō)
|
|
51
|
+
'澗', // 10^36 (kan)
|
|
52
|
+
'正', // 10^40 (sei)
|
|
53
|
+
'載', // 10^44 (sai)
|
|
54
|
+
'極', // 10^48 (goku)
|
|
55
|
+
'恒河沙', // 10^52 (gōgasha)
|
|
56
|
+
'阿僧祇', // 10^56 (asōgi)
|
|
57
|
+
'那由他', // 10^60 (nayuta)
|
|
58
|
+
'不可思議', // 10^64 (fukashigi)
|
|
59
|
+
'無量大数' // 10^68 (muryōtaisū)
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Converts a group of up to 4 digits to Japanese kanji.
|
|
64
|
+
* Handles special rules for omitting 一 before 十, 百, 千.
|
|
65
|
+
*
|
|
66
|
+
* Rule: Within a group, omit 一 before 十/百/千 EXCEPT when:
|
|
67
|
+
* - It's a lower group (not isTopGroup) AND
|
|
68
|
+
* - It would be the only character in that position
|
|
69
|
+
*
|
|
70
|
+
* @param {bigint} num - Number from 0 to 9999
|
|
71
|
+
* @param {boolean} isTopGroup - Whether this is the highest non-zero group
|
|
72
|
+
* @returns {string} Japanese kanji representation
|
|
73
|
+
*/
|
|
74
|
+
convertGroup (num, isTopGroup = false) {
|
|
75
|
+
if (num === 0n) return ''
|
|
76
|
+
|
|
77
|
+
const thousands = num / 1000n
|
|
78
|
+
const hundreds = (num % 1000n) / 100n
|
|
79
|
+
const tens = (num % 100n) / 10n
|
|
80
|
+
const ones = num % 10n
|
|
81
|
+
|
|
82
|
+
let result = ''
|
|
83
|
+
|
|
84
|
+
// Thousands (千)
|
|
85
|
+
if (thousands > 0n) {
|
|
86
|
+
// Always omit 一 before 千 when thousands === 1
|
|
87
|
+
if (thousands === 1n) {
|
|
88
|
+
result += '千'
|
|
89
|
+
} else {
|
|
90
|
+
result += this.digits[Number(thousands) - 1] + '千'
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Hundreds (百)
|
|
95
|
+
if (hundreds > 0n) {
|
|
96
|
+
// Always omit 一 before 百 when hundreds === 1
|
|
97
|
+
if (hundreds === 1n) {
|
|
98
|
+
result += '百'
|
|
99
|
+
} else {
|
|
100
|
+
result += this.digits[Number(hundreds) - 1] + '百'
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Tens (十)
|
|
105
|
+
if (tens > 0n) {
|
|
106
|
+
// Always omit 一 before 十 when tens === 1
|
|
107
|
+
if (tens === 1n) {
|
|
108
|
+
result += '十'
|
|
109
|
+
} else {
|
|
110
|
+
result += this.digits[Number(tens) - 1] + '十'
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Ones
|
|
115
|
+
if (ones > 0n) {
|
|
116
|
+
result += this.digits[Number(ones) - 1]
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return result
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Converts a BigInt number to Japanese cardinal form.
|
|
124
|
+
*
|
|
125
|
+
* @param {bigint} number - The number to convert
|
|
126
|
+
* @returns {string} Japanese kanji representation
|
|
127
|
+
*/
|
|
128
|
+
convertWholePart (number) {
|
|
129
|
+
if (number === 0n) {
|
|
130
|
+
return this.zeroWord
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
let temp = number
|
|
134
|
+
let scaleIndex = 0
|
|
135
|
+
const groups = []
|
|
136
|
+
|
|
137
|
+
// Split into groups of 4 digits (万-based system)
|
|
138
|
+
while (temp > 0n) {
|
|
139
|
+
const group = temp % 10000n
|
|
140
|
+
if (group > 0n) {
|
|
141
|
+
groups.push({ value: group, scale: scaleIndex })
|
|
142
|
+
}
|
|
143
|
+
temp = temp / 10000n
|
|
144
|
+
scaleIndex++
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Reverse to process from highest to lowest
|
|
148
|
+
groups.reverse()
|
|
149
|
+
|
|
150
|
+
let result = ''
|
|
151
|
+
|
|
152
|
+
for (let i = 0; i < groups.length; i++) {
|
|
153
|
+
const { value, scale } = groups[i]
|
|
154
|
+
const isTopGroup = (i === 0)
|
|
155
|
+
|
|
156
|
+
const groupStr = this.convertGroup(value, isTopGroup)
|
|
157
|
+
|
|
158
|
+
// For scales >= 1 (万 and above), always add the scale word
|
|
159
|
+
if (scale >= 1) {
|
|
160
|
+
// Special case: if group is 1 and scale >= 1, we need 一 before the scale
|
|
161
|
+
if (value === 1n) {
|
|
162
|
+
result += '一' + this.scales[scale]
|
|
163
|
+
} else {
|
|
164
|
+
result += groupStr + this.scales[scale]
|
|
165
|
+
}
|
|
166
|
+
} else {
|
|
167
|
+
result += groupStr
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return result
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Converts a value to cardinal (written) form in Japanese.
|
|
177
|
+
*
|
|
178
|
+
* @param {number|string|bigint} value - Number to convert.
|
|
179
|
+
* @param {Object} [options] - Options for the converter.
|
|
180
|
+
* @returns {string} Value in Japanese kanji numerals.
|
|
181
|
+
* @throws {Error} If value is invalid.
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* convertToWords(42, { lang: 'ja' }); // '四十二'
|
|
185
|
+
* convertToWords(1000, { lang: 'ja' }); // '千'
|
|
186
|
+
* convertToWords(10000, { lang: 'ja' }); // '一万'
|
|
187
|
+
*/
|
|
188
|
+
export default function convertToWords (value, options = {}) {
|
|
189
|
+
return new Japanese(options).convertToWords(value)
|
|
190
|
+
}
|