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/CHANGELOG.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
## 3.0.0 - Functional Architecture
|
|
2
|
+
|
|
3
|
+
Complete rewrite from class-based to functional architecture with major performance improvements.
|
|
4
|
+
|
|
5
|
+
### ⚠ BREAKING CHANGES
|
|
6
|
+
|
|
7
|
+
**Migration required** - API has changed:
|
|
8
|
+
|
|
9
|
+
#### From v1.x (default export)
|
|
10
|
+
|
|
11
|
+
> **Note:** v1 bundled all languages into a single wrapper function with runtime language selection via `{ lang: 'es' }`. v3 requires explicit language imports for tree-shaking—only the languages you import are included in your bundle.
|
|
12
|
+
|
|
13
|
+
| Context | v1 | v3 |
|
|
14
|
+
|---------|----|----|
|
|
15
|
+
| **Import (all)** | `import n2words from 'n2words'` | `import { en, es } from 'n2words'` |
|
|
16
|
+
| **Import (single)** | `import es from 'n2words/languages/es'` | `import { toWords } from 'n2words/es'` |
|
|
17
|
+
| **Usage** | `n2words(42, { lang: 'es' })` | `es(42)` or `toWords(42)` |
|
|
18
|
+
| **Browser** | `n2words(42, { lang: 'es' })` | `n2words.es(42)` |
|
|
19
|
+
| **CDN (single)** | `.../dist/languages/es.js` | `.../dist/languages/es.js` (unchanged) |
|
|
20
|
+
|
|
21
|
+
#### From v2.x (class-based)
|
|
22
|
+
|
|
23
|
+
| Context | v2 | v3 |
|
|
24
|
+
|---------|----|----|
|
|
25
|
+
| **Import (all)** | `import { EnglishConverter } from 'n2words'` | `import { en } from 'n2words'` |
|
|
26
|
+
| **Import (single)** | `import { EnglishConverter } from 'n2words/en'` | `import { toWords } from 'n2words/en'` |
|
|
27
|
+
| **Usage** | `EnglishConverter(42)` | `en(42)` or `toWords(42)` |
|
|
28
|
+
| **Browser** | `n2words.EnglishConverter(42)` | `n2words.en(42)` |
|
|
29
|
+
| **CDN (single)** | `.../dist/EnglishConverter.js` | `.../dist/languages/en.js` |
|
|
30
|
+
|
|
31
|
+
### Highlights
|
|
32
|
+
|
|
33
|
+
- **3x-85x faster** conversion across languages
|
|
34
|
+
- **70-96% less memory** per conversion
|
|
35
|
+
- **75-92% smaller** per-language bundles
|
|
36
|
+
- **52 languages** - all self-contained, tree-shakeable modules
|
|
37
|
+
|
|
38
|
+
### New Languages
|
|
39
|
+
|
|
40
|
+
`am` (Amharic), `am-Latn` (Amharic Latin), `fi` (Finnish), `ha` (Hausa), `hbo` (Biblical Hebrew), `sr-Cyrl` (Serbian Cyrillic), `zh-Hant` (Traditional Chinese)
|
|
41
|
+
|
|
42
|
+
### Performance Improvements
|
|
43
|
+
|
|
44
|
+
- Precomputed lookup tables (en, pt, he, hbo)
|
|
45
|
+
- BigInt modulo instead of string slicing (ja, sw)
|
|
46
|
+
- Eliminated class instantiation overhead
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
See [#206](https://github.com/forzagreen/n2words/pull/206) for full details.
|
package/README.md
CHANGED
|
@@ -8,32 +8,15 @@
|
|
|
8
8
|
[](https://npmjs.com/package/n2words)
|
|
9
9
|
[](https://www.jsdelivr.com/package/npm/n2words)
|
|
10
10
|
|
|
11
|
-
**Convert numbers to words in
|
|
11
|
+
**Convert numbers to words in 52 languages with zero dependencies.**
|
|
12
12
|
|
|
13
13
|
## Why n2words?
|
|
14
14
|
|
|
15
|
-
- **
|
|
16
|
-
- **Zero Dependencies** — Pure JavaScript
|
|
17
|
-
- **Universal Compatibility** — Works in Node.js, browsers (via CDN), and all modern bundlers
|
|
15
|
+
- **52 Languages** — European, Asian, Middle Eastern, and regional variants
|
|
16
|
+
- **Zero Dependencies** — Pure JavaScript, works everywhere (Node.js, browsers, bundlers)
|
|
18
17
|
- **Type-Safe** — Full TypeScript support with generated `.d.ts` declarations
|
|
19
|
-
- **Production Ready** — Comprehensive test coverage (unit, integration, browser, type checking)
|
|
20
18
|
- **BigInt Support** — Handle arbitrarily large numbers without precision loss
|
|
21
|
-
- **
|
|
22
|
-
- **Tree-Shakable** — Import only the languages you need (~2-5 KB gzipped per language)
|
|
23
|
-
- **Browser Tested** — Verified in Chromium, Firefox, and WebKit via automated tests
|
|
24
|
-
|
|
25
|
-
## Contents
|
|
26
|
-
|
|
27
|
-
- [Quick Start](#quick-start)
|
|
28
|
-
- [Usage](#usage) — ESM, CommonJS, Browser (UMD)
|
|
29
|
-
- [Type Safety](#type-safety) — TypeScript support
|
|
30
|
-
- [Supported Languages](#supported-languages-48) — 48 languages with options
|
|
31
|
-
- [Browser Compatibility](#browser-compatibility) — Chrome 67+, Firefox 68+, Safari 14+, Edge 79+
|
|
32
|
-
- [Performance & Bundle Size](#performance--bundle-size) — Tree-shaking and benchmarks
|
|
33
|
-
- [Examples](#examples) — Basic, gender agreement, language-specific features
|
|
34
|
-
- [Documentation](#documentation) — Guides and API reference
|
|
35
|
-
- [Contributing](#contributing) — How to contribute
|
|
36
|
-
- [License](#license)
|
|
19
|
+
- **High Performance** — 1M+ ops/sec, ~1.4 KB gzipped per language
|
|
37
20
|
|
|
38
21
|
## Quick Start
|
|
39
22
|
|
|
@@ -42,11 +25,11 @@ npm install n2words
|
|
|
42
25
|
```
|
|
43
26
|
|
|
44
27
|
```js
|
|
45
|
-
import {
|
|
28
|
+
import { en, es, ar } from 'n2words'
|
|
46
29
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
30
|
+
en(123) // 'one hundred and twenty-three'
|
|
31
|
+
es(123) // 'ciento veintitrés'
|
|
32
|
+
ar(1, { gender: 'feminine' }) // 'واحدة' (with options)
|
|
50
33
|
```
|
|
51
34
|
|
|
52
35
|
## Usage
|
|
@@ -54,92 +37,70 @@ ArabicConverter(1, { gender: 'feminine' }) // 'واحدة' (with options)
|
|
|
54
37
|
**ESM (Node.js, modern bundlers):**
|
|
55
38
|
|
|
56
39
|
```js
|
|
57
|
-
|
|
40
|
+
// Named imports (tree-shakable)
|
|
41
|
+
import { en, es } from 'n2words'
|
|
42
|
+
|
|
43
|
+
// Subpath imports (smallest bundle, recommended for single language)
|
|
44
|
+
import { toWords } from 'n2words/en'
|
|
45
|
+
import { toWords as esWords } from 'n2words/es'
|
|
58
46
|
```
|
|
59
47
|
|
|
60
48
|
**CommonJS (Node.js):**
|
|
61
49
|
|
|
62
|
-
n2words is an ES module. For CommonJS environments, use dynamic import
|
|
50
|
+
n2words is an ES module. For CommonJS environments, use dynamic import:
|
|
63
51
|
|
|
64
52
|
```js
|
|
65
53
|
// Promise-based
|
|
66
|
-
import('n2words').then(({
|
|
67
|
-
console.log(
|
|
54
|
+
import('n2words').then(({ en }) => {
|
|
55
|
+
console.log(en(42)) // 'forty-two'
|
|
68
56
|
})
|
|
69
57
|
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
58
|
+
// Subpath import (smallest bundle)
|
|
59
|
+
import('n2words/en').then(({ toWords }) => {
|
|
60
|
+
console.log(toWords(42)) // 'forty-two'
|
|
61
|
+
})
|
|
75
62
|
```
|
|
76
63
|
|
|
77
64
|
**Browser (UMD via CDN):**
|
|
78
65
|
|
|
79
66
|
```html
|
|
80
|
-
<!-- All languages
|
|
67
|
+
<!-- All languages -->
|
|
81
68
|
<script src="https://cdn.jsdelivr.net/npm/n2words/dist/n2words.js"></script>
|
|
82
69
|
<script>
|
|
83
|
-
n2words.
|
|
70
|
+
n2words.en(42) // 'forty-two'
|
|
71
|
+
n2words.es(123) // 'ciento veintitrés'
|
|
84
72
|
</script>
|
|
85
73
|
|
|
86
|
-
<!--
|
|
87
|
-
<script src="https://cdn.jsdelivr.net/npm/n2words/dist/
|
|
88
|
-
<script src="https://cdn.jsdelivr.net/npm/n2words/dist/SpanishConverter.js"></script>
|
|
74
|
+
<!-- Single language (smallest, ~1.4 KB gzipped) -->
|
|
75
|
+
<script src="https://cdn.jsdelivr.net/npm/n2words/dist/languages/en.js"></script>
|
|
89
76
|
<script>
|
|
90
|
-
n2words.
|
|
91
|
-
n2words.SpanishConverter(123) // 'ciento veintitrés'
|
|
77
|
+
n2words.en(42) // 'forty-two'
|
|
92
78
|
</script>
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
## Type Safety
|
|
96
|
-
|
|
97
|
-
Full TypeScript support via JSDoc annotations and generated type definitions - works in both JavaScript and TypeScript projects with IntelliSense and type checking:
|
|
98
|
-
|
|
99
|
-
```typescript
|
|
100
|
-
import { EnglishConverter, ArabicConverter, SimplifiedChineseConverter } from 'n2words'
|
|
101
|
-
import type { NumericValue, ArabicOptions } from 'n2words'
|
|
102
79
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
//
|
|
109
|
-
|
|
110
|
-
ArabicConverter(1, { invalid: true }) // ✗ TypeScript error: invalid property
|
|
111
|
-
|
|
112
|
-
SimplifiedChineseConverter(123, { formal: false }) // ✓ '一百二十三' (common style)
|
|
113
|
-
SimplifiedChineseConverter(123, { formal: 'yes' }) // ✗ TypeScript error: wrong type
|
|
80
|
+
<!-- Multiple single-language bundles (no conflicts) -->
|
|
81
|
+
<script src="https://cdn.jsdelivr.net/npm/n2words/dist/languages/en.js"></script>
|
|
82
|
+
<script src="https://cdn.jsdelivr.net/npm/n2words/dist/languages/es.js"></script>
|
|
83
|
+
<script>
|
|
84
|
+
n2words.en(42) // 'forty-two'
|
|
85
|
+
n2words.es(42) // 'cuarenta y dos'
|
|
86
|
+
</script>
|
|
114
87
|
```
|
|
115
88
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
n2words includes TypeScript declaration files (`.d.ts`) generated from JSDoc annotations:
|
|
119
|
-
|
|
120
|
-
- **Source**: JSDoc annotations in JavaScript source files
|
|
121
|
-
- **Generated**: TypeScript declarations built via `tsc` during package preparation
|
|
122
|
-
- **Included**: Published to npm with the package (no separate `@types` package needed)
|
|
123
|
-
- **Validated**: Comprehensive type tests ensure correctness
|
|
124
|
-
|
|
125
|
-
**Exported Types:**
|
|
126
|
-
|
|
127
|
-
- `NumericValue` - Accepted input types: `number | bigint | string`
|
|
128
|
-
- Language-specific option types (e.g., `ArabicOptions`, `SimplifiedChineseOptions`, `DutchOptions`, etc.)
|
|
129
|
-
|
|
130
|
-
## Supported Languages (48)
|
|
89
|
+
## Supported Languages (52)
|
|
131
90
|
|
|
132
91
|
Language codes follow [IETF BCP 47](https://tools.ietf.org/html/bcp47) standards.
|
|
133
92
|
|
|
134
93
|
| Code | Language | Options | Code | Language | Options |
|
|
135
94
|
| --------- | ------------------- | ------- | --------- | ------------------- | ------- |
|
|
95
|
+
| `am` | Amharic | | `am-Latn` | Amharic Latin | |
|
|
136
96
|
| `ar` | Arabic | ✓ | `az` | Azerbaijani | |
|
|
137
|
-
| `bn` | Bengali | | `cs` | Czech |
|
|
138
|
-
| `da` | Danish |
|
|
97
|
+
| `bn` | Bengali | | `cs` | Czech | |
|
|
98
|
+
| `da` | Danish | | `de` | German | |
|
|
139
99
|
| `el` | Greek | | `en` | English | |
|
|
140
100
|
| `es` | Spanish | ✓ | `fa` | Persian | |
|
|
141
|
-
| `
|
|
142
|
-
| `fr
|
|
101
|
+
| `fi` | Finnish | | `fil` | Filipino | |
|
|
102
|
+
| `fr` | French | ✓ | `fr-BE` | Belgian French | ✓ |
|
|
103
|
+
| `gu` | Gujarati | | `ha` | Hausa | |
|
|
143
104
|
| `hbo` | Biblical Hebrew | ✓ | `he` | Modern Hebrew | ✓ |
|
|
144
105
|
| `hi` | Hindi | | `hr` | Croatian | ✓ |
|
|
145
106
|
| `hu` | Hungarian | | `id` | Indonesian | |
|
|
@@ -160,10 +121,10 @@ Language codes follow [IETF BCP 47](https://tools.ietf.org/html/bcp47) standards
|
|
|
160
121
|
|
|
161
122
|
### Language Options
|
|
162
123
|
|
|
163
|
-
|
|
124
|
+
19 languages support additional options. Common options include:
|
|
164
125
|
|
|
165
|
-
**`gender`** (`'masculine'` | `'feminine'`) -
|
|
166
|
-
Arabic, Biblical Hebrew, Croatian,
|
|
126
|
+
**`gender`** (`'masculine'` | `'feminine'`) - 12 languages
|
|
127
|
+
Arabic, Biblical Hebrew, Croatian, Latvian, Lithuanian, Polish, Romanian, Russian, Serbian (both scripts), Spanish, Ukrainian
|
|
167
128
|
|
|
168
129
|
**`formal`** (`boolean`) - 2 languages
|
|
169
130
|
Simplified Chinese, Traditional Chinese - Toggle between formal/financial and common numerals
|
|
@@ -171,14 +132,11 @@ Simplified Chinese, Traditional Chinese - Toggle between formal/financial and co
|
|
|
171
132
|
**Other options:**
|
|
172
133
|
|
|
173
134
|
- Dutch: `includeOptionalAnd`, `accentOne`, `noHundredPairing`
|
|
174
|
-
- French/French
|
|
135
|
+
- French/Belgian French: `withHyphenSeparator`
|
|
175
136
|
- Hebrew (Modern & Biblical): `andWord`
|
|
176
137
|
- Turkish: `dropSpaces`
|
|
177
|
-
- Danish: `ordFlag` (ordinal numbers)
|
|
178
138
|
- Arabic: `negativeWord` (custom negative word)
|
|
179
139
|
|
|
180
|
-
[See complete options reference →](docs/ARCHITECTURE.md#language-specific-options)
|
|
181
|
-
|
|
182
140
|
## Browser Compatibility
|
|
183
141
|
|
|
184
142
|
**Minimum Requirements** (due to BigInt):
|
|
@@ -200,27 +158,38 @@ Simplified Chinese, Traditional Chinese - Toggle between formal/financial and co
|
|
|
200
158
|
|
|
201
159
|
### Bundle Size Comparison
|
|
202
160
|
|
|
203
|
-
| Import Strategy
|
|
204
|
-
|
|
|
205
|
-
| All languages (UMD)
|
|
206
|
-
| Single language (UMD)
|
|
207
|
-
|
|
|
208
|
-
|
|
|
209
|
-
|
|
|
161
|
+
| Import Strategy | Bundle Size | Gzipped | Languages |
|
|
162
|
+
| ------------------------------ | ----------- | --------- | --------- |
|
|
163
|
+
| All languages (UMD) | ~116 KB | ~28 KB | All 52 |
|
|
164
|
+
| Single language (UMD) | ~3-5 KB | ~1.4-2 KB | 1 |
|
|
165
|
+
| **Subpath import (ESM)** ⭐ | ~3 KB | ~1.4 KB | 1 |
|
|
166
|
+
| Named imports (ESM, 1 lang) | ~3-5 KB | ~1.4-2 KB | 1 |
|
|
167
|
+
| Named imports (ESM, 3 langs) | ~9-15 KB | ~4-6 KB | 3 |
|
|
168
|
+
| Named imports (ESM, 10 langs) | ~30-50 KB | ~10-15 KB | 10 |
|
|
210
169
|
|
|
211
170
|
### Performance Characteristics
|
|
212
171
|
|
|
213
|
-
- **
|
|
214
|
-
- **
|
|
215
|
-
- **
|
|
216
|
-
- **
|
|
172
|
+
- **1M+ ops/sec**: Most languages exceed 1 million conversions per second
|
|
173
|
+
- **Sub-millisecond**: Typical conversion takes < 1 microsecond
|
|
174
|
+
- **Low memory**: ~500-800 bytes per conversion (no allocations for small numbers)
|
|
175
|
+
- **BigInt optimized**: Uses BigInt modulo instead of string manipulation
|
|
176
|
+
- **Precomputed tables**: Common segments (0-999) precomputed at module load
|
|
217
177
|
|
|
218
|
-
**
|
|
178
|
+
**Subpath imports (recommended for single language):**
|
|
219
179
|
|
|
220
180
|
```js
|
|
221
|
-
//
|
|
222
|
-
import {
|
|
223
|
-
//
|
|
181
|
+
// Smallest possible bundle - no barrel file overhead
|
|
182
|
+
import { toWords } from 'n2words/en'
|
|
183
|
+
toWords(42) // 'forty-two'
|
|
184
|
+
// Final bundle: ~1.4 KB gzipped
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**Named imports (for multiple languages):**
|
|
188
|
+
|
|
189
|
+
```js
|
|
190
|
+
// Bundler tree-shakes unused languages
|
|
191
|
+
import { en, es } from 'n2words'
|
|
192
|
+
// Final bundle: ~3-4 KB gzipped (English + Spanish)
|
|
224
193
|
```
|
|
225
194
|
|
|
226
195
|
**Run benchmarks:**
|
|
@@ -232,103 +201,33 @@ npm run bench:memory # Memory usage benchmarks
|
|
|
232
201
|
|
|
233
202
|
## Examples
|
|
234
203
|
|
|
235
|
-
### Basic Conversions
|
|
236
|
-
|
|
237
|
-
```js
|
|
238
|
-
import { EnglishConverter } from 'n2words'
|
|
239
|
-
|
|
240
|
-
// Basic numbers
|
|
241
|
-
EnglishConverter(0) // 'zero'
|
|
242
|
-
EnglishConverter(42) // 'forty-two'
|
|
243
|
-
EnglishConverter(1000000) // 'one million'
|
|
244
|
-
|
|
245
|
-
// Decimals & negatives
|
|
246
|
-
EnglishConverter(3.14) // 'three point one four'
|
|
247
|
-
EnglishConverter(-42) // 'minus forty-two'
|
|
248
|
-
|
|
249
|
-
// Large numbers & BigInt
|
|
250
|
-
EnglishConverter(1234567890) // 'one billion two hundred and thirty-four million five hundred and sixty-seven thousand eight hundred and ninety'
|
|
251
|
-
EnglishConverter(123456789012345n) // Works with arbitrarily large integers
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
### Gender Agreement
|
|
255
|
-
|
|
256
204
|
```js
|
|
257
|
-
import {
|
|
205
|
+
import { en, es, ar, zhHans } from 'n2words'
|
|
258
206
|
|
|
259
|
-
//
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
SpanishConverter(21, { gender: 'feminine' }) // 'veintiuna'
|
|
207
|
+
// Basic conversions
|
|
208
|
+
en(42) // 'forty-two'
|
|
209
|
+
en(3.14) // 'three point one four'
|
|
210
|
+
en(-1000000) // 'minus one million'
|
|
264
211
|
|
|
265
|
-
//
|
|
266
|
-
|
|
267
|
-
|
|
212
|
+
// Input types: number, string, or BigInt
|
|
213
|
+
en('42') // 'forty-two'
|
|
214
|
+
en(42n) // 'forty-two'
|
|
215
|
+
en(999999999999999999999999n) // Works with arbitrarily large integers
|
|
268
216
|
|
|
269
|
-
//
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
### Language-Specific Features
|
|
275
|
-
|
|
276
|
-
```js
|
|
277
|
-
import {
|
|
278
|
-
SimplifiedChineseConverter,
|
|
279
|
-
TraditionalChineseConverter,
|
|
280
|
-
JapaneseConverter,
|
|
281
|
-
DutchConverter,
|
|
282
|
-
FrenchConverter
|
|
283
|
-
} from 'n2words'
|
|
217
|
+
// Gender agreement (12 languages)
|
|
218
|
+
es(1) // 'uno' (masculine, default)
|
|
219
|
+
es(1, { gender: 'feminine' }) // 'una'
|
|
220
|
+
ar(1, { gender: 'feminine' }) // 'واحدة'
|
|
284
221
|
|
|
285
222
|
// Chinese: formal (financial) vs common numerals
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
TraditionalChineseConverter(456) // '肆佰伍拾陸' (formal, default)
|
|
290
|
-
TraditionalChineseConverter(456, { formal: false }) // '四百五十六' (common)
|
|
291
|
-
|
|
292
|
-
// Japanese: digit-by-digit decimals
|
|
293
|
-
JapaneseConverter(3.14) // '三点一四'
|
|
294
|
-
|
|
295
|
-
// Dutch: flexible formatting
|
|
296
|
-
DutchConverter(123) // 'honderddrieëntwintig' (default compound)
|
|
297
|
-
DutchConverter(101, { includeOptionalAnd: true }) // 'honderdeneen' (with optional "en")
|
|
298
|
-
DutchConverter(1) // 'één' (accented, default)
|
|
299
|
-
DutchConverter(1, { accentOne: false }) // 'een' (unaccented)
|
|
300
|
-
|
|
301
|
-
// French: selective hyphens vs all hyphens
|
|
302
|
-
FrenchConverter(123) // 'cent vingt-trois' (default)
|
|
303
|
-
FrenchConverter(123, { withHyphenSeparator: true }) // 'cent-vingt-trois' (all hyphens)
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
### Input Flexibility
|
|
307
|
-
|
|
308
|
-
```js
|
|
309
|
-
import { EnglishConverter } from 'n2words'
|
|
310
|
-
|
|
311
|
-
// Multiple input types
|
|
312
|
-
EnglishConverter(42) // number → 'forty-two'
|
|
313
|
-
EnglishConverter('42') // string → 'forty-two'
|
|
314
|
-
EnglishConverter(42n) // BigInt → 'forty-two'
|
|
315
|
-
|
|
316
|
-
// Decimal strings
|
|
317
|
-
EnglishConverter('3.14') // 'three point one four'
|
|
318
|
-
EnglishConverter('.5') // 'zero point five'
|
|
319
|
-
|
|
320
|
-
// Negative strings
|
|
321
|
-
EnglishConverter('-42') // 'minus forty-two'
|
|
322
|
-
|
|
323
|
-
// Large BigInts (no precision loss)
|
|
324
|
-
EnglishConverter(999999999999999999999999n) // Accurate conversion
|
|
223
|
+
zhHans(123) // '壹佰贰拾叁' (formal, default)
|
|
224
|
+
zhHans(123, { formal: false }) // '一百二十三' (common)
|
|
325
225
|
```
|
|
326
226
|
|
|
327
227
|
## Documentation
|
|
328
228
|
|
|
329
229
|
- **[Compatibility Guide](COMPATIBILITY.md)** - Browser and Node.js compatibility
|
|
330
230
|
- **[Contributing Guide](CONTRIBUTING.md)** - How to contribute and add languages
|
|
331
|
-
- **[Architecture Guide](docs/ARCHITECTURE.md)** - Implementation patterns and options reference
|
|
332
231
|
- **[Code of Conduct](CODE_OF_CONDUCT.md)** - Community standards
|
|
333
232
|
|
|
334
233
|
## Contributing
|
|
@@ -337,7 +236,6 @@ We welcome contributions! Add a new language or improve existing ones:
|
|
|
337
236
|
|
|
338
237
|
```bash
|
|
339
238
|
npm run lang:add <code> # Scaffold a new language (BCP 47 code)
|
|
340
|
-
npm run lang:validate -- <code> # Validate implementation
|
|
341
239
|
npm test # Run full test suite
|
|
342
240
|
```
|
|
343
241
|
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/*! n2words/am-Latn v3.0.0 | MIT License | github.com/forzagreen/n2words */
|
|
2
|
+
var e,t;e=this,t=function(e){"use strict";function t(e){const t="-"===e[0];t&&(e=e.slice(1));const n=e.indexOf(".");if(-1===n)return{isNegative:t,integerPart:BigInt(e)};const i=e.slice(0,n)||"0",r=e.slice(n+1);return{isNegative:t,integerPart:BigInt(i),decimalPart:r}}function n(e){const[t,n]=e.toLowerCase().split("e"),i=parseInt(n,10),r=t.indexOf("."),s=-1===r?t:t.slice(0,r)+t.slice(r+1),a=(-1===r?t.length:r)+i;return a>=s.length?s+"0".repeat(a-s.length):a<=0?"0."+"0".repeat(-a)+s:s.slice(0,a)+"."+s.slice(a)}const i=["","and","hulet","sost","arat","amist","siddist","sebat","siment","zeteny"],r=["asir","asra and","asra hulet","asra sost","asra arat","asra amist","asra siddist","asra sebat","asra siment","asra zeteny"],s=["","","haya","selasa","arba","hamsa","silsa","seba","semanya","zetena"],a="zero",o=["","shi","miliyon","billiyon"];function u(e){if(0===e)return"";const t=e%10,n=Math.floor(e/10)%10,a=Math.floor(e/100),o=[];return a>0&&o.push(i[a]+" meto"),1===n?o.push(r[t]):(n>1&&o.push(s[n]),t>0&&o.push(i[t])),o.join(" ")}const c=new Array(1e3);for(let e=0;e<1e3;e++)c[e]=u(e);e.amLatn=function(e){const{isNegative:r,integerPart:s,decimalPart:u}=function(e){const i=typeof e;if("bigint"===i)return e<0n?{isNegative:!0,integerPart:-e}:{isNegative:!1,integerPart:e};if("number"===i){if(!Number.isFinite(e))throw new Error("Number must be finite (NaN and Infinity are not supported)");return Number.isSafeInteger(e)?e<0?{isNegative:!0,integerPart:BigInt(-e)}:{isNegative:!1,integerPart:BigInt(e)}:t(function(e){const t=e.toString();return t.includes("e")||t.includes("E")?n(t):t}(e))}if("string"===i)return t(function(e){const t=e.trim();if(0===t.length||Number.isNaN(Number(t)))throw new Error(`Invalid number format: "${e}"`);return t.includes("e")||t.includes("E")?n(t):t}(e));throw new TypeError(`Invalid value type: expected number, string, or bigint, received ${i}`)}(e);let l="";return r&&(l="asitegna "),l+=0n===(f=s)?a:f<1000n?c[Number(f)]:function(e){const t=e.toString(),n=t.length,i=[],r=n%3;let s=0;for(r>0&&(i.push(Number(t.slice(0,r))),s=r);s<n;)i.push(Number(t.slice(s,s+3))),s+=3;const a=[];let u=i.length-1;for(let e=0;e<i.length;e++){const t=i[e];0!==t&&a.push(0===u?c[t]:c[t]+" "+(o[u]||"")),u--}return a.join(" ")}(f),u&&(l+=" netib "+function(e){const t=[];for(const n of e){const e=parseInt(n,10);t.push(0===e?a:i[e])}return t.join(" ")}(u)),l;var f}},"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).n2words=e.n2words||{});
|
|
3
|
+
//# sourceMappingURL=am-Latn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"am-Latn.js","sources":["../../lib/utils/parse-numeric.js","../../lib/languages/am-Latn.js"],"sourcesContent":["/**\n * Numeric value parsing utility.\n * Transforms user input (number, string, or bigint) into normalized components.\n * @module parse-numeric\n */\n\n/**\n * Parses a numeric value into its components.\n * @param {number|string|bigint} value\n * @returns {{isNegative: boolean, integerPart: bigint, decimalPart?: string}}\n * @throws {TypeError} If value is not number, string, or bigint\n * @throws {Error} If value is not a valid number format\n */\nexport function parseNumericValue (value) {\n const type = typeof value\n\n // BigInt: simplest case\n if (type === 'bigint') {\n return value < 0n\n ? { isNegative: true, integerPart: -value }\n : { isNegative: false, integerPart: value }\n }\n\n // Number: fast path for safe integers\n if (type === 'number') {\n if (!Number.isFinite(value)) {\n throw new Error('Number must be finite (NaN and Infinity are not supported)')\n }\n if (Number.isSafeInteger(value)) {\n return value < 0\n ? { isNegative: true, integerPart: BigInt(-value) }\n : { isNegative: false, integerPart: BigInt(value) }\n }\n return parseNumericString(numberToString(value))\n }\n\n // String input\n if (type === 'string') {\n return parseNumericString(normalizeString(value))\n }\n\n throw new TypeError(\n `Invalid value type: expected number, string, or bigint, received ${type}`\n )\n}\n\n/**\n * Converts a number to decimal string, expanding scientific notation if needed.\n */\nfunction numberToString (value) {\n const str = value.toString()\n return (str.includes('e') || str.includes('E'))\n ? expandScientificNotation(str)\n : str\n}\n\n/**\n * Validates and normalizes a string numeric input.\n */\nfunction normalizeString (value) {\n const trimmed = value.trim()\n if (trimmed.length === 0 || Number.isNaN(Number(trimmed))) {\n throw new Error(`Invalid number format: \"${value}\"`)\n }\n return (trimmed.includes('e') || trimmed.includes('E'))\n ? expandScientificNotation(trimmed)\n : trimmed\n}\n\n/**\n * Parses a normalized numeric string into components.\n */\nfunction parseNumericString (str) {\n const isNegative = str[0] === '-'\n if (isNegative) str = str.slice(1)\n\n const dotIndex = str.indexOf('.')\n if (dotIndex === -1) {\n return { isNegative, integerPart: BigInt(str) }\n }\n\n const integerStr = str.slice(0, dotIndex) || '0'\n const decimalPart = str.slice(dotIndex + 1)\n return { isNegative, integerPart: BigInt(integerStr), decimalPart }\n}\n\n/**\n * Expands scientific notation to decimal form (e.g., \"1e21\" → \"1000...\").\n */\nfunction expandScientificNotation (str) {\n const [mantissa, expStr] = str.toLowerCase().split('e')\n const exp = parseInt(expStr, 10)\n\n const dotIndex = mantissa.indexOf('.')\n const digits = dotIndex === -1\n ? mantissa\n : mantissa.slice(0, dotIndex) + mantissa.slice(dotIndex + 1)\n const integerLength = dotIndex === -1 ? mantissa.length : dotIndex\n const newDotPosition = integerLength + exp\n\n if (newDotPosition >= digits.length) {\n return digits + '0'.repeat(newDotPosition - digits.length)\n }\n if (newDotPosition <= 0) {\n return '0.' + '0'.repeat(-newDotPosition) + digits\n }\n return digits.slice(0, newDotPosition) + '.' + digits.slice(newDotPosition)\n}\n","/**\n * Amharic Latin language converter - Functional Implementation\n *\n * Self-contained converter with precomputed lookup tables.\n * Latin/ASCII romanization of Amharic numerals.\n *\n * Key features:\n * - Romanized numerals (and, hulet, sost)\n * - Teens formed with \"asra\" prefix\n * - Keeps \"one\" before hundred: \"and meto\" (100)\n * - Short scale naming\n * - Per-digit decimal reading\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ONES = ['', 'and', 'hulet', 'sost', 'arat', 'amist', 'siddist', 'sebat', 'siment', 'zeteny']\nconst TEENS = ['asir', 'asra and', 'asra hulet', 'asra sost', 'asra arat', 'asra amist', 'asra siddist', 'asra sebat', 'asra siment', 'asra zeteny']\nconst TENS = ['', '', 'haya', 'selasa', 'arba', 'hamsa', 'silsa', 'seba', 'semanya', 'zetena']\n\nconst HUNDRED = 'meto'\nconst THOUSAND = 'shi'\n\nconst ZERO = 'zero'\nconst NEGATIVE = 'asitegna'\nconst DECIMAL_SEP = 'netib'\n\n// Short scale\nconst SCALE_WORDS = ['', THOUSAND, 'miliyon', 'billiyon']\n\n// ============================================================================\n// Precomputed Lookup Table\n// ============================================================================\n\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tensDigit = Math.floor(n / 10) % 10\n const hundredsDigit = Math.floor(n / 100)\n\n const parts = []\n\n // Amharic keeps \"one\" before hundred: \"and meto\" (100)\n if (hundredsDigit > 0) {\n parts.push(ONES[hundredsDigit] + ' ' + HUNDRED)\n }\n\n if (tensDigit === 1) {\n parts.push(TEENS[ones])\n } else {\n if (tensDigit > 1) {\n parts.push(TENS[tensDigit])\n }\n if (ones > 0) {\n parts.push(ONES[ones])\n }\n }\n\n return parts.join(' ')\n}\n\nconst SEGMENTS = new Array(1000)\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS[i] = buildSegment(i)\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n if (n < 1000n) {\n return SEGMENTS[Number(n)]\n }\n\n return buildLargeNumberWords(n)\n}\n\nfunction buildLargeNumberWords (n) {\n const numStr = n.toString()\n const len = numStr.length\n\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n const scaleWord = SCALE_WORDS[scaleIndex] || ''\n\n if (scaleIndex === 0) {\n parts.push(SEGMENTS[segment])\n } else {\n parts.push(SEGMENTS[segment] + ' ' + scaleWord)\n }\n }\n\n scaleIndex--\n }\n\n return parts.join(' ')\n}\n\nfunction decimalPartToWords (decimalPart) {\n // Per-digit decimal reading\n const digits = []\n for (const char of decimalPart) {\n const d = parseInt(char, 10)\n digits.push(d === 0 ? ZERO : ONES[d])\n }\n return digits.join(' ')\n}\n\n/**\n * Converts a numeric value to Amharic (Latin script) words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Amharic Latin words\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n"],"names":["parseNumericString","str","isNegative","slice","dotIndex","indexOf","integerPart","BigInt","integerStr","decimalPart","expandScientificNotation","mantissa","expStr","toLowerCase","split","exp","parseInt","digits","newDotPosition","length","repeat","ONES","TEENS","TENS","ZERO","SCALE_WORDS","buildSegment","n","ones","tensDigit","Math","floor","hundredsDigit","parts","push","join","SEGMENTS","Array","i","value","type","Number","isFinite","Error","isSafeInteger","toString","includes","numberToString","trimmed","trim","isNaN","normalizeString","TypeError","parseNumericValue","result","NEGATIVE","numStr","len","segments","remainderLen","pos","scaleIndex","segment","buildLargeNumberWords","char","d","decimalPartToWords"],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/*! n2words/am v3.0.0 | MIT License | github.com/forzagreen/n2words */
|
|
2
|
+
var e,t;e=this,t=function(e){"use strict";function t(e){const t="-"===e[0];t&&(e=e.slice(1));const n=e.indexOf(".");if(-1===n)return{isNegative:t,integerPart:BigInt(e)};const r=e.slice(0,n)||"0",i=e.slice(n+1);return{isNegative:t,integerPart:BigInt(r),decimalPart:i}}function n(e){const[t,n]=e.toLowerCase().split("e"),r=parseInt(n,10),i=t.indexOf("."),o=-1===i?t:t.slice(0,i)+t.slice(i+1),s=(-1===i?t.length:i)+r;return s>=o.length?o+"0".repeat(s-o.length):s<=0?"0."+"0".repeat(-s)+o:o.slice(0,s)+"."+o.slice(s)}const r=["","አንድ","ሁለት","ሶስት","አራት","አምስት","ስድስት","ሰባት","ስምንት","ዘጠኝ"],i=["አስር","አስራ አንድ","አስራ ሁለት","አስራ ሶስት","አስራ አራት","አስራ አምስት","አስራ ስድስት","አስራ ሰባት","አስራ ስምንት","አስራ ዘጠኝ"],o=["","","ሃያ","ሰላሳ","አርባ","ሃምሳ","ስልሳ","ሰባ","ሰማንያ","ዘጠና"],s=["","ሺ","ሚሊዮን","ቢሊዮን"];function u(e){if(0===e)return"";const t=e%10,n=Math.floor(e/10)%10,s=Math.floor(e/100),u=[];return s>0&&u.push(r[s]+" መቶ"),1===n?u.push(i[t]):(n>1&&u.push(o[n]),t>0&&u.push(r[t])),u.join(" ")}const c=new Array(1e3);for(let e=0;e<1e3;e++)c[e]=u(e);e.am=function(e){const{isNegative:i,integerPart:o,decimalPart:u}=function(e){const r=typeof e;if("bigint"===r)return e<0n?{isNegative:!0,integerPart:-e}:{isNegative:!1,integerPart:e};if("number"===r){if(!Number.isFinite(e))throw new Error("Number must be finite (NaN and Infinity are not supported)");return Number.isSafeInteger(e)?e<0?{isNegative:!0,integerPart:BigInt(-e)}:{isNegative:!1,integerPart:BigInt(e)}:t(function(e){const t=e.toString();return t.includes("e")||t.includes("E")?n(t):t}(e))}if("string"===r)return t(function(e){const t=e.trim();if(0===t.length||Number.isNaN(Number(t)))throw new Error(`Invalid number format: "${e}"`);return t.includes("e")||t.includes("E")?n(t):t}(e));throw new TypeError(`Invalid value type: expected number, string, or bigint, received ${r}`)}(e);let f="";return i&&(f="አሉታዊ "),f+=0n===(a=o)?"ዜሮ":a<1000n?c[Number(a)]:function(e){const t=e.toString(),n=t.length,r=[],i=n%3;let o=0;for(i>0&&(r.push(Number(t.slice(0,i))),o=i);o<n;)r.push(Number(t.slice(o,o+3))),o+=3;const u=[];let f=r.length-1;for(let e=0;e<r.length;e++){const t=r[e];0!==t&&u.push(0===f?c[t]:c[t]+" "+(s[f]||"")),f--}return u.join(" ")}(a),u&&(f+=" ነጥብ "+function(e){const t=[];for(const n of e){const e=parseInt(n,10);t.push(0===e?"ዜሮ":r[e])}return t.join(" ")}(u)),f;var a}},"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).n2words=e.n2words||{});
|
|
3
|
+
//# sourceMappingURL=am.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"am.js","sources":["../../lib/utils/parse-numeric.js","../../lib/languages/am.js"],"sourcesContent":["/**\n * Numeric value parsing utility.\n * Transforms user input (number, string, or bigint) into normalized components.\n * @module parse-numeric\n */\n\n/**\n * Parses a numeric value into its components.\n * @param {number|string|bigint} value\n * @returns {{isNegative: boolean, integerPart: bigint, decimalPart?: string}}\n * @throws {TypeError} If value is not number, string, or bigint\n * @throws {Error} If value is not a valid number format\n */\nexport function parseNumericValue (value) {\n const type = typeof value\n\n // BigInt: simplest case\n if (type === 'bigint') {\n return value < 0n\n ? { isNegative: true, integerPart: -value }\n : { isNegative: false, integerPart: value }\n }\n\n // Number: fast path for safe integers\n if (type === 'number') {\n if (!Number.isFinite(value)) {\n throw new Error('Number must be finite (NaN and Infinity are not supported)')\n }\n if (Number.isSafeInteger(value)) {\n return value < 0\n ? { isNegative: true, integerPart: BigInt(-value) }\n : { isNegative: false, integerPart: BigInt(value) }\n }\n return parseNumericString(numberToString(value))\n }\n\n // String input\n if (type === 'string') {\n return parseNumericString(normalizeString(value))\n }\n\n throw new TypeError(\n `Invalid value type: expected number, string, or bigint, received ${type}`\n )\n}\n\n/**\n * Converts a number to decimal string, expanding scientific notation if needed.\n */\nfunction numberToString (value) {\n const str = value.toString()\n return (str.includes('e') || str.includes('E'))\n ? expandScientificNotation(str)\n : str\n}\n\n/**\n * Validates and normalizes a string numeric input.\n */\nfunction normalizeString (value) {\n const trimmed = value.trim()\n if (trimmed.length === 0 || Number.isNaN(Number(trimmed))) {\n throw new Error(`Invalid number format: \"${value}\"`)\n }\n return (trimmed.includes('e') || trimmed.includes('E'))\n ? expandScientificNotation(trimmed)\n : trimmed\n}\n\n/**\n * Parses a normalized numeric string into components.\n */\nfunction parseNumericString (str) {\n const isNegative = str[0] === '-'\n if (isNegative) str = str.slice(1)\n\n const dotIndex = str.indexOf('.')\n if (dotIndex === -1) {\n return { isNegative, integerPart: BigInt(str) }\n }\n\n const integerStr = str.slice(0, dotIndex) || '0'\n const decimalPart = str.slice(dotIndex + 1)\n return { isNegative, integerPart: BigInt(integerStr), decimalPart }\n}\n\n/**\n * Expands scientific notation to decimal form (e.g., \"1e21\" → \"1000...\").\n */\nfunction expandScientificNotation (str) {\n const [mantissa, expStr] = str.toLowerCase().split('e')\n const exp = parseInt(expStr, 10)\n\n const dotIndex = mantissa.indexOf('.')\n const digits = dotIndex === -1\n ? mantissa\n : mantissa.slice(0, dotIndex) + mantissa.slice(dotIndex + 1)\n const integerLength = dotIndex === -1 ? mantissa.length : dotIndex\n const newDotPosition = integerLength + exp\n\n if (newDotPosition >= digits.length) {\n return digits + '0'.repeat(newDotPosition - digits.length)\n }\n if (newDotPosition <= 0) {\n return '0.' + '0'.repeat(-newDotPosition) + digits\n }\n return digits.slice(0, newDotPosition) + '.' + digits.slice(newDotPosition)\n}\n","/**\n * Amharic language converter - Functional Implementation\n *\n * Self-contained converter with precomputed lookup tables.\n * Native Ge'ez script (ግዕዝ) output.\n *\n * Key features:\n * - Ge'ez/Ethiopic script numerals\n * - Teens formed with \"አስራ\" prefix\n * - Keeps \"one\" before hundred: \"አንድ መቶ\" (100)\n * - Short scale naming\n * - Per-digit decimal reading\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ONES = ['', 'አንድ', 'ሁለት', 'ሶስት', 'አራት', 'አምስት', 'ስድስት', 'ሰባት', 'ስምንት', 'ዘጠኝ']\nconst TEENS = ['አስር', 'አስራ አንድ', 'አስራ ሁለት', 'አስራ ሶስት', 'አስራ አራት', 'አስራ አምስት', 'አስራ ስድስት', 'አስራ ሰባት', 'አስራ ስምንት', 'አስራ ዘጠኝ']\nconst TENS = ['', '', 'ሃያ', 'ሰላሳ', 'አርባ', 'ሃምሳ', 'ስልሳ', 'ሰባ', 'ሰማንያ', 'ዘጠና']\n\nconst HUNDRED = 'መቶ'\nconst THOUSAND = 'ሺ'\n\nconst ZERO = 'ዜሮ'\nconst NEGATIVE = 'አሉታዊ'\nconst DECIMAL_SEP = 'ነጥብ'\n\n// Short scale\nconst SCALE_WORDS = ['', THOUSAND, 'ሚሊዮን', 'ቢሊዮን']\n\n// ============================================================================\n// Precomputed Lookup Table\n// ============================================================================\n\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tensDigit = Math.floor(n / 10) % 10\n const hundredsDigit = Math.floor(n / 100)\n\n const parts = []\n\n // Amharic keeps \"one\" before hundred: \"አንድ መቶ\" (100)\n if (hundredsDigit > 0) {\n parts.push(ONES[hundredsDigit] + ' ' + HUNDRED)\n }\n\n if (tensDigit === 1) {\n parts.push(TEENS[ones])\n } else {\n if (tensDigit > 1) {\n parts.push(TENS[tensDigit])\n }\n if (ones > 0) {\n parts.push(ONES[ones])\n }\n }\n\n return parts.join(' ')\n}\n\nconst SEGMENTS = new Array(1000)\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS[i] = buildSegment(i)\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n if (n < 1000n) {\n return SEGMENTS[Number(n)]\n }\n\n return buildLargeNumberWords(n)\n}\n\nfunction buildLargeNumberWords (n) {\n const numStr = n.toString()\n const len = numStr.length\n\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n const scaleWord = SCALE_WORDS[scaleIndex] || ''\n\n if (scaleIndex === 0) {\n parts.push(SEGMENTS[segment])\n } else {\n parts.push(SEGMENTS[segment] + ' ' + scaleWord)\n }\n }\n\n scaleIndex--\n }\n\n return parts.join(' ')\n}\n\nfunction decimalPartToWords (decimalPart) {\n // Per-digit decimal reading\n const digits = []\n for (const char of decimalPart) {\n const d = parseInt(char, 10)\n digits.push(d === 0 ? ZERO : ONES[d])\n }\n return digits.join(' ')\n}\n\n/**\n * Converts a numeric value to Amharic words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Amharic words\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n"],"names":["parseNumericString","str","isNegative","slice","dotIndex","indexOf","integerPart","BigInt","integerStr","decimalPart","expandScientificNotation","mantissa","expStr","toLowerCase","split","exp","parseInt","digits","newDotPosition","length","repeat","ONES","TEENS","TENS","SCALE_WORDS","buildSegment","n","ones","tensDigit","Math","floor","hundredsDigit","parts","push","join","SEGMENTS","Array","i","value","type","Number","isFinite","Error","isSafeInteger","toString","includes","numberToString","trimmed","trim","isNaN","normalizeString","TypeError","parseNumericValue","result","NEGATIVE","numStr","len","segments","remainderLen","pos","scaleIndex","segment","buildLargeNumberWords","char","d","decimalPartToWords"],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/*! n2words/ar v3.0.0 | MIT License | github.com/forzagreen/n2words */
|
|
2
|
+
var e,t;e=this,t=function(e){"use strict";function t(e){const t="-"===e[0];t&&(e=e.slice(1));const n=e.indexOf(".");if(-1===n)return{isNegative:t,integerPart:BigInt(e)};const i=e.slice(0,n)||"0",r=e.slice(n+1);return{isNegative:t,integerPart:BigInt(i),decimalPart:r}}function n(e){const[t,n]=e.toLowerCase().split("e"),i=parseInt(n,10),r=t.indexOf("."),o=-1===r?t:t.slice(0,r)+t.slice(r+1),s=(-1===r?t.length:r)+i;return s>=o.length?o+"0".repeat(s-o.length):s<=0?"0."+"0".repeat(-s)+o:o.slice(0,s)+"."+o.slice(s)}const i=["عشرون","ثلاثون","أربعون","خمسون","ستون","سبعون","ثمانون","تسعون"],r=["","مائة","مئتان","ثلاثمائة","أربعمائة","خمسمائة","ستمائة","سبعمائة","ثمانمائة","تسعمائة"],o=["مائة","ألف","مليون","مليار","تريليون","كوادريليون","كوينتليون","سكستيليون"],s=["","ألفاً","مليوناً","ملياراً","تريليوناً","كوادريليوناً","كوينتليوناً","سكستيليوناً"],u=["","آلاف","ملايين","مليارات","تريليونات","كوادريليونات","كوينتليونات","سكستيليونات"],c=["مئتان","ألفان","مليونان","ملياران","تريليونان","كوادريليونان","كوينتليونان","سكستيليونان"],f=["مئتا","ألفا","مليونا","مليارا","تريليونا","كوادريليونا","كوينتليونا","سكستيليونا"],l=["واحد","اثنان","ثلاثة","أربعة","خمسة","ستة","سبعة","ثمانية","تسعة","عشرة","أحد عشر","اثنا عشر","ثلاثة عشر","أربعة عشر","خمسة عشر","ستة عشر","سبعة عشر","ثمانية عشر","تسعة عشر"],a=["واحدة","اثنتان","ثلاث","أربع","خمس","ست","سبع","ثمان","تسع","عشر","إحدى عشرة","اثنتا عشرة","ثلاث عشرة","أربع عشرة","خمس عشرة","ست عشرة","سبع عشرة","ثماني عشرة","تسع عشرة"];function g(e,t,n,s){const u=e%100,l=Math.trunc(e/100);let a="";if(l>0)if(0===u&&2===l)a=c[0];else{const e=r[l];e&&(a=e,0!==u&&(a+=" و"))}if(u>0)if(u<20)if(2===u&&0===l&&t>0){const i=Number(n),r=Math.trunc(Math.log10(i));r%3==0&&n===BigInt(2*Math.pow(10,r))?a+=2===e?c[t]:f[t]:a+=c[t]}else a+=1===u&&t>0?o[t]:s[u-1];else{const e=u%10,t=Math.trunc(u/10)-2;e>0&&(a+=s[e-1]+" و"),a+=i[t]}return a}function p(e,t){if(0n===e)return"صفر";const n="feminine"===(t.gender||"masculine")?a:l;let i=e,r=0;const c=[];for(;i>0n;){const t=Number(i%1000n);if(i/=1000n,t>0){const i=g(t,r,e,n);if(i){let e=i;r>0&&t>2&&(e+=1==t%100?" "+o[r]:t>=3&&t<=10?" "+u[r]:" "+(c.length>0?s[r]:o[r])),c.unshift(e)}}r++}if(1===c.length)return c[0];let f=c[0];for(let e=1;e<c.length;e++)f+=" و"+c[e];return f}e.ar=function(e,i){i=function(e){if(void 0===e)return{};if(function(e){if(null===e||"object"!=typeof e)return!1;const t=Object.getPrototypeOf(e);return null===t||t===Object.prototype}(e))return e;throw new TypeError("Invalid options: expected plain object or undefined, got "+typeof e)}(i);const{isNegative:r,integerPart:o,decimalPart:s}=function(e){const i=typeof e;if("bigint"===i)return e<0n?{isNegative:!0,integerPart:-e}:{isNegative:!1,integerPart:e};if("number"===i){if(!Number.isFinite(e))throw new Error("Number must be finite (NaN and Infinity are not supported)");return Number.isSafeInteger(e)?e<0?{isNegative:!0,integerPart:BigInt(-e)}:{isNegative:!1,integerPart:BigInt(e)}:t(function(e){const t=e.toString();return t.includes("e")||t.includes("E")?n(t):t}(e))}if("string"===i)return t(function(e){const t=e.trim();if(0===t.length||Number.isNaN(Number(t)))throw new Error(`Invalid number format: "${e}"`);return t.includes("e")||t.includes("E")?n(t):t}(e));throw new TypeError(`Invalid value type: expected number, string, or bigint, received ${i}`)}(e),u=[];return r&&u.push(i.negativeWord||"ناقص"),u.push(p(o,i)),s&&(u.push("فاصلة"),u.push(function(e,t){const n=[];let i=0;for(;i<e.length&&"0"===e[i];)n.push("صفر"),i++;const r=e.slice(i);return r&&n.push(p(BigInt(r),t)),n.join(" ")}(s,i))),u.join(" ")}},"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).n2words=e.n2words||{});
|
|
3
|
+
//# sourceMappingURL=ar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ar.js","sources":["../../lib/utils/parse-numeric.js","../../lib/languages/ar.js","../../lib/utils/validate-options.js","../../lib/utils/is-plain-object.js"],"sourcesContent":["/**\n * Numeric value parsing utility.\n * Transforms user input (number, string, or bigint) into normalized components.\n * @module parse-numeric\n */\n\n/**\n * Parses a numeric value into its components.\n * @param {number|string|bigint} value\n * @returns {{isNegative: boolean, integerPart: bigint, decimalPart?: string}}\n * @throws {TypeError} If value is not number, string, or bigint\n * @throws {Error} If value is not a valid number format\n */\nexport function parseNumericValue (value) {\n const type = typeof value\n\n // BigInt: simplest case\n if (type === 'bigint') {\n return value < 0n\n ? { isNegative: true, integerPart: -value }\n : { isNegative: false, integerPart: value }\n }\n\n // Number: fast path for safe integers\n if (type === 'number') {\n if (!Number.isFinite(value)) {\n throw new Error('Number must be finite (NaN and Infinity are not supported)')\n }\n if (Number.isSafeInteger(value)) {\n return value < 0\n ? { isNegative: true, integerPart: BigInt(-value) }\n : { isNegative: false, integerPart: BigInt(value) }\n }\n return parseNumericString(numberToString(value))\n }\n\n // String input\n if (type === 'string') {\n return parseNumericString(normalizeString(value))\n }\n\n throw new TypeError(\n `Invalid value type: expected number, string, or bigint, received ${type}`\n )\n}\n\n/**\n * Converts a number to decimal string, expanding scientific notation if needed.\n */\nfunction numberToString (value) {\n const str = value.toString()\n return (str.includes('e') || str.includes('E'))\n ? expandScientificNotation(str)\n : str\n}\n\n/**\n * Validates and normalizes a string numeric input.\n */\nfunction normalizeString (value) {\n const trimmed = value.trim()\n if (trimmed.length === 0 || Number.isNaN(Number(trimmed))) {\n throw new Error(`Invalid number format: \"${value}\"`)\n }\n return (trimmed.includes('e') || trimmed.includes('E'))\n ? expandScientificNotation(trimmed)\n : trimmed\n}\n\n/**\n * Parses a normalized numeric string into components.\n */\nfunction parseNumericString (str) {\n const isNegative = str[0] === '-'\n if (isNegative) str = str.slice(1)\n\n const dotIndex = str.indexOf('.')\n if (dotIndex === -1) {\n return { isNegative, integerPart: BigInt(str) }\n }\n\n const integerStr = str.slice(0, dotIndex) || '0'\n const decimalPart = str.slice(dotIndex + 1)\n return { isNegative, integerPart: BigInt(integerStr), decimalPart }\n}\n\n/**\n * Expands scientific notation to decimal form (e.g., \"1e21\" → \"1000...\").\n */\nfunction expandScientificNotation (str) {\n const [mantissa, expStr] = str.toLowerCase().split('e')\n const exp = parseInt(expStr, 10)\n\n const dotIndex = mantissa.indexOf('.')\n const digits = dotIndex === -1\n ? mantissa\n : mantissa.slice(0, dotIndex) + mantissa.slice(dotIndex + 1)\n const integerLength = dotIndex === -1 ? mantissa.length : dotIndex\n const newDotPosition = integerLength + exp\n\n if (newDotPosition >= digits.length) {\n return digits + '0'.repeat(newDotPosition - digits.length)\n }\n if (newDotPosition <= 0) {\n return '0.' + '0'.repeat(-newDotPosition) + digits\n }\n return digits.slice(0, newDotPosition) + '.' + digits.slice(newDotPosition)\n}\n","/**\n * Arabic language converter - Functional Implementation\n *\n * Self-contained converter with gender agreement and complex pluralization.\n *\n * Key features:\n * - Gender agreement (masculine/feminine forms)\n * - Complex pluralization (singular/dual/plural)\n * - Traditional Arabic number naming conventions\n * - \"و\" (and) conjunction between segments\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\nimport { validateOptions } from '../utils/validate-options.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst TENS = ['عشرون', 'ثلاثون', 'أربعون', 'خمسون', 'ستون', 'سبعون', 'ثمانون', 'تسعون']\nconst HUNDREDS = ['', 'مائة', 'مئتان', 'ثلاثمائة', 'أربعمائة', 'خمسمائة', 'ستمائة', 'سبعمائة', 'ثمانمائة', 'تسعمائة']\n\n// Magnitude words with three forms: singular, appended (tanween), plural\nconst SCALE_WORDS = ['مائة', 'ألف', 'مليون', 'مليار', 'تريليون', 'كوادريليون', 'كوينتليون', 'سكستيليون']\nconst SCALE_APPENDED = ['', 'ألفاً', 'مليوناً', 'ملياراً', 'تريليوناً', 'كوادريليوناً', 'كوينتليوناً', 'سكستيليوناً']\nconst SCALE_PLURAL = ['', 'آلاف', 'ملايين', 'مليارات', 'تريليونات', 'كوادريليونات', 'كوينتليونات', 'سكستيليونات']\n\n// Dual forms\nconst DUAL = ['مئتان', 'ألفان', 'مليونان', 'ملياران', 'تريليونان', 'كوادريليونان', 'كوينتليونان', 'سكستيليونان']\nconst DUAL_APPENDED = ['مئتا', 'ألفا', 'مليونا', 'مليارا', 'تريليونا', 'كوادريليونا', 'كوينتليونا', 'سكستيليونا']\n\n// Gender-specific forms (1-19)\nconst ONES_MASC = ['واحد', 'اثنان', 'ثلاثة', 'أربعة', 'خمسة', 'ستة', 'سبعة', 'ثمانية', 'تسعة', 'عشرة', 'أحد عشر', 'اثنا عشر', 'ثلاثة عشر', 'أربعة عشر', 'خمسة عشر', 'ستة عشر', 'سبعة عشر', 'ثمانية عشر', 'تسعة عشر']\nconst ONES_FEM = ['واحدة', 'اثنتان', 'ثلاث', 'أربع', 'خمس', 'ست', 'سبع', 'ثمان', 'تسع', 'عشر', 'إحدى عشرة', 'اثنتا عشرة', 'ثلاث عشرة', 'أربع عشرة', 'خمس عشرة', 'ست عشرة', 'سبع عشرة', 'ثماني عشرة', 'تسع عشرة']\n\nconst ZERO = 'صفر'\nconst NEGATIVE = 'ناقص'\nconst DECIMAL_SEP = 'فاصلة'\nconst AND = 'و'\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Convert a 3-digit group to words.\n * Returns a clean string with no leading/trailing spaces.\n * Arabic \"و\" (and) is attached to following word: \"مائة وخمسة\" not \"مائة و خمسة\"\n */\nfunction segmentToWords (groupNumber, groupLevel, fullNumber, ones) {\n const tensValue = groupNumber % 100\n const hundredsDigit = Math.trunc(groupNumber / 100)\n let result = ''\n\n // Process hundreds\n if (hundredsDigit > 0) {\n if (tensValue === 0 && hundredsDigit === 2) {\n result = DUAL[0]\n } else {\n const hundredsWord = HUNDREDS[hundredsDigit]\n if (hundredsWord) {\n result = hundredsWord\n if (tensValue !== 0) {\n result += ' ' + AND // \"مائة و\" - و attaches to next word\n }\n }\n }\n }\n\n // Process tens and ones\n if (tensValue > 0) {\n if (tensValue < 20) {\n if (tensValue === 2 && hundredsDigit === 0 && groupLevel > 0) {\n const numValue = Number(fullNumber)\n const pow = Math.trunc(Math.log10(numValue))\n if (pow % 3 === 0 && fullNumber === BigInt(2 * Math.pow(10, pow))) {\n result += (groupNumber === 2 ? DUAL[groupLevel] : DUAL_APPENDED[groupLevel])\n } else {\n result += DUAL[groupLevel]\n }\n } else if (tensValue === 1 && groupLevel > 0) {\n result += SCALE_WORDS[groupLevel]\n } else {\n result += ones[tensValue - 1]\n }\n } else {\n const onesDigit = tensValue % 10\n const tensIndex = Math.trunc(tensValue / 10) - 2\n\n if (onesDigit > 0) {\n result += ones[onesDigit - 1] + ' ' + AND // \"ستة و\" attaches to tens\n }\n result += TENS[tensIndex]\n }\n }\n\n return result\n}\n\nfunction integerToWords (n, options) {\n if (n === 0n) return ZERO\n\n const gender = options.gender || 'masculine'\n const ones = gender === 'feminine' ? ONES_FEM : ONES_MASC\n\n let temp = n\n let group = 0\n const groups = []\n\n while (temp > 0n) {\n const numberToProcess = Number(temp % 1000n)\n temp = temp / 1000n\n\n if (numberToProcess > 0) {\n const groupDescription = segmentToWords(numberToProcess, group, n, ones)\n\n if (groupDescription) {\n let groupText = groupDescription\n\n // Add scale word for groups > 0\n if (group > 0 && numberToProcess > 2) {\n const remainder = numberToProcess % 100\n if (remainder === 1) {\n groupText += ' ' + SCALE_WORDS[group]\n } else if (numberToProcess >= 3 && numberToProcess <= 10) {\n groupText += ' ' + SCALE_PLURAL[group]\n } else {\n groupText += ' ' + (groups.length > 0 ? SCALE_APPENDED[group] : SCALE_WORDS[group])\n }\n }\n\n groups.unshift(groupText)\n }\n }\n\n group++\n }\n\n // Join groups with و (and) - space before و, attaches to next word\n // Use simple join since segmentToWords returns clean strings\n if (groups.length === 1) return groups[0]\n\n // Build result: \"group1 وgroup2 وgroup3\"\n let result = groups[0]\n for (let i = 1; i < groups.length; i++) {\n result += ' ' + AND + groups[i]\n }\n return result\n}\n\nfunction decimalPartToWords (decimalPart, options) {\n const parts = []\n let i = 0\n\n while (i < decimalPart.length && decimalPart[i] === '0') {\n parts.push(ZERO)\n i++\n }\n\n const remainder = decimalPart.slice(i)\n if (remainder) {\n parts.push(integerToWords(BigInt(remainder), options))\n }\n\n return parts.join(' ')\n}\n\n/**\n * Converts a numeric value to Arabic words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @param {Object} [options] - Optional configuration\n * @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender\n * @param {string} [options.negativeWord] - Custom word for negative numbers\n * @returns {string} The number in Arabic words\n *\n * @example\n * toWords(1) // 'واحد'\n * toWords(1, {gender: 'feminine'}) // 'واحدة'\n */\nfunction toWords (value, options) {\n options = validateOptions(options)\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n const parts = []\n\n if (isNegative) {\n parts.push(options.negativeWord || NEGATIVE)\n }\n\n parts.push(integerToWords(integerPart, options))\n\n if (decimalPart) {\n parts.push(DECIMAL_SEP)\n parts.push(decimalPartToWords(decimalPart, options))\n }\n\n return parts.join(' ')\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","import { isPlainObject } from './is-plain-object.js'\n\n/**\n * Validates and normalizes the options parameter.\n *\n * @param {*} options The options value to validate\n * @returns {Object} A valid options object (empty object if undefined)\n * @throws {TypeError} If options is not undefined or a plain object\n */\nexport function validateOptions (options) {\n if (options === undefined) return {}\n if (isPlainObject(options)) return options\n throw new TypeError(\n `Invalid options: expected plain object or undefined, got ${typeof options}`\n )\n}\n","/**\n * Checks if a value is a plain object (not null, array, or other object types).\n *\n * A plain object is one created by:\n * - Object literal: `{}`\n * - Object.create(null): null-prototype object\n *\n * This excludes arrays, class instances, Map, Set, and other object types.\n *\n * @param {*} value Value to check\n * @returns {boolean} True if value is a plain object\n */\nexport function isPlainObject (value) {\n if (value === null || typeof value !== 'object') return false\n const proto = Object.getPrototypeOf(value)\n return proto === null || proto === Object.prototype\n}\n"],"names":["parseNumericString","str","isNegative","slice","dotIndex","indexOf","integerPart","BigInt","integerStr","decimalPart","expandScientificNotation","mantissa","expStr","toLowerCase","split","exp","parseInt","digits","newDotPosition","length","repeat","TENS","HUNDREDS","SCALE_WORDS","SCALE_APPENDED","SCALE_PLURAL","DUAL","DUAL_APPENDED","ONES_MASC","ONES_FEM","segmentToWords","groupNumber","groupLevel","fullNumber","ones","tensValue","hundredsDigit","Math","trunc","result","hundredsWord","numValue","Number","pow","log10","onesDigit","tensIndex","integerToWords","n","options","gender","temp","group","groups","numberToProcess","groupDescription","groupText","unshift","i","value","undefined","proto","Object","getPrototypeOf","prototype","isPlainObject","TypeError","validateOptions","type","isFinite","Error","isSafeInteger","toString","includes","numberToString","trimmed","trim","isNaN","normalizeString","parseNumericValue","parts","push","negativeWord","remainder","join","decimalPartToWords"],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/*! n2words/az v3.0.0 | MIT License | github.com/forzagreen/n2words */
|
|
2
|
+
var e,n;e=this,n=function(e){"use strict";function n(e){const n="-"===e[0];n&&(e=e.slice(1));const t=e.indexOf(".");if(-1===t)return{isNegative:n,integerPart:BigInt(e)};const i=e.slice(0,t)||"0",r=e.slice(t+1);return{isNegative:n,integerPart:BigInt(i),decimalPart:r}}function t(e){const[n,t]=e.toLowerCase().split("e"),i=parseInt(t,10),r=n.indexOf("."),o=-1===r?n:n.slice(0,r)+n.slice(r+1),s=(-1===r?n.length:r)+i;return s>=o.length?o+"0".repeat(s-o.length):s<=0?"0."+"0".repeat(-s)+o:o.slice(0,s)+"."+o.slice(s)}const i=["","bir","iki","üç","dörd","beş","altı","yeddi","səkkiz","doqquz"],r=["on","on bir","on iki","on üç","on dörd","on beş","on altı","on yeddi","on səkkiz","on doqquz"],o=["","","iyirmi","otuz","qırx","əlli","altmış","yetmiş","səksən","doxsan"],s="sıfır",u=["","min","milyon","milyar","trilyon","katrilyon","kentilyon"];function l(e){if(0===e)return"";const n=e%10,t=Math.floor(e/10)%10,s=Math.floor(e/100),u=[];return s>0&&u.push(1===s?"yüz":i[s]+" yüz"),1===t?u.push(r[n]):(t>1&&u.push(o[t]),n>0&&u.push(i[n])),u.join(" ")}const c=new Array(1e3);for(let e=0;e<1e3;e++)c[e]=l(e);function a(e){return 0n===e?s:e<1000n?c[Number(e)]:function(e){const n=e.toString(),t=n.length,i=[],r=t%3;let o=0;for(r>0&&(i.push(Number(n.slice(0,r))),o=r);o<t;)i.push(Number(n.slice(o,o+3))),o+=3;const s=[];let l=i.length-1;for(let e=0;e<i.length;e++){const n=i[e];if(0!==n){const e=u[l]||"";s.push(0===l?c[n]:1===l&&1===n?e:c[n]+" "+e)}l--}return s.join(" ")}(e)}e.az=function(e){const{isNegative:i,integerPart:r,decimalPart:o}=function(e){const i=typeof e;if("bigint"===i)return e<0n?{isNegative:!0,integerPart:-e}:{isNegative:!1,integerPart:e};if("number"===i){if(!Number.isFinite(e))throw new Error("Number must be finite (NaN and Infinity are not supported)");return Number.isSafeInteger(e)?e<0?{isNegative:!0,integerPart:BigInt(-e)}:{isNegative:!1,integerPart:BigInt(e)}:n(function(e){const n=e.toString();return n.includes("e")||n.includes("E")?t(n):n}(e))}if("string"===i)return n(function(e){const n=e.trim();if(0===n.length||Number.isNaN(Number(n)))throw new Error(`Invalid number format: "${e}"`);return n.includes("e")||n.includes("E")?t(n):n}(e));throw new TypeError(`Invalid value type: expected number, string, or bigint, received ${i}`)}(e);let u="";return i&&(u="mənfi "),u+=a(r),o&&(u+=" nöqtə "+function(e){let n="",t=0;for(;t<e.length&&"0"===e[t];)n&&(n+=" "),n+=s,t++;const i=e.slice(t);return i&&(n&&(n+=" "),n+=a(BigInt(i))),n}(o)),u}},"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).n2words=e.n2words||{});
|
|
3
|
+
//# sourceMappingURL=az.js.map
|