bahttext 2.3.0 → 2.3.2
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/.eslintignore +0 -0
- package/.eslintrc +12 -0
- package/README-en.md +1 -0
- package/README.md +1 -0
- package/package.json +9 -10
- package/src/index.d.ts +27 -17
- package/src/index.js +221 -88
package/.eslintignore
ADDED
|
File without changes
|
package/.eslintrc
ADDED
package/README-en.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# bahttext
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/bahttext)
|
|
4
|
+
[](https://www.npmjs.com/package/bahttext)
|
|
4
5
|
[](http://opensource.org/licenses/MIT)
|
|
5
6
|
[](https://github.com/semantic-release/semantic-release) [](https://greenkeeper.io/)
|
|
6
7
|
[](https://codecov.io/github/jojoee/bahttext)
|
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# bahttext
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/bahttext)
|
|
4
|
+
[](https://www.npmjs.com/package/bahttext)
|
|
4
5
|
[](http://opensource.org/licenses/MIT)
|
|
5
6
|
[](https://github.com/semantic-release/semantic-release) [](https://greenkeeper.io/)
|
|
6
7
|
[](https://codecov.io/github/jojoee/bahttext)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bahttext",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.2",
|
|
4
4
|
"description": "Change number to Thai pronunciation string",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"typings": "src/index.d.ts",
|
|
@@ -35,16 +35,15 @@
|
|
|
35
35
|
},
|
|
36
36
|
"homepage": "https://github.com/jojoee/bahttext#readme",
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@stryker-mutator/core": "^
|
|
39
|
-
"@stryker-mutator/jest-runner": "^
|
|
40
|
-
"@to-da-moon/thai-baht-lib": "^0.0.12",
|
|
38
|
+
"@stryker-mutator/core": "^8.7.1",
|
|
39
|
+
"@stryker-mutator/jest-runner": "^8.7.1",
|
|
41
40
|
"baht": "^0.7.1",
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
41
|
+
"bahttext230": "npm:bahttext@2.3.0",
|
|
42
|
+
"bahttext231": "npm:bahttext@2.3.1",
|
|
43
|
+
"jest": "^29.7.0",
|
|
44
|
+
"jest-expect-message": "^1.1.3",
|
|
45
|
+
"semantic-release": "^24.2.2",
|
|
46
|
+
"standard": "^17.1.2",
|
|
48
47
|
"typescript": "*"
|
|
49
48
|
},
|
|
50
49
|
"jest": {
|
package/src/index.d.ts
CHANGED
|
@@ -3,33 +3,43 @@ declare namespace _default {
|
|
|
3
3
|
}
|
|
4
4
|
export default _default;
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* Convert number → Thai Baht text (supports number or numeric string)
|
|
7
|
+
* Mirrors behaviour of previous implementation but with faster core.
|
|
7
8
|
*
|
|
8
9
|
* @public
|
|
9
|
-
* @param {number}
|
|
10
|
+
* @param {number|string} input
|
|
10
11
|
* @returns {string}
|
|
11
12
|
*/
|
|
12
|
-
export function bahttext(
|
|
13
|
+
export function bahttext(input: number | string): string;
|
|
13
14
|
/**
|
|
14
15
|
* @private
|
|
15
|
-
*
|
|
16
|
-
* @
|
|
16
|
+
* Handle numeric input and extract baht/satang values
|
|
17
|
+
* @param {number} input - numeric input (positive or negative)
|
|
18
|
+
* @returns {{baht: number, bahtStr: string, satang: number, isNegative: boolean} | false}
|
|
17
19
|
*/
|
|
18
|
-
export function
|
|
20
|
+
export function handleNumericInput(input: number): {
|
|
21
|
+
baht: number;
|
|
22
|
+
bahtStr: string;
|
|
23
|
+
satang: number;
|
|
24
|
+
isNegative: boolean;
|
|
25
|
+
} | false;
|
|
19
26
|
/**
|
|
20
27
|
* @private
|
|
21
|
-
*
|
|
22
|
-
* @param {string}
|
|
23
|
-
* @returns {string}
|
|
28
|
+
* Handle string input and extract baht/satang values
|
|
29
|
+
* @param {string} input - string input (numeric string, positive or negative)
|
|
30
|
+
* @returns {{baht: number, bahtStr: string, satang: number, isNegative: boolean} | false}
|
|
24
31
|
*/
|
|
25
|
-
export function
|
|
32
|
+
export function handleStringInput(input: string): {
|
|
33
|
+
baht: number;
|
|
34
|
+
bahtStr: string;
|
|
35
|
+
satang: number;
|
|
36
|
+
isNegative: boolean;
|
|
37
|
+
} | false;
|
|
26
38
|
/**
|
|
27
|
-
* bahtxtCombine baht and satang
|
|
28
|
-
* and also adding unit
|
|
29
|
-
*
|
|
30
39
|
* @private
|
|
31
|
-
*
|
|
32
|
-
* @param {
|
|
33
|
-
* @
|
|
40
|
+
* Format satang portion of the output
|
|
41
|
+
* @param {number} baht - baht amount
|
|
42
|
+
* @param {number} satang - satang amount
|
|
43
|
+
* @returns {string} formatted satang string
|
|
34
44
|
*/
|
|
35
|
-
export function
|
|
45
|
+
export function formatSatang(baht: number, satang: number): string;
|
package/src/index.js
CHANGED
|
@@ -1,125 +1,258 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
const EMPTY = ''
|
|
2
|
+
const ONE = 'หนึ่ง'
|
|
3
|
+
const TWO = 'สอง'
|
|
4
|
+
const THREE_TO_NINE = ['สาม', 'สี่', 'ห้า', 'หก', 'เจ็ด', 'แปด', 'เก้า']
|
|
5
|
+
const ED = 'เอ็ด'
|
|
6
|
+
const YEE = 'ยี่'
|
|
7
|
+
const LAN = 'ล้าน'
|
|
8
|
+
|
|
9
|
+
// Index 0-5 correspond to units,สิบ,ร้อย,พัน,หมื่น,แสน
|
|
10
|
+
const DIGIT = [EMPTY, 'สิบ', 'ร้อย', 'พัน', 'หมื่น', 'แสน']
|
|
11
|
+
|
|
12
|
+
// Helpers to build word tables quickly
|
|
13
|
+
const ONES = [EMPTY, ED, TWO, ...THREE_TO_NINE]
|
|
14
|
+
const TENS = [
|
|
15
|
+
EMPTY,
|
|
16
|
+
...[EMPTY, YEE, ...THREE_TO_NINE].map(t => t + DIGIT[1]) // "สิบ" family
|
|
17
|
+
]
|
|
18
|
+
// 0-99 lookup (fast satang conversion)
|
|
19
|
+
const SUB_HUNDRED = TENS.flatMap(t => ONES.map(o => t + o))
|
|
20
|
+
// Special case: 1 should read "หนึ่ง" not "เอ็ด"
|
|
21
|
+
SUB_HUNDRED[1] = ONE
|
|
22
|
+
|
|
23
|
+
// Single-digit lookup for generic conversion (no suffix)
|
|
24
|
+
const SUB_TEN = [EMPTY, ONE, TWO, ...THREE_TO_NINE]
|
|
25
|
+
|
|
26
|
+
// Pre-compiled constants for frequently used combinations
|
|
27
|
+
const YEE_SIB = YEE + DIGIT[1] // "ยี่สิบ"
|
|
28
|
+
const SIB = DIGIT[1] // "สิบ"
|
|
29
|
+
|
|
30
|
+
// Default fallback text used throughout tests
|
|
31
|
+
const DEFAULT_RESULT = 'ศูนย์บาทถ้วน'
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Convert number → Thai Baht text (supports number or numeric string)
|
|
35
|
+
* Mirrors behaviour of previous implementation but with faster core.
|
|
36
|
+
*
|
|
37
|
+
* @public
|
|
38
|
+
* @param {number|string} input
|
|
39
|
+
* @returns {string}
|
|
40
|
+
*/
|
|
41
|
+
function bahttext (input) {
|
|
42
|
+
// Early invalidation – keep behaviour expected by test suite
|
|
43
|
+
if (input === null || input === undefined ||
|
|
44
|
+
typeof input === 'boolean' || Array.isArray(input) ||
|
|
45
|
+
(typeof input !== 'number' && typeof input !== 'string')) {
|
|
46
|
+
return DEFAULT_RESULT
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const converted = convertInternal(input)
|
|
50
|
+
return converted === false ? DEFAULT_RESULT : converted
|
|
5
51
|
}
|
|
6
52
|
|
|
7
53
|
/**
|
|
8
54
|
* @private
|
|
9
|
-
* @param {
|
|
55
|
+
* @param {string} numStr – e.g. "123" (no sign, may include leading zeros)
|
|
10
56
|
* @returns {string}
|
|
11
57
|
*/
|
|
12
|
-
function
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
58
|
+
function numberToWords (numStr) {
|
|
59
|
+
// Short-circuit trivial "0"
|
|
60
|
+
if (numStr === '0') return ''
|
|
61
|
+
|
|
62
|
+
let output = EMPTY
|
|
63
|
+
const len = numStr.length
|
|
64
|
+
|
|
65
|
+
for (let i = 0; i < len; i++) {
|
|
66
|
+
const d = numStr[i]
|
|
67
|
+
const di = len - i - 1 // distance from rightmost digit
|
|
68
|
+
const diMod = di % 6 // position inside a 6-digit group
|
|
69
|
+
|
|
70
|
+
if (d === '0') {
|
|
71
|
+
// skip zero – no wording
|
|
72
|
+
} else if (diMod === 1) {
|
|
73
|
+
// tens column inside group
|
|
74
|
+
if (d === '1') {
|
|
75
|
+
// "สิบ"
|
|
76
|
+
output += SIB
|
|
77
|
+
} else if (d === '2') {
|
|
78
|
+
// "ยี่สิบ"
|
|
79
|
+
output += YEE_SIB
|
|
80
|
+
} else {
|
|
81
|
+
const digitCode = d.charCodeAt(0) - 48
|
|
82
|
+
output += SUB_TEN[digitCode] + DIGIT[diMod]
|
|
28
83
|
}
|
|
84
|
+
} else if (diMod === 0 && d === '1' && i !== 0) {
|
|
85
|
+
// trailing 1 (in ones place, not first digit in overall number) → "เอ็ด"
|
|
86
|
+
// BUT: if it's a stand-alone digit in hundreds place (like 201), use "หนึ่ง"
|
|
87
|
+
const isStandAloneInHundreds = (di === 0 && len > 1 && numStr[i - 1] === '0')
|
|
88
|
+
output += isStandAloneInHundreds ? ONE : ED
|
|
89
|
+
} else {
|
|
90
|
+
const digitCode = d.charCodeAt(0) - 48 // faster than Number(d)
|
|
91
|
+
output += SUB_TEN[digitCode] + DIGIT[diMod]
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// add "ล้าน" after finishing each 6-digit block (except last group)
|
|
95
|
+
if (diMod === 0 && di) {
|
|
96
|
+
output += LAN
|
|
29
97
|
}
|
|
30
98
|
}
|
|
31
99
|
|
|
32
|
-
return
|
|
100
|
+
return output
|
|
33
101
|
}
|
|
34
102
|
|
|
35
103
|
/**
|
|
36
104
|
* @private
|
|
37
|
-
*
|
|
38
|
-
* @param {
|
|
39
|
-
* @returns {string}
|
|
105
|
+
* Handle numeric input and extract baht/satang values
|
|
106
|
+
* @param {number} input - numeric input (positive or negative)
|
|
107
|
+
* @returns {{baht: number, bahtStr: string, satang: number, isNegative: boolean} | false}
|
|
40
108
|
*/
|
|
41
|
-
function
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
109
|
+
function handleNumericInput (input) {
|
|
110
|
+
if (!Number.isFinite(input)) return false
|
|
111
|
+
if (input < Number.MIN_SAFE_INTEGER || input > Number.MAX_SAFE_INTEGER) {
|
|
112
|
+
return false
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const isNegative = input < 0
|
|
116
|
+
if (isNegative) {
|
|
117
|
+
input = -input
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const baht = Math.floor(input)
|
|
121
|
+
const satang = Number.isInteger(input)
|
|
122
|
+
? 0
|
|
123
|
+
: Math.floor(((input + Number.EPSILON * (baht || 1)) * 100) % 100)
|
|
124
|
+
const bahtStr = baht.toString() // slightly faster than String(baht)
|
|
125
|
+
|
|
126
|
+
return { baht, bahtStr, satang, isNegative }
|
|
45
127
|
}
|
|
46
128
|
|
|
47
129
|
/**
|
|
48
|
-
* bahtxtCombine baht and satang
|
|
49
|
-
* and also adding unit
|
|
50
|
-
*
|
|
51
130
|
* @private
|
|
52
|
-
*
|
|
53
|
-
* @param {string}
|
|
54
|
-
* @returns {string}
|
|
131
|
+
* Handle string input and extract baht/satang values
|
|
132
|
+
* @param {string} input - string input (numeric string, positive or negative)
|
|
133
|
+
* @returns {{baht: number, bahtStr: string, satang: number, isNegative: boolean} | false}
|
|
55
134
|
*/
|
|
56
|
-
function
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
return satang + 'สตางค์'
|
|
135
|
+
function handleStringInput (input) {
|
|
136
|
+
let formatted = input.trim()
|
|
137
|
+
|
|
138
|
+
if (formatted.startsWith('-')) {
|
|
139
|
+
formatted = formatted.replace(/^-0+/, '-') // keep sign, drop leading zeros
|
|
140
|
+
if (formatted === '-') formatted = '0' // catch "-" or "-0"
|
|
63
141
|
} else {
|
|
64
|
-
|
|
142
|
+
formatted = formatted.replace(/^0+/, '') || '0' // combine with empty check
|
|
65
143
|
}
|
|
144
|
+
|
|
145
|
+
// Not a valid number?
|
|
146
|
+
if (formatted === '') formatted = '0'
|
|
147
|
+
let inputNum = Number(formatted)
|
|
148
|
+
if (Number.isNaN(inputNum)) return false
|
|
149
|
+
|
|
150
|
+
const isNegative = inputNum < 0
|
|
151
|
+
if (isNegative) {
|
|
152
|
+
inputNum = -inputNum
|
|
153
|
+
// remove sign for string slicing later
|
|
154
|
+
formatted = formatted.slice(1)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Safe-range check after absolute value
|
|
158
|
+
if (inputNum < Number.MIN_SAFE_INTEGER || inputNum > Number.MAX_SAFE_INTEGER) {
|
|
159
|
+
return false
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
let baht, bahtStr, satang, satangStr
|
|
163
|
+
|
|
164
|
+
// Handle decimal part by string slicing to avoid FP errors
|
|
165
|
+
const periodIdx = formatted.lastIndexOf('.')
|
|
166
|
+
if (periodIdx !== -1) {
|
|
167
|
+
bahtStr = formatted.slice(0, periodIdx) || '0'
|
|
168
|
+
baht = Number(bahtStr)
|
|
169
|
+
|
|
170
|
+
satangStr = formatted.slice(periodIdx + 1)
|
|
171
|
+
satang = satangStr
|
|
172
|
+
? Number(satangStr.slice(0, 2)) * (satangStr.length >= 2 ? 1 : [100, 10][satangStr.length])
|
|
173
|
+
: 0
|
|
174
|
+
} else {
|
|
175
|
+
baht = inputNum
|
|
176
|
+
bahtStr = formatted
|
|
177
|
+
satang = 0
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return { baht, bahtStr, satang, isNegative }
|
|
66
181
|
}
|
|
67
182
|
|
|
68
183
|
/**
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
* @
|
|
72
|
-
* @param {number}
|
|
73
|
-
* @returns {string}
|
|
184
|
+
* @private
|
|
185
|
+
* Format satang portion of the output
|
|
186
|
+
* @param {number} baht - baht amount
|
|
187
|
+
* @param {number} satang - satang amount
|
|
188
|
+
* @returns {string} formatted satang string
|
|
74
189
|
*/
|
|
75
|
-
function
|
|
76
|
-
if (
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
return
|
|
190
|
+
function formatSatang (baht, satang) {
|
|
191
|
+
if (satang) {
|
|
192
|
+
let output = ''
|
|
193
|
+
if (baht) output += 'บาท'
|
|
194
|
+
output += SUB_HUNDRED[satang] + 'สตางค์'
|
|
195
|
+
return output
|
|
196
|
+
} else {
|
|
197
|
+
return 'บาทถ้วน'
|
|
83
198
|
}
|
|
199
|
+
}
|
|
84
200
|
|
|
85
|
-
|
|
86
|
-
|
|
201
|
+
/**
|
|
202
|
+
* @private
|
|
203
|
+
* Returns false when input cannot be parsed as numeric.
|
|
204
|
+
*/
|
|
205
|
+
function convertInternal (input) {
|
|
206
|
+
let baht, bahtStr, satang
|
|
207
|
+
let isNegative = false
|
|
87
208
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
209
|
+
if (typeof input === 'number') {
|
|
210
|
+
// Numeric input handling
|
|
211
|
+
const result = handleNumericInput(input)
|
|
212
|
+
if (result === false) return false
|
|
213
|
+
|
|
214
|
+
baht = result.baht
|
|
215
|
+
bahtStr = result.bahtStr
|
|
216
|
+
satang = result.satang
|
|
217
|
+
isNegative = result.isNegative
|
|
218
|
+
} else if (typeof input === 'string') {
|
|
219
|
+
// String input handling
|
|
220
|
+
const result = handleStringInput(input)
|
|
221
|
+
if (result === false) return false
|
|
222
|
+
|
|
223
|
+
baht = result.baht
|
|
224
|
+
bahtStr = result.bahtStr
|
|
225
|
+
satang = result.satang
|
|
226
|
+
isNegative = result.isNegative
|
|
227
|
+
} else {
|
|
228
|
+
// unreachable due to early guard, but for safety
|
|
229
|
+
return false
|
|
230
|
+
}
|
|
92
231
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
232
|
+
// No value at all → zero
|
|
233
|
+
if (!baht && !satang) {
|
|
234
|
+
return DEFAULT_RESULT
|
|
235
|
+
}
|
|
97
236
|
|
|
98
|
-
//
|
|
99
|
-
let
|
|
100
|
-
let satang = bahtxtNum2Word(satangArr)
|
|
237
|
+
// Build output
|
|
238
|
+
let output = isNegative ? 'ลบ' : EMPTY
|
|
101
239
|
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
240
|
+
// Baht part – using fast converter
|
|
241
|
+
const bahtWords = numberToWords(bahtStr)
|
|
242
|
+
output += bahtWords
|
|
105
243
|
|
|
106
|
-
//
|
|
107
|
-
|
|
244
|
+
// Satang
|
|
245
|
+
output += formatSatang(baht, satang)
|
|
108
246
|
|
|
109
|
-
return
|
|
247
|
+
return output
|
|
110
248
|
}
|
|
111
249
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
bahtxtNum2Word,
|
|
119
|
-
bahtxtGrammarFix,
|
|
120
|
-
bahtxtCombine
|
|
121
|
-
}
|
|
122
|
-
exports.default = {
|
|
123
|
-
bahttext
|
|
124
|
-
}
|
|
250
|
+
module.exports = {
|
|
251
|
+
bahttext,
|
|
252
|
+
// Export private functions for testing
|
|
253
|
+
handleNumericInput,
|
|
254
|
+
handleStringInput,
|
|
255
|
+
formatSatang
|
|
125
256
|
}
|
|
257
|
+
|
|
258
|
+
exports.default = { bahttext }
|