n2words 1.24.0 → 2.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/README.md +285 -156
- package/dist/ArabicConverter.js +3 -0
- package/dist/ArabicConverter.js.map +1 -0
- package/dist/AzerbaijaniConverter.js +3 -0
- package/dist/AzerbaijaniConverter.js.map +1 -0
- package/dist/BanglaConverter.js +3 -0
- package/dist/BanglaConverter.js.map +1 -0
- package/dist/BiblicalHebrewConverter.js +3 -0
- package/dist/BiblicalHebrewConverter.js.map +1 -0
- package/dist/CroatianConverter.js +3 -0
- package/dist/CroatianConverter.js.map +1 -0
- package/dist/CzechConverter.js +3 -0
- package/dist/CzechConverter.js.map +1 -0
- package/dist/DanishConverter.js +3 -0
- package/dist/DanishConverter.js.map +1 -0
- package/dist/DutchConverter.js +3 -0
- package/dist/DutchConverter.js.map +1 -0
- package/dist/EnglishConverter.js +3 -0
- package/dist/EnglishConverter.js.map +1 -0
- package/dist/FilipinoConverter.js +3 -0
- package/dist/FilipinoConverter.js.map +1 -0
- package/dist/FrenchBelgiumConverter.js +3 -0
- package/dist/FrenchBelgiumConverter.js.map +1 -0
- package/dist/FrenchConverter.js +3 -0
- package/dist/FrenchConverter.js.map +1 -0
- package/dist/GermanConverter.js +3 -0
- package/dist/GermanConverter.js.map +1 -0
- package/dist/GreekConverter.js +3 -0
- package/dist/GreekConverter.js.map +1 -0
- package/dist/GujaratiConverter.js +3 -0
- package/dist/GujaratiConverter.js.map +1 -0
- package/dist/HebrewConverter.js +3 -0
- package/dist/HebrewConverter.js.map +1 -0
- package/dist/HindiConverter.js +3 -0
- package/dist/HindiConverter.js.map +1 -0
- package/dist/HungarianConverter.js +3 -0
- package/dist/HungarianConverter.js.map +1 -0
- package/dist/IndonesianConverter.js +3 -0
- package/dist/IndonesianConverter.js.map +1 -0
- package/dist/ItalianConverter.js +3 -0
- package/dist/ItalianConverter.js.map +1 -0
- package/dist/JapaneseConverter.js +3 -0
- package/dist/JapaneseConverter.js.map +1 -0
- package/dist/KannadaConverter.js +3 -0
- package/dist/KannadaConverter.js.map +1 -0
- package/dist/KoreanConverter.js +3 -0
- package/dist/KoreanConverter.js.map +1 -0
- package/dist/LatvianConverter.js +3 -0
- package/dist/LatvianConverter.js.map +1 -0
- package/dist/LithuanianConverter.js +3 -0
- package/dist/LithuanianConverter.js.map +1 -0
- package/dist/MalayConverter.js +3 -0
- package/dist/MalayConverter.js.map +1 -0
- package/dist/MarathiConverter.js +3 -0
- package/dist/MarathiConverter.js.map +1 -0
- package/dist/NorwegianBokmalConverter.js +3 -0
- package/dist/NorwegianBokmalConverter.js.map +1 -0
- package/dist/PersianConverter.js +3 -0
- package/dist/PersianConverter.js.map +1 -0
- package/dist/PolishConverter.js +3 -0
- package/dist/PolishConverter.js.map +1 -0
- package/dist/PortugueseConverter.js +3 -0
- package/dist/PortugueseConverter.js.map +1 -0
- package/dist/PunjabiConverter.js +3 -0
- package/dist/PunjabiConverter.js.map +1 -0
- package/dist/RomanianConverter.js +3 -0
- package/dist/RomanianConverter.js.map +1 -0
- package/dist/RussianConverter.js +3 -0
- package/dist/RussianConverter.js.map +1 -0
- package/dist/SerbianCyrillicConverter.js +3 -0
- package/dist/SerbianCyrillicConverter.js.map +1 -0
- package/dist/SerbianLatinConverter.js +3 -0
- package/dist/SerbianLatinConverter.js.map +1 -0
- package/dist/SimplifiedChineseConverter.js +3 -0
- package/dist/SimplifiedChineseConverter.js.map +1 -0
- package/dist/SpanishConverter.js +3 -0
- package/dist/SpanishConverter.js.map +1 -0
- package/dist/SwahiliConverter.js +3 -0
- package/dist/SwahiliConverter.js.map +1 -0
- package/dist/SwedishConverter.js +3 -0
- package/dist/SwedishConverter.js.map +1 -0
- package/dist/TamilConverter.js +3 -0
- package/dist/TamilConverter.js.map +1 -0
- package/dist/TeluguConverter.js +3 -0
- package/dist/TeluguConverter.js.map +1 -0
- package/dist/ThaiConverter.js +3 -0
- package/dist/ThaiConverter.js.map +1 -0
- package/dist/TraditionalChineseConverter.js +3 -0
- package/dist/TraditionalChineseConverter.js.map +1 -0
- package/dist/TurkishConverter.js +3 -0
- package/dist/TurkishConverter.js.map +1 -0
- package/dist/UkrainianConverter.js +3 -0
- package/dist/UkrainianConverter.js.map +1 -0
- package/dist/UrduConverter.js +3 -0
- package/dist/UrduConverter.js.map +1 -0
- package/dist/VietnameseConverter.js +3 -0
- package/dist/VietnameseConverter.js.map +1 -0
- package/dist/n2words.js +3 -2
- package/dist/n2words.js.map +1 -1
- package/lib/classes/abstract-language.d.ts +178 -0
- package/lib/classes/abstract-language.js +192 -185
- package/lib/classes/greedy-scale-language.d.ts +109 -0
- package/lib/classes/greedy-scale-language.js +96 -90
- package/lib/classes/slavic-language.d.ts +148 -0
- package/lib/classes/slavic-language.js +136 -106
- package/lib/classes/south-asian-language.d.ts +70 -0
- package/lib/classes/south-asian-language.js +58 -65
- package/lib/classes/turkic-language.d.ts +26 -0
- package/lib/classes/turkic-language.js +22 -26
- package/lib/languages/ar.d.ts +30 -0
- package/lib/languages/ar.js +49 -133
- package/lib/languages/az.d.ts +12 -0
- package/lib/languages/az.js +7 -23
- package/lib/languages/bn.d.ts +11 -0
- package/lib/languages/bn.js +12 -7
- package/lib/languages/cs.d.ts +88 -0
- package/lib/languages/cs.js +44 -113
- package/lib/languages/da.d.ts +15 -0
- package/lib/languages/da.js +40 -87
- package/lib/languages/de.d.ts +14 -0
- package/lib/languages/de.js +34 -68
- package/lib/languages/el.d.ts +14 -0
- package/lib/languages/el.js +22 -48
- package/lib/languages/en.d.ts +16 -0
- package/lib/languages/en.js +22 -59
- package/lib/languages/es.d.ts +15 -0
- package/lib/languages/es.js +49 -81
- package/lib/languages/fa.d.ts +47 -0
- package/lib/languages/fa.js +90 -73
- package/lib/languages/fil.d.ts +16 -0
- package/lib/languages/fil.js +35 -76
- package/lib/languages/fr-BE.d.ts +11 -0
- package/lib/languages/fr-BE.js +15 -51
- package/lib/languages/fr.d.ts +15 -0
- package/lib/languages/fr.js +33 -72
- package/lib/languages/gu.d.ts +11 -0
- package/lib/languages/gu.js +10 -34
- package/lib/languages/hbo.d.ts +113 -0
- package/lib/languages/hbo.js +251 -0
- package/lib/languages/he.d.ts +80 -0
- package/lib/languages/he.js +41 -164
- package/lib/languages/hi.d.ts +11 -0
- package/lib/languages/hi.js +12 -7
- package/lib/languages/hr.d.ts +80 -0
- package/lib/languages/hr.js +51 -95
- package/lib/languages/hu.d.ts +22 -0
- package/lib/languages/hu.js +35 -53
- package/lib/languages/id.d.ts +37 -0
- package/lib/languages/id.js +29 -44
- package/lib/languages/it.d.ts +37 -0
- package/lib/languages/it.js +36 -52
- package/lib/languages/ja.d.ts +17 -0
- package/lib/languages/ja.js +22 -75
- package/lib/languages/kn.d.ts +11 -0
- package/lib/languages/kn.js +10 -39
- package/lib/languages/ko.d.ts +14 -0
- package/lib/languages/ko.js +17 -45
- package/lib/languages/lt.d.ts +70 -0
- package/lib/languages/lt.js +28 -63
- package/lib/languages/lv.d.ts +70 -0
- package/lib/languages/lv.js +35 -58
- package/lib/languages/mr.d.ts +11 -0
- package/lib/languages/mr.js +10 -34
- package/lib/languages/ms.d.ts +31 -0
- package/lib/languages/ms.js +24 -20
- package/lib/languages/nb.d.ts +12 -0
- package/lib/languages/nb.js +36 -56
- package/lib/languages/nl.d.ts +16 -0
- package/lib/languages/nl.js +58 -109
- package/lib/languages/pa.d.ts +11 -0
- package/lib/languages/{pa-Guru.js → pa.js} +12 -7
- package/lib/languages/pl.d.ts +80 -0
- package/lib/languages/pl.js +26 -105
- package/lib/languages/pt.d.ts +29 -0
- package/lib/languages/pt.js +29 -64
- package/lib/languages/ro.d.ts +158 -0
- package/lib/languages/ro.js +60 -167
- package/lib/languages/ru.d.ts +85 -0
- package/lib/languages/ru.js +17 -37
- package/lib/languages/sr-Cyrl.d.ts +80 -0
- package/lib/languages/sr-Cyrl.js +113 -0
- package/lib/languages/sr-Latn.d.ts +80 -0
- package/lib/languages/sr-Latn.js +54 -98
- package/lib/languages/sv.d.ts +14 -0
- package/lib/languages/sv.js +26 -63
- package/lib/languages/sw.d.ts +39 -0
- package/lib/languages/sw.js +26 -21
- package/lib/languages/ta.d.ts +20 -0
- package/lib/languages/ta.js +26 -26
- package/lib/languages/te.d.ts +22 -0
- package/lib/languages/te.js +28 -38
- package/lib/languages/th.d.ts +17 -0
- package/lib/languages/th.js +25 -31
- package/lib/languages/tr.d.ts +12 -0
- package/lib/languages/tr.js +11 -38
- package/lib/languages/uk.d.ts +85 -0
- package/lib/languages/uk.js +18 -44
- package/lib/languages/ur.d.ts +11 -0
- package/lib/languages/ur.js +12 -7
- package/lib/languages/vi.d.ts +72 -0
- package/lib/languages/vi.js +25 -71
- package/lib/languages/zh-Hans.d.ts +21 -0
- package/lib/languages/zh-Hans.js +33 -87
- package/lib/languages/zh-Hant.d.ts +21 -0
- package/lib/languages/zh-Hant.js +111 -0
- package/lib/n2words.d.ts +209 -0
- package/lib/n2words.js +474 -191
- package/package.json +106 -67
- package/dist/languages/ar.js +0 -2
- package/dist/languages/ar.js.map +0 -1
- package/dist/languages/az.js +0 -2
- package/dist/languages/az.js.map +0 -1
- package/dist/languages/bn.js +0 -2
- package/dist/languages/bn.js.map +0 -1
- package/dist/languages/cs.js +0 -2
- package/dist/languages/cs.js.map +0 -1
- package/dist/languages/da.js +0 -2
- package/dist/languages/da.js.map +0 -1
- package/dist/languages/de.js +0 -2
- package/dist/languages/de.js.map +0 -1
- package/dist/languages/el.js +0 -2
- package/dist/languages/el.js.map +0 -1
- package/dist/languages/en.js +0 -2
- package/dist/languages/en.js.map +0 -1
- package/dist/languages/es.js +0 -2
- package/dist/languages/es.js.map +0 -1
- package/dist/languages/fa.js +0 -2
- package/dist/languages/fa.js.map +0 -1
- package/dist/languages/fil.js +0 -2
- package/dist/languages/fil.js.map +0 -1
- package/dist/languages/fr-BE.js +0 -2
- package/dist/languages/fr-BE.js.map +0 -1
- package/dist/languages/fr.js +0 -2
- package/dist/languages/fr.js.map +0 -1
- package/dist/languages/gu.js +0 -2
- package/dist/languages/gu.js.map +0 -1
- package/dist/languages/he.js +0 -2
- package/dist/languages/he.js.map +0 -1
- package/dist/languages/hi.js +0 -2
- package/dist/languages/hi.js.map +0 -1
- package/dist/languages/hr.js +0 -2
- package/dist/languages/hr.js.map +0 -1
- package/dist/languages/hu.js +0 -2
- package/dist/languages/hu.js.map +0 -1
- package/dist/languages/id.js +0 -2
- package/dist/languages/id.js.map +0 -1
- package/dist/languages/it.js +0 -2
- package/dist/languages/it.js.map +0 -1
- package/dist/languages/ja.js +0 -2
- package/dist/languages/ja.js.map +0 -1
- package/dist/languages/kn.js +0 -2
- package/dist/languages/kn.js.map +0 -1
- package/dist/languages/ko.js +0 -2
- package/dist/languages/ko.js.map +0 -1
- package/dist/languages/lt.js +0 -2
- package/dist/languages/lt.js.map +0 -1
- package/dist/languages/lv.js +0 -2
- package/dist/languages/lv.js.map +0 -1
- package/dist/languages/mr.js +0 -2
- package/dist/languages/mr.js.map +0 -1
- package/dist/languages/ms.js +0 -2
- package/dist/languages/ms.js.map +0 -1
- package/dist/languages/nb.js +0 -2
- package/dist/languages/nb.js.map +0 -1
- package/dist/languages/nl.js +0 -2
- package/dist/languages/nl.js.map +0 -1
- package/dist/languages/pa-Guru.js +0 -2
- package/dist/languages/pa-Guru.js.map +0 -1
- package/dist/languages/pl.js +0 -2
- package/dist/languages/pl.js.map +0 -1
- package/dist/languages/pt.js +0 -2
- package/dist/languages/pt.js.map +0 -1
- package/dist/languages/ro.js +0 -2
- package/dist/languages/ro.js.map +0 -1
- package/dist/languages/ru.js +0 -2
- package/dist/languages/ru.js.map +0 -1
- package/dist/languages/sr-Latn.js +0 -2
- package/dist/languages/sr-Latn.js.map +0 -1
- package/dist/languages/sv.js +0 -2
- package/dist/languages/sv.js.map +0 -1
- package/dist/languages/sw.js +0 -2
- package/dist/languages/sw.js.map +0 -1
- package/dist/languages/ta.js +0 -2
- package/dist/languages/ta.js.map +0 -1
- package/dist/languages/te.js +0 -2
- package/dist/languages/te.js.map +0 -1
- package/dist/languages/th.js +0 -2
- package/dist/languages/th.js.map +0 -1
- package/dist/languages/tr.js +0 -2
- package/dist/languages/tr.js.map +0 -1
- package/dist/languages/uk.js +0 -2
- package/dist/languages/uk.js.map +0 -1
- package/dist/languages/ur.js +0 -2
- package/dist/languages/ur.js.map +0 -1
- package/dist/languages/vi.js +0 -2
- package/dist/languages/vi.js.map +0 -1
- package/dist/languages/zh-Hans.js +0 -2
- package/dist/languages/zh-Hans.js.map +0 -1
- 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
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Greedy scale language converter implementing the "highest-matching scale word" algorithm.
|
|
3
|
+
*
|
|
4
|
+
* Responsibilities:
|
|
5
|
+
* - Decompose an integer into a sequence of word-sets using greedy matching.
|
|
6
|
+
* - Provide helpers to reduce and post-process matched word-sets.
|
|
7
|
+
* - Inherits decimal handling from AbstractLanguage (supports grouped and per-digit
|
|
8
|
+
* modes via the `usePerDigitDecimals` class property).
|
|
9
|
+
*
|
|
10
|
+
* Subclass requirements:
|
|
11
|
+
* - Define `scaleWords` (ordered descending) as `[bigint, string]` tuples.
|
|
12
|
+
* - Implement 'combineWordSets(preceding, following)' to combine adjacent word-sets
|
|
13
|
+
* per language grammar.
|
|
14
|
+
*
|
|
15
|
+
* Scale words specification:
|
|
16
|
+
* - `scaleWords` is an Array of 2-tuples: `[bigint, string]` where the first element
|
|
17
|
+
* is the numeric scale value and the second is the word for that value.
|
|
18
|
+
* - Scale words MUST be ordered from largest to smallest (descending) for the algorithm
|
|
19
|
+
* to function correctly.
|
|
20
|
+
*
|
|
21
|
+
* Terminology:
|
|
22
|
+
* - **Scale**: A magnitude value (100n, 1000n, 1000000n)
|
|
23
|
+
* - **Word-set**: An object `{ word: bigint }` representing a partial result
|
|
24
|
+
* - **Scale word**: The word for a scale value ("hundred", "thousand")
|
|
25
|
+
*
|
|
26
|
+
* @abstract
|
|
27
|
+
* @extends AbstractLanguage
|
|
28
|
+
*/
|
|
29
|
+
export class GreedyScaleLanguage extends AbstractLanguage {
|
|
30
|
+
/**
|
|
31
|
+
* Array of scale words mapping numeric values to their word representations.
|
|
32
|
+
*
|
|
33
|
+
* Each element is a 2-tuple: `[bigint, string]` where the first element is the
|
|
34
|
+
* numeric scale value and the second is the word for that value. The array MUST be
|
|
35
|
+
* ordered from largest to smallest (descending) for the greedy algorithm to work correctly.
|
|
36
|
+
*
|
|
37
|
+
* @type {Array<[bigint, string]>}
|
|
38
|
+
* @example
|
|
39
|
+
* // English scale words (descending order):
|
|
40
|
+
* // [[1000000000n, 'billion'], [1000000n, 'million'], [1000n, 'thousand'], [100n, 'hundred'], ..., [1n, 'one']]
|
|
41
|
+
*/
|
|
42
|
+
scaleWords: Array<[bigint, string]>;
|
|
43
|
+
/**
|
|
44
|
+
* Returns the word for an exact scale value.
|
|
45
|
+
*
|
|
46
|
+
* @param {bigint} scale The scale value to look up (prefer BigInt for exact matching).
|
|
47
|
+
* @returns {string|undefined} The word for the provided scale, or `undefined`.
|
|
48
|
+
*/
|
|
49
|
+
wordForScale(scale: bigint): string | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* Decomposes an integer into a sequence of word-sets.
|
|
52
|
+
*
|
|
53
|
+
* This internal helper returns a nested structure that represents quantities and
|
|
54
|
+
* their matching scale words (e.g. `[{ 'one': 1n }, { 'hundred': 100n }, ...]`).
|
|
55
|
+
* The result is designed to be reduced by `reduceWordSets()` using language-specific `combineWordSets()`.
|
|
56
|
+
*
|
|
57
|
+
* For quantities > 1, the multiplier is recursively decomposed. For quantity = 1,
|
|
58
|
+
* the implicit "one" is represented with `{ 'one': 1n }` and typically omitted during combineWordSets().
|
|
59
|
+
*
|
|
60
|
+
* @protected
|
|
61
|
+
* @param {bigint} integerPart The integer to decompose.
|
|
62
|
+
* @returns {Array<Object|Array>} An array of word-set objects and possibly nested arrays.
|
|
63
|
+
*/
|
|
64
|
+
protected decomposeInteger(integerPart: bigint): Array<Object | any[]>;
|
|
65
|
+
/**
|
|
66
|
+
* Reduces a nested array of word-sets into a single word-set object.
|
|
67
|
+
*
|
|
68
|
+
* This method repeatedly applies the subclass `combineWordSets()` operation until a single
|
|
69
|
+
* object remains. It normalizes nested arrays by recursively reducing them.
|
|
70
|
+
*
|
|
71
|
+
* @protected
|
|
72
|
+
* @param {Array<Object|Array>} wordSets Array of word-set objects and nested arrays.
|
|
73
|
+
* @returns {Object} Reduced word-set where the single object key is the language string
|
|
74
|
+
* and its value is the numeric BigInt result for that string.
|
|
75
|
+
*/
|
|
76
|
+
protected reduceWordSets(wordSets: Array<Object | any[]>): Object;
|
|
77
|
+
/**
|
|
78
|
+
* Combines two adjacent word-sets into a single word-set.
|
|
79
|
+
*
|
|
80
|
+
* This is the core language-specific method that must be implemented by subclasses
|
|
81
|
+
* to define how adjacent word-sets are combined according to the language's grammar.
|
|
82
|
+
* For example, English combines "twenty" + "three" → "twenty-three", while
|
|
83
|
+
* French might combine "quatre-vingts" + "dix" → "quatre-vingt-dix".
|
|
84
|
+
*
|
|
85
|
+
* @abstract
|
|
86
|
+
* @protected
|
|
87
|
+
* @param {Object} preceding Preceding word-set as `{ word: bigint }`.
|
|
88
|
+
* @param {Object} following Following word-set as `{ word: bigint }`.
|
|
89
|
+
* @returns {Object} Combined word-set with merged text and resulting numeric value.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* // English implementation might handle:
|
|
93
|
+
* // combineWordSets({ 'twenty': 20n }, { 'three': 3n }) → { 'twenty-three': 23n }
|
|
94
|
+
* // combineWordSets({ 'one': 1n }, { 'hundred': 100n }) → { 'one hundred': 100n }
|
|
95
|
+
*/
|
|
96
|
+
protected combineWordSets(preceding: Object, following: Object): Object;
|
|
97
|
+
/**
|
|
98
|
+
* Final string post-processing hook.
|
|
99
|
+
*
|
|
100
|
+
* Subclasses may override to apply language-specific whitespace, punctuation or
|
|
101
|
+
* orthographic corrections.
|
|
102
|
+
*
|
|
103
|
+
* @protected
|
|
104
|
+
* @param {string} output Language string produced by the conversion flow.
|
|
105
|
+
* @returns {string} Final formatted string.
|
|
106
|
+
*/
|
|
107
|
+
protected finalizeWords(output: string): string;
|
|
108
|
+
}
|
|
109
|
+
import { AbstractLanguage } from './abstract-language.js';
|
|
@@ -1,144 +1,148 @@
|
|
|
1
|
-
import AbstractLanguage from './abstract-language.js'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @typedef {Object} WordSet
|
|
5
|
-
* @property {string} word - The language word or phrase
|
|
6
|
-
* @property {bigint} value - The numeric value represented by the word
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @typedef {Array.<Array.<(bigint|string)>>} ScaleWordPairs
|
|
11
|
-
* Array of scale word pairs in descending order. Each pair contains:
|
|
12
|
-
* - [0]: BigInt numeric value
|
|
13
|
-
* - [1]: String word representation
|
|
14
|
-
* Must be ordered largest to smallest for the greedy algorithm.
|
|
15
|
-
*/
|
|
1
|
+
import { AbstractLanguage } from './abstract-language.js'
|
|
16
2
|
|
|
17
3
|
/**
|
|
18
4
|
* Greedy scale language converter implementing the "highest-matching scale word" algorithm.
|
|
19
5
|
*
|
|
20
6
|
* Responsibilities:
|
|
21
|
-
* - Decompose
|
|
22
|
-
* - Provide helpers to
|
|
7
|
+
* - Decompose an integer into a sequence of word-sets using greedy matching.
|
|
8
|
+
* - Provide helpers to reduce and post-process matched word-sets.
|
|
23
9
|
* - Inherits decimal handling from AbstractLanguage (supports grouped and per-digit
|
|
24
|
-
* modes via the `
|
|
10
|
+
* modes via the `usePerDigitDecimals` class property).
|
|
25
11
|
*
|
|
26
12
|
* Subclass requirements:
|
|
27
|
-
* - Define `
|
|
28
|
-
* - Implement
|
|
13
|
+
* - Define `scaleWords` (ordered descending) as `[bigint, string]` tuples.
|
|
14
|
+
* - Implement 'combineWordSets(preceding, following)' to combine adjacent word-sets
|
|
29
15
|
* per language grammar.
|
|
30
16
|
*
|
|
31
17
|
* Scale words specification:
|
|
32
|
-
* - `
|
|
33
|
-
* is the numeric value
|
|
18
|
+
* - `scaleWords` is an Array of 2-tuples: `[bigint, string]` where the first element
|
|
19
|
+
* is the numeric scale value and the second is the word for that value.
|
|
34
20
|
* - Scale words MUST be ordered from largest to smallest (descending) for the algorithm
|
|
35
21
|
* to function correctly.
|
|
36
22
|
*
|
|
23
|
+
* Terminology:
|
|
24
|
+
* - **Scale**: A magnitude value (100n, 1000n, 1000000n)
|
|
25
|
+
* - **Word-set**: An object `{ word: bigint }` representing a partial result
|
|
26
|
+
* - **Scale word**: The word for a scale value ("hundred", "thousand")
|
|
27
|
+
*
|
|
37
28
|
* @abstract
|
|
38
29
|
* @extends AbstractLanguage
|
|
39
|
-
* @example
|
|
40
|
-
* // Example `scaleWordPairs` for English (descending order):
|
|
41
|
-
* // [[1000000000n, 'billion'], [1000000n, 'million'], [1000n, 'thousand'], [100n, 'hundred'], ..., [1n, 'one']]
|
|
42
30
|
*/
|
|
43
31
|
|
|
44
|
-
class GreedyScaleLanguage extends AbstractLanguage {
|
|
32
|
+
export class GreedyScaleLanguage extends AbstractLanguage {
|
|
33
|
+
// ============================================================================
|
|
34
|
+
// Required Properties (subclasses must define)
|
|
35
|
+
// ============================================================================
|
|
36
|
+
|
|
45
37
|
/**
|
|
46
|
-
* Array of scale
|
|
38
|
+
* Array of scale words mapping numeric values to their word representations.
|
|
47
39
|
*
|
|
48
|
-
* Each element is a 2-tuple: `[
|
|
49
|
-
* numeric value and the second is the word for that value. The array MUST be
|
|
40
|
+
* Each element is a 2-tuple: `[bigint, string]` where the first element is the
|
|
41
|
+
* numeric scale value and the second is the word for that value. The array MUST be
|
|
50
42
|
* ordered from largest to smallest (descending) for the greedy algorithm to work correctly.
|
|
51
43
|
*
|
|
52
|
-
* @type {Array
|
|
44
|
+
* @type {Array<[bigint, string]>}
|
|
53
45
|
* @example
|
|
54
46
|
* // English scale words (descending order):
|
|
55
47
|
* // [[1000000000n, 'billion'], [1000000n, 'million'], [1000n, 'thousand'], [100n, 'hundred'], ..., [1n, 'one']]
|
|
56
48
|
*/
|
|
57
|
-
|
|
49
|
+
scaleWords
|
|
50
|
+
|
|
51
|
+
// ============================================================================
|
|
52
|
+
// Public Methods
|
|
53
|
+
// ============================================================================
|
|
58
54
|
|
|
59
55
|
/**
|
|
60
|
-
*
|
|
56
|
+
* Returns the word for an exact scale value.
|
|
61
57
|
*
|
|
62
|
-
* @param {bigint
|
|
63
|
-
* @returns {string|undefined} The word for the provided scale
|
|
58
|
+
* @param {bigint} scale The scale value to look up (prefer BigInt for exact matching).
|
|
59
|
+
* @returns {string|undefined} The word for the provided scale, or `undefined`.
|
|
64
60
|
*/
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
return
|
|
61
|
+
wordForScale (scale) {
|
|
62
|
+
const match = this.scaleWords.find((pair) => pair[0] === scale)
|
|
63
|
+
return match?.[1]
|
|
68
64
|
}
|
|
69
65
|
|
|
66
|
+
// ============================================================================
|
|
67
|
+
// Protected Methods (subclasses may call or override)
|
|
68
|
+
// ============================================================================
|
|
69
|
+
|
|
70
70
|
/**
|
|
71
|
-
*
|
|
71
|
+
* Decomposes an integer into a sequence of word-sets.
|
|
72
72
|
*
|
|
73
73
|
* This internal helper returns a nested structure that represents quantities and
|
|
74
74
|
* their matching scale words (e.g. `[{ 'one': 1n }, { 'hundred': 100n }, ...]`).
|
|
75
|
-
* The result is designed to be reduced by `
|
|
75
|
+
* The result is designed to be reduced by `reduceWordSets()` using language-specific `combineWordSets()`.
|
|
76
76
|
*
|
|
77
77
|
* For quantities > 1, the multiplier is recursively decomposed. For quantity = 1,
|
|
78
|
-
* the implicit "one" is represented with `{ 'one': 1n }` and typically omitted during
|
|
78
|
+
* the implicit "one" is represented with `{ 'one': 1n }` and typically omitted during combineWordSets().
|
|
79
79
|
*
|
|
80
80
|
* @protected
|
|
81
|
-
* @param {bigint}
|
|
81
|
+
* @param {bigint} integerPart The integer to decompose.
|
|
82
82
|
* @returns {Array<Object|Array>} An array of word-set objects and possibly nested arrays.
|
|
83
83
|
*/
|
|
84
|
-
|
|
85
|
-
const
|
|
86
|
-
let
|
|
84
|
+
decomposeInteger (integerPart) {
|
|
85
|
+
const wordSets = []
|
|
86
|
+
let remaining = integerPart
|
|
87
87
|
|
|
88
88
|
do {
|
|
89
|
-
const
|
|
90
|
-
if (!
|
|
89
|
+
const match = this.scaleWords.find((pair) => remaining >= pair[0])
|
|
90
|
+
if (!match) break
|
|
91
91
|
|
|
92
|
-
const multiplier =
|
|
92
|
+
const multiplier = remaining === 0n ? 1n : remaining / match[0]
|
|
93
93
|
|
|
94
94
|
if (multiplier === 1n) {
|
|
95
|
-
|
|
95
|
+
wordSets.push({ [this.wordForScale(1n)]: 1n })
|
|
96
96
|
} else {
|
|
97
|
-
|
|
97
|
+
wordSets.push(this.decomposeInteger(multiplier))
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
wordSets.push({ [match[1]]: match[0] })
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
} while (
|
|
102
|
+
remaining = remaining === 0n ? 0n : remaining % match[0]
|
|
103
|
+
} while (remaining > 0n)
|
|
104
104
|
|
|
105
|
-
return
|
|
105
|
+
return wordSets
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
/**
|
|
109
|
-
*
|
|
109
|
+
* Reduces a nested array of word-sets into a single word-set object.
|
|
110
110
|
*
|
|
111
|
-
* This method repeatedly applies the subclass `
|
|
112
|
-
* object remains. It normalizes nested arrays by recursively
|
|
111
|
+
* This method repeatedly applies the subclass `combineWordSets()` operation until a single
|
|
112
|
+
* object remains. It normalizes nested arrays by recursively reducing them.
|
|
113
113
|
*
|
|
114
114
|
* @protected
|
|
115
|
-
* @param {Array<Object|Array>}
|
|
116
|
-
* @returns {Object}
|
|
115
|
+
* @param {Array<Object|Array>} wordSets Array of word-set objects and nested arrays.
|
|
116
|
+
* @returns {Object} Reduced word-set where the single object key is the language string
|
|
117
117
|
* and its value is the numeric BigInt result for that string.
|
|
118
118
|
*/
|
|
119
|
-
|
|
120
|
-
while (
|
|
121
|
-
const [
|
|
119
|
+
reduceWordSets (wordSets) {
|
|
120
|
+
while (wordSets.length > 1) {
|
|
121
|
+
const [first, second, ...rest] = wordSets
|
|
122
122
|
|
|
123
|
-
if (!Array.isArray(
|
|
124
|
-
const
|
|
125
|
-
|
|
123
|
+
if (!Array.isArray(first) && !Array.isArray(second)) {
|
|
124
|
+
const combined = this.combineWordSets(first, second)
|
|
125
|
+
wordSets = rest.length > 0 ? [combined, rest] : [combined]
|
|
126
126
|
continue
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
const
|
|
130
|
-
if (!Array.isArray(
|
|
131
|
-
return
|
|
129
|
+
const normalized = wordSets.map((element) => {
|
|
130
|
+
if (!Array.isArray(element)) return element
|
|
131
|
+
return element.length === 1 ? element[0] : this.reduceWordSets(element)
|
|
132
132
|
})
|
|
133
133
|
|
|
134
|
-
|
|
134
|
+
wordSets = normalized
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
return
|
|
137
|
+
return wordSets[0]
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
// ============================================================================
|
|
141
|
+
// Abstract Methods (subclasses must implement)
|
|
142
|
+
// ============================================================================
|
|
143
|
+
|
|
140
144
|
/**
|
|
141
|
-
*
|
|
145
|
+
* Combines two adjacent word-sets into a single word-set.
|
|
142
146
|
*
|
|
143
147
|
* This is the core language-specific method that must be implemented by subclasses
|
|
144
148
|
* to define how adjacent word-sets are combined according to the language's grammar.
|
|
@@ -147,19 +151,23 @@ class GreedyScaleLanguage extends AbstractLanguage {
|
|
|
147
151
|
*
|
|
148
152
|
* @abstract
|
|
149
153
|
* @protected
|
|
150
|
-
* @param {Object}
|
|
151
|
-
* @param {Object}
|
|
152
|
-
* @returns {Object}
|
|
154
|
+
* @param {Object} preceding Preceding word-set as `{ word: bigint }`.
|
|
155
|
+
* @param {Object} following Following word-set as `{ word: bigint }`.
|
|
156
|
+
* @returns {Object} Combined word-set with merged text and resulting numeric value.
|
|
153
157
|
*
|
|
154
158
|
* @example
|
|
155
159
|
* // English implementation might handle:
|
|
156
|
-
* //
|
|
157
|
-
* //
|
|
160
|
+
* // combineWordSets({ 'twenty': 20n }, { 'three': 3n }) → { 'twenty-three': 23n }
|
|
161
|
+
* // combineWordSets({ 'one': 1n }, { 'hundred': 100n }) → { 'one hundred': 100n }
|
|
158
162
|
*/
|
|
159
|
-
|
|
160
|
-
throw new Error('
|
|
163
|
+
combineWordSets (preceding, following) {
|
|
164
|
+
throw new Error('combineWordSets() must be implemented by subclass')
|
|
161
165
|
}
|
|
162
166
|
|
|
167
|
+
// ============================================================================
|
|
168
|
+
// Optional Methods (subclasses may override)
|
|
169
|
+
// ============================================================================
|
|
170
|
+
|
|
163
171
|
/**
|
|
164
172
|
* Final string post-processing hook.
|
|
165
173
|
*
|
|
@@ -167,7 +175,7 @@ class GreedyScaleLanguage extends AbstractLanguage {
|
|
|
167
175
|
* orthographic corrections.
|
|
168
176
|
*
|
|
169
177
|
* @protected
|
|
170
|
-
* @param {string} output
|
|
178
|
+
* @param {string} output Language string produced by the conversion flow.
|
|
171
179
|
* @returns {string} Final formatted string.
|
|
172
180
|
*/
|
|
173
181
|
finalizeWords (output) {
|
|
@@ -175,21 +183,19 @@ class GreedyScaleLanguage extends AbstractLanguage {
|
|
|
175
183
|
}
|
|
176
184
|
|
|
177
185
|
/**
|
|
178
|
-
*
|
|
186
|
+
* Converts an integer to its language-specific cardinal words.
|
|
179
187
|
*
|
|
180
|
-
* This method orchestrates decomposition,
|
|
188
|
+
* This method orchestrates decomposition, reduction, and final formatting. It does
|
|
181
189
|
* not handle decimals or sign; those concerns are implemented in
|
|
182
|
-
* `AbstractLanguage.
|
|
190
|
+
* `AbstractLanguage.toWords` which calls this method for the integer part.
|
|
183
191
|
*
|
|
184
|
-
* @param {bigint
|
|
185
|
-
* @returns {string} The cardinal representation for
|
|
192
|
+
* @param {bigint} integerPart The integer to convert.
|
|
193
|
+
* @returns {string} The cardinal representation for the integer in the language.
|
|
186
194
|
*/
|
|
187
|
-
|
|
188
|
-
const
|
|
189
|
-
const
|
|
190
|
-
const
|
|
191
|
-
return this.finalizeWords(
|
|
195
|
+
integerToWords (integerPart) {
|
|
196
|
+
const wordSets = this.decomposeInteger(integerPart)
|
|
197
|
+
const reduced = this.reduceWordSets(wordSets)
|
|
198
|
+
const result = Object.keys(reduced)[0]
|
|
199
|
+
return this.finalizeWords(result)
|
|
192
200
|
}
|
|
193
201
|
}
|
|
194
|
-
|
|
195
|
-
export default GreedyScaleLanguage
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base class for Slavic and related languages with complex pluralization.
|
|
3
|
+
*
|
|
4
|
+
* This class provides a reusable implementation for languages that share:
|
|
5
|
+
* - Three-form pluralization (singular/few/many)
|
|
6
|
+
* - Gender-aware number forms (masculine/feminine for 1-9)
|
|
7
|
+
* - Hundreds, tens, ones decomposition pattern
|
|
8
|
+
* - Segment-based large number handling (thousands, millions, etc.)
|
|
9
|
+
* - Inherits decimal handling from AbstractLanguage (supports both grouped and
|
|
10
|
+
* per-digit modes via the `usePerDigitDecimals` class property).
|
|
11
|
+
*
|
|
12
|
+
* Used by: Russian (ru), Czech (cs), Polish (pl), Ukrainian (uk), Serbian (sr-Latn),
|
|
13
|
+
* Croatian (hr), Lithuanian (lt), Latvian (lv), Hebrew (he), and Biblical Hebrew (hbo).
|
|
14
|
+
*
|
|
15
|
+
* Subclasses MUST define these properties with language-specific vocabulary:
|
|
16
|
+
* - `onesWords` - Object mapping 1-9 to masculine forms (or default forms)
|
|
17
|
+
* - `onesFeminineWords` - Object mapping 1-9 to feminine forms (if gender distinction exists)
|
|
18
|
+
* - `teensWords` - Object mapping 0-9 to teen numbers (10-19)
|
|
19
|
+
* - `twentiesWords` - Object mapping 2-9 to tens (20-90)
|
|
20
|
+
* - `hundredsWords` - Object mapping 1-9 to hundreds (100-900) or special hundreds handling
|
|
21
|
+
* - `pluralForms` - Object mapping segment indices to [singular, few, many] plural forms
|
|
22
|
+
*
|
|
23
|
+
* Optional properties:
|
|
24
|
+
* - `scaleGenders` - Object mapping segment indices to boolean (true = feminine scale word)
|
|
25
|
+
* If not defined, defaults to thousands (index 1) being feminine, others masculine.
|
|
26
|
+
*
|
|
27
|
+
* @abstract
|
|
28
|
+
* @extends AbstractLanguage
|
|
29
|
+
*/
|
|
30
|
+
export class SlavicLanguage extends AbstractLanguage {
|
|
31
|
+
/**
|
|
32
|
+
* Constructs a SlavicLanguage instance with optional configuration.
|
|
33
|
+
*
|
|
34
|
+
* @param {Object} [options] Configuration options.
|
|
35
|
+
* @param {('masculine'|'feminine')} [options.gender='masculine'] Grammatical gender for number forms.
|
|
36
|
+
*/
|
|
37
|
+
constructor(options?: {
|
|
38
|
+
gender?: "feminine" | "masculine" | undefined;
|
|
39
|
+
});
|
|
40
|
+
/**
|
|
41
|
+
* Masculine forms for digits 1-9 (or default forms if no gender distinction).
|
|
42
|
+
*
|
|
43
|
+
* @type {Object.<number, string>}
|
|
44
|
+
*/
|
|
45
|
+
onesWords: {
|
|
46
|
+
[x: number]: string;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Feminine forms for digits 1-9 (if language has gender distinction).
|
|
50
|
+
*
|
|
51
|
+
* @type {Object.<number, string>}
|
|
52
|
+
*/
|
|
53
|
+
onesFeminineWords: {
|
|
54
|
+
[x: number]: string;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Words for teen numbers (10-19).
|
|
58
|
+
*
|
|
59
|
+
* @type {Object.<number, string>}
|
|
60
|
+
*/
|
|
61
|
+
teensWords: {
|
|
62
|
+
[x: number]: string;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Words for multiples of ten (20, 30, 40, etc.).
|
|
66
|
+
*
|
|
67
|
+
* @type {Object.<number, string>}
|
|
68
|
+
*/
|
|
69
|
+
twentiesWords: {
|
|
70
|
+
[x: number]: string;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Words for hundreds (100, 200, 300, etc.) or special hundreds handling.
|
|
74
|
+
*
|
|
75
|
+
* @type {Object.<number, string>}
|
|
76
|
+
*/
|
|
77
|
+
hundredsWords: {
|
|
78
|
+
[x: number]: string;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Plural forms for scale words (thousands, millions, billions, etc.).
|
|
82
|
+
* Maps segment indices to [singular, few, many] forms.
|
|
83
|
+
*
|
|
84
|
+
* @type {Object.<number, string[]>}
|
|
85
|
+
*/
|
|
86
|
+
pluralForms: {
|
|
87
|
+
[x: number]: string[];
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Gender of each scale word.
|
|
91
|
+
* Maps segment indices to boolean: true = feminine, false = masculine.
|
|
92
|
+
* Default is empty (all masculine). Languages with feminine thousands
|
|
93
|
+
* (Russian, Ukrainian, Serbian, Croatian) should set `{ 1: true }`.
|
|
94
|
+
*
|
|
95
|
+
* @type {Object.<number, boolean>}
|
|
96
|
+
*/
|
|
97
|
+
scaleGenders: {
|
|
98
|
+
[x: number]: boolean;
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Whether to omit "one" before scale words (e.g., "thousand" instead of "one thousand").
|
|
102
|
+
* When true, 1000 becomes "tysiąc" (Polish) instead of "jeden tysiąc".
|
|
103
|
+
* Used by Polish, Czech, and similar languages.
|
|
104
|
+
*
|
|
105
|
+
* @type {boolean}
|
|
106
|
+
*/
|
|
107
|
+
omitOneBeforeScale: boolean;
|
|
108
|
+
/**
|
|
109
|
+
* Splits a number string into segments of specified size from right to left.
|
|
110
|
+
*
|
|
111
|
+
* Example: splitToSegments('1234567', 3) => [1n, 234n, 567n]
|
|
112
|
+
* This represents: 1 million + 234 thousand + 567 ones
|
|
113
|
+
*
|
|
114
|
+
* @param {string} numberString The number as a string.
|
|
115
|
+
* @param {number} segmentSize Segment size (typically 3 for thousands grouping).
|
|
116
|
+
* @returns {bigint[]} Array of BigInt segments from highest to lowest scale.
|
|
117
|
+
*/
|
|
118
|
+
splitToSegments(numberString: string, segmentSize: number): bigint[];
|
|
119
|
+
/**
|
|
120
|
+
* Extracts individual digits from a number (units, tens, hundreds).
|
|
121
|
+
*
|
|
122
|
+
* Returns digits in reverse order: [ones, tens, hundreds]
|
|
123
|
+
* Example: 456 => [6n, 5n, 4n]
|
|
124
|
+
*
|
|
125
|
+
* @param {bigint} value The number to extract digits from (0-999).
|
|
126
|
+
* @returns {bigint[]} Array of [ones, tens, hundreds] as BigInts.
|
|
127
|
+
*/
|
|
128
|
+
extractDigits(value: bigint): bigint[];
|
|
129
|
+
/**
|
|
130
|
+
* Selects the correct plural form based on Slavic pluralization rules.
|
|
131
|
+
*
|
|
132
|
+
* Slavic languages typically use three forms:
|
|
133
|
+
* - Form 0 (singular): numbers ending in 1, except 11 (1, 21, 31, 101...)
|
|
134
|
+
* - Form 1 (few): numbers ending in 2-4, except 12-14 (2-4, 22-24, 32-34...)
|
|
135
|
+
* - Form 2 (many): all other numbers (0, 5-20, 25-30, 100, 111-119...)
|
|
136
|
+
*
|
|
137
|
+
* Examples using Russian тысяча (thousand):
|
|
138
|
+
* - 1, 21, 31... ⇒ тысяча (form 0, singular)
|
|
139
|
+
* - 2-4, 22-24, 32-34... ⇒ тысячи (form 1, few)
|
|
140
|
+
* - 0, 5-20, 25-30, 100... ⇒ тысяч (form 2, many)
|
|
141
|
+
*
|
|
142
|
+
* @param {bigint} number The number to check.
|
|
143
|
+
* @param {string[]} pluralForms Array of [singular, few, many] forms.
|
|
144
|
+
* @returns {string} The appropriate form for the number.
|
|
145
|
+
*/
|
|
146
|
+
pluralize(number: bigint, pluralForms: string[]): string;
|
|
147
|
+
}
|
|
148
|
+
import { AbstractLanguage } from './abstract-language.js';
|