n2words 1.24.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 +183 -156
- 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 -2
- package/dist/languages/ar.js.map +1 -1
- package/dist/languages/az.js +3 -2
- package/dist/languages/az.js.map +1 -1
- package/dist/languages/bn.js +3 -2
- package/dist/languages/bn.js.map +1 -1
- package/dist/languages/cs.js +3 -2
- package/dist/languages/cs.js.map +1 -1
- package/dist/languages/da.js +3 -2
- package/dist/languages/da.js.map +1 -1
- package/dist/languages/de.js +3 -2
- package/dist/languages/de.js.map +1 -1
- package/dist/languages/el.js +3 -2
- package/dist/languages/el.js.map +1 -1
- package/dist/languages/en.js +3 -2
- package/dist/languages/en.js.map +1 -1
- package/dist/languages/es.js +3 -2
- package/dist/languages/es.js.map +1 -1
- package/dist/languages/fa.js +3 -2
- package/dist/languages/fa.js.map +1 -1
- package/dist/languages/fi.js +3 -0
- package/dist/languages/fi.js.map +1 -0
- package/dist/languages/fil.js +3 -2
- package/dist/languages/fil.js.map +1 -1
- package/dist/languages/fr-BE.js +3 -2
- package/dist/languages/fr-BE.js.map +1 -1
- package/dist/languages/fr.js +3 -2
- package/dist/languages/fr.js.map +1 -1
- package/dist/languages/gu.js +3 -2
- package/dist/languages/gu.js.map +1 -1
- 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 -2
- package/dist/languages/he.js.map +1 -1
- package/dist/languages/hi.js +3 -2
- package/dist/languages/hi.js.map +1 -1
- package/dist/languages/hr.js +3 -2
- package/dist/languages/hr.js.map +1 -1
- package/dist/languages/hu.js +3 -2
- package/dist/languages/hu.js.map +1 -1
- package/dist/languages/id.js +3 -2
- package/dist/languages/id.js.map +1 -1
- package/dist/languages/it.js +3 -2
- package/dist/languages/it.js.map +1 -1
- package/dist/languages/ja.js +3 -2
- package/dist/languages/ja.js.map +1 -1
- package/dist/languages/kn.js +3 -2
- package/dist/languages/kn.js.map +1 -1
- package/dist/languages/ko.js +3 -2
- package/dist/languages/ko.js.map +1 -1
- package/dist/languages/lt.js +3 -2
- package/dist/languages/lt.js.map +1 -1
- package/dist/languages/lv.js +3 -2
- package/dist/languages/lv.js.map +1 -1
- package/dist/languages/mr.js +3 -2
- package/dist/languages/mr.js.map +1 -1
- package/dist/languages/ms.js +3 -2
- package/dist/languages/ms.js.map +1 -1
- package/dist/languages/nb.js +3 -2
- package/dist/languages/nb.js.map +1 -1
- package/dist/languages/nl.js +3 -2
- package/dist/languages/nl.js.map +1 -1
- package/dist/languages/pa.js +3 -0
- package/dist/languages/pa.js.map +1 -0
- package/dist/languages/pl.js +3 -2
- package/dist/languages/pl.js.map +1 -1
- package/dist/languages/pt.js +3 -2
- package/dist/languages/pt.js.map +1 -1
- package/dist/languages/ro.js +3 -2
- package/dist/languages/ro.js.map +1 -1
- package/dist/languages/ru.js +3 -2
- package/dist/languages/ru.js.map +1 -1
- package/dist/languages/sr-Cyrl.js +3 -0
- package/dist/languages/sr-Cyrl.js.map +1 -0
- package/dist/languages/sr-Latn.js +3 -2
- package/dist/languages/sr-Latn.js.map +1 -1
- package/dist/languages/sv.js +3 -2
- package/dist/languages/sv.js.map +1 -1
- package/dist/languages/sw.js +3 -2
- package/dist/languages/sw.js.map +1 -1
- package/dist/languages/ta.js +3 -2
- package/dist/languages/ta.js.map +1 -1
- package/dist/languages/te.js +3 -2
- package/dist/languages/te.js.map +1 -1
- package/dist/languages/th.js +3 -2
- package/dist/languages/th.js.map +1 -1
- package/dist/languages/tr.js +3 -2
- package/dist/languages/tr.js.map +1 -1
- package/dist/languages/uk.js +3 -2
- package/dist/languages/uk.js.map +1 -1
- package/dist/languages/ur.js +3 -2
- package/dist/languages/ur.js.map +1 -1
- package/dist/languages/vi.js +3 -2
- package/dist/languages/vi.js.map +1 -1
- package/dist/languages/zh-Hans.js +3 -2
- package/dist/languages/zh-Hans.js.map +1 -1
- package/dist/languages/zh-Hant.js +3 -0
- package/dist/languages/zh-Hant.js.map +1 -0
- package/dist/n2words.js +3 -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 +17 -0
- package/lib/languages/ar.js +171 -209
- package/lib/languages/az.d.ts +7 -0
- package/lib/languages/az.js +167 -49
- package/lib/languages/bn.d.ts +7 -0
- package/lib/languages/bn.js +142 -123
- package/lib/languages/cs.d.ts +18 -0
- package/lib/languages/cs.js +303 -176
- package/lib/languages/da.d.ts +14 -0
- package/lib/languages/da.js +267 -139
- package/lib/languages/de.d.ts +17 -0
- package/lib/languages/de.js +310 -113
- package/lib/languages/el.d.ts +14 -0
- package/lib/languages/el.js +225 -98
- package/lib/languages/en.d.ts +17 -0
- package/lib/languages/en.js +235 -102
- package/lib/languages/es.d.ts +21 -0
- package/lib/languages/es.js +307 -125
- package/lib/languages/fa.d.ts +7 -0
- package/lib/languages/fa.js +115 -108
- package/lib/languages/fi.d.ts +14 -0
- package/lib/languages/fi.js +245 -0
- package/lib/languages/fil.d.ts +7 -0
- package/lib/languages/fil.js +199 -139
- package/lib/languages/fr-BE.d.ts +11 -0
- package/lib/languages/fr-BE.js +287 -48
- package/lib/languages/fr.d.ts +21 -0
- package/lib/languages/fr.js +343 -119
- package/lib/languages/gu.d.ts +7 -0
- package/lib/languages/gu.js +125 -144
- package/lib/languages/ha.d.ts +7 -0
- package/lib/languages/ha.js +230 -0
- package/lib/languages/hbo.d.ts +13 -0
- package/lib/languages/hbo.js +300 -0
- package/lib/languages/he.d.ts +13 -0
- package/lib/languages/he.js +230 -283
- package/lib/languages/hi.d.ts +7 -0
- package/lib/languages/hi.js +142 -123
- package/lib/languages/hr.d.ts +11 -0
- package/lib/languages/hr.js +190 -129
- package/lib/languages/hu.d.ts +7 -0
- package/lib/languages/hu.js +194 -133
- package/lib/languages/id.d.ts +7 -0
- package/lib/languages/id.js +167 -140
- package/lib/languages/it.d.ts +19 -0
- package/lib/languages/it.js +337 -108
- package/lib/languages/ja.d.ts +17 -0
- package/lib/languages/ja.js +224 -155
- package/lib/languages/kn.d.ts +7 -0
- package/lib/languages/kn.js +128 -62
- package/lib/languages/ko.d.ts +14 -0
- package/lib/languages/ko.js +250 -70
- package/lib/languages/lt.d.ts +18 -0
- package/lib/languages/lt.js +287 -148
- package/lib/languages/lv.d.ts +18 -0
- package/lib/languages/lv.js +291 -123
- package/lib/languages/mr.d.ts +7 -0
- package/lib/languages/mr.js +125 -144
- package/lib/languages/ms.d.ts +7 -0
- package/lib/languages/ms.js +171 -112
- package/lib/languages/nb.d.ts +14 -0
- package/lib/languages/nb.js +275 -100
- package/lib/languages/nl.d.ts +26 -0
- package/lib/languages/nl.js +307 -174
- package/lib/languages/pa.d.ts +7 -0
- package/lib/languages/pa.js +163 -0
- package/lib/languages/pl.d.ts +22 -0
- package/lib/languages/pl.js +299 -158
- package/lib/languages/pt.d.ts +17 -0
- package/lib/languages/pt.js +279 -120
- package/lib/languages/ro.d.ts +18 -0
- package/lib/languages/ro.js +214 -337
- package/lib/languages/ru.d.ts +11 -0
- package/lib/languages/ru.js +219 -95
- package/lib/languages/sr-Cyrl.d.ts +11 -0
- package/lib/languages/sr-Cyrl.js +215 -0
- package/lib/languages/sr-Latn.d.ts +11 -0
- package/lib/languages/sr-Latn.js +190 -132
- package/lib/languages/sv.d.ts +14 -0
- package/lib/languages/sv.js +280 -103
- package/lib/languages/sw.d.ts +7 -0
- package/lib/languages/sw.js +135 -103
- package/lib/languages/ta.d.ts +7 -0
- package/lib/languages/ta.js +133 -205
- package/lib/languages/te.d.ts +7 -0
- package/lib/languages/te.js +148 -213
- package/lib/languages/th.d.ts +7 -0
- package/lib/languages/th.js +139 -101
- package/lib/languages/tr.d.ts +18 -0
- package/lib/languages/tr.js +246 -66
- package/lib/languages/uk.d.ts +11 -0
- package/lib/languages/uk.js +197 -101
- package/lib/languages/ur.d.ts +7 -0
- package/lib/languages/ur.js +160 -123
- package/lib/languages/vi.d.ts +17 -0
- package/lib/languages/vi.js +287 -164
- package/lib/languages/zh-Hans.d.ts +11 -0
- package/lib/languages/zh-Hans.js +159 -142
- package/lib/languages/zh-Hant.d.ts +11 -0
- package/lib/languages/zh-Hant.js +202 -0
- package/lib/n2words.d.ts +53 -0
- package/lib/n2words.js +91 -227
- 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 +118 -67
- package/dist/languages/pa-Guru.js +0 -2
- package/dist/languages/pa-Guru.js.map +0 -1
- package/lib/classes/abstract-language.js +0 -261
- package/lib/classes/greedy-scale-language.js +0 -195
- package/lib/classes/slavic-language.js +0 -251
- package/lib/classes/south-asian-language.js +0 -161
- package/lib/classes/turkic-language.js +0 -63
- package/lib/languages/pa-Guru.js +0 -126
- package/typings/classes/abstract-language.d.ts +0 -144
- package/typings/classes/greedy-scale-language.d.ts +0 -148
- package/typings/classes/slavic-language.d.ts +0 -145
- package/typings/classes/south-asian-language.d.ts +0 -101
- package/typings/classes/turkic-language.d.ts +0 -42
- package/typings/languages/ar.d.ts +0 -93
- package/typings/languages/az.d.ts +0 -25
- package/typings/languages/bn.d.ts +0 -1
- package/typings/languages/cs.d.ts +0 -120
- package/typings/languages/da.d.ts +0 -53
- package/typings/languages/de.d.ts +0 -26
- package/typings/languages/el.d.ts +0 -11
- package/typings/languages/en.d.ts +0 -30
- package/typings/languages/es.d.ts +0 -43
- package/typings/languages/fa.d.ts +0 -81
- package/typings/languages/fil.d.ts +0 -12
- package/typings/languages/fr-BE.d.ts +0 -41
- package/typings/languages/fr.d.ts +0 -43
- package/typings/languages/gu.d.ts +0 -12
- package/typings/languages/he.d.ts +0 -197
- package/typings/languages/hi.d.ts +0 -1
- package/typings/languages/hr.d.ts +0 -110
- package/typings/languages/hu.d.ts +0 -37
- package/typings/languages/id.d.ts +0 -69
- package/typings/languages/it.d.ts +0 -51
- package/typings/languages/ja.d.ts +0 -58
- package/typings/languages/kn.d.ts +0 -11
- package/typings/languages/ko.d.ts +0 -25
- package/typings/languages/lt.d.ts +0 -110
- package/typings/languages/lv.d.ts +0 -99
- package/typings/languages/mr.d.ts +0 -12
- package/typings/languages/ms.d.ts +0 -37
- package/typings/languages/nb.d.ts +0 -27
- package/typings/languages/nl.d.ts +0 -65
- package/typings/languages/pa-Guru.d.ts +0 -1
- package/typings/languages/pl.d.ts +0 -116
- package/typings/languages/pt.d.ts +0 -39
- package/typings/languages/ro.d.ts +0 -229
- package/typings/languages/ru.d.ts +0 -108
- package/typings/languages/sr-Latn.d.ts +0 -98
- package/typings/languages/sv.d.ts +0 -30
- package/typings/languages/sw.d.ts +0 -1
- package/typings/languages/ta.d.ts +0 -1
- package/typings/languages/te.d.ts +0 -1
- package/typings/languages/th.d.ts +0 -1
- package/typings/languages/tr.d.ts +0 -46
- package/typings/languages/uk.d.ts +0 -117
- package/typings/languages/ur.d.ts +0 -1
- package/typings/languages/vi.d.ts +0 -116
- package/typings/languages/zh-Hans.d.ts +0 -57
- package/typings/n2words.d.ts +0 -177
package/lib/languages/ar.js
CHANGED
|
@@ -1,243 +1,205 @@
|
|
|
1
|
-
import AbstractLanguage from '../classes/abstract-language.js'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @typedef {Object} ArabicOptions
|
|
5
|
-
* @property {string} [negativeWord='ناقص'] Word for negative numbers (minus).
|
|
6
|
-
* @property {boolean} [feminine=false] Use feminine forms for numbers.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
1
|
/**
|
|
10
|
-
* Arabic language converter
|
|
11
|
-
*
|
|
12
|
-
* Converts numbers to Arabic words with full grammatical support:
|
|
13
|
-
* - Gender agreement (masculine/feminine forms)
|
|
14
|
-
* - Complex pluralization rules (singular, dual, plural forms)
|
|
15
|
-
* - Special handling for hundreds, thousands, millions, etc.
|
|
16
|
-
* - Right-to-left text orientation
|
|
17
|
-
* - Traditional Arabic number naming conventions
|
|
18
|
-
*
|
|
19
|
-
* Key Features:
|
|
20
|
-
* - Gender-aware number forms (واحد masculine vs واحدة feminine)
|
|
21
|
-
* - Dual forms: اثنان/اثنتان (two masculine/feminine)
|
|
22
|
-
* - Complex rule system for numbers 3-10 (requiring feminine when referring to countables)
|
|
23
|
-
* - Group-based algorithm: splits number by powers of 1000 (ones, thousands, millions, billions)
|
|
24
|
-
* - Tanween (nunation) for indefinite numbers
|
|
25
|
-
* - Sophisticated pluralization with singular, dual, and plural forms
|
|
2
|
+
* Arabic language converter - Functional Implementation
|
|
26
3
|
*
|
|
27
|
-
*
|
|
28
|
-
* 1. Break number into groups of 3 digits (right to left)
|
|
29
|
-
* 2. For each non-zero group, convert to words using gender and plural rules
|
|
30
|
-
* 3. Append the appropriate magnitude word (ألف/مليون/مليار) with proper plural form
|
|
31
|
-
* 4. Join all groups with spaces
|
|
4
|
+
* Self-contained converter with gender agreement and complex pluralization.
|
|
32
5
|
*
|
|
33
|
-
*
|
|
34
|
-
* -
|
|
35
|
-
* -
|
|
36
|
-
* - Complex group processing for large numbers
|
|
37
|
-
* - Right-to-left text orientation
|
|
6
|
+
* Key features:
|
|
7
|
+
* - Gender agreement (masculine/feminine forms)
|
|
8
|
+
* - Complex pluralization (singular/dual/plural)
|
|
38
9
|
* - Traditional Arabic number naming conventions
|
|
10
|
+
* - "و" (and) conjunction between segments
|
|
39
11
|
*/
|
|
40
|
-
export class Arabic extends AbstractLanguage {
|
|
41
|
-
decimalSeparatorWord = 'فاصلة'
|
|
42
|
-
zeroWord = 'صفر'
|
|
43
|
-
arabicTens = ['عشرون', 'ثلاثون', 'أربعون', 'خمسون', 'ستون', 'سبعون', 'ثمانون', 'تسعون']
|
|
44
|
-
arabicHundreds = ['', 'مائة', 'مئتان', 'ثلاثمائة', 'أربعمائة', 'خمسمائة', 'ستمائة', 'سبعمائة', 'ثمانمائة', 'تسعمائة']
|
|
45
|
-
arabicAppendedTwos = ['مئتا', 'ألفا', 'مليونا', 'مليارا', 'تريليونا', 'كوادريليونا', 'كوينتليونا', 'سكستيليونا']
|
|
46
|
-
arabicTwos = ['مئتان', 'ألفان', 'مليونان', 'ملياران', 'تريليونان', 'كوادريليونان', 'كوينتليونان', 'سكستيليونان']
|
|
47
|
-
arabicGroup = ['مائة', 'ألف', 'مليون', 'مليار', 'تريليون', 'كوادريليون', 'كوينتليون', 'سكستيليون']
|
|
48
|
-
arabicAppendedGroup = ['', 'ألفاً', 'مليوناً', 'ملياراً', 'تريليوناً', 'كوادريليوناً', 'كوينتليوناً', 'سكستيليوناً']
|
|
49
|
-
arabicPluralGroups = ['', 'آلاف', 'ملايين', 'مليارات', 'تريليونات', 'كوادريليونات', 'كوينتليونات', 'سكستيليونات']
|
|
50
|
-
ones = {
|
|
51
|
-
masculine: [
|
|
52
|
-
'واحد',
|
|
53
|
-
'اثنان',
|
|
54
|
-
'ثلاثة',
|
|
55
|
-
'أربعة',
|
|
56
|
-
'خمسة',
|
|
57
|
-
'ستة',
|
|
58
|
-
'سبعة',
|
|
59
|
-
'ثمانية',
|
|
60
|
-
'تسعة',
|
|
61
|
-
'عشرة',
|
|
62
|
-
'أحد عشر',
|
|
63
|
-
'اثنا عشر',
|
|
64
|
-
'ثلاثة عشر',
|
|
65
|
-
'أربعة عشر',
|
|
66
|
-
'خمسة عشر',
|
|
67
|
-
'ستة عشر',
|
|
68
|
-
'سبعة عشر',
|
|
69
|
-
'ثمانية عشر',
|
|
70
|
-
'تسعة عشر'
|
|
71
|
-
],
|
|
72
|
-
feminine: [
|
|
73
|
-
'واحدة',
|
|
74
|
-
'اثنتان',
|
|
75
|
-
'ثلاث',
|
|
76
|
-
'أربع',
|
|
77
|
-
'خمس',
|
|
78
|
-
'ست',
|
|
79
|
-
'سبع',
|
|
80
|
-
'ثمان',
|
|
81
|
-
'تسع',
|
|
82
|
-
'عشر',
|
|
83
|
-
'إحدى عشرة',
|
|
84
|
-
'اثنتا عشرة',
|
|
85
|
-
'ثلاث عشرة',
|
|
86
|
-
'أربع عشرة',
|
|
87
|
-
'خمس عشرة',
|
|
88
|
-
'ست عشرة',
|
|
89
|
-
'سبع عشرة',
|
|
90
|
-
'ثماني عشرة',
|
|
91
|
-
'تسع عشرة'
|
|
92
|
-
]
|
|
93
|
-
}
|
|
94
12
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
*
|
|
98
|
-
* @param {ArabicOptions} [options={}] Configuration options.
|
|
99
|
-
*/
|
|
100
|
-
constructor ({ negativeWord = 'ناقص', feminine = false } = {}) {
|
|
101
|
-
super()
|
|
13
|
+
import { parseNumericValue } from '../utils/parse-numeric.js'
|
|
14
|
+
import { validateOptions } from '../utils/validate-options.js'
|
|
102
15
|
|
|
103
|
-
|
|
104
|
-
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Vocabulary
|
|
18
|
+
// ============================================================================
|
|
105
19
|
|
|
106
|
-
|
|
107
|
-
|
|
20
|
+
const TENS = ['عشرون', 'ثلاثون', 'أربعون', 'خمسون', 'ستون', 'سبعون', 'ثمانون', 'تسعون']
|
|
21
|
+
const HUNDREDS = ['', 'مائة', 'مئتان', 'ثلاثمائة', 'أربعمائة', 'خمسمائة', 'ستمائة', 'سبعمائة', 'ثمانمائة', 'تسعمائة']
|
|
22
|
+
|
|
23
|
+
// Magnitude words with three forms: singular, appended (tanween), plural
|
|
24
|
+
const SCALE_WORDS = ['مائة', 'ألف', 'مليون', 'مليار', 'تريليون', 'كوادريليون', 'كوينتليون', 'سكستيليون']
|
|
25
|
+
const SCALE_APPENDED = ['', 'ألفاً', 'مليوناً', 'ملياراً', 'تريليوناً', 'كوادريليوناً', 'كوينتليوناً', 'سكستيليوناً']
|
|
26
|
+
const SCALE_PLURAL = ['', 'آلاف', 'ملايين', 'مليارات', 'تريليونات', 'كوادريليونات', 'كوينتليونات', 'سكستيليونات']
|
|
27
|
+
|
|
28
|
+
// Dual forms
|
|
29
|
+
const DUAL = ['مئتان', 'ألفان', 'مليونان', 'ملياران', 'تريليونان', 'كوادريليونان', 'كوينتليونان', 'سكستيليونان']
|
|
30
|
+
const DUAL_APPENDED = ['مئتا', 'ألفا', 'مليونا', 'مليارا', 'تريليونا', 'كوادريليونا', 'كوينتليونا', 'سكستيليونا']
|
|
31
|
+
|
|
32
|
+
// Gender-specific forms (1-19)
|
|
33
|
+
const ONES_MASC = ['واحد', 'اثنان', 'ثلاثة', 'أربعة', 'خمسة', 'ستة', 'سبعة', 'ثمانية', 'تسعة', 'عشرة', 'أحد عشر', 'اثنا عشر', 'ثلاثة عشر', 'أربعة عشر', 'خمسة عشر', 'ستة عشر', 'سبعة عشر', 'ثمانية عشر', 'تسعة عشر']
|
|
34
|
+
const ONES_FEM = ['واحدة', 'اثنتان', 'ثلاث', 'أربع', 'خمس', 'ست', 'سبع', 'ثمان', 'تسع', 'عشر', 'إحدى عشرة', 'اثنتا عشرة', 'ثلاث عشرة', 'أربع عشرة', 'خمس عشرة', 'ست عشرة', 'سبع عشرة', 'ثماني عشرة', 'تسع عشرة']
|
|
35
|
+
|
|
36
|
+
const ZERO = 'صفر'
|
|
37
|
+
const NEGATIVE = 'ناقص'
|
|
38
|
+
const DECIMAL_SEP = 'فاصلة'
|
|
39
|
+
const AND = 'و'
|
|
108
40
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
41
|
+
// ============================================================================
|
|
42
|
+
// Conversion Functions
|
|
43
|
+
// ============================================================================
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Convert a 3-digit group to words.
|
|
47
|
+
* Returns a clean string with no leading/trailing spaces.
|
|
48
|
+
* Arabic "و" (and) is attached to following word: "مائة وخمسة" not "مائة و خمسة"
|
|
49
|
+
*/
|
|
50
|
+
function segmentToWords (groupNumber, groupLevel, fullNumber, ones) {
|
|
51
|
+
const tensValue = groupNumber % 100
|
|
52
|
+
const hundredsDigit = Math.trunc(groupNumber / 100)
|
|
53
|
+
let result = ''
|
|
54
|
+
|
|
55
|
+
// Process hundreds
|
|
56
|
+
if (hundredsDigit > 0) {
|
|
57
|
+
if (tensValue === 0 && hundredsDigit === 2) {
|
|
58
|
+
result = DUAL[0]
|
|
59
|
+
} else {
|
|
60
|
+
const hundredsWord = HUNDREDS[hundredsDigit]
|
|
61
|
+
if (hundredsWord) {
|
|
62
|
+
result = hundredsWord
|
|
63
|
+
if (tensValue !== 0) {
|
|
64
|
+
result += ' ' + AND // "مائة و" - و attaches to next word
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
117
68
|
}
|
|
118
69
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const hundreds = Math.trunc(hundredsRaw)
|
|
130
|
-
let returnValue = ''
|
|
131
|
-
|
|
132
|
-
// Process hundreds
|
|
133
|
-
if (hundreds > 0) {
|
|
134
|
-
if (tens === 0 && hundreds === 2) {
|
|
135
|
-
returnValue = this.arabicTwos[0]
|
|
136
|
-
} else {
|
|
137
|
-
const hundredsWord = this.arabicHundreds[hundreds]
|
|
138
|
-
if (hundredsWord) {
|
|
139
|
-
returnValue = hundredsWord
|
|
140
|
-
if (tens !== 0) {
|
|
141
|
-
returnValue += ' و'
|
|
142
|
-
}
|
|
70
|
+
// Process tens and ones
|
|
71
|
+
if (tensValue > 0) {
|
|
72
|
+
if (tensValue < 20) {
|
|
73
|
+
if (tensValue === 2 && hundredsDigit === 0 && groupLevel > 0) {
|
|
74
|
+
const numValue = Number(fullNumber)
|
|
75
|
+
const pow = Math.trunc(Math.log10(numValue))
|
|
76
|
+
if (pow % 3 === 0 && fullNumber === BigInt(2 * Math.pow(10, pow))) {
|
|
77
|
+
result += (groupNumber === 2 ? DUAL[groupLevel] : DUAL_APPENDED[groupLevel])
|
|
78
|
+
} else {
|
|
79
|
+
result += DUAL[groupLevel]
|
|
143
80
|
}
|
|
81
|
+
} else if (tensValue === 1 && groupLevel > 0) {
|
|
82
|
+
result += SCALE_WORDS[groupLevel]
|
|
83
|
+
} else {
|
|
84
|
+
result += ones[tensValue - 1]
|
|
144
85
|
}
|
|
86
|
+
} else {
|
|
87
|
+
const onesDigit = tensValue % 10
|
|
88
|
+
const tensIndex = Math.trunc(tensValue / 10) - 2
|
|
89
|
+
|
|
90
|
+
if (onesDigit > 0) {
|
|
91
|
+
result += ones[onesDigit - 1] + ' ' + AND // "ستة و" attaches to tens
|
|
92
|
+
}
|
|
93
|
+
result += TENS[tensIndex]
|
|
145
94
|
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return result
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function integerToWords (n, options) {
|
|
101
|
+
if (n === 0n) return ZERO
|
|
102
|
+
|
|
103
|
+
const gender = options.gender || 'masculine'
|
|
104
|
+
const ones = gender === 'feminine' ? ONES_FEM : ONES_MASC
|
|
105
|
+
|
|
106
|
+
let temp = n
|
|
107
|
+
let group = 0
|
|
108
|
+
const groups = []
|
|
146
109
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
110
|
+
while (temp > 0n) {
|
|
111
|
+
const numberToProcess = Number(temp % 1000n)
|
|
112
|
+
temp = temp / 1000n
|
|
113
|
+
|
|
114
|
+
if (numberToProcess > 0) {
|
|
115
|
+
const groupDescription = segmentToWords(numberToProcess, group, n, ones)
|
|
116
|
+
|
|
117
|
+
if (groupDescription) {
|
|
118
|
+
let groupText = groupDescription
|
|
119
|
+
|
|
120
|
+
// Add scale word for groups > 0
|
|
121
|
+
if (group > 0 && numberToProcess > 2) {
|
|
122
|
+
const remainder = numberToProcess % 100
|
|
123
|
+
if (remainder === 1) {
|
|
124
|
+
groupText += ' ' + SCALE_WORDS[group]
|
|
125
|
+
} else if (numberToProcess >= 3 && numberToProcess <= 10) {
|
|
126
|
+
groupText += ' ' + SCALE_PLURAL[group]
|
|
156
127
|
} else {
|
|
157
|
-
|
|
128
|
+
groupText += ' ' + (groups.length > 0 ? SCALE_APPENDED[group] : SCALE_WORDS[group])
|
|
158
129
|
}
|
|
159
|
-
} else if (tens === 1 && groupLevel > 0) {
|
|
160
|
-
returnValue += this.arabicGroup[groupLevel]
|
|
161
|
-
} else {
|
|
162
|
-
returnValue += this.selectedOnes[tens - 1]
|
|
163
130
|
}
|
|
164
|
-
} else { // 20 -> 99
|
|
165
|
-
const ones = tens % 10
|
|
166
|
-
const tensIndex = Math.trunc(tens / 10) - 2
|
|
167
131
|
|
|
168
|
-
|
|
169
|
-
returnValue += this.selectedOnes[ones - 1]
|
|
170
|
-
returnValue += ' و'
|
|
171
|
-
}
|
|
172
|
-
returnValue += this.arabicTens[tensIndex]
|
|
132
|
+
groups.unshift(groupText)
|
|
173
133
|
}
|
|
174
134
|
}
|
|
175
135
|
|
|
176
|
-
|
|
136
|
+
group++
|
|
177
137
|
}
|
|
178
138
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
* @param {bigint} number - The number to convert.
|
|
183
|
-
* @returns {string} The cardinal representation of the number in Arabic.
|
|
184
|
-
*/
|
|
185
|
-
convertWholePart (number) {
|
|
186
|
-
if (number === 0n) {
|
|
187
|
-
return this.zeroWord
|
|
188
|
-
}
|
|
189
|
-
let temp = number
|
|
190
|
-
let group = 0
|
|
191
|
-
let result = ''
|
|
192
|
-
|
|
193
|
-
// Process each group of 3 digits (right to left)
|
|
194
|
-
while (temp > 0n) {
|
|
195
|
-
const numberToProcess = Number(temp % 1000n)
|
|
196
|
-
temp = temp / 1000n
|
|
197
|
-
|
|
198
|
-
if (numberToProcess > 0) {
|
|
199
|
-
const groupDescription = this.processArabicGroup(numberToProcess, group, number)
|
|
200
|
-
|
|
201
|
-
if (groupDescription) {
|
|
202
|
-
// Add group name for thousands, millions, etc.
|
|
203
|
-
if (group > 0) {
|
|
204
|
-
if (result) {
|
|
205
|
-
result = ' و' + result
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
if (numberToProcess > 2) {
|
|
209
|
-
const remainder = numberToProcess % 100
|
|
210
|
-
if (remainder === 1) {
|
|
211
|
-
result = this.arabicGroup[group] + ' ' + result
|
|
212
|
-
} else if (numberToProcess >= 3 && numberToProcess <= 10) {
|
|
213
|
-
result = this.arabicPluralGroups[group] + ' ' + result
|
|
214
|
-
} else {
|
|
215
|
-
result = (result ? this.arabicAppendedGroup[group] : this.arabicGroup[group]) + ' ' + result
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
139
|
+
// Join groups with و (and) - space before و, attaches to next word
|
|
140
|
+
// Use simple join since segmentToWords returns clean strings
|
|
141
|
+
if (groups.length === 1) return groups[0]
|
|
219
142
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
143
|
+
// Build result: "group1 وgroup2 وgroup3"
|
|
144
|
+
let result = groups[0]
|
|
145
|
+
for (let i = 1; i < groups.length; i++) {
|
|
146
|
+
result += ' ' + AND + groups[i]
|
|
147
|
+
}
|
|
148
|
+
return result
|
|
149
|
+
}
|
|
224
150
|
|
|
225
|
-
|
|
226
|
-
|
|
151
|
+
function decimalPartToWords (decimalPart, options) {
|
|
152
|
+
const parts = []
|
|
153
|
+
let i = 0
|
|
154
|
+
|
|
155
|
+
while (i < decimalPart.length && decimalPart[i] === '0') {
|
|
156
|
+
parts.push(ZERO)
|
|
157
|
+
i++
|
|
158
|
+
}
|
|
227
159
|
|
|
228
|
-
|
|
160
|
+
const remainder = decimalPart.slice(i)
|
|
161
|
+
if (remainder) {
|
|
162
|
+
parts.push(integerToWords(BigInt(remainder), options))
|
|
229
163
|
}
|
|
164
|
+
|
|
165
|
+
return parts.join(' ')
|
|
230
166
|
}
|
|
231
167
|
|
|
232
168
|
/**
|
|
233
|
-
* Converts a
|
|
169
|
+
* Converts a numeric value to Arabic words.
|
|
234
170
|
*
|
|
235
|
-
* @param {number|string|bigint} value The
|
|
236
|
-
* @param {
|
|
237
|
-
* @
|
|
238
|
-
* @
|
|
239
|
-
* @
|
|
171
|
+
* @param {number | string | bigint} value - The numeric value to convert
|
|
172
|
+
* @param {Object} [options] - Optional configuration
|
|
173
|
+
* @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender
|
|
174
|
+
* @param {string} [options.negativeWord] - Custom word for negative numbers
|
|
175
|
+
* @returns {string} The number in Arabic words
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* toWords(1) // 'واحد'
|
|
179
|
+
* toWords(1, {gender: 'feminine'}) // 'واحدة'
|
|
240
180
|
*/
|
|
241
|
-
|
|
242
|
-
|
|
181
|
+
function toWords (value, options) {
|
|
182
|
+
options = validateOptions(options)
|
|
183
|
+
const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
|
|
184
|
+
|
|
185
|
+
const parts = []
|
|
186
|
+
|
|
187
|
+
if (isNegative) {
|
|
188
|
+
parts.push(options.negativeWord || NEGATIVE)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
parts.push(integerToWords(integerPart, options))
|
|
192
|
+
|
|
193
|
+
if (decimalPart) {
|
|
194
|
+
parts.push(DECIMAL_SEP)
|
|
195
|
+
parts.push(decimalPartToWords(decimalPart, options))
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return parts.join(' ')
|
|
243
199
|
}
|
|
200
|
+
|
|
201
|
+
// ============================================================================
|
|
202
|
+
// Exports
|
|
203
|
+
// ============================================================================
|
|
204
|
+
|
|
205
|
+
export { toWords }
|
package/lib/languages/az.js
CHANGED
|
@@ -1,58 +1,176 @@
|
|
|
1
|
-
import TurkicLanguage from '../classes/turkic-language.js'
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
|
-
* Azerbaijani language converter
|
|
2
|
+
* Azerbaijani language converter - Functional Implementation
|
|
3
|
+
*
|
|
4
|
+
* Self-contained converter with precomputed lookup tables.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* -
|
|
8
|
-
* -
|
|
9
|
-
* -
|
|
6
|
+
* Key features:
|
|
7
|
+
* - Turkic language patterns
|
|
8
|
+
* - Implicit "bir" (one) omission before hundreds and thousands
|
|
9
|
+
* - Short scale naming
|
|
10
10
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
11
|
+
|
|
12
|
+
import { parseNumericValue } from '../utils/parse-numeric.js'
|
|
13
|
+
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// Vocabulary
|
|
16
|
+
// ============================================================================
|
|
17
|
+
|
|
18
|
+
const ONES = ['', 'bir', 'iki', 'üç', 'dörd', 'beş', 'altı', 'yeddi', 'səkkiz', 'doqquz']
|
|
19
|
+
const TEENS = ['on', 'on bir', 'on iki', 'on üç', 'on dörd', 'on beş', 'on altı', 'on yeddi', 'on səkkiz', 'on doqquz']
|
|
20
|
+
const TENS = ['', '', 'iyirmi', 'otuz', 'qırx', 'əlli', 'altmış', 'yetmiş', 'səksən', 'doxsan']
|
|
21
|
+
|
|
22
|
+
const HUNDRED = 'yüz'
|
|
23
|
+
const THOUSAND = 'min'
|
|
24
|
+
|
|
25
|
+
const ZERO = 'sıfır'
|
|
26
|
+
const NEGATIVE = 'mənfi'
|
|
27
|
+
const DECIMAL_SEP = 'nöqtə'
|
|
28
|
+
|
|
29
|
+
// Short scale
|
|
30
|
+
const SCALE_WORDS = ['', THOUSAND, 'milyon', 'milyar', 'trilyon', 'katrilyon', 'kentilyon']
|
|
31
|
+
|
|
32
|
+
// ============================================================================
|
|
33
|
+
// Precomputed Lookup Table
|
|
34
|
+
// ============================================================================
|
|
35
|
+
|
|
36
|
+
function buildSegment (n) {
|
|
37
|
+
if (n === 0) return ''
|
|
38
|
+
|
|
39
|
+
const ones = n % 10
|
|
40
|
+
const tensDigit = Math.floor(n / 10) % 10
|
|
41
|
+
const hundredsDigit = Math.floor(n / 100)
|
|
42
|
+
|
|
43
|
+
const parts = []
|
|
44
|
+
|
|
45
|
+
if (hundredsDigit > 0) {
|
|
46
|
+
if (hundredsDigit === 1) {
|
|
47
|
+
parts.push(HUNDRED)
|
|
48
|
+
} else {
|
|
49
|
+
parts.push(ONES[hundredsDigit] + ' ' + HUNDRED)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (tensDigit === 1) {
|
|
54
|
+
parts.push(TEENS[ones])
|
|
55
|
+
} else {
|
|
56
|
+
if (tensDigit > 1) {
|
|
57
|
+
parts.push(TENS[tensDigit])
|
|
58
|
+
}
|
|
59
|
+
if (ones > 0) {
|
|
60
|
+
parts.push(ONES[ones])
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return parts.join(' ')
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const SEGMENTS = new Array(1000)
|
|
68
|
+
for (let i = 0; i < 1000; i++) {
|
|
69
|
+
SEGMENTS[i] = buildSegment(i)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ============================================================================
|
|
73
|
+
// Conversion Functions
|
|
74
|
+
// ============================================================================
|
|
75
|
+
|
|
76
|
+
function integerToWords (n) {
|
|
77
|
+
if (n === 0n) return ZERO
|
|
78
|
+
|
|
79
|
+
if (n < 1000n) {
|
|
80
|
+
return SEGMENTS[Number(n)]
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return buildLargeNumberWords(n)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function buildLargeNumberWords (n) {
|
|
87
|
+
const numStr = n.toString()
|
|
88
|
+
const len = numStr.length
|
|
89
|
+
|
|
90
|
+
const segments = []
|
|
91
|
+
const segmentSize = 3
|
|
92
|
+
|
|
93
|
+
const remainderLen = len % segmentSize
|
|
94
|
+
let pos = 0
|
|
95
|
+
if (remainderLen > 0) {
|
|
96
|
+
segments.push(Number(numStr.slice(0, remainderLen)))
|
|
97
|
+
pos = remainderLen
|
|
98
|
+
}
|
|
99
|
+
while (pos < len) {
|
|
100
|
+
segments.push(Number(numStr.slice(pos, pos + segmentSize)))
|
|
101
|
+
pos += segmentSize
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const parts = []
|
|
105
|
+
let scaleIndex = segments.length - 1
|
|
106
|
+
|
|
107
|
+
for (let i = 0; i < segments.length; i++) {
|
|
108
|
+
const segment = segments[i]
|
|
109
|
+
|
|
110
|
+
if (segment !== 0) {
|
|
111
|
+
const scaleWord = SCALE_WORDS[scaleIndex] || ''
|
|
112
|
+
|
|
113
|
+
if (scaleIndex === 0) {
|
|
114
|
+
parts.push(SEGMENTS[segment])
|
|
115
|
+
} else if (scaleIndex === 1 && segment === 1) {
|
|
116
|
+
// Omit "bir" before thousand
|
|
117
|
+
parts.push(scaleWord)
|
|
118
|
+
} else {
|
|
119
|
+
parts.push(SEGMENTS[segment] + ' ' + scaleWord)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
scaleIndex--
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return parts.join(' ')
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function decimalPartToWords (decimalPart) {
|
|
130
|
+
let result = ''
|
|
131
|
+
let i = 0
|
|
132
|
+
|
|
133
|
+
while (i < decimalPart.length && decimalPart[i] === '0') {
|
|
134
|
+
if (result) result += ' '
|
|
135
|
+
result += ZERO
|
|
136
|
+
i++
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const remainder = decimalPart.slice(i)
|
|
140
|
+
if (remainder) {
|
|
141
|
+
if (result) result += ' '
|
|
142
|
+
result += integerToWords(BigInt(remainder))
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return result
|
|
41
146
|
}
|
|
42
147
|
|
|
43
148
|
/**
|
|
44
|
-
* Converts a
|
|
149
|
+
* Converts a numeric value to Azerbaijani words.
|
|
45
150
|
*
|
|
46
|
-
* @param {number|string|bigint} value The
|
|
47
|
-
* @
|
|
48
|
-
* @returns {string} The number expressed in Azerbaijani words.
|
|
49
|
-
* @throws {TypeError} If value is NaN or invalid type.
|
|
50
|
-
* @throws {Error} If value is an invalid number string.
|
|
51
|
-
*
|
|
52
|
-
* @example
|
|
53
|
-
* convertToWords(42, { lang: 'az' }); // 'qırx iki'
|
|
54
|
-
* convertToWords(1000, { lang: 'az' }); // 'min'
|
|
151
|
+
* @param {number | string | bigint} value - The numeric value to convert
|
|
152
|
+
* @returns {string} The number in Azerbaijani words
|
|
55
153
|
*/
|
|
56
|
-
|
|
57
|
-
|
|
154
|
+
function toWords (value) {
|
|
155
|
+
const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
|
|
156
|
+
|
|
157
|
+
let result = ''
|
|
158
|
+
|
|
159
|
+
if (isNegative) {
|
|
160
|
+
result = NEGATIVE + ' '
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
result += integerToWords(integerPart)
|
|
164
|
+
|
|
165
|
+
if (decimalPart) {
|
|
166
|
+
result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return result
|
|
58
170
|
}
|
|
171
|
+
|
|
172
|
+
// ============================================================================
|
|
173
|
+
// Exports
|
|
174
|
+
// ============================================================================
|
|
175
|
+
|
|
176
|
+
export { toWords }
|