tanisa 1.0.1 → 1.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/README.md +26 -2
- package/package.json +12 -3
- package/src/converter.ts +40 -15
- package/src/interface.ts +15 -0
- package/tests/converter.test.ts +74 -0
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
- ✅ **Decimal support** : Gracefully converts those pesky fractions into spoken form.
|
|
12
12
|
- ✅ **Large number linguistics**: Tackles big numbers with the appropriate Malagasy terminology.
|
|
13
13
|
- ✅ **User-friendly API** : So intuitive, you'll feel like you've been speaking number-words your whole life.
|
|
14
|
+
- ✅ **Configurable options** : Customize the conversion behavior, like controlling how decimal places are handled and more...
|
|
14
15
|
- 🛡️ **Error Handling** : Throws helpful errors when you try to feed it something it can't digest.
|
|
15
16
|
|
|
16
17
|
## Installation
|
|
@@ -22,13 +23,13 @@
|
|
|
22
23
|
1. Import the Magic:
|
|
23
24
|
|
|
24
25
|
```
|
|
25
|
-
import {
|
|
26
|
+
import { Tanisa } from 'tanisa'
|
|
26
27
|
```
|
|
27
28
|
|
|
28
29
|
2. Simply use it:
|
|
29
30
|
|
|
30
31
|
```
|
|
31
|
-
const tanisa = new
|
|
32
|
+
const tanisa = new Tanisa();
|
|
32
33
|
|
|
33
34
|
tanisa.toWords(233)
|
|
34
35
|
```
|
|
@@ -40,3 +41,26 @@ tanisa.toWords(233) ==> Telo amby telopolo sy roanjato
|
|
|
40
41
|
tanisa.toWords(18.3) ==> Valo amby folo faingo telo
|
|
41
42
|
tanisa.toWords(0.008) ==> Aotra faingo aotra aotra valo
|
|
42
43
|
```
|
|
44
|
+
|
|
45
|
+
## Options
|
|
46
|
+
|
|
47
|
+
| Option Name | Type | Default | Description |
|
|
48
|
+
| :-------------- | :-------- | :------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
49
|
+
| `ignoreDecimal` | `boolean` | `false` | If set to `true`, the converter completely disregards any digits after the decimal point. Only the integer part of the number is considered. |
|
|
50
|
+
| `decimalPlaces` | `number` | `undefined` (converts all) | Specifies the maximum number of digits to convert in the decimal part. Extra digits are truncated (not rounded). Set to `0` to ignore decimals. |
|
|
51
|
+
|
|
52
|
+
**Note:** If both `ignoreDecimal` is `true` and `decimalPlaces` is set, `ignoreDecimal: true` takes precedence, and the decimal part will be entirely ignored.
|
|
53
|
+
|
|
54
|
+
Examples:
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
converter.toWords("456.789", { ignoreDecimal: true }) => Enina amby dimampolo sy efajato
|
|
58
|
+
|
|
59
|
+
converter.toWords("3.14567", { decimalPlaces: 2 }); ==> Telo faingo efatra amby folo
|
|
60
|
+
|
|
61
|
+
converter.toWords("3.14567", { decimalPlaces: 2, ignoreDecimal: true }); ==> Telo
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## License
|
|
65
|
+
|
|
66
|
+
This project is proudly released under the [MIT License](https://github.com/nifaliana/tanisa/blob/main/LICENSE)
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tanisa",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"main": "src/index.ts",
|
|
5
5
|
"description": "An utility to convert Malagasy 🇲🇬 numbers, including decimals, into their word representations.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Rija Nifaliana",
|
|
9
|
-
"email": "rija.nifaliana@gmail.com"
|
|
9
|
+
"email": "rija.nifaliana@gmail.com",
|
|
10
|
+
"url": "https://github.com/nifaliana"
|
|
10
11
|
},
|
|
11
12
|
"scripts": {
|
|
12
13
|
"test": "vitest run",
|
|
@@ -46,5 +47,13 @@
|
|
|
46
47
|
"fanisana",
|
|
47
48
|
"typescript",
|
|
48
49
|
"javascript"
|
|
49
|
-
]
|
|
50
|
+
],
|
|
51
|
+
"repository": {
|
|
52
|
+
"type": "git",
|
|
53
|
+
"url": "git+https://github.com/nifaliana/tanisa.git"
|
|
54
|
+
},
|
|
55
|
+
"bugs": {
|
|
56
|
+
"url": "https://github.com/nifaliana/tanisa/issues"
|
|
57
|
+
},
|
|
58
|
+
"homepage": "https://github.com/nifaliana/tanisa#readme"
|
|
50
59
|
}
|
package/src/converter.ts
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import { LargeNumberUnit, MalagasyNumerals } from './dictionary'
|
|
2
|
+
import { TanisaOptions } from './interface'
|
|
2
3
|
|
|
3
4
|
export class Tanisa {
|
|
4
|
-
public toWords(number: number | string): string {
|
|
5
|
-
const
|
|
5
|
+
public toWords(number: number | string, options?: TanisaOptions): string {
|
|
6
|
+
const ignoreDecimal = options?.ignoreDecimal ?? false
|
|
7
|
+
const decimalPlaces = options?.decimalPlaces ?? -1
|
|
8
|
+
|
|
9
|
+
const numStr = String(number).trim()
|
|
6
10
|
const [integerPartStr, decimalPartStr] = numStr.split('.')
|
|
7
11
|
const integerPartNum = parseInt(integerPartStr || '0', 10)
|
|
8
12
|
|
|
9
13
|
if (
|
|
10
14
|
isNaN(integerPartNum) ||
|
|
11
|
-
(decimalPartStr &&
|
|
15
|
+
(decimalPartStr &&
|
|
16
|
+
decimalPartStr.length > 0 &&
|
|
17
|
+
isNaN(parseInt(decimalPartStr, 10)))
|
|
12
18
|
) {
|
|
13
19
|
throw new TypeError(`Invalid number input: "${number}"`)
|
|
14
20
|
}
|
|
@@ -29,21 +35,40 @@ export class Tanisa {
|
|
|
29
35
|
const integerWords = this.convertInteger(integerPartNum)
|
|
30
36
|
|
|
31
37
|
let decimalWords = ''
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
const processDecimals =
|
|
39
|
+
decimalPartStr &&
|
|
40
|
+
decimalPartStr.length > 0 &&
|
|
41
|
+
!ignoreDecimal &&
|
|
42
|
+
decimalPlaces !== 0
|
|
43
|
+
|
|
44
|
+
if (processDecimals) {
|
|
45
|
+
let effectiveDecimalPartStr = decimalPartStr
|
|
46
|
+
|
|
47
|
+
if (decimalPlaces > 0 && decimalPartStr.length > decimalPlaces) {
|
|
48
|
+
effectiveDecimalPartStr = decimalPartStr.substring(0, decimalPlaces)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (parseInt(effectiveDecimalPartStr || '0', 10) > 0) {
|
|
52
|
+
let tempDecimalWords = ''
|
|
53
|
+
for (let i = 0; i < effectiveDecimalPartStr.length; i++) {
|
|
54
|
+
const digit = effectiveDecimalPartStr[i]
|
|
55
|
+
if (digit === '0') {
|
|
56
|
+
tempDecimalWords += MalagasyNumerals.GLUE_DECIMAL_ZERO
|
|
57
|
+
} else {
|
|
58
|
+
const remainingDecimal = effectiveDecimalPartStr.substring(i)
|
|
59
|
+
tempDecimalWords += this.convertInteger(
|
|
60
|
+
parseInt(remainingDecimal, 10)
|
|
61
|
+
)
|
|
62
|
+
break
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (tempDecimalWords) {
|
|
66
|
+
decimalWords = MalagasyNumerals.GLUE_FAINGO + tempDecimalWords
|
|
41
67
|
}
|
|
42
68
|
}
|
|
43
|
-
return integerWords + MalagasyNumerals.GLUE_FAINGO + decimalWords
|
|
44
|
-
} else {
|
|
45
|
-
return integerWords
|
|
46
69
|
}
|
|
70
|
+
|
|
71
|
+
return integerWords + decimalWords
|
|
47
72
|
}
|
|
48
73
|
|
|
49
74
|
private convertInteger(num: number): string {
|
package/src/interface.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface TanisaOptions {
|
|
2
|
+
/**
|
|
3
|
+
* If true, ignores the decimal part of the number.
|
|
4
|
+
* @default false
|
|
5
|
+
*/
|
|
6
|
+
ignoreDecimal?: boolean
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Specifies the maximum number of decimal places to convert.
|
|
10
|
+
* Extra decimal places will be truncated (not rounded).
|
|
11
|
+
* Set to 0 to ignore decimals.
|
|
12
|
+
* @default undefined (converts all decimal places)
|
|
13
|
+
*/
|
|
14
|
+
decimalPlaces?: number
|
|
15
|
+
}
|
package/tests/converter.test.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach } from 'vitest'
|
|
2
2
|
import { Tanisa, MalagasyNumerals } from '../src'
|
|
3
|
+
import { TanisaOptions } from '../src/interface'
|
|
3
4
|
|
|
4
5
|
describe('MalagasyNumberToWords', () => {
|
|
5
6
|
let converter: Tanisa
|
|
@@ -124,4 +125,77 @@ describe('MalagasyNumberToWords', () => {
|
|
|
124
125
|
expect(() => converter.toWords(safeInt)).not.toThrow()
|
|
125
126
|
})
|
|
126
127
|
})
|
|
128
|
+
|
|
129
|
+
describe('ignoreDecimal option', () => {
|
|
130
|
+
it('should ignore decimal part when ignoreDecimal is true', () => {
|
|
131
|
+
const options: TanisaOptions = { ignoreDecimal: true }
|
|
132
|
+
expect(converter.toWords(123.456, options)).toBe(
|
|
133
|
+
'telo amby roapolo amby zato'
|
|
134
|
+
)
|
|
135
|
+
expect(converter.toWords(0.99, options)).toBe('aotra')
|
|
136
|
+
expect(converter.toWords(1000.001, options)).toBe('arivo')
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it('should NOT ignore decimal part when ignoreDecimal is false or absent', () => {
|
|
140
|
+
expect(converter.toWords(10.5, { ignoreDecimal: false })).toBe(
|
|
141
|
+
'folo faingo dimy'
|
|
142
|
+
)
|
|
143
|
+
expect(converter.toWords(10.5)).toBe('folo faingo dimy')
|
|
144
|
+
})
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
describe('decimalPlaces option', () => {
|
|
148
|
+
it('should ignore decimal part when decimalPlaces is 0', () => {
|
|
149
|
+
const options: TanisaOptions = { decimalPlaces: 0 }
|
|
150
|
+
expect(converter.toWords(123.456, options)).toBe(
|
|
151
|
+
'telo amby roapolo amby zato'
|
|
152
|
+
)
|
|
153
|
+
expect(converter.toWords(0.99, options)).toBe('aotra')
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
it('should truncate decimal part to specified places', () => {
|
|
157
|
+
expect(converter.toWords(1.2345, { decimalPlaces: 1 })).toBe(
|
|
158
|
+
'iray faingo roa'
|
|
159
|
+
)
|
|
160
|
+
expect(converter.toWords(1.2345, { decimalPlaces: 2 })).toBe(
|
|
161
|
+
'iray faingo telo amby roapolo'
|
|
162
|
+
)
|
|
163
|
+
expect(converter.toWords(1.2345, { decimalPlaces: 3 })).toBe(
|
|
164
|
+
'iray faingo efatra amby telopolo sy roanjato'
|
|
165
|
+
)
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it('should handle leading zeros correctly with truncation', () => {
|
|
169
|
+
expect(converter.toWords(10.056, { decimalPlaces: 1 })).toBe('folo')
|
|
170
|
+
expect(converter.toWords(10.056, { decimalPlaces: 2 })).toBe(
|
|
171
|
+
'folo faingo aotra dimy'
|
|
172
|
+
)
|
|
173
|
+
expect(converter.toWords(10.056, { decimalPlaces: 3 })).toBe(
|
|
174
|
+
'folo faingo aotra enina amby dimampolo'
|
|
175
|
+
)
|
|
176
|
+
expect(converter.toWords(10.009, { decimalPlaces: 2 })).toBe('folo')
|
|
177
|
+
expect(converter.toWords(10.009, { decimalPlaces: 3 })).toBe(
|
|
178
|
+
'folo faingo aotra aotra sivy'
|
|
179
|
+
)
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
it('should convert all decimals if decimalPlaces is equal to or greater than actual places', () => {
|
|
183
|
+
expect(converter.toWords(5.67, { decimalPlaces: 2 })).toBe(
|
|
184
|
+
'dimy faingo fito amby enimpolo'
|
|
185
|
+
)
|
|
186
|
+
expect(converter.toWords(5.67, { decimalPlaces: 3 })).toBe(
|
|
187
|
+
'dimy faingo fito amby enimpolo'
|
|
188
|
+
)
|
|
189
|
+
expect(converter.toWords(5.67, { decimalPlaces: 10 })).toBe(
|
|
190
|
+
'dimy faingo fito amby enimpolo'
|
|
191
|
+
)
|
|
192
|
+
})
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
describe('decimalPlaces and ignoreDecimal options', () => {
|
|
196
|
+
it('should ignore decimal part if both params is set', () => {
|
|
197
|
+
const options: TanisaOptions = { decimalPlaces: 2, ignoreDecimal: true }
|
|
198
|
+
expect(converter.toWords(3.134343, options)).toBe('telo')
|
|
199
|
+
})
|
|
200
|
+
})
|
|
127
201
|
})
|