n2words 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +49 -0
- package/README.md +86 -188
- package/dist/languages/am-Latn.js +3 -0
- package/dist/languages/am-Latn.js.map +1 -0
- package/dist/languages/am.js +3 -0
- package/dist/languages/am.js.map +1 -0
- package/dist/languages/ar.js +3 -0
- package/dist/languages/ar.js.map +1 -0
- package/dist/languages/az.js +3 -0
- package/dist/languages/az.js.map +1 -0
- package/dist/languages/bn.js +3 -0
- package/dist/languages/bn.js.map +1 -0
- package/dist/languages/cs.js +3 -0
- package/dist/languages/cs.js.map +1 -0
- package/dist/languages/da.js +3 -0
- package/dist/languages/da.js.map +1 -0
- package/dist/languages/de.js +3 -0
- package/dist/languages/de.js.map +1 -0
- package/dist/languages/el.js +3 -0
- package/dist/languages/el.js.map +1 -0
- package/dist/languages/en.js +3 -0
- package/dist/languages/en.js.map +1 -0
- package/dist/languages/es.js +3 -0
- package/dist/languages/es.js.map +1 -0
- package/dist/languages/fa.js +3 -0
- package/dist/languages/fa.js.map +1 -0
- package/dist/languages/fi.js +3 -0
- package/dist/languages/fi.js.map +1 -0
- package/dist/languages/fil.js +3 -0
- package/dist/languages/fil.js.map +1 -0
- package/dist/languages/fr-BE.js +3 -0
- package/dist/languages/fr-BE.js.map +1 -0
- package/dist/languages/fr.js +3 -0
- package/dist/languages/fr.js.map +1 -0
- package/dist/languages/gu.js +3 -0
- package/dist/languages/gu.js.map +1 -0
- package/dist/languages/ha.js +3 -0
- package/dist/languages/ha.js.map +1 -0
- package/dist/languages/hbo.js +3 -0
- package/dist/languages/hbo.js.map +1 -0
- package/dist/languages/he.js +3 -0
- package/dist/languages/he.js.map +1 -0
- package/dist/languages/hi.js +3 -0
- package/dist/languages/hi.js.map +1 -0
- package/dist/languages/hr.js +3 -0
- package/dist/languages/hr.js.map +1 -0
- package/dist/languages/hu.js +3 -0
- package/dist/languages/hu.js.map +1 -0
- package/dist/languages/id.js +3 -0
- package/dist/languages/id.js.map +1 -0
- package/dist/languages/it.js +3 -0
- package/dist/languages/it.js.map +1 -0
- package/dist/languages/ja.js +3 -0
- package/dist/languages/ja.js.map +1 -0
- package/dist/languages/kn.js +3 -0
- package/dist/languages/kn.js.map +1 -0
- package/dist/languages/ko.js +3 -0
- package/dist/languages/ko.js.map +1 -0
- package/dist/languages/lt.js +3 -0
- package/dist/languages/lt.js.map +1 -0
- package/dist/languages/lv.js +3 -0
- package/dist/languages/lv.js.map +1 -0
- package/dist/languages/mr.js +3 -0
- package/dist/languages/mr.js.map +1 -0
- package/dist/languages/ms.js +3 -0
- package/dist/languages/ms.js.map +1 -0
- package/dist/languages/nb.js +3 -0
- package/dist/languages/nb.js.map +1 -0
- package/dist/languages/nl.js +3 -0
- package/dist/languages/nl.js.map +1 -0
- package/dist/languages/pa.js +3 -0
- package/dist/languages/pa.js.map +1 -0
- package/dist/languages/pl.js +3 -0
- package/dist/languages/pl.js.map +1 -0
- package/dist/languages/pt.js +3 -0
- package/dist/languages/pt.js.map +1 -0
- package/dist/languages/ro.js +3 -0
- package/dist/languages/ro.js.map +1 -0
- package/dist/languages/ru.js +3 -0
- package/dist/languages/ru.js.map +1 -0
- package/dist/languages/sr-Cyrl.js +3 -0
- package/dist/languages/sr-Cyrl.js.map +1 -0
- package/dist/languages/sr-Latn.js +3 -0
- package/dist/languages/sr-Latn.js.map +1 -0
- package/dist/languages/sv.js +3 -0
- package/dist/languages/sv.js.map +1 -0
- package/dist/languages/sw.js +3 -0
- package/dist/languages/sw.js.map +1 -0
- package/dist/languages/ta.js +3 -0
- package/dist/languages/ta.js.map +1 -0
- package/dist/languages/te.js +3 -0
- package/dist/languages/te.js.map +1 -0
- package/dist/languages/th.js +3 -0
- package/dist/languages/th.js.map +1 -0
- package/dist/languages/tr.js +3 -0
- package/dist/languages/tr.js.map +1 -0
- package/dist/languages/uk.js +3 -0
- package/dist/languages/uk.js.map +1 -0
- package/dist/languages/ur.js +3 -0
- package/dist/languages/ur.js.map +1 -0
- package/dist/languages/vi.js +3 -0
- package/dist/languages/vi.js.map +1 -0
- package/dist/languages/zh-Hans.js +3 -0
- package/dist/languages/zh-Hans.js.map +1 -0
- package/dist/languages/zh-Hant.js +3 -0
- package/dist/languages/zh-Hant.js.map +1 -0
- package/dist/n2words.js +2 -2
- package/dist/n2words.js.map +1 -1
- package/lib/languages/am-Latn.d.ts +7 -0
- package/lib/languages/am-Latn.js +164 -0
- package/lib/languages/am.d.ts +7 -0
- package/lib/languages/am.js +164 -0
- package/lib/languages/ar.d.ts +14 -27
- package/lib/languages/ar.js +175 -129
- package/lib/languages/az.d.ts +4 -9
- package/lib/languages/az.js +171 -37
- package/lib/languages/bn.d.ts +4 -8
- package/lib/languages/bn.js +138 -124
- package/lib/languages/cs.d.ts +15 -85
- package/lib/languages/cs.js +310 -114
- package/lib/languages/da.d.ts +11 -12
- package/lib/languages/da.js +276 -101
- package/lib/languages/de.d.ts +14 -11
- package/lib/languages/de.js +317 -86
- package/lib/languages/el.d.ts +11 -11
- package/lib/languages/el.js +231 -78
- package/lib/languages/en.d.ts +14 -13
- package/lib/languages/en.js +242 -72
- package/lib/languages/es.d.ts +18 -12
- package/lib/languages/es.js +317 -103
- package/lib/languages/fa.d.ts +4 -44
- package/lib/languages/fa.js +112 -122
- package/lib/languages/fi.d.ts +14 -0
- package/lib/languages/fi.js +245 -0
- package/lib/languages/fil.d.ts +4 -13
- package/lib/languages/fil.js +207 -106
- package/lib/languages/fr-BE.d.ts +8 -8
- package/lib/languages/fr-BE.js +294 -19
- package/lib/languages/fr.d.ts +18 -12
- package/lib/languages/fr.js +352 -89
- package/lib/languages/gu.d.ts +4 -8
- package/lib/languages/gu.js +130 -125
- package/lib/languages/ha.d.ts +7 -0
- package/lib/languages/ha.js +230 -0
- package/lib/languages/hbo.d.ts +10 -110
- package/lib/languages/hbo.js +263 -214
- package/lib/languages/he.d.ts +10 -77
- package/lib/languages/he.js +242 -172
- package/lib/languages/hi.d.ts +4 -8
- package/lib/languages/hi.js +138 -124
- package/lib/languages/hr.d.ts +8 -77
- package/lib/languages/hr.js +194 -89
- package/lib/languages/hu.d.ts +4 -19
- package/lib/languages/hu.js +198 -119
- package/lib/languages/id.d.ts +4 -34
- package/lib/languages/id.js +171 -129
- package/lib/languages/it.d.ts +16 -34
- package/lib/languages/it.js +339 -94
- package/lib/languages/ja.d.ts +14 -14
- package/lib/languages/ja.js +233 -111
- package/lib/languages/kn.d.ts +4 -8
- package/lib/languages/kn.js +130 -35
- package/lib/languages/ko.d.ts +11 -11
- package/lib/languages/ko.js +257 -49
- package/lib/languages/lt.d.ts +15 -67
- package/lib/languages/lt.js +296 -122
- package/lib/languages/lv.d.ts +15 -67
- package/lib/languages/lv.js +297 -106
- package/lib/languages/mr.d.ts +4 -8
- package/lib/languages/mr.js +130 -125
- package/lib/languages/ms.d.ts +4 -28
- package/lib/languages/ms.js +171 -116
- package/lib/languages/nb.d.ts +11 -9
- package/lib/languages/nb.js +282 -87
- package/lib/languages/nl.d.ts +23 -13
- package/lib/languages/nl.js +317 -133
- package/lib/languages/pa.d.ts +4 -8
- package/lib/languages/pa.js +156 -124
- package/lib/languages/pl.d.ts +19 -77
- package/lib/languages/pl.js +307 -87
- package/lib/languages/pt.d.ts +14 -26
- package/lib/languages/pt.js +286 -92
- package/lib/languages/ro.d.ts +15 -155
- package/lib/languages/ro.js +219 -235
- package/lib/languages/ru.d.ts +8 -82
- package/lib/languages/ru.js +222 -78
- package/lib/languages/sr-Cyrl.d.ts +8 -77
- package/lib/languages/sr-Cyrl.js +191 -89
- package/lib/languages/sr-Latn.d.ts +8 -77
- package/lib/languages/sr-Latn.js +191 -89
- package/lib/languages/sv.d.ts +11 -11
- package/lib/languages/sv.js +288 -74
- package/lib/languages/sw.d.ts +4 -36
- package/lib/languages/sw.js +133 -106
- package/lib/languages/ta.d.ts +4 -17
- package/lib/languages/ta.js +129 -201
- package/lib/languages/te.d.ts +4 -19
- package/lib/languages/te.js +141 -196
- package/lib/languages/th.d.ts +4 -14
- package/lib/languages/th.js +135 -91
- package/lib/languages/tr.d.ts +15 -9
- package/lib/languages/tr.js +256 -49
- package/lib/languages/uk.d.ts +8 -82
- package/lib/languages/uk.js +200 -78
- package/lib/languages/ur.d.ts +4 -8
- package/lib/languages/ur.js +156 -124
- package/lib/languages/vi.d.ts +14 -69
- package/lib/languages/vi.js +294 -125
- package/lib/languages/zh-Hans.d.ts +8 -18
- package/lib/languages/zh-Hans.js +163 -92
- package/lib/languages/zh-Hant.d.ts +8 -18
- package/lib/languages/zh-Hant.js +181 -90
- package/lib/n2words.d.ts +53 -209
- package/lib/n2words.js +111 -530
- package/lib/utils/is-plain-object.d.ts +13 -0
- package/lib/utils/is-plain-object.js +17 -0
- package/lib/utils/parse-numeric.d.ts +17 -0
- package/lib/utils/parse-numeric.js +108 -0
- package/lib/utils/validate-options.d.ts +8 -0
- package/lib/utils/validate-options.js +16 -0
- package/package.json +26 -14
- package/dist/ArabicConverter.js +0 -3
- package/dist/ArabicConverter.js.map +0 -1
- package/dist/AzerbaijaniConverter.js +0 -3
- package/dist/AzerbaijaniConverter.js.map +0 -1
- package/dist/BanglaConverter.js +0 -3
- package/dist/BanglaConverter.js.map +0 -1
- package/dist/BiblicalHebrewConverter.js +0 -3
- package/dist/BiblicalHebrewConverter.js.map +0 -1
- package/dist/CroatianConverter.js +0 -3
- package/dist/CroatianConverter.js.map +0 -1
- package/dist/CzechConverter.js +0 -3
- package/dist/CzechConverter.js.map +0 -1
- package/dist/DanishConverter.js +0 -3
- package/dist/DanishConverter.js.map +0 -1
- package/dist/DutchConverter.js +0 -3
- package/dist/DutchConverter.js.map +0 -1
- package/dist/EnglishConverter.js +0 -3
- package/dist/EnglishConverter.js.map +0 -1
- package/dist/FilipinoConverter.js +0 -3
- package/dist/FilipinoConverter.js.map +0 -1
- package/dist/FrenchBelgiumConverter.js +0 -3
- package/dist/FrenchBelgiumConverter.js.map +0 -1
- package/dist/FrenchConverter.js +0 -3
- package/dist/FrenchConverter.js.map +0 -1
- package/dist/GermanConverter.js +0 -3
- package/dist/GermanConverter.js.map +0 -1
- package/dist/GreekConverter.js +0 -3
- package/dist/GreekConverter.js.map +0 -1
- package/dist/GujaratiConverter.js +0 -3
- package/dist/GujaratiConverter.js.map +0 -1
- package/dist/HebrewConverter.js +0 -3
- package/dist/HebrewConverter.js.map +0 -1
- package/dist/HindiConverter.js +0 -3
- package/dist/HindiConverter.js.map +0 -1
- package/dist/HungarianConverter.js +0 -3
- package/dist/HungarianConverter.js.map +0 -1
- package/dist/IndonesianConverter.js +0 -3
- package/dist/IndonesianConverter.js.map +0 -1
- package/dist/ItalianConverter.js +0 -3
- package/dist/ItalianConverter.js.map +0 -1
- package/dist/JapaneseConverter.js +0 -3
- package/dist/JapaneseConverter.js.map +0 -1
- package/dist/KannadaConverter.js +0 -3
- package/dist/KannadaConverter.js.map +0 -1
- package/dist/KoreanConverter.js +0 -3
- package/dist/KoreanConverter.js.map +0 -1
- package/dist/LatvianConverter.js +0 -3
- package/dist/LatvianConverter.js.map +0 -1
- package/dist/LithuanianConverter.js +0 -3
- package/dist/LithuanianConverter.js.map +0 -1
- package/dist/MalayConverter.js +0 -3
- package/dist/MalayConverter.js.map +0 -1
- package/dist/MarathiConverter.js +0 -3
- package/dist/MarathiConverter.js.map +0 -1
- package/dist/NorwegianBokmalConverter.js +0 -3
- package/dist/NorwegianBokmalConverter.js.map +0 -1
- package/dist/PersianConverter.js +0 -3
- package/dist/PersianConverter.js.map +0 -1
- package/dist/PolishConverter.js +0 -3
- package/dist/PolishConverter.js.map +0 -1
- package/dist/PortugueseConverter.js +0 -3
- package/dist/PortugueseConverter.js.map +0 -1
- package/dist/PunjabiConverter.js +0 -3
- package/dist/PunjabiConverter.js.map +0 -1
- package/dist/RomanianConverter.js +0 -3
- package/dist/RomanianConverter.js.map +0 -1
- package/dist/RussianConverter.js +0 -3
- package/dist/RussianConverter.js.map +0 -1
- package/dist/SerbianCyrillicConverter.js +0 -3
- package/dist/SerbianCyrillicConverter.js.map +0 -1
- package/dist/SerbianLatinConverter.js +0 -3
- package/dist/SerbianLatinConverter.js.map +0 -1
- package/dist/SimplifiedChineseConverter.js +0 -3
- package/dist/SimplifiedChineseConverter.js.map +0 -1
- package/dist/SpanishConverter.js +0 -3
- package/dist/SpanishConverter.js.map +0 -1
- package/dist/SwahiliConverter.js +0 -3
- package/dist/SwahiliConverter.js.map +0 -1
- package/dist/SwedishConverter.js +0 -3
- package/dist/SwedishConverter.js.map +0 -1
- package/dist/TamilConverter.js +0 -3
- package/dist/TamilConverter.js.map +0 -1
- package/dist/TeluguConverter.js +0 -3
- package/dist/TeluguConverter.js.map +0 -1
- package/dist/ThaiConverter.js +0 -3
- package/dist/ThaiConverter.js.map +0 -1
- package/dist/TraditionalChineseConverter.js +0 -3
- package/dist/TraditionalChineseConverter.js.map +0 -1
- package/dist/TurkishConverter.js +0 -3
- package/dist/TurkishConverter.js.map +0 -1
- package/dist/UkrainianConverter.js +0 -3
- package/dist/UkrainianConverter.js.map +0 -1
- package/dist/UrduConverter.js +0 -3
- package/dist/UrduConverter.js.map +0 -1
- package/dist/VietnameseConverter.js +0 -3
- package/dist/VietnameseConverter.js.map +0 -1
- package/lib/classes/abstract-language.d.ts +0 -178
- package/lib/classes/abstract-language.js +0 -268
- package/lib/classes/greedy-scale-language.d.ts +0 -109
- package/lib/classes/greedy-scale-language.js +0 -201
- package/lib/classes/slavic-language.d.ts +0 -148
- package/lib/classes/slavic-language.js +0 -281
- package/lib/classes/south-asian-language.d.ts +0 -70
- package/lib/classes/south-asian-language.js +0 -154
- package/lib/classes/turkic-language.d.ts +0 -26
- package/lib/classes/turkic-language.js +0 -59
package/lib/languages/fa.js
CHANGED
|
@@ -1,144 +1,134 @@
|
|
|
1
|
-
import { AbstractLanguage } from '../classes/abstract-language.js'
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
|
-
* Persian language converter
|
|
2
|
+
* Persian language converter - Functional Implementation
|
|
3
|
+
*
|
|
4
|
+
* Self-contained converter using recursive decomposition.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* Key features:
|
|
7
7
|
* - "و" (and) conjunction for compound numbers
|
|
8
|
-
* -
|
|
8
|
+
* - Omit "یک" (one) before thousand
|
|
9
|
+
* - Pre-composed hundreds (دویست, سيصد, etc.)
|
|
9
10
|
*/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
11
|
+
|
|
12
|
+
import { parseNumericValue } from '../utils/parse-numeric.js'
|
|
13
|
+
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// Vocabulary
|
|
16
|
+
// ============================================================================
|
|
17
|
+
|
|
18
|
+
const ONES = { 1: 'یک', 2: 'دو', 3: 'سه', 4: 'چهار', 5: 'پنج', 6: 'شش', 7: 'هفت', 8: 'هشت', 9: 'نه' }
|
|
19
|
+
const TEENS = { 10: 'ده', 11: 'یازده', 12: 'دوازده', 13: 'سیزده', 14: 'چهارده', 15: 'پانزده', 16: 'شانزده', 17: 'هفده', 18: 'هجده', 19: 'نوزده' }
|
|
20
|
+
const TENS = { 20: 'بیست', 30: 'سی', 40: 'چهل', 50: 'پنجاه', 60: 'شصت', 70: 'هفتاد', 80: 'هشتاد', 90: 'نود' }
|
|
21
|
+
const HUNDREDS = { 100: 'صد', 200: 'دویست', 300: 'سيصد', 400: 'چهار صد', 500: 'پانصد', 600: 'ششصد', 700: 'هفتصد', 800: 'هشتصد', 900: 'نهصد' }
|
|
22
|
+
|
|
23
|
+
const THOUSAND = 'هزار'
|
|
24
|
+
const MILLION = 'میلیون'
|
|
25
|
+
|
|
26
|
+
const ZERO = 'صفر'
|
|
27
|
+
const NEGATIVE = 'منفى'
|
|
28
|
+
const DECIMAL_SEP = 'ممیّز'
|
|
29
|
+
|
|
30
|
+
// ============================================================================
|
|
31
|
+
// Conversion Functions
|
|
32
|
+
// ============================================================================
|
|
33
|
+
|
|
34
|
+
function integerToWords (n) {
|
|
35
|
+
if (n === 0n) return ZERO
|
|
36
|
+
|
|
37
|
+
// 1-9
|
|
38
|
+
if (n <= 9n) {
|
|
39
|
+
return ONES[Number(n)]
|
|
29
40
|
}
|
|
30
41
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
*/
|
|
35
|
-
teensWords = {
|
|
36
|
-
10: 'ده',
|
|
37
|
-
11: 'یازده',
|
|
38
|
-
12: 'دوازده',
|
|
39
|
-
13: 'سیزده',
|
|
40
|
-
14: 'چهارده',
|
|
41
|
-
15: 'پانزده',
|
|
42
|
-
16: 'شانزده',
|
|
43
|
-
17: 'هفده',
|
|
44
|
-
18: 'هجده',
|
|
45
|
-
19: 'نوزده'
|
|
42
|
+
// 10-19
|
|
43
|
+
if (n <= 19n) {
|
|
44
|
+
return TEENS[Number(n)]
|
|
46
45
|
}
|
|
47
46
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
50: 'پنجاه',
|
|
57
|
-
60: 'شصت',
|
|
58
|
-
70: 'هفتاد',
|
|
59
|
-
80: 'هشتاد',
|
|
60
|
-
90: 'نود'
|
|
47
|
+
// 20-99
|
|
48
|
+
if (n < 100n) {
|
|
49
|
+
const ones = n % 10n
|
|
50
|
+
const tens = n - ones
|
|
51
|
+
if (ones === 0n) {
|
|
52
|
+
return TENS[Number(tens)]
|
|
53
|
+
}
|
|
54
|
+
return `${TENS[Number(tens)]} و ${ONES[Number(ones)]}`
|
|
61
55
|
}
|
|
62
56
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
400: 'چهار صد',
|
|
72
|
-
500: 'پانصد',
|
|
73
|
-
600: 'ششصد',
|
|
74
|
-
700: 'هفتصد',
|
|
75
|
-
800: 'هشتصد',
|
|
76
|
-
900: 'نهصد'
|
|
57
|
+
// 100-999
|
|
58
|
+
if (n < 1000n) {
|
|
59
|
+
const hundreds = 100n * (n / 100n)
|
|
60
|
+
const remainder = n - hundreds
|
|
61
|
+
if (remainder === 0n) {
|
|
62
|
+
return HUNDREDS[Number(hundreds)]
|
|
63
|
+
}
|
|
64
|
+
return `${HUNDREDS[Number(hundreds)]} و ${integerToWords(remainder)}`
|
|
77
65
|
}
|
|
78
66
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
67
|
+
// 1000-999999
|
|
68
|
+
if (n < 1_000_000n) {
|
|
69
|
+
const thousandMultiplier = n / 1000n
|
|
70
|
+
// Persian omits "one" before thousand: 1000 is just "هزار", not "یک هزار"
|
|
71
|
+
const thousandPrefix = thousandMultiplier === 1n
|
|
72
|
+
? ''
|
|
73
|
+
: integerToWords(thousandMultiplier) + ' '
|
|
74
|
+
const remainder = n % 1000n
|
|
75
|
+
const suffix = remainder === 0n ? '' : ' ' + integerToWords(remainder)
|
|
76
|
+
return `${thousandPrefix}${THOUSAND}${suffix}`
|
|
86
77
|
}
|
|
87
78
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
79
|
+
// 1000000+
|
|
80
|
+
const millionMultiplier = n / 1_000_000n
|
|
81
|
+
const millionPrefix = integerToWords(millionMultiplier) + ' ' + MILLION
|
|
82
|
+
const remainder = n % 1_000_000n
|
|
83
|
+
const suffix = remainder === 0n ? '' : ' و ' + integerToWords(remainder)
|
|
84
|
+
return `${millionPrefix}${suffix}`
|
|
85
|
+
}
|
|
94
86
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
87
|
+
function decimalPartToWords (decimalPart) {
|
|
88
|
+
let result = ''
|
|
89
|
+
let i = 0
|
|
99
90
|
|
|
100
|
-
|
|
101
|
-
if (
|
|
102
|
-
|
|
103
|
-
|
|
91
|
+
while (i < decimalPart.length && decimalPart[i] === '0') {
|
|
92
|
+
if (result) result += ' '
|
|
93
|
+
result += ZERO
|
|
94
|
+
i++
|
|
95
|
+
}
|
|
104
96
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return this.tensWords[tens]
|
|
111
|
-
}
|
|
112
|
-
return `${this.tensWords[tens]} و ${this.onesWords[ones]}`
|
|
113
|
-
}
|
|
97
|
+
const remainder = decimalPart.slice(i)
|
|
98
|
+
if (remainder) {
|
|
99
|
+
if (result) result += ' '
|
|
100
|
+
result += integerToWords(BigInt(remainder))
|
|
101
|
+
}
|
|
114
102
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const hundreds = 100n * (integerPart / 100n)
|
|
118
|
-
const remainder = integerPart - hundreds
|
|
119
|
-
if (remainder === 0n) {
|
|
120
|
-
return this.hundredsWords[hundreds]
|
|
121
|
-
}
|
|
122
|
-
return `${this.hundredsWords[hundreds]} و ${this.integerToWords(remainder)}`
|
|
123
|
-
}
|
|
103
|
+
return result
|
|
104
|
+
}
|
|
124
105
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
106
|
+
/**
|
|
107
|
+
* Converts a numeric value to Persian words.
|
|
108
|
+
*
|
|
109
|
+
* @param {number | string | bigint} value - The numeric value to convert
|
|
110
|
+
* @returns {string} The number in Persian words
|
|
111
|
+
*/
|
|
112
|
+
function toWords (value) {
|
|
113
|
+
const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
|
|
114
|
+
|
|
115
|
+
let result = ''
|
|
116
|
+
|
|
117
|
+
if (isNegative) {
|
|
118
|
+
result = NEGATIVE + ' '
|
|
119
|
+
}
|
|
136
120
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const suffix = remainder === 0n ? '' : ' و ' + this.integerToWords(remainder)
|
|
142
|
-
return `${millionPrefix}${suffix}`
|
|
121
|
+
result += integerToWords(integerPart)
|
|
122
|
+
|
|
123
|
+
if (decimalPart) {
|
|
124
|
+
result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)
|
|
143
125
|
}
|
|
126
|
+
|
|
127
|
+
return result
|
|
144
128
|
}
|
|
129
|
+
|
|
130
|
+
// ============================================================================
|
|
131
|
+
// Exports
|
|
132
|
+
// ============================================================================
|
|
133
|
+
|
|
134
|
+
export { toWords }
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a numeric value to Finnish words.
|
|
3
|
+
*
|
|
4
|
+
* @param {number | string | bigint} value - The numeric value to convert
|
|
5
|
+
* @returns {string} The number in Finnish words
|
|
6
|
+
* @throws {TypeError} If value is not a valid numeric type
|
|
7
|
+
* @throws {Error} If value is not a valid number format
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* toWords(21) // 'kaksikymmentäyksi'
|
|
11
|
+
* toWords(1000) // 'tuhat'
|
|
12
|
+
* toWords('3.14') // 'kolme pilkku yksi neljä'
|
|
13
|
+
*/
|
|
14
|
+
export function toWords(value: number | string | bigint): string;
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finnish language converter - Functional Implementation
|
|
3
|
+
*
|
|
4
|
+
* A performance-optimized number-to-words converter using precomputed lookup tables.
|
|
5
|
+
*
|
|
6
|
+
* Key features:
|
|
7
|
+
* - Compound tens+ones without spaces: "kaksikymmentäyksi" (21)
|
|
8
|
+
* - Teens with "-toista" suffix
|
|
9
|
+
* - Omit "yksi" before sata/tuhat but keep before miljoona+
|
|
10
|
+
* - Long scale: miljoona, miljardi, biljoona
|
|
11
|
+
* - Per-digit decimal reading
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { parseNumericValue } from '../utils/parse-numeric.js'
|
|
15
|
+
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Vocabulary (module-level constants)
|
|
18
|
+
// ============================================================================
|
|
19
|
+
|
|
20
|
+
const ONES = ['', 'yksi', 'kaksi', 'kolme', 'neljä', 'viisi', 'kuusi', 'seitsemän', 'kahdeksan', 'yhdeksän']
|
|
21
|
+
|
|
22
|
+
const TEENS = ['kymmenen', 'yksitoista', 'kaksitoista', 'kolmetoista', 'neljätoista', 'viisitoista', 'kuusitoista', 'seitsemäntoista', 'kahdeksantoista', 'yhdeksäntoista']
|
|
23
|
+
|
|
24
|
+
// Tens use "kymmentä" suffix
|
|
25
|
+
const TENS = ['', '', 'kaksikymmentä', 'kolmekymmentä', 'neljäkymmentä', 'viisikymmentä', 'kuusikymmentä', 'seitsemänkymmentä', 'kahdeksankymmentä', 'yhdeksänkymmentä']
|
|
26
|
+
|
|
27
|
+
const HUNDRED = 'sata'
|
|
28
|
+
const THOUSAND = 'tuhat'
|
|
29
|
+
|
|
30
|
+
const ZERO = 'nolla'
|
|
31
|
+
const NEGATIVE = 'miinus'
|
|
32
|
+
const DECIMAL_SEP = 'pilkku'
|
|
33
|
+
|
|
34
|
+
// Long scale
|
|
35
|
+
const SCALES = ['miljoona', 'miljardi', 'biljoona', 'triljoona']
|
|
36
|
+
|
|
37
|
+
// ============================================================================
|
|
38
|
+
// Precomputed Lookup Tables (built once at module load)
|
|
39
|
+
// ============================================================================
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Builds segment word for 0-999.
|
|
43
|
+
* Omits "yksi" before "sata" (hundred).
|
|
44
|
+
*/
|
|
45
|
+
function buildSegment (n) {
|
|
46
|
+
if (n === 0) return ''
|
|
47
|
+
|
|
48
|
+
const ones = n % 10
|
|
49
|
+
const tens = Math.floor(n / 10) % 10
|
|
50
|
+
const hundreds = Math.floor(n / 100)
|
|
51
|
+
|
|
52
|
+
const parts = []
|
|
53
|
+
|
|
54
|
+
// Hundreds - omit "yksi" before sata
|
|
55
|
+
if (hundreds > 0) {
|
|
56
|
+
if (hundreds === 1) {
|
|
57
|
+
parts.push(HUNDRED)
|
|
58
|
+
} else {
|
|
59
|
+
parts.push(ONES[hundreds] + ' ' + HUNDRED)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Tens and ones
|
|
64
|
+
const tensOnes = n % 100
|
|
65
|
+
|
|
66
|
+
if (tensOnes === 0) {
|
|
67
|
+
// Just hundreds
|
|
68
|
+
} else if (tensOnes < 10) {
|
|
69
|
+
parts.push(ONES[ones])
|
|
70
|
+
} else if (tensOnes < 20) {
|
|
71
|
+
parts.push(TEENS[ones])
|
|
72
|
+
} else if (ones === 0) {
|
|
73
|
+
parts.push(TENS[tens])
|
|
74
|
+
} else {
|
|
75
|
+
// Compound: "kaksikymmentäyksi" (no space)
|
|
76
|
+
parts.push(TENS[tens] + ONES[ones])
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return parts.join(' ')
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Precompute all 1000 segment words (0-999)
|
|
83
|
+
const SEGMENTS = new Array(1000)
|
|
84
|
+
|
|
85
|
+
for (let i = 0; i < 1000; i++) {
|
|
86
|
+
SEGMENTS[i] = buildSegment(i)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ============================================================================
|
|
90
|
+
// Conversion Functions
|
|
91
|
+
// ============================================================================
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Converts a non-negative integer to Finnish words.
|
|
95
|
+
*
|
|
96
|
+
* @param {bigint} n - Non-negative integer to convert
|
|
97
|
+
* @returns {string} Finnish words
|
|
98
|
+
*/
|
|
99
|
+
function integerToWords (n) {
|
|
100
|
+
if (n === 0n) return ZERO
|
|
101
|
+
|
|
102
|
+
// Fast path: numbers < 1000 (direct lookup)
|
|
103
|
+
if (n < 1000n) {
|
|
104
|
+
return SEGMENTS[Number(n)]
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Fast path: numbers < 1,000,000 (thousands)
|
|
108
|
+
if (n < 1_000_000n) {
|
|
109
|
+
const thousands = Number(n / 1000n)
|
|
110
|
+
const remainder = Number(n % 1000n)
|
|
111
|
+
|
|
112
|
+
// Omit "yksi" before tuhat
|
|
113
|
+
let result
|
|
114
|
+
if (thousands === 1) {
|
|
115
|
+
result = THOUSAND
|
|
116
|
+
} else {
|
|
117
|
+
result = SEGMENTS[thousands] + ' ' + THOUSAND
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (remainder > 0) {
|
|
121
|
+
result += ' ' + SEGMENTS[remainder]
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return result
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// For numbers >= 1,000,000, use scale decomposition
|
|
128
|
+
return buildLargeNumberWords(n)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Builds words for numbers >= 1,000,000.
|
|
133
|
+
*
|
|
134
|
+
* @param {bigint} n - Number >= 1,000,000
|
|
135
|
+
* @returns {string} Finnish words
|
|
136
|
+
*/
|
|
137
|
+
function buildLargeNumberWords (n) {
|
|
138
|
+
const numStr = n.toString()
|
|
139
|
+
const len = numStr.length
|
|
140
|
+
|
|
141
|
+
// Build segments of 3 digits from right to left
|
|
142
|
+
const segments = []
|
|
143
|
+
const segmentSize = 3
|
|
144
|
+
|
|
145
|
+
const remainderLen = len % segmentSize
|
|
146
|
+
let pos = 0
|
|
147
|
+
if (remainderLen > 0) {
|
|
148
|
+
segments.push(Number(numStr.slice(0, remainderLen)))
|
|
149
|
+
pos = remainderLen
|
|
150
|
+
}
|
|
151
|
+
while (pos < len) {
|
|
152
|
+
segments.push(Number(numStr.slice(pos, pos + segmentSize)))
|
|
153
|
+
pos += segmentSize
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Convert segments to words
|
|
157
|
+
const parts = []
|
|
158
|
+
let scaleIndex = segments.length - 1
|
|
159
|
+
|
|
160
|
+
for (let i = 0; i < segments.length; i++) {
|
|
161
|
+
const segment = segments[i]
|
|
162
|
+
|
|
163
|
+
if (segment !== 0) {
|
|
164
|
+
const segmentWord = SEGMENTS[segment]
|
|
165
|
+
|
|
166
|
+
if (scaleIndex === 0) {
|
|
167
|
+
// Units segment
|
|
168
|
+
parts.push(segmentWord)
|
|
169
|
+
} else if (scaleIndex === 1) {
|
|
170
|
+
// Thousands - omit "yksi" before tuhat
|
|
171
|
+
if (segment === 1) {
|
|
172
|
+
parts.push(THOUSAND)
|
|
173
|
+
} else {
|
|
174
|
+
parts.push(segmentWord + ' ' + THOUSAND)
|
|
175
|
+
}
|
|
176
|
+
} else {
|
|
177
|
+
// Millions+ - keep "yksi" before scale words
|
|
178
|
+
const scaleWord = SCALES[scaleIndex - 2]
|
|
179
|
+
parts.push(segmentWord + ' ' + scaleWord)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
scaleIndex--
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return parts.join(' ')
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Converts decimal digits to Finnish words (per-digit).
|
|
191
|
+
*
|
|
192
|
+
* @param {string} decimalPart - Decimal digits (without the point)
|
|
193
|
+
* @returns {string} Finnish words for decimal part
|
|
194
|
+
*/
|
|
195
|
+
function decimalPartToWords (decimalPart) {
|
|
196
|
+
const parts = []
|
|
197
|
+
|
|
198
|
+
for (const digit of decimalPart) {
|
|
199
|
+
const d = parseInt(digit, 10)
|
|
200
|
+
if (d === 0) {
|
|
201
|
+
parts.push(ZERO)
|
|
202
|
+
} else {
|
|
203
|
+
parts.push(ONES[d])
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return parts.join(' ')
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Converts a numeric value to Finnish words.
|
|
212
|
+
*
|
|
213
|
+
* @param {number | string | bigint} value - The numeric value to convert
|
|
214
|
+
* @returns {string} The number in Finnish words
|
|
215
|
+
* @throws {TypeError} If value is not a valid numeric type
|
|
216
|
+
* @throws {Error} If value is not a valid number format
|
|
217
|
+
*
|
|
218
|
+
* @example
|
|
219
|
+
* toWords(21) // 'kaksikymmentäyksi'
|
|
220
|
+
* toWords(1000) // 'tuhat'
|
|
221
|
+
* toWords('3.14') // 'kolme pilkku yksi neljä'
|
|
222
|
+
*/
|
|
223
|
+
function toWords (value) {
|
|
224
|
+
const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
|
|
225
|
+
|
|
226
|
+
let result = ''
|
|
227
|
+
|
|
228
|
+
if (isNegative) {
|
|
229
|
+
result = NEGATIVE + ' '
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
result += integerToWords(integerPart)
|
|
233
|
+
|
|
234
|
+
if (decimalPart) {
|
|
235
|
+
result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return result
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ============================================================================
|
|
242
|
+
// Public API
|
|
243
|
+
// ============================================================================
|
|
244
|
+
|
|
245
|
+
export { toWords }
|
package/lib/languages/fil.d.ts
CHANGED
|
@@ -1,16 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Filipino
|
|
2
|
+
* Converts a numeric value to Filipino words.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* - Implicit "one" omission
|
|
7
|
-
* - Special linkers for certain tens (e.g., "limampung")
|
|
4
|
+
* @param {number | string | bigint} value - The numeric value to convert
|
|
5
|
+
* @returns {string} The number in Filipino words
|
|
8
6
|
*/
|
|
9
|
-
export
|
|
10
|
-
scaleWords: (string | bigint)[][];
|
|
11
|
-
/** Converts integer part with explicit zero handling. */
|
|
12
|
-
integerToWords(integerPart: any): string;
|
|
13
|
-
/** Combines two word-sets with Filipino connector and linker rules. */
|
|
14
|
-
combineWordSets(preceding: any, following: any): any;
|
|
15
|
-
}
|
|
16
|
-
import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js';
|
|
7
|
+
export function toWords(value: number | string | bigint): string;
|