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/ja.js
CHANGED
|
@@ -1,137 +1,259 @@
|
|
|
1
|
-
import { AbstractLanguage } from '../classes/abstract-language.js'
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
|
-
* Japanese language converter
|
|
2
|
+
* Japanese 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-9999) at module load.
|
|
8
|
+
* This eliminates all per-call string manipulation for segment conversion.
|
|
5
9
|
*
|
|
6
|
-
*
|
|
7
|
-
* -
|
|
8
|
-
* -
|
|
9
|
-
* -
|
|
10
|
+
* Japanese-specific rules (handled in precomputation):
|
|
11
|
+
* - Myriad (万-based) grouping: 4 digits per segment instead of 3
|
|
12
|
+
* - 一 omission: Omit "一" before 十, 百, 千 but NOT before 万 and higher scales
|
|
13
|
+
* - Kanji numerals: 零一二三四五六七八九
|
|
14
|
+
* - No spaces between characters
|
|
10
15
|
*/
|
|
11
|
-
export class Japanese extends AbstractLanguage {
|
|
12
|
-
negativeWord = 'マイナス'
|
|
13
|
-
decimalSeparatorWord = '点'
|
|
14
|
-
zeroWord = '零'
|
|
15
|
-
wordSeparator = '' // Japanese doesn't use spaces between characters
|
|
16
|
-
usePerDigitDecimals = true // Enable digit-by-digit decimal conversion
|
|
17
|
-
|
|
18
|
-
// Ones words used for group conversion (1-9)
|
|
19
|
-
onesWords = ['一', '二', '三', '四', '五', '六', '七', '八', '九']
|
|
20
|
-
|
|
21
|
-
// Scale words for grouping by 10^4
|
|
22
|
-
scaleWords = [
|
|
23
|
-
'', // 10^0 (ones)
|
|
24
|
-
'万', // 10^4 (man)
|
|
25
|
-
'億', // 10^8 (oku)
|
|
26
|
-
'兆', // 10^12 (chō)
|
|
27
|
-
'京', // 10^16 (kei)
|
|
28
|
-
'垓', // 10^20 (gai)
|
|
29
|
-
'秭', // 10^24 (jo/shi)
|
|
30
|
-
'穣', // 10^28 (jō)
|
|
31
|
-
'溝', // 10^32 (kō)
|
|
32
|
-
'澗', // 10^36 (kan)
|
|
33
|
-
'正', // 10^40 (sei)
|
|
34
|
-
'載', // 10^44 (sai)
|
|
35
|
-
'極', // 10^48 (goku)
|
|
36
|
-
'恒河沙', // 10^52 (gōgasha)
|
|
37
|
-
'阿僧祇', // 10^56 (asōgi)
|
|
38
|
-
'那由他', // 10^60 (nayuta)
|
|
39
|
-
'不可思議', // 10^64 (fukashigi)
|
|
40
|
-
'無量大数' // 10^68 (muryōtaisū)
|
|
41
|
-
]
|
|
42
|
-
|
|
43
|
-
/** Converts a segment of up to 4 digits to Japanese kanji with 一 omission rules. */
|
|
44
|
-
segmentToWords (num) {
|
|
45
|
-
if (num === 0n) return ''
|
|
46
|
-
|
|
47
|
-
const thousands = num / 1000n
|
|
48
|
-
const hundreds = (num % 1000n) / 100n
|
|
49
|
-
const tens = (num % 100n) / 10n
|
|
50
|
-
const ones = num % 10n
|
|
51
|
-
|
|
52
|
-
let result = ''
|
|
53
|
-
|
|
54
|
-
// Thousands (千)
|
|
55
|
-
if (thousands > 0n) {
|
|
56
|
-
// Always omit 一 before 千 when thousands === 1
|
|
57
|
-
if (thousands === 1n) {
|
|
58
|
-
result += '千'
|
|
59
|
-
} else {
|
|
60
|
-
result += this.onesWords[Number(thousands) - 1] + '千'
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
16
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
17
|
+
import { parseNumericValue } from '../utils/parse-numeric.js'
|
|
18
|
+
|
|
19
|
+
// ============================================================================
|
|
20
|
+
// Vocabulary (module-level constants)
|
|
21
|
+
// ============================================================================
|
|
22
|
+
|
|
23
|
+
// Ones words (1-9), index 0 unused
|
|
24
|
+
const ONES = ['', '一', '二', '三', '四', '五', '六', '七', '八', '九']
|
|
25
|
+
|
|
26
|
+
// Scale words for powers of 10,000 (万-based system)
|
|
27
|
+
// Index 0 = 万 (10^4), 1 = 億 (10^8), 2 = 兆 (10^12), etc.
|
|
28
|
+
const SCALES = [
|
|
29
|
+
'万', // 10^4 (man)
|
|
30
|
+
'億', // 10^8 (oku)
|
|
31
|
+
'兆', // 10^12 (chō)
|
|
32
|
+
'京', // 10^16 (kei)
|
|
33
|
+
'垓', // 10^20 (gai)
|
|
34
|
+
'秭', // 10^24 (jo/shi)
|
|
35
|
+
'穣', // 10^28 (jō)
|
|
36
|
+
'溝', // 10^32 (kō)
|
|
37
|
+
'澗', // 10^36 (kan)
|
|
38
|
+
'正', // 10^40 (sei)
|
|
39
|
+
'載', // 10^44 (sai)
|
|
40
|
+
'極', // 10^48 (goku)
|
|
41
|
+
'恒河沙', // 10^52 (gōgasha)
|
|
42
|
+
'阿僧祇', // 10^56 (asōgi)
|
|
43
|
+
'那由他', // 10^60 (nayuta)
|
|
44
|
+
'不可思議', // 10^64 (fukashigi)
|
|
45
|
+
'無量大数' // 10^68 (muryōtaisū)
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
const ZERO = '零'
|
|
49
|
+
const NEGATIVE = 'マイナス'
|
|
50
|
+
const DECIMAL_SEP = '点'
|
|
51
|
+
|
|
52
|
+
// Internal scale words (within 4-digit segments)
|
|
53
|
+
const TEN = '十'
|
|
54
|
+
const HUNDRED = '百'
|
|
55
|
+
const THOUSAND = '千'
|
|
56
|
+
|
|
57
|
+
// ============================================================================
|
|
58
|
+
// Precomputed Lookup Tables (built once at module load)
|
|
59
|
+
// ============================================================================
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Builds segment word for 0-9999 with 一 omission rules.
|
|
63
|
+
* - Omit 一 before 十, 百, 千
|
|
64
|
+
* Only used during table construction.
|
|
65
|
+
*/
|
|
66
|
+
function buildSegment (n) {
|
|
67
|
+
if (n === 0) return ''
|
|
68
|
+
|
|
69
|
+
const ones = n % 10
|
|
70
|
+
const tens = Math.floor(n / 10) % 10
|
|
71
|
+
const hundreds = Math.floor(n / 100) % 10
|
|
72
|
+
const thousands = Math.floor(n / 1000)
|
|
73
|
+
|
|
74
|
+
let result = ''
|
|
75
|
+
|
|
76
|
+
// Thousands (千) - omit 一 when 1
|
|
77
|
+
if (thousands > 0) {
|
|
78
|
+
if (thousands === 1) {
|
|
79
|
+
result += THOUSAND
|
|
80
|
+
} else {
|
|
81
|
+
result += ONES[thousands] + THOUSAND
|
|
72
82
|
}
|
|
83
|
+
}
|
|
73
84
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
result += this.onesWords[Number(tens) - 1] + '十'
|
|
81
|
-
}
|
|
85
|
+
// Hundreds (百) - omit 一 when 1
|
|
86
|
+
if (hundreds > 0) {
|
|
87
|
+
if (hundreds === 1) {
|
|
88
|
+
result += HUNDRED
|
|
89
|
+
} else {
|
|
90
|
+
result += ONES[hundreds] + HUNDRED
|
|
82
91
|
}
|
|
92
|
+
}
|
|
83
93
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
94
|
+
// Tens (十) - omit 一 when 1
|
|
95
|
+
if (tens > 0) {
|
|
96
|
+
if (tens === 1) {
|
|
97
|
+
result += TEN
|
|
98
|
+
} else {
|
|
99
|
+
result += ONES[tens] + TEN
|
|
87
100
|
}
|
|
101
|
+
}
|
|
88
102
|
|
|
89
|
-
|
|
103
|
+
// Ones
|
|
104
|
+
if (ones > 0) {
|
|
105
|
+
result += ONES[ones]
|
|
90
106
|
}
|
|
91
107
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (integerPart === 0n) {
|
|
95
|
-
return this.zeroWord
|
|
96
|
-
}
|
|
108
|
+
return result
|
|
109
|
+
}
|
|
97
110
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
111
|
+
// Precompute all 10000 segment words (0-9999)
|
|
112
|
+
// SEGMENTS[n] gives the Japanese word for n within a segment
|
|
113
|
+
const SEGMENTS = new Array(10000)
|
|
114
|
+
for (let i = 0; i < 10000; i++) {
|
|
115
|
+
SEGMENTS[i] = buildSegment(i)
|
|
116
|
+
}
|
|
101
117
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
118
|
+
// ============================================================================
|
|
119
|
+
// Conversion Functions
|
|
120
|
+
// ============================================================================
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Converts a non-negative integer to Japanese words.
|
|
124
|
+
*
|
|
125
|
+
* @param {bigint} n - Non-negative integer to convert
|
|
126
|
+
* @returns {string} Japanese kanji words
|
|
127
|
+
*/
|
|
128
|
+
function integerToWords (n) {
|
|
129
|
+
if (n === 0n) return ZERO
|
|
130
|
+
|
|
131
|
+
// Fast path: numbers < 10000 (direct lookup)
|
|
132
|
+
if (n < 10000n) {
|
|
133
|
+
return SEGMENTS[Number(n)]
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Fast path: numbers < 100,000,000 (万 range)
|
|
137
|
+
if (n < 100_000_000n) {
|
|
138
|
+
const man = Number(n / 10000n)
|
|
139
|
+
const remainder = Number(n % 10000n)
|
|
140
|
+
|
|
141
|
+
// For 万 and above, we need 一 before the scale word when segment is 1
|
|
142
|
+
let result
|
|
143
|
+
if (man === 1) {
|
|
144
|
+
result = '一' + SCALES[0] // 一万
|
|
145
|
+
} else {
|
|
146
|
+
result = SEGMENTS[man] + SCALES[0]
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (remainder > 0) {
|
|
150
|
+
result += SEGMENTS[remainder]
|
|
110
151
|
}
|
|
111
152
|
|
|
112
|
-
|
|
113
|
-
|
|
153
|
+
return result
|
|
154
|
+
}
|
|
114
155
|
|
|
115
|
-
|
|
156
|
+
// For numbers >= 100,000,000, use scale decomposition
|
|
157
|
+
return buildLargeNumberWords(n)
|
|
158
|
+
}
|
|
116
159
|
|
|
117
|
-
|
|
118
|
-
|
|
160
|
+
/**
|
|
161
|
+
* Builds words for numbers >= 100,000,000.
|
|
162
|
+
* Uses BigInt modulo for 4-digit (myriad) segment extraction.
|
|
163
|
+
*
|
|
164
|
+
* @param {bigint} n - Number >= 100,000,000
|
|
165
|
+
* @returns {string} Japanese kanji words
|
|
166
|
+
*/
|
|
167
|
+
function buildLargeNumberWords (n) {
|
|
168
|
+
// Extract segments using BigInt modulo (faster than string slicing)
|
|
169
|
+
// Segments stored least-significant first (index 0 = units, 1 = 万, etc.)
|
|
170
|
+
const segments = []
|
|
171
|
+
let temp = n
|
|
172
|
+
while (temp > 0n) {
|
|
173
|
+
segments.push(Number(temp % 10000n))
|
|
174
|
+
temp = temp / 10000n
|
|
175
|
+
}
|
|
119
176
|
|
|
120
|
-
|
|
177
|
+
// Build result string directly (process from most-significant to least)
|
|
178
|
+
let result = ''
|
|
121
179
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
180
|
+
for (let i = segments.length - 1; i >= 0; i--) {
|
|
181
|
+
const segment = segments[i]
|
|
182
|
+
if (segment === 0) continue
|
|
183
|
+
|
|
184
|
+
if (i > 0) {
|
|
185
|
+
// For scales >= 万, we need 一 before scale word when segment is 1
|
|
186
|
+
if (segment === 1) {
|
|
187
|
+
result += '一' + SCALES[i - 1]
|
|
130
188
|
} else {
|
|
131
|
-
result +=
|
|
189
|
+
result += SEGMENTS[segment] + SCALES[i - 1]
|
|
132
190
|
}
|
|
191
|
+
} else {
|
|
192
|
+
// Units segment (no scale word)
|
|
193
|
+
result += SEGMENTS[segment]
|
|
133
194
|
}
|
|
195
|
+
}
|
|
134
196
|
|
|
135
|
-
|
|
197
|
+
return result || ZERO
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Converts decimal digits to Japanese words (digit by digit).
|
|
202
|
+
*
|
|
203
|
+
* @param {string} decimalPart - Decimal digits (without the point)
|
|
204
|
+
* @returns {string} Japanese kanji words for decimal part
|
|
205
|
+
*/
|
|
206
|
+
function decimalPartToWords (decimalPart) {
|
|
207
|
+
let result = ''
|
|
208
|
+
|
|
209
|
+
for (let i = 0; i < decimalPart.length; i++) {
|
|
210
|
+
const digit = parseInt(decimalPart[i], 10)
|
|
211
|
+
if (digit === 0) {
|
|
212
|
+
result += ZERO
|
|
213
|
+
} else {
|
|
214
|
+
result += ONES[digit]
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return result
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Converts a numeric value to Japanese words.
|
|
223
|
+
*
|
|
224
|
+
* This is the main public API. It accepts any valid numeric input
|
|
225
|
+
* (number, string, or bigint) and handles parsing internally.
|
|
226
|
+
*
|
|
227
|
+
* @param {number | string | bigint} value - The numeric value to convert
|
|
228
|
+
* @returns {string} The number in Japanese kanji words
|
|
229
|
+
* @throws {TypeError} If value is not a valid numeric type
|
|
230
|
+
* @throws {Error} If value is not a valid number format
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* toWords(42) // '四十二'
|
|
234
|
+
* toWords(10000) // '一万'
|
|
235
|
+
* toWords(100000000) // '一億'
|
|
236
|
+
*/
|
|
237
|
+
function toWords (value) {
|
|
238
|
+
const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
|
|
239
|
+
|
|
240
|
+
let result = ''
|
|
241
|
+
|
|
242
|
+
if (isNegative) {
|
|
243
|
+
result = NEGATIVE
|
|
136
244
|
}
|
|
245
|
+
|
|
246
|
+
result += integerToWords(integerPart)
|
|
247
|
+
|
|
248
|
+
if (decimalPart) {
|
|
249
|
+
result += DECIMAL_SEP + decimalPartToWords(decimalPart)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return result
|
|
137
253
|
}
|
|
254
|
+
|
|
255
|
+
// ============================================================================
|
|
256
|
+
// Public API
|
|
257
|
+
// ============================================================================
|
|
258
|
+
|
|
259
|
+
export { toWords }
|
package/lib/languages/kn.d.ts
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Kannada
|
|
2
|
+
* Converts a numeric value to Kannada words.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* - Kannada 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 Kannada words
|
|
8
6
|
*/
|
|
9
|
-
export
|
|
10
|
-
}
|
|
11
|
-
import { SouthAsianLanguage } from '../classes/south-asian-language.js';
|
|
7
|
+
export function toWords(value: number | string | bigint): string;
|
package/lib/languages/kn.js
CHANGED
|
@@ -1,42 +1,137 @@
|
|
|
1
|
-
import { SouthAsianLanguage } from '../classes/south-asian-language.js'
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
|
-
* Kannada language converter
|
|
2
|
+
* Kannada language converter - Functional Implementation
|
|
3
|
+
*
|
|
4
|
+
* Self-contained converter for South Asian numbering.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* Key features:
|
|
7
7
|
* - Indian numbering system (ಸಾವಿರ, ಲಕ್ಷ, ಕೋಟಿ)
|
|
8
8
|
* - Kannada script
|
|
9
|
+
* - 3-2-2 grouping pattern (last 3 digits, then groups of 2)
|
|
9
10
|
* - Complete word forms for 0-99
|
|
11
|
+
* - Per-digit decimal reading
|
|
10
12
|
*/
|
|
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
|
-
|
|
13
|
+
|
|
14
|
+
import { parseNumericValue } from '../utils/parse-numeric.js'
|
|
15
|
+
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Vocabulary
|
|
18
|
+
// ============================================================================
|
|
19
|
+
|
|
20
|
+
const ZERO = 'ಸೊನ್ನೆ'
|
|
21
|
+
const NEGATIVE = 'ಋಣಾತ್ಮಕ'
|
|
22
|
+
const DECIMAL_SEP = 'ದಶಮಾಂಶ'
|
|
23
|
+
const HUNDRED = 'ನೂರು'
|
|
24
|
+
|
|
25
|
+
const BELOW_HUNDRED = [
|
|
26
|
+
'ಸೊನ್ನೆ', 'ಒಂದು', 'ಎರಡು', 'ಮೂರು', 'ನಾಲ್ಕು', 'ಐದು', 'ಆರು', 'ಏಳು', 'ಎಂಟು', 'ಒಂಬತ್ತು',
|
|
27
|
+
'ಹತ್ತು', 'ಹನ್ನೊಂದು', 'ಹನ್ನೆರಡು', 'ಹದಿಮೂರು', 'ಹದಿನಾಲ್ಕು', 'ಹದಿನೈದು', 'ಹದಿನಾರು', 'ಹದಿನೇಳು', 'ಹದಿನೆಂಟು', 'ಹತ್ತೊಂಬತ್ತು',
|
|
28
|
+
'ಇಪ್ಪತ್ತು', 'ಇಪ್ಪತ್ತೊಂದು', 'ಇಪ್ಪತ್ತೆರಡು', 'ಇಪ್ಪತ್ತಮೂರು', 'ಇಪ್ಪತ್ತನಾಲ್ಕು', 'ಇಪ್ಪತ್ತೈದು', 'ಇಪ್ಪತ್ತಾರು', 'ಇಪ್ಪತ್ತೇಳು', 'ಇಪ್ಪತ್ತೆಂಟು', 'ಇಪ್ಪತ್ತೊಂಬತ್ತು',
|
|
29
|
+
'ಮೂವತ್ತು', 'ಮೂವತ್ತೊಂದು', 'ಮೂವತ್ತೆರಡು', 'ಮೂವತ್ತಮೂರು', 'ಮೂವತ್ತನಾಲ್ಕು', 'ಮೂವತ್ತೈದು', 'ಮೂವತ್ತಾರು', 'ಮೂವತ್ತೇಳು', 'ಮೂವತ್ತೆಂಟು', 'ಮೂವತ್ತೊಂಬತ್ತು',
|
|
30
|
+
'ನಲವತ್ತು', 'ನಲವತ್ತೊಂದು', 'ನಲವತ್ತೆರಡು', 'ನಲವತ್ತಮೂರು', 'ನಲವತ್ತನಾಲ್ಕು', 'ನಲವತ್ತೈದು', 'ನಲವತ್ತಾರು', 'ನಲವತ್ತೇಳು', 'ನಲವತ್ತೆಂಟು', 'ನಲವತ್ತೊಂಬತ್ತು',
|
|
31
|
+
'ಐವತ್ತು', 'ಐವತ್ತೊಂದು', 'ಐವತ್ತೆರಡು', 'ಐವತ್ತಮೂರು', 'ಐವತ್ತನಾಲ್ಕು', 'ಐವತ್ತೈದು', 'ಐವತ್ತಾರು', 'ಐವತ್ತೇಳು', 'ಐವತ್ತೆಂಟು', 'ಐವತ್ತೊಂಬತ್ತು',
|
|
32
|
+
'ಅರವತ್ತು', 'ಅರವತ್ತೊಂದು', 'ಅರವತ್ತೆರಡು', 'ಅರವತ್ತಮೂರು', 'ಅರವತ್ತನಾಲ್ಕು', 'ಅರವತ್ತೈದು', 'ಅರವತ್ತಾರು', 'ಅರವತ್ತೇಳು', 'ಅರವತ್ತೆಂಟು', 'ಅರವತ್ತೊಂಬತ್ತು',
|
|
33
|
+
'ಎಪ್ಪತ್ತು', 'ಎಪ್ಪತ್ತೊಂದು', 'ಎಪ್ಪತ್ತೆರಡು', 'ಎಪ್ಪತ್ತಮೂರು', 'ಎಪ್ಪತ್ತನಾಲ್ಕು', 'ಎಪ್ಪತ್ತೈದು', 'ಎಪ್ಪತ್ತಾರು', 'ಎಪ್ಪತ್ತೇಳು', 'ಎಪ್ಪತ್ತೆಂಟು', 'ಎಪ್ಪತ್ತೊಂಬತ್ತು',
|
|
34
|
+
'ಎಂಬತ್ತು', 'ಎಂಬತ್ತೊಂದು', 'ಎಂಬತ್ತೆರಡು', 'ಎಂಬತ್ತಮೂರು', 'ಎಂಬತ್ತನಾಲ್ಕು', 'ಎಂಬತ್ತೈದು', 'ಎಂಬತ್ತಾರು', 'ಎಂಬತ್ತೇಳು', 'ಎಂಬತ್ತೆಂಟು', 'ಎಂಬತ್ತೊಂಬತ್ತು',
|
|
35
|
+
'ತೊಂಬತ್ತು', 'ತೊಂಬತ್ತೊಂದು', 'ತೊಂಬತ್ತೆರಡು', 'ತೊಂಬತ್ತಮೂರು', 'ತೊಂಬತ್ತನಾಲ್ಕು', 'ತೊಂಬತ್ತೈದು', 'ತೊಂಬತ್ತಾರು', 'ತೊಂಬತ್ತೇಳು', 'ತೊಂಬತ್ತೆಂಟು', 'ತೊಂಬತ್ತೊಂಬತ್ತು'
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
// Scale words: index 0 = units (empty), 1 = thousand, 2 = lakh, 3 = crore, etc.
|
|
39
|
+
const SCALE_WORDS = ['', 'ಸಾವಿರ', 'ಲಕ್ಷ', 'ಕೋಟಿ', 'ಅಬ್ಜ', 'ಖರ್ವ', 'ನೀಲ', 'ಪದ್ಮ', 'ಶಂಖ']
|
|
40
|
+
|
|
41
|
+
// ============================================================================
|
|
42
|
+
// Segment Splitting (inlined for performance)
|
|
43
|
+
// ============================================================================
|
|
44
|
+
|
|
45
|
+
function groupByThreeThenTwos (n) {
|
|
46
|
+
const numStr = n.toString()
|
|
47
|
+
if (numStr.length <= 3) return [Number(numStr)]
|
|
48
|
+
|
|
49
|
+
const segments = []
|
|
50
|
+
segments.unshift(Number(numStr.slice(-3)))
|
|
51
|
+
|
|
52
|
+
let remaining = numStr.slice(0, -3)
|
|
53
|
+
while (remaining.length > 0) {
|
|
54
|
+
segments.unshift(Number(remaining.slice(-2)))
|
|
55
|
+
remaining = remaining.slice(0, -2)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return segments
|
|
42
59
|
}
|
|
60
|
+
|
|
61
|
+
function segmentToWords (n) {
|
|
62
|
+
if (n === 0) return ''
|
|
63
|
+
if (n < 100) return BELOW_HUNDRED[n]
|
|
64
|
+
|
|
65
|
+
const hundreds = Math.trunc(n / 100)
|
|
66
|
+
const remainder = n % 100
|
|
67
|
+
|
|
68
|
+
if (remainder === 0) {
|
|
69
|
+
return BELOW_HUNDRED[hundreds] + ' ' + HUNDRED
|
|
70
|
+
}
|
|
71
|
+
return BELOW_HUNDRED[hundreds] + ' ' + HUNDRED + ' ' + BELOW_HUNDRED[remainder]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ============================================================================
|
|
75
|
+
// Conversion Functions
|
|
76
|
+
// ============================================================================
|
|
77
|
+
|
|
78
|
+
function integerToWords (n) {
|
|
79
|
+
if (n === 0n) return ZERO
|
|
80
|
+
|
|
81
|
+
const segments = groupByThreeThenTwos(n)
|
|
82
|
+
const segmentCount = segments.length
|
|
83
|
+
const words = []
|
|
84
|
+
|
|
85
|
+
for (let i = 0; i < segmentCount; i++) {
|
|
86
|
+
const segmentValue = segments[i]
|
|
87
|
+
if (segmentValue === 0) continue
|
|
88
|
+
|
|
89
|
+
const scaleIndex = segmentCount - i - 1
|
|
90
|
+
words.push(segmentToWords(segmentValue))
|
|
91
|
+
if (scaleIndex > 0 && SCALE_WORDS[scaleIndex]) {
|
|
92
|
+
words.push(SCALE_WORDS[scaleIndex])
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return words.join(' ').trim()
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function decimalPartToWords (decimalPart) {
|
|
100
|
+
// Per-digit decimal reading
|
|
101
|
+
const digits = []
|
|
102
|
+
for (const char of decimalPart) {
|
|
103
|
+
const d = parseInt(char, 10)
|
|
104
|
+
digits.push(d === 0 ? ZERO : BELOW_HUNDRED[d])
|
|
105
|
+
}
|
|
106
|
+
return digits.join(' ')
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Converts a numeric value to Kannada words.
|
|
111
|
+
*
|
|
112
|
+
* @param {number | string | bigint} value - The numeric value to convert
|
|
113
|
+
* @returns {string} The number in Kannada words
|
|
114
|
+
*/
|
|
115
|
+
function toWords (value) {
|
|
116
|
+
const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
|
|
117
|
+
|
|
118
|
+
let result = ''
|
|
119
|
+
|
|
120
|
+
if (isNegative) {
|
|
121
|
+
result = NEGATIVE + ' '
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
result += integerToWords(integerPart)
|
|
125
|
+
|
|
126
|
+
if (decimalPart) {
|
|
127
|
+
result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return result
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ============================================================================
|
|
134
|
+
// Exports
|
|
135
|
+
// ============================================================================
|
|
136
|
+
|
|
137
|
+
export { toWords }
|
package/lib/languages/ko.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Korean
|
|
2
|
+
* Converts a numeric value to Korean words.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* @param {number | string | bigint} value - The numeric value to convert
|
|
5
|
+
* @returns {string} The number in Korean words
|
|
6
|
+
* @throws {TypeError} If value is not a valid numeric type
|
|
7
|
+
* @throws {Error} If value is not a valid number format
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* toWords(21) // '이십일'
|
|
11
|
+
* toWords(10000) // '만'
|
|
12
|
+
* toWords(1000000) // '백만'
|
|
8
13
|
*/
|
|
9
|
-
export
|
|
10
|
-
scaleWords: (string | bigint)[][];
|
|
11
|
-
/** Combines two word-sets according to Korean grammar rules. */
|
|
12
|
-
combineWordSets(preceding: any, following: any): any;
|
|
13
|
-
}
|
|
14
|
-
import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js';
|
|
14
|
+
export function toWords(value: number | string | bigint): string;
|