n2words 2.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +64 -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/ka.js +3 -0
- package/dist/languages/ka.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/yo.js +3 -0
- package/dist/languages/yo.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 +159 -0
- package/lib/languages/am.d.ts +7 -0
- package/lib/languages/am.js +159 -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 +166 -37
- package/lib/languages/bn.d.ts +4 -8
- package/lib/languages/bn.js +159 -124
- package/lib/languages/cs.d.ts +15 -85
- package/lib/languages/cs.js +293 -114
- package/lib/languages/da.d.ts +11 -12
- package/lib/languages/da.js +269 -101
- package/lib/languages/de.d.ts +14 -11
- package/lib/languages/de.js +305 -86
- package/lib/languages/el.d.ts +11 -11
- package/lib/languages/el.js +224 -78
- package/lib/languages/en.d.ts +14 -13
- package/lib/languages/en.js +228 -72
- package/lib/languages/es.d.ts +18 -12
- package/lib/languages/es.js +297 -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 +238 -0
- package/lib/languages/fil.d.ts +4 -13
- package/lib/languages/fil.js +196 -106
- package/lib/languages/fr-BE.d.ts +8 -8
- package/lib/languages/fr-BE.js +285 -19
- package/lib/languages/fr.d.ts +18 -12
- package/lib/languages/fr.js +339 -89
- package/lib/languages/gu.d.ts +4 -8
- package/lib/languages/gu.js +143 -125
- package/lib/languages/ha.d.ts +7 -0
- package/lib/languages/ha.js +225 -0
- package/lib/languages/hbo.d.ts +10 -110
- package/lib/languages/hbo.js +245 -214
- package/lib/languages/he.d.ts +10 -77
- package/lib/languages/he.js +231 -172
- package/lib/languages/hi.d.ts +4 -8
- package/lib/languages/hi.js +163 -124
- package/lib/languages/hr.d.ts +8 -77
- package/lib/languages/hr.js +200 -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 +166 -129
- package/lib/languages/it.d.ts +16 -34
- package/lib/languages/it.js +307 -97
- package/lib/languages/ja.d.ts +14 -14
- package/lib/languages/ja.js +221 -111
- package/lib/languages/ka.d.ts +17 -0
- package/lib/languages/ka.js +291 -0
- package/lib/languages/kn.d.ts +4 -8
- package/lib/languages/kn.js +143 -35
- package/lib/languages/ko.d.ts +11 -11
- package/lib/languages/ko.js +250 -49
- package/lib/languages/lt.d.ts +15 -67
- package/lib/languages/lt.js +287 -122
- package/lib/languages/lv.d.ts +15 -67
- package/lib/languages/lv.js +288 -106
- package/lib/languages/mr.d.ts +4 -8
- package/lib/languages/mr.js +143 -125
- package/lib/languages/ms.d.ts +4 -28
- package/lib/languages/ms.js +166 -116
- package/lib/languages/nb.d.ts +11 -9
- package/lib/languages/nb.js +272 -87
- package/lib/languages/nl.d.ts +23 -13
- package/lib/languages/nl.js +299 -133
- package/lib/languages/pa.d.ts +4 -8
- package/lib/languages/pa.js +151 -124
- package/lib/languages/pl.d.ts +19 -77
- package/lib/languages/pl.js +294 -87
- package/lib/languages/pt.d.ts +14 -26
- package/lib/languages/pt.js +272 -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 +239 -90
- package/lib/languages/sr-Cyrl.d.ts +8 -77
- package/lib/languages/sr-Cyrl.js +197 -89
- package/lib/languages/sr-Latn.d.ts +8 -77
- package/lib/languages/sr-Latn.js +197 -89
- package/lib/languages/sv.d.ts +11 -11
- package/lib/languages/sv.js +278 -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 +143 -202
- package/lib/languages/te.d.ts +4 -19
- package/lib/languages/te.js +133 -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 +245 -49
- package/lib/languages/uk.d.ts +8 -82
- package/lib/languages/uk.js +206 -78
- package/lib/languages/ur.d.ts +4 -8
- package/lib/languages/ur.js +151 -124
- package/lib/languages/vi.d.ts +14 -69
- package/lib/languages/vi.js +278 -129
- package/lib/languages/yo.d.ts +7 -0
- package/lib/languages/yo.js +303 -0
- 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 +55 -209
- package/lib/n2words.js +115 -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
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"CzechConverter.js","sources":["../lib/classes/abstract-language.js","../lib/classes/slavic-language.js","../lib/n2words.js","../lib/languages/cs.js"],"sourcesContent":["/**\n * Abstract base class for language converters.\n *\n * This class provides the framework for converting numbers to words in any language.\n * It handles the common conversion flow while delegating language-specific logic to subclasses.\n *\n * ## Responsibilities\n *\n * - Receives pre-validated and normalized input from the public API (n2words.js)\n * - Handles negative number prefixing via `negativeWord`\n * - Converts decimals via `decimalDigitsToWords()`, preserving leading zeros\n * - Delegates integer conversion to `integerToWords()` (implemented by subclasses)\n *\n * ## Required Subclass Implementation\n *\n * Subclasses MUST provide:\n * - `integerToWords(integerPart)` - Core conversion logic (abstract method)\n * - `negativeWord` - Word preceding negative numbers (e.g., \"minus\"))\n * - `zeroWord` - Word for the digit 0 (e.g., \"zero\")\n * - `decimalSeparatorWord` - Word between whole and decimal parts (e.g., \"point\")\n *\n * ## Optional Overrides\n *\n * Subclasses MAY override:\n * - `wordSeparator` - Character(s) between words (default: space, empty for CJK languages)\n * - `usePerDigitDecimals` - Enable per-digit decimal mode (default: false)\n * - `decimalIntegerToWords()` - Custom decimal conversion (e.g., Romanian masculine forms)\n * - `decimalDigitsToWords()` - Complete decimal conversion override\n * - `toWords()` - Override to capture integerPart for context-dependent rules (e.g., Czech)\n *\n * ## Input Contract\n *\n * Input validation and normalization happen at the public API boundary (n2words.js).\n * This class assumes it receives clean, pre-processed data via `toWords()`.\n *\n * @abstract\n */\nexport class AbstractLanguage {\n // ============================================================================\n // Private Fields\n // ============================================================================\n\n /**\n * Private storage for options.\n * @type {Object}\n */\n #options = {}\n\n // ============================================================================\n // Required Properties (subclasses must define)\n // ============================================================================\n\n /**\n * Word that precedes negative numbers (e.g., \"minus\", \"negative\", \"moins\").\n * @type {string}\n */\n negativeWord = ''\n\n /**\n * Word that separates integer and decimal parts (e.g., \"point\", \"virgule\", \"comma\").\n * @type {string}\n */\n decimalSeparatorWord = ''\n\n /**\n * Word representation for the digit 0 (e.g., \"zero\", \"zéro\", \"null\").\n * Used for zero values and leading zeros in decimals.\n * @type {string}\n */\n zeroWord = ''\n\n // ============================================================================\n // Optional Properties (subclasses may override)\n // ============================================================================\n\n /**\n * Character(s) used to separate words in the output.\n *\n * Defaults to a single space. Set to empty string for languages without\n * word separators (e.g., Japanese, Thai, Chinese).\n *\n * @type {string}\n */\n wordSeparator = ' '\n\n /**\n * Whether to convert decimal digits individually rather than grouped.\n *\n * - `false` (default): Leading zeros preserved, remaining digits grouped as a number\n * - Example: \"05\" → [\"zero\", \"five\"], \"14\" → [\"fourteen\"]\n * - Used by: English, Spanish, French, German, etc.\n *\n * - `true`: Each digit converted separately\n * - Example: \"05\" → [\"zero\", \"five\"], \"14\" → [\"one\", \"four\"]\n * - Used by: Japanese, Thai, Tamil, Telugu, Greek, Hebrew, Filipino\n *\n * @type {boolean}\n */\n usePerDigitDecimals = false\n\n // ============================================================================\n // Public Accessors\n // ============================================================================\n\n /**\n * Read-only access to options set via `setOptions()`.\n * @type {Object}\n */\n get options () {\n return this.#options\n }\n\n // ============================================================================\n // Public Methods\n // ============================================================================\n\n /**\n * Converts pre-normalized numeric components to words.\n *\n * This is the main entry point called by the public API (makeConverter in n2words.js).\n * It assembles the final word representation from the provided components.\n *\n * **Caller contract (enforced by makeConverter):**\n * - `integerPart` is a non-negative BigInt (>= 0n)\n * - `decimalPart` is a string of digits only (no sign, no decimal point)\n * - `isNegative` reflects the original input sign\n *\n * **Conversion flow:**\n * 1. Prepend negative word if applicable\n * 2. Convert integer part via `integerToWords()`\n * 3. If decimals present: append separator and decimal words\n * 4. Join all parts with `wordSeparator`\n *\n * Subclasses needing access to the integer part during decimal conversion\n * (e.g., for context-dependent separator words) should override this\n * method to cache the value before calling super.toWords().\n *\n * @public\n * @param {boolean} isNegative Whether the original number was negative\n * @param {bigint} integerPart The integer part (always non-negative)\n * @param {string} [decimalPart] - Decimal digits if present (e.g., \"14\" for 3.14)\n * @returns {string} The localized cardinal string\n */\n toWords (isNegative, integerPart, decimalPart) {\n const words = []\n\n if (isNegative) words.push(this.negativeWord)\n\n words.push(this.integerToWords(integerPart))\n\n if (decimalPart) {\n words.push(this.decimalSeparatorWord)\n words.push(...this.decimalDigitsToWords(decimalPart))\n }\n\n return words.join(this.wordSeparator)\n }\n\n // ============================================================================\n // Abstract Methods (subclasses must implement)\n // ============================================================================\n\n /**\n * Converts a BigInt integer part to its cardinal word representation.\n *\n * This is the core template method that subclasses MUST implement to provide\n * language-specific number conversion logic.\n *\n * @abstract\n * @param {bigint} integerPart The integer part to convert (always >= 0n)\n * @returns {string} The cardinal representation in the target language\n * @throws {Error} If not implemented by subclass\n */\n integerToWords (integerPart) {\n throw new Error('integerToWords() must be implemented by subclass')\n }\n\n // ============================================================================\n // Protected Methods (subclasses may override or call)\n // ============================================================================\n\n /**\n * Sets options by merging language defaults with user-provided options.\n *\n * Merges defaults first, then user options (later keys override earlier ones).\n * Stores the result in the private `#options` field, accessible via the\n * read-only `options` getter.\n *\n * @protected\n * @param {Object} [defaults={}] - Default option values for the language\n * @param {Object} [userOptions={}] - Runtime options supplied by the caller\n *\n * @example\n * constructor(options = {}) {\n * super()\n * this.setOptions({ gender: 'masculine' }, options)\n * }\n */\n setOptions (defaults = {}, userOptions = {}) {\n this.#options = {\n ...defaults,\n ...userOptions\n }\n }\n\n /**\n * Converts an integer to words in decimal context.\n *\n * By default, delegates to `integerToWords()`. Override this method\n * when decimal conversion requires different behavior than integer conversion.\n *\n * Called with:\n * - Single digits (0-9) when `usePerDigitDecimals = true`\n * - Grouped numbers when `usePerDigitDecimals = false`\n *\n * **Use cases for overriding:**\n * - Romanian: Decimals always use masculine forms regardless of gender option\n * - Languages with different plural/gender rules for decimal vs integer parts\n *\n * @protected\n * @param {bigint} integerPart The integer to convert (single digit or grouped)\n * @returns {string} The word representation for use in decimal context\n */\n decimalIntegerToWords (integerPart) {\n return this.integerToWords(integerPart)\n }\n\n /**\n * Converts decimal fractional digits into an array of words.\n *\n * Leading zeros are always preserved individually. The remaining digits\n * are converted based on `usePerDigitDecimals`:\n *\n * - `false` (default): Remaining digits grouped as a single number\n * - \"05\" → [\"zero\", \"five\"], \"14\" → [\"fourteen\"]\n *\n * - `true`: Each remaining digit converted separately\n * - \"05\" → [\"zero\", \"five\"], \"14\" → [\"one\", \"four\"]\n *\n * @protected\n * @param {string} decimalPart Decimal digits as string (e.g., \"05\" for 3.05)\n * @returns {string[]} Array of word tokens for the fractional part\n */\n decimalDigitsToWords (decimalPart) {\n const words = []\n\n // Always preserve leading zeros individually\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n words.push(this.zeroWord)\n i++\n }\n\n const remainder = decimalPart.slice(i)\n if (!remainder) return words\n\n // Convert remainder: per-digit or as single number\n if (this.usePerDigitDecimals) {\n for (const char of remainder) {\n words.push(this.decimalIntegerToWords(BigInt(char)))\n }\n } else {\n words.push(this.decimalIntegerToWords(BigInt(remainder)))\n }\n\n return words\n }\n}\n","import { AbstractLanguage } from './abstract-language.js'\n\n/**\n * Base class for Slavic and related languages with complex pluralization.\n *\n * This class provides a reusable implementation for languages that share:\n * - Three-form pluralization (singular/few/many)\n * - Gender-aware number forms (masculine/feminine for 1-9)\n * - Hundreds, tens, ones decomposition pattern\n * - Segment-based large number handling (thousands, millions, etc.)\n * - Inherits decimal handling from AbstractLanguage (supports both grouped and\n * per-digit modes via the `usePerDigitDecimals` class property).\n *\n * Used by: Russian (ru), Czech (cs), Polish (pl), Ukrainian (uk), Serbian (sr-Latn),\n * Croatian (hr), Lithuanian (lt), Latvian (lv), Hebrew (he), and Biblical Hebrew (hbo).\n *\n * Subclasses MUST define these properties with language-specific vocabulary:\n * - `onesWords` - Object mapping 1-9 to masculine forms (or default forms)\n * - `onesFeminineWords` - Object mapping 1-9 to feminine forms (if gender distinction exists)\n * - `teensWords` - Object mapping 0-9 to teen numbers (10-19)\n * - `twentiesWords` - Object mapping 2-9 to tens (20-90)\n * - `hundredsWords` - Object mapping 1-9 to hundreds (100-900) or special hundreds handling\n * - `pluralForms` - Object mapping segment indices to [singular, few, many] plural forms\n *\n * Optional properties:\n * - `scaleGenders` - Object mapping segment indices to boolean (true = feminine scale word)\n * If not defined, defaults to thousands (index 1) being feminine, others masculine.\n *\n * @abstract\n * @extends AbstractLanguage\n */\nexport class SlavicLanguage extends AbstractLanguage {\n // ============================================================================\n // Required Properties (subclasses must define)\n // ============================================================================\n\n /**\n * Masculine forms for digits 1-9 (or default forms if no gender distinction).\n *\n * @type {Object.<number, string>}\n */\n onesWords = {}\n\n /**\n * Feminine forms for digits 1-9 (if language has gender distinction).\n *\n * @type {Object.<number, string>}\n */\n onesFeminineWords = {}\n\n /**\n * Words for teen numbers (10-19).\n *\n * @type {Object.<number, string>}\n */\n teensWords = {}\n\n /**\n * Words for multiples of ten (20, 30, 40, etc.).\n *\n * @type {Object.<number, string>}\n */\n twentiesWords = {}\n\n /**\n * Words for hundreds (100, 200, 300, etc.) or special hundreds handling.\n *\n * @type {Object.<number, string>}\n */\n hundredsWords = {}\n\n /**\n * Plural forms for scale words (thousands, millions, billions, etc.).\n * Maps segment indices to [singular, few, many] forms.\n *\n * @type {Object.<number, string[]>}\n */\n pluralForms = {}\n\n // ============================================================================\n // Optional Properties (subclasses may override)\n // ============================================================================\n\n /**\n * Gender of each scale word.\n * Maps segment indices to boolean: true = feminine, false = masculine.\n * Default is empty (all masculine). Languages with feminine thousands\n * (Russian, Ukrainian, Serbian, Croatian) should set `{ 1: true }`.\n *\n * @type {Object.<number, boolean>}\n */\n scaleGenders = {}\n\n /**\n * Whether to omit \"one\" before scale words (e.g., \"thousand\" instead of \"one thousand\").\n * When true, 1000 becomes \"tysiąc\" (Polish) instead of \"jeden tysiąc\".\n * Used by Polish, Czech, and similar languages.\n *\n * @type {boolean}\n */\n omitOneBeforeScale = false\n\n // ============================================================================\n // Constructor\n // ============================================================================\n\n /**\n * Constructs a SlavicLanguage instance with optional configuration.\n *\n * @param {Object} [options] Configuration options.\n * @param {('masculine'|'feminine')} [options.gender='masculine'] Grammatical gender for number forms.\n */\n constructor (options = {}) {\n super()\n\n this.setOptions({\n gender: 'masculine'\n }, options)\n }\n\n // ============================================================================\n // Public Methods\n // ============================================================================\n\n /**\n * Converts an integer to its word representation.\n *\n * This method implements the Slavic number construction algorithm:\n * 1. Split number into 3-digit segments from right to left (567, 234, 1 for 1,234,567)\n * 2. For each segment (processing left to right): convert hundreds, tens, ones\n * 3. Apply gender rules: feminine forms for thousands segment or when feminine=true\n * 4. Add appropriate pluralized scale word (thousand/million/billion/etc.)\n * 5. Join all parts with spaces\n *\n * @param {bigint} integerPart The integer to convert (non-negative).\n * @returns {string} The number in words.\n */\n integerToWords (integerPart) {\n if (integerPart === 0n) {\n return this.zeroWord\n }\n\n const words = []\n const segments = this.splitToSegments(integerPart.toString(), 3)\n let segmentIndex = segments.length\n\n for (const segmentValue of segments) {\n segmentIndex = segmentIndex - 1\n\n if (segmentValue === 0n) {\n continue\n }\n\n const [onesDigit, tensDigit, hundredsDigit] = this.extractDigits(segmentValue)\n\n if (hundredsDigit > 0n) {\n words.push(this.hundredsWords[hundredsDigit])\n }\n\n if (tensDigit > 1n) {\n words.push(this.twentiesWords[tensDigit])\n }\n\n // Handle teens (10-19) or ones (1-9)\n if (tensDigit === 1n) {\n // Teens: use teensWords array directly\n words.push(this.teensWords[onesDigit])\n } else if (onesDigit > 0n) {\n // Skip \"one\" before scale words if omitOneBeforeScale is set\n // e.g., Polish says \"tysiąc\" not \"jeden tysiąc\" for 1000\n const shouldOmitOne = this.omitOneBeforeScale && segmentIndex > 0 && segmentValue === 1n\n\n if (!shouldOmitOne) {\n // Determine if feminine forms should be used:\n // 1. Check scaleGenders for this segment index (e.g., thousands = index 1)\n // 2. Also use feminine if user requested gender='feminine' for the ones segment\n const isScaleFeminine = this.scaleGenders[segmentIndex] === true\n const isFeminine = isScaleFeminine || (this.options.gender === 'feminine' && segmentIndex === 0)\n const onesArray = isFeminine ? this.onesFeminineWords : this.onesWords\n words.push(onesArray[onesDigit])\n }\n }\n\n // Add power word (thousand, million, etc.) with proper pluralization\n if (segmentIndex > 0) {\n words.push(this.pluralize(segmentValue, this.pluralForms[segmentIndex]))\n }\n }\n\n return words.join(' ')\n }\n\n // ============================================================================\n // Protected Methods (subclasses may call or override)\n // ============================================================================\n\n /**\n * Splits a number string into segments of specified size from right to left.\n *\n * Example: splitToSegments('1234567', 3) => [1n, 234n, 567n]\n * This represents: 1 million + 234 thousand + 567 ones\n *\n * @param {string} numberString The number as a string.\n * @param {number} segmentSize Segment size (typically 3 for thousands grouping).\n * @returns {bigint[]} Array of BigInt segments from highest to lowest scale.\n */\n splitToSegments (numberString, segmentSize) {\n const segments = []\n const stringLength = numberString.length\n\n if (stringLength > segmentSize) {\n const remainderLength = stringLength % segmentSize\n\n if (remainderLength > 0) {\n segments.push(BigInt(numberString.slice(0, remainderLength)))\n }\n\n for (let i = remainderLength; i < stringLength; i += segmentSize) {\n segments.push(BigInt(numberString.slice(i, i + segmentSize)))\n }\n } else {\n segments.push(BigInt(numberString))\n }\n\n return segments\n }\n\n /**\n * Extracts individual digits from a number (units, tens, hundreds).\n *\n * Returns digits in reverse order: [ones, tens, hundreds]\n * Example: 456 => [6n, 5n, 4n]\n *\n * @param {bigint} value The number to extract digits from (0-999).\n * @returns {bigint[]} Array of [ones, tens, hundreds] as BigInts.\n */\n extractDigits (value) {\n // Direct BigInt arithmetic is faster than string manipulation\n const onesPlace = value % 10n\n const tensPlace = (value / 10n) % 10n\n const hundredsPlace = value / 100n\n return [onesPlace, tensPlace, hundredsPlace]\n }\n\n /**\n * Selects the correct plural form based on Slavic pluralization rules.\n *\n * Slavic languages typically use three forms:\n * - Form 0 (singular): numbers ending in 1, except 11 (1, 21, 31, 101...)\n * - Form 1 (few): numbers ending in 2-4, except 12-14 (2-4, 22-24, 32-34...)\n * - Form 2 (many): all other numbers (0, 5-20, 25-30, 100, 111-119...)\n *\n * Examples using Russian тысяча (thousand):\n * - 1, 21, 31... ⇒ тысяча (form 0, singular)\n * - 2-4, 22-24, 32-34... ⇒ тысячи (form 1, few)\n * - 0, 5-20, 25-30, 100... ⇒ тысяч (form 2, many)\n *\n * @param {bigint} number The number to check.\n * @param {string[]} pluralForms Array of [singular, few, many] forms.\n * @returns {string} The appropriate form for the number.\n */\n pluralize (number, pluralForms) {\n const remainder100 = number % 100n\n const remainder10 = number % 10n\n\n // Check if in 11-19 range (special case)\n if (remainder100 >= 10n && remainder100 <= 20n) {\n return pluralForms[2] // Always use \"many\" form for 11-20\n }\n\n if (remainder10 === 1n) {\n return pluralForms[0] // Singular\n }\n\n if (remainder10 >= 2n && remainder10 <= 4n) {\n return pluralForms[1] // Few (2-4)\n }\n\n return pluralForms[2] // Many\n }\n}\n","/**\n * n2words - Convert numbers to words in multiple languages\n *\n * This file is the main entry point for the n2words library.\n * It exports converter functions for all supported languages.\n *\n * ## For Contributors\n *\n * When adding a new language, this file must be updated in THREE sections:\n * 1. Language Imports - Add import statement (alphabetically sorted)\n * 2. Language Converters - Create converter with makeConverter() (alphabetically sorted)\n * 3. Exports - Add to export list (alphabetically sorted)\n *\n * Use the scaffolding tool to automate this process:\n * npm run lang:add <language-code>\n *\n * ## Public API Structure\n *\n * Each language exports a converter function:\n * - Name: `{LanguageName}Converter` (e.g., EnglishConverter, SpanishConverter)\n * - Signature: `(value: NumericValue, options?: Options) => string`\n * - Input: number, bigint, or string (numeric strings only)\n * - Output: Words representing the number in the target language\n *\n * Languages without options use signature: `(value: NumericValue) => string`\n * Languages with options define a typedef (e.g., ArabicOptions) and use: `(value: NumericValue, options?: ArabicOptions) => string`\n *\n * @module n2words\n */\n\n// ============================================================================\n// Language Imports\n// ============================================================================\n\nimport { Arabic } from './languages/ar.js'\nimport { Azerbaijani } from './languages/az.js'\nimport { Bangla } from './languages/bn.js'\nimport { Czech } from './languages/cs.js'\nimport { Danish } from './languages/da.js'\nimport { German } from './languages/de.js'\nimport { Greek } from './languages/el.js'\nimport { English } from './languages/en.js'\nimport { Spanish } from './languages/es.js'\nimport { Persian } from './languages/fa.js'\nimport { Filipino } from './languages/fil.js'\nimport { French } from './languages/fr.js'\nimport { FrenchBelgium } from './languages/fr-BE.js'\nimport { Gujarati } from './languages/gu.js'\nimport { Hebrew } from './languages/he.js'\nimport { BiblicalHebrew } from './languages/hbo.js'\nimport { Hindi } from './languages/hi.js'\nimport { Croatian } from './languages/hr.js'\nimport { Hungarian } from './languages/hu.js'\nimport { Indonesian } from './languages/id.js'\nimport { Italian } from './languages/it.js'\nimport { Japanese } from './languages/ja.js'\nimport { Kannada } from './languages/kn.js'\nimport { Korean } from './languages/ko.js'\nimport { Lithuanian } from './languages/lt.js'\nimport { Latvian } from './languages/lv.js'\nimport { Marathi } from './languages/mr.js'\nimport { Malay } from './languages/ms.js'\nimport { Dutch } from './languages/nl.js'\nimport { NorwegianBokmal } from './languages/nb.js'\nimport { Punjabi } from './languages/pa.js'\nimport { Polish } from './languages/pl.js'\nimport { Portuguese } from './languages/pt.js'\nimport { Romanian } from './languages/ro.js'\nimport { Russian } from './languages/ru.js'\nimport { SerbianCyrillic } from './languages/sr-Cyrl.js'\nimport { SerbianLatin } from './languages/sr-Latn.js'\nimport { Swedish } from './languages/sv.js'\nimport { Swahili } from './languages/sw.js'\nimport { Tamil } from './languages/ta.js'\nimport { Telugu } from './languages/te.js'\nimport { Thai } from './languages/th.js'\nimport { Turkish } from './languages/tr.js'\nimport { Ukrainian } from './languages/uk.js'\nimport { Urdu } from './languages/ur.js'\nimport { Vietnamese } from './languages/vi.js'\nimport { SimplifiedChinese } from './languages/zh-Hans.js'\nimport { TraditionalChinese } from './languages/zh-Hant.js'\n\n// ============================================================================\n// Type Definitions\n// ============================================================================\n//\n// This section defines TypeScript-compatible JSDoc types for:\n// - NumericValue: The input types accepted by all converters\n// - {Language}Options: Optional configuration for languages that support it\n//\n// Keep options typedefs alphabetically sorted for maintainability.\n// ============================================================================\n\n/**\n * Numeric value that can be converted to words.\n * Accepts number, bigint, or numeric string representations.\n * @typedef {number | bigint | string} NumericValue\n */\n\n/**\n * @typedef {Object} ArabicOptions\n * @property {string} [negativeWord] Word for negative numbers\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} BiblicalHebrewOptions\n * @property {string} [andWord='ו'] Conjunction character (typically 'ו' for and)\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} CroatianOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} CzechOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} DanishOptions\n * @property {boolean} [ordFlag=false] Enable ordinal number conversion\n */\n\n/**\n * @typedef {Object} DutchOptions\n * @property {boolean} [includeOptionalAnd=false] Include optional \"en\" separator\n * @property {boolean} [noHundredPairing=false] Disable hundred-pairing (e.g., \"twelve hundred\" becomes \"one thousand two hundred\")\n * @property {boolean} [accentOne=true] Use accented \"één\" for one\n */\n\n/**\n * @typedef {Object} FrenchOptions\n * @property {boolean} [withHyphenSeparator=false] Use hyphens (true) instead of spaces (false) in compounds\n */\n\n/**\n * @typedef {Object} FrenchBelgiumOptions\n * @property {boolean} [withHyphenSeparator=false] Use hyphens (true) instead of spaces (false) in compounds\n */\n\n/**\n * @typedef {Object} HebrewOptions\n * @property {string} [andWord='ו'] Conjunction character (typically 'ו' for and)\n */\n\n/**\n * @typedef {Object} LatvianOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} LithuanianOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} PolishOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} RomanianOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} RussianOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} SerbianCyrillicOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} SerbianLatinOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} SimplifiedChineseOptions\n * @property {boolean} [formal=true] Use formal/financial numerals (壹贰叁) vs. common numerals (一二三)\n */\n\n/**\n * @typedef {Object} SpanishOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} TraditionalChineseOptions\n * @property {boolean} [formal=true] Use formal/financial numerals (壹貳參) vs. common numerals (一二三)\n */\n\n/**\n * @typedef {Object} TurkishOptions\n * @property {boolean} [dropSpaces=false] Remove spaces between words if true\n */\n\n/**\n * @typedef {Object} UkrainianOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n// ============================================================================\n// Converter Factory\n// ============================================================================\n//\n// makeConverter() is a factory function that creates converter functions from\n// language classes. It provides a consistent functional API for all languages:\n//\n// const result = EnglishConverter(42) // \"forty-two\"\n// const result = ArabicConverter(1, {gender: 'feminine'}) // \"واحدة\"\n//\n// This design:\n// - Hides class instantiation details from public API\n// - Ensures each conversion uses a fresh instance\n// - Maintains immutability (no shared state between conversions)\n// ============================================================================\n\n/**\n * Creates a converter function for a language class.\n *\n * This factory handles all input validation and normalization at the public API\n * boundary, then delegates to the language class with pre-processed data.\n *\n * @template {Object} [TOptions={}]\n * @param {new (options?: TOptions) => { toWords: (isNegative: boolean, integerPart: bigint, decimalPart?: string) => string }} LanguageClass - Language class constructor\n * @returns {(value: NumericValue, options?: TOptions) => string} Converter function\n */\nfunction makeConverter (LanguageClass) {\n /**\n * @param {NumericValue} value\n * @param {TOptions} [options]\n * @returns {string}\n */\n return function convertToWords (value, options) {\n if (options !== undefined && !isPlainObject(options)) {\n throw new TypeError('options must be a plain object if provided')\n }\n\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n return new LanguageClass(options).toWords(isNegative, integerPart, decimalPart)\n }\n}\n\n// ============================================================================\n// Input Parsing Utilities\n// ============================================================================\n\n/**\n * @typedef {Object} ParsedNumericValue\n * @property {boolean} isNegative - Whether the value is negative\n * @property {bigint} integerPart - The absolute integer part\n * @property {string} [decimalPart] - The decimal digits (without the point)\n */\n\n/**\n * Parses and validates a numeric value into its components.\n * Handles number, string, and bigint inputs uniformly.\n *\n * @param {NumericValue} value The value to parse\n * @returns {ParsedNumericValue} The parsed components\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n */\nfunction parseNumericValue (value) {\n const type = typeof value\n\n // BigInt: simplest case - no decimals, no scientific notation\n if (type === 'bigint') {\n return value < 0n\n ? { isNegative: true, integerPart: -value }\n : { isNegative: false, integerPart: value }\n }\n\n // Reject invalid types early\n if (type !== 'number' && type !== 'string') {\n throw new TypeError(\n `Invalid value type: expected number, string, or bigint, received ${type}`\n )\n }\n\n // Convert to normalized string\n const str = type === 'number'\n ? numberToString(value)\n : stringToNormalizedForm(value)\n\n return parseNumericString(str)\n}\n\n/**\n * Converts a JavaScript number to a decimal string.\n * Handles special cases: Infinity, NaN, and scientific notation.\n *\n * @param {number} value The number to convert\n * @returns {string} Decimal string representation\n * @throws {Error} If value is not finite\n */\nfunction numberToString (value) {\n if (!Number.isFinite(value)) {\n throw new Error('Number must be finite (NaN and Infinity are not supported)')\n }\n\n const str = value.toString()\n\n // Expand scientific notation (used for values >= 1e21 or < 1e-6)\n if (str.includes('e') || str.includes('E')) {\n return expandScientificNotation(str)\n }\n\n return str\n}\n\n/**\n * Validates and normalizes a string numeric input.\n *\n * @param {string} value The string to validate\n * @returns {string} Trimmed and validated string\n * @throws {Error} If string is empty or not a valid number format\n */\nfunction stringToNormalizedForm (value) {\n const trimmed = value.trim()\n\n if (trimmed.length === 0) {\n throw new Error(`Invalid number format: \"${value}\"`)\n }\n\n // Validate by attempting conversion (handles edge cases like \"1e21\", \"-.5\", etc.)\n if (Number.isNaN(Number(trimmed))) {\n throw new Error(`Invalid number format: \"${value}\"`)\n }\n\n // Expand scientific notation if present\n if (trimmed.includes('e') || trimmed.includes('E')) {\n return expandScientificNotation(trimmed)\n }\n\n return trimmed\n}\n\n/**\n * Parses a normalized numeric string into its components.\n *\n * @param {string} str A normalized decimal string (no scientific notation)\n * @returns {ParsedNumericValue} The parsed components\n */\nfunction parseNumericString (str) {\n // Extract sign\n const isNegative = str[0] === '-'\n if (isNegative) {\n str = str.slice(1)\n }\n\n // Split into integer and decimal parts\n const dotIndex = str.indexOf('.')\n if (dotIndex === -1) {\n return { isNegative, integerPart: BigInt(str) }\n }\n\n const integerStr = str.slice(0, dotIndex) || '0'\n const decimalPart = str.slice(dotIndex + 1)\n\n return { isNegative, integerPart: BigInt(integerStr), decimalPart }\n}\n\n/**\n * Expands scientific notation to decimal form.\n * JavaScript uses scientific notation for values >= 1e21 or < 1e-6.\n *\n * @param {string} str String possibly in scientific notation (e.g., \"1e+21\")\n * @returns {string} Decimal form (e.g., \"1000000000000000000000\")\n */\nfunction expandScientificNotation (str) {\n const [mantissa, expStr] = str.toLowerCase().split('e')\n const exp = parseInt(expStr, 10)\n\n // Extract digits and determine original decimal position\n const dotIndex = mantissa.indexOf('.')\n const digits = dotIndex === -1\n ? mantissa\n : mantissa.slice(0, dotIndex) + mantissa.slice(dotIndex + 1)\n const integerLength = dotIndex === -1 ? mantissa.length : dotIndex\n\n // Calculate new decimal position after applying exponent\n const newDotPosition = integerLength + exp\n\n if (newDotPosition >= digits.length) {\n // Pure integer: pad with trailing zeros\n return digits + '0'.repeat(newDotPosition - digits.length)\n }\n\n if (newDotPosition <= 0) {\n // Pure decimal: pad with leading zeros after decimal point\n return '0.' + '0'.repeat(-newDotPosition) + digits\n }\n\n // Mixed: insert decimal point at new position\n return digits.slice(0, newDotPosition) + '.' + digits.slice(newDotPosition)\n}\n\n/**\n * Checks if a value is a plain object (not null, array, or other object types).\n *\n * @param {*} value Value to check\n * @returns {boolean} True if value is a plain object\n */\nfunction isPlainObject (value) {\n if (value === null || typeof value !== 'object') return false\n const proto = Object.getPrototypeOf(value)\n return proto === Object.prototype || proto === null\n}\n\n// ============================================================================\n// Language Converters\n// ============================================================================\n//\n// Each converter is created using makeConverter() with explicit type annotations.\n//\n// Pattern for languages WITHOUT options:\n// const LanguageConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Language))\n//\n// Pattern for languages WITH options:\n// const LanguageConverter = /** @type {(value: NumericValue, options?: LanguageOptions) => string} */ (makeConverter(Language))\n//\n// IMPORTANT: Keep converters alphabetically sorted by converter name.\n// ============================================================================\n\nconst ArabicConverter = /** @type {(value: NumericValue, options?: ArabicOptions) => string} */ (makeConverter(Arabic))\nconst AzerbaijaniConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Azerbaijani))\nconst BanglaConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Bangla))\nconst BiblicalHebrewConverter = /** @type {(value: NumericValue, options?: BiblicalHebrewOptions) => string} */ (makeConverter(BiblicalHebrew))\nconst CroatianConverter = /** @type {(value: NumericValue, options?: CroatianOptions) => string} */ (makeConverter(Croatian))\nconst CzechConverter = /** @type {(value: NumericValue, options?: CzechOptions) => string} */ (makeConverter(Czech))\nconst DanishConverter = /** @type {(value: NumericValue, options?: DanishOptions) => string} */ (makeConverter(Danish))\nconst DutchConverter = /** @type {(value: NumericValue, options?: DutchOptions) => string} */ (makeConverter(Dutch))\nconst EnglishConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(English))\nconst FilipinoConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Filipino))\nconst FrenchConverter = /** @type {(value: NumericValue, options?: FrenchOptions) => string} */ (makeConverter(French))\nconst FrenchBelgiumConverter = /** @type {(value: NumericValue, options?: FrenchBelgiumOptions) => string} */ (makeConverter(FrenchBelgium))\nconst GermanConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(German))\nconst GreekConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Greek))\nconst GujaratiConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Gujarati))\nconst HebrewConverter = /** @type {(value: NumericValue, options?: HebrewOptions) => string} */ (makeConverter(Hebrew))\nconst HindiConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Hindi))\nconst HungarianConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Hungarian))\nconst IndonesianConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Indonesian))\nconst ItalianConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Italian))\nconst JapaneseConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Japanese))\nconst KannadaConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Kannada))\nconst KoreanConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Korean))\nconst LatvianConverter = /** @type {(value: NumericValue, options?: LatvianOptions) => string} */ (makeConverter(Latvian))\nconst LithuanianConverter = /** @type {(value: NumericValue, options?: LithuanianOptions) => string} */ (makeConverter(Lithuanian))\nconst MalayConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Malay))\nconst MarathiConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Marathi))\nconst NorwegianBokmalConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(NorwegianBokmal))\nconst PersianConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Persian))\nconst PolishConverter = /** @type {(value: NumericValue, options?: PolishOptions) => string} */ (makeConverter(Polish))\nconst PortugueseConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Portuguese))\nconst PunjabiConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Punjabi))\nconst RomanianConverter = /** @type {(value: NumericValue, options?: RomanianOptions) => string} */ (makeConverter(Romanian))\nconst RussianConverter = /** @type {(value: NumericValue, options?: RussianOptions) => string} */ (makeConverter(Russian))\nconst SerbianCyrillicConverter = /** @type {(value: NumericValue, options?: SerbianCyrillicOptions) => string} */ (makeConverter(SerbianCyrillic))\nconst SerbianLatinConverter = /** @type {(value: NumericValue, options?: SerbianLatinOptions) => string} */ (makeConverter(SerbianLatin))\nconst SimplifiedChineseConverter = /** @type {(value: NumericValue, options?: SimplifiedChineseOptions) => string} */ (makeConverter(SimplifiedChinese))\nconst SpanishConverter = /** @type {(value: NumericValue, options?: SpanishOptions) => string} */ (makeConverter(Spanish))\nconst SwahiliConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Swahili))\nconst SwedishConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Swedish))\nconst TamilConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Tamil))\nconst TeluguConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Telugu))\nconst ThaiConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Thai))\nconst TraditionalChineseConverter = /** @type {(value: NumericValue, options?: TraditionalChineseOptions) => string} */ (makeConverter(TraditionalChinese))\nconst TurkishConverter = /** @type {(value: NumericValue, options?: TurkishOptions) => string} */ (makeConverter(Turkish))\nconst UkrainianConverter = /** @type {(value: NumericValue, options?: UkrainianOptions) => string} */ (makeConverter(Ukrainian))\nconst UrduConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Urdu))\nconst VietnameseConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Vietnamese))\n\n// ============================================================================\n// Exports\n// ============================================================================\n//\n// All converter functions are exported for public use.\n//\n// IMPORTANT: Keep exports alphabetically sorted for maintainability.\n// ============================================================================\n\nexport {\n ArabicConverter,\n AzerbaijaniConverter,\n BanglaConverter,\n BiblicalHebrewConverter,\n CroatianConverter,\n CzechConverter,\n DanishConverter,\n DutchConverter,\n EnglishConverter,\n FilipinoConverter,\n FrenchConverter,\n FrenchBelgiumConverter,\n GermanConverter,\n GreekConverter,\n GujaratiConverter,\n HebrewConverter,\n HindiConverter,\n HungarianConverter,\n IndonesianConverter,\n ItalianConverter,\n JapaneseConverter,\n KannadaConverter,\n KoreanConverter,\n LatvianConverter,\n LithuanianConverter,\n MalayConverter,\n MarathiConverter,\n NorwegianBokmalConverter,\n PersianConverter,\n PolishConverter,\n PortugueseConverter,\n PunjabiConverter,\n RomanianConverter,\n RussianConverter,\n SerbianCyrillicConverter,\n SerbianLatinConverter,\n SimplifiedChineseConverter,\n SpanishConverter,\n SwahiliConverter,\n SwedishConverter,\n TamilConverter,\n TeluguConverter,\n ThaiConverter,\n TraditionalChineseConverter,\n TurkishConverter,\n UkrainianConverter,\n UrduConverter,\n VietnameseConverter\n}\n","import { SlavicLanguage } from '../classes/slavic-language.js'\n\n/**\n * Czech language converter.\n *\n * Supports:\n * - Three-form pluralization (one/few/many)\n * - Dynamic decimal separator (celá/celé/celých)\n * - Gender agreement in number words\n */\nexport class Czech extends SlavicLanguage {\n negativeWord = 'mínus'\n zeroWord = 'nula'\n\n onesWords = {\n 1: 'jedna',\n 2: 'dva',\n 3: 'tři',\n 4: 'čtyři',\n 5: 'pět',\n 6: 'šest',\n 7: 'sedm',\n 8: 'osm',\n 9: 'devět'\n }\n\n onesFeminineWords = {\n 1: 'jedna',\n 2: 'dvě',\n 3: 'tři',\n 4: 'čtyři',\n 5: 'pět',\n 6: 'šest',\n 7: 'sedm',\n 8: 'osm',\n 9: 'devět'\n }\n\n teensWords = {\n 0: 'deset',\n 1: 'jedenáct',\n 2: 'dvanáct',\n 3: 'třináct',\n 4: 'čtrnáct',\n 5: 'patnáct',\n 6: 'šestnáct',\n 7: 'sedmnáct',\n 8: 'osmnáct',\n 9: 'devatenáct'\n }\n\n twentiesWords = {\n 2: 'dvacet',\n 3: 'třicet',\n 4: 'čtyřicet',\n 5: 'padesát',\n 6: 'šedesát',\n 7: 'sedmdesát',\n 8: 'osmdesát',\n 9: 'devadesát'\n }\n\n hundredsWords = {\n 1: 'sto',\n 2: 'dvě stě',\n 3: 'tři sta',\n 4: 'čtyři sta',\n 5: 'pět set',\n 6: 'šest set',\n 7: 'sedm set',\n 8: 'osm set',\n 9: 'devět set'\n }\n\n pluralForms = {\n 1: ['tisíc', 'tisíce', 'tisíc'], // 10^ 3\n 2: ['milion', 'miliony', 'milionů'], // 10^ 6\n 3: ['miliarda', 'miliardy', 'miliard'], // 10^ 9\n 4: ['bilion', 'biliony', 'bilionů'], // 10^ 12\n 5: ['biliarda', 'biliardy', 'biliard'], // 10^ 15\n 6: ['trilion', 'triliony', 'trilionů'], // 10^ 18\n 7: ['triliarda', 'triliardy', 'triliard'], // 10^ 21\n 8: ['kvadrilion', 'kvadriliony', 'kvadrilionů'], // 10^ 24\n 9: ['kvadriliarda', 'kvadriliardy', 'kvadriliard'], // 10^ 27\n 10: ['quintillion', 'quintilliony', 'quintillionů'] // 10^ 30\n }\n\n /**\n * Czech omits \"one\" before scale words.\n * e.g., 1000 is \"tisíc\" not \"jedna tisíc\"\n */\n omitOneBeforeScale = true\n\n /**\n * Cached integer part for decimal separator word selection.\n * Set by toWords() before calling super.toWords().\n * @private\n */\n #integerPart = 0n\n\n /** Returns decimal separator word based on integer part (celá/celé/celých). */\n get decimalSeparatorWord () {\n if (this.#integerPart === 0n || this.#integerPart === 1n) {\n return 'celá'\n } else if (this.#integerPart >= 2n && this.#integerPart <= 4n) {\n return 'celé'\n } else {\n return 'celých'\n }\n }\n\n constructor (options = {}) {\n super(options)\n\n // Remove the inherited decimalSeparatorWord property so our getter works\n delete this.decimalSeparatorWord\n }\n\n /**\n * Override toWords to cache integer part before decimal separator is accessed.\n */\n toWords (isNegative, integerPart, decimalPart) {\n this.#integerPart = integerPart\n return super.toWords(isNegative, integerPart, decimalPart)\n }\n\n /** Selects Czech plural form: 1 = singular, 2-4 = few, else = many. */\n pluralize (n, forms) {\n if (n === 1n) {\n return forms[0]\n }\n\n // Check if n is in the \"few\" form range (2-4, 22-24, 32-34, etc., excluding teens)\n const lastDigit = n % 10n\n const lastTwoDigits = n % 100n\n\n if (lastDigit > 1n && lastDigit < 5n && (lastTwoDigits < 10n || lastTwoDigits > 20n)) {\n return forms[1]\n }\n\n return forms[2]\n }\n}\n"],"names":["AbstractLanguage","options","negativeWord","decimalSeparatorWord","zeroWord","wordSeparator","usePerDigitDecimals","this","toWords","isNegative","integerPart","decimalPart","words","push","integerToWords","decimalDigitsToWords","join","Error","setOptions","defaults","userOptions","decimalIntegerToWords","i","length","remainder","slice","char","BigInt","SlavicLanguage","onesWords","onesFeminineWords","teensWords","twentiesWords","hundredsWords","pluralForms","scaleGenders","omitOneBeforeScale","constructor","super","gender","segments","splitToSegments","toString","segmentIndex","segmentValue","onesDigit","tensDigit","hundredsDigit","extractDigits","onesArray","pluralize","numberString","segmentSize","stringLength","remainderLength","value","number","remainder100","remainder10","expandScientificNotation","str","mantissa","expStr","toLowerCase","split","exp","parseInt","dotIndex","indexOf","digits","newDotPosition","repeat","CzechConverter","LanguageClass","n","forms","lastDigit","lastTwoDigits","undefined","proto","Object","getPrototypeOf","prototype","isPlainObject","TypeError","type","Number","isFinite","includes","numberToString","trimmed","trim","isNaN","stringToNormalizedForm","integerStr","parseNumericString","parseNumericValue"],"mappings":"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"}
|
package/dist/DanishConverter.js
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
/*! n2words/DanishConverter v2.0.0 | MIT License | github.com/forzagreen/n2words */
|
|
2
|
-
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).n2words=e.n2words||{})}(this,function(e){"use strict";class n{#e={};negativeWord="";decimalSeparatorWord="";zeroWord="";wordSeparator=" ";usePerDigitDecimals=!1;get options(){return this.#e}toWords(e,n,t){const r=[];return e&&r.push(this.negativeWord),r.push(this.integerToWords(n)),t&&(r.push(this.decimalSeparatorWord),r.push(...this.decimalDigitsToWords(t))),r.join(this.wordSeparator)}integerToWords(e){throw new Error("integerToWords() must be implemented by subclass")}setOptions(e={},n={}){this.#e={...e,...n}}decimalIntegerToWords(e){return this.integerToWords(e)}decimalDigitsToWords(e){const n=[];let t=0;for(;t<e.length&&"0"===e[t];)n.push(this.zeroWord),t++;const r=e.slice(t);if(!r)return n;if(this.usePerDigitDecimals)for(const e of r)n.push(this.decimalIntegerToWords(BigInt(e)));else n.push(this.decimalIntegerToWords(BigInt(r)));return n}}class t extends n{scaleWords;wordForScale(e){const n=this.scaleWords.find(n=>n[0]===e);return n?.[1]}decomposeInteger(e){const n=[];let t=e;do{const e=this.scaleWords.find(e=>t>=e[0]);if(!e)break;const r=0n===t?1n:t/e[0];1n===r?n.push({[this.wordForScale(1n)]:1n}):n.push(this.decomposeInteger(r)),n.push({[e[1]]:e[0]}),t=0n===t?0n:t%e[0]}while(t>0n);return n}reduceWordSets(e){for(;e.length>1;){const[n,t,...r]=e;if(!Array.isArray(n)&&!Array.isArray(t)){const i=this.combineWordSets(n,t);e=r.length>0?[i,r]:[i];continue}const i=e.map(e=>Array.isArray(e)?1===e.length?e[0]:this.reduceWordSets(e):e);e=i}return e[0]}combineWordSets(e,n){throw new Error("combineWordSets() must be implemented by subclass")}finalizeWords(e){return e.trimEnd()}integerToWords(e){const n=this.decomposeInteger(e),t=this.reduceWordSets(n),r=Object.keys(t)[0];return this.finalizeWords(r)}}function r(e){const[n,t]=e.toLowerCase().split("e"),r=parseInt(t,10),i=n.indexOf("."),o=-1===i?n:n.slice(0,i)+n.slice(i+1),s=(-1===i?n.length:i)+r;return s>=o.length?o+"0".repeat(s-o.length):s<=0?"0."+"0".repeat(-s)+o:o.slice(0,s)+"."+o.slice(s)}const i=(o=class extends t{negativeWord="minus";decimalSeparatorWord="komma";zeroWord="nul";scaleWords=[[1000000000000000000000000000n,"quadrillarder"],[1000000000000000000000000n,"quadrillioner"],[1000000000000000000000n,"trillarder"],[1000000000000000000n,"trillioner"],[1000000000000000n,"billarder"],[1000000000000n,"billioner"],[1000000000n,"millarder"],[1000000n,"millioner"],[1000n,"tusind"],[100n,"hundrede"],[90n,"halvfems"],[80n,"firs"],[70n,"halvfjerds"],[60n,"treds"],[50n,"halvtreds"],[40n,"fyrre"],[30n,"tredive"],[20n,"tyve"],[19n,"nitten"],[18n,"atten"],[17n,"sytten"],[16n,"seksten"],[15n,"femten"],[14n,"fjorten"],[13n,"tretten"],[12n,"tolv"],[11n,"elleve"],[10n,"ti"],[9n,"ni"],[8n,"otte"],[7n,"syv"],[6n,"seks"],[5n,"fem"],[4n,"fire"],[3n,"tre"],[2n,"to"],[1n,"et"],[0n,"nul"]];constructor(e={}){super(),this.setOptions({ordFlag:!1},e)}combineWordSets(e,n){let t=Object.keys(e)[0],r=Object.keys(n)[0];const i=Object.values(e)[0],o=Object.values(n)[0];if(100n!==o&&1000n!==o||(n={[`et${r}`]:o}),1n===i){if(o<1000000n||this.options.ordFlag)return n;t="en"}if(o>i)return o>=1000000n&&(t+=" "),{[`${t}${r}`]:i*o};if(i>=100n&&i<1000n?t+=" og ":i>=1000n&&i<=100000n&&(t+="e og "),o<10n&&i>10n&&i<100n){1n===o&&(r="en");const e=r;r=t,t=e+"og"}else i>=1000000n&&(t+=" ");return{[`${t}${r}`]:i+o}}},function(e,n){if(void 0!==n&&!function(e){if(null===e||"object"!=typeof e)return!1;const n=Object.getPrototypeOf(e);return n===Object.prototype||null===n}(n))throw new TypeError("options must be a plain object if provided");const{isNegative:t,integerPart:i,decimalPart:s}=function(e){const n=typeof e;if("bigint"===n)return e<0n?{isNegative:!0,integerPart:-e}:{isNegative:!1,integerPart:e};if("number"!==n&&"string"!==n)throw new TypeError(`Invalid value type: expected number, string, or bigint, received ${n}`);const t="number"===n?function(e){if(!Number.isFinite(e))throw new Error("Number must be finite (NaN and Infinity are not supported)");const n=e.toString();return n.includes("e")||n.includes("E")?r(n):n}(e):function(e){const n=e.trim();if(0===n.length)throw new Error(`Invalid number format: "${e}"`);if(Number.isNaN(Number(n)))throw new Error(`Invalid number format: "${e}"`);return n.includes("e")||n.includes("E")?r(n):n}(e);return function(e){const n="-"===e[0];n&&(e=e.slice(1));const t=e.indexOf(".");if(-1===t)return{isNegative:n,integerPart:BigInt(e)};const r=e.slice(0,t)||"0",i=e.slice(t+1);return{isNegative:n,integerPart:BigInt(r),decimalPart:i}}(t)}(e);return new o(n).toWords(t,i,s)});var o;e.DanishConverter=i});
|
|
3
|
-
//# sourceMappingURL=DanishConverter.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"DanishConverter.js","sources":["../lib/classes/abstract-language.js","../lib/classes/greedy-scale-language.js","../lib/n2words.js","../lib/languages/da.js"],"sourcesContent":["/**\n * Abstract base class for language converters.\n *\n * This class provides the framework for converting numbers to words in any language.\n * It handles the common conversion flow while delegating language-specific logic to subclasses.\n *\n * ## Responsibilities\n *\n * - Receives pre-validated and normalized input from the public API (n2words.js)\n * - Handles negative number prefixing via `negativeWord`\n * - Converts decimals via `decimalDigitsToWords()`, preserving leading zeros\n * - Delegates integer conversion to `integerToWords()` (implemented by subclasses)\n *\n * ## Required Subclass Implementation\n *\n * Subclasses MUST provide:\n * - `integerToWords(integerPart)` - Core conversion logic (abstract method)\n * - `negativeWord` - Word preceding negative numbers (e.g., \"minus\"))\n * - `zeroWord` - Word for the digit 0 (e.g., \"zero\")\n * - `decimalSeparatorWord` - Word between whole and decimal parts (e.g., \"point\")\n *\n * ## Optional Overrides\n *\n * Subclasses MAY override:\n * - `wordSeparator` - Character(s) between words (default: space, empty for CJK languages)\n * - `usePerDigitDecimals` - Enable per-digit decimal mode (default: false)\n * - `decimalIntegerToWords()` - Custom decimal conversion (e.g., Romanian masculine forms)\n * - `decimalDigitsToWords()` - Complete decimal conversion override\n * - `toWords()` - Override to capture integerPart for context-dependent rules (e.g., Czech)\n *\n * ## Input Contract\n *\n * Input validation and normalization happen at the public API boundary (n2words.js).\n * This class assumes it receives clean, pre-processed data via `toWords()`.\n *\n * @abstract\n */\nexport class AbstractLanguage {\n // ============================================================================\n // Private Fields\n // ============================================================================\n\n /**\n * Private storage for options.\n * @type {Object}\n */\n #options = {}\n\n // ============================================================================\n // Required Properties (subclasses must define)\n // ============================================================================\n\n /**\n * Word that precedes negative numbers (e.g., \"minus\", \"negative\", \"moins\").\n * @type {string}\n */\n negativeWord = ''\n\n /**\n * Word that separates integer and decimal parts (e.g., \"point\", \"virgule\", \"comma\").\n * @type {string}\n */\n decimalSeparatorWord = ''\n\n /**\n * Word representation for the digit 0 (e.g., \"zero\", \"zéro\", \"null\").\n * Used for zero values and leading zeros in decimals.\n * @type {string}\n */\n zeroWord = ''\n\n // ============================================================================\n // Optional Properties (subclasses may override)\n // ============================================================================\n\n /**\n * Character(s) used to separate words in the output.\n *\n * Defaults to a single space. Set to empty string for languages without\n * word separators (e.g., Japanese, Thai, Chinese).\n *\n * @type {string}\n */\n wordSeparator = ' '\n\n /**\n * Whether to convert decimal digits individually rather than grouped.\n *\n * - `false` (default): Leading zeros preserved, remaining digits grouped as a number\n * - Example: \"05\" → [\"zero\", \"five\"], \"14\" → [\"fourteen\"]\n * - Used by: English, Spanish, French, German, etc.\n *\n * - `true`: Each digit converted separately\n * - Example: \"05\" → [\"zero\", \"five\"], \"14\" → [\"one\", \"four\"]\n * - Used by: Japanese, Thai, Tamil, Telugu, Greek, Hebrew, Filipino\n *\n * @type {boolean}\n */\n usePerDigitDecimals = false\n\n // ============================================================================\n // Public Accessors\n // ============================================================================\n\n /**\n * Read-only access to options set via `setOptions()`.\n * @type {Object}\n */\n get options () {\n return this.#options\n }\n\n // ============================================================================\n // Public Methods\n // ============================================================================\n\n /**\n * Converts pre-normalized numeric components to words.\n *\n * This is the main entry point called by the public API (makeConverter in n2words.js).\n * It assembles the final word representation from the provided components.\n *\n * **Caller contract (enforced by makeConverter):**\n * - `integerPart` is a non-negative BigInt (>= 0n)\n * - `decimalPart` is a string of digits only (no sign, no decimal point)\n * - `isNegative` reflects the original input sign\n *\n * **Conversion flow:**\n * 1. Prepend negative word if applicable\n * 2. Convert integer part via `integerToWords()`\n * 3. If decimals present: append separator and decimal words\n * 4. Join all parts with `wordSeparator`\n *\n * Subclasses needing access to the integer part during decimal conversion\n * (e.g., for context-dependent separator words) should override this\n * method to cache the value before calling super.toWords().\n *\n * @public\n * @param {boolean} isNegative Whether the original number was negative\n * @param {bigint} integerPart The integer part (always non-negative)\n * @param {string} [decimalPart] - Decimal digits if present (e.g., \"14\" for 3.14)\n * @returns {string} The localized cardinal string\n */\n toWords (isNegative, integerPart, decimalPart) {\n const words = []\n\n if (isNegative) words.push(this.negativeWord)\n\n words.push(this.integerToWords(integerPart))\n\n if (decimalPart) {\n words.push(this.decimalSeparatorWord)\n words.push(...this.decimalDigitsToWords(decimalPart))\n }\n\n return words.join(this.wordSeparator)\n }\n\n // ============================================================================\n // Abstract Methods (subclasses must implement)\n // ============================================================================\n\n /**\n * Converts a BigInt integer part to its cardinal word representation.\n *\n * This is the core template method that subclasses MUST implement to provide\n * language-specific number conversion logic.\n *\n * @abstract\n * @param {bigint} integerPart The integer part to convert (always >= 0n)\n * @returns {string} The cardinal representation in the target language\n * @throws {Error} If not implemented by subclass\n */\n integerToWords (integerPart) {\n throw new Error('integerToWords() must be implemented by subclass')\n }\n\n // ============================================================================\n // Protected Methods (subclasses may override or call)\n // ============================================================================\n\n /**\n * Sets options by merging language defaults with user-provided options.\n *\n * Merges defaults first, then user options (later keys override earlier ones).\n * Stores the result in the private `#options` field, accessible via the\n * read-only `options` getter.\n *\n * @protected\n * @param {Object} [defaults={}] - Default option values for the language\n * @param {Object} [userOptions={}] - Runtime options supplied by the caller\n *\n * @example\n * constructor(options = {}) {\n * super()\n * this.setOptions({ gender: 'masculine' }, options)\n * }\n */\n setOptions (defaults = {}, userOptions = {}) {\n this.#options = {\n ...defaults,\n ...userOptions\n }\n }\n\n /**\n * Converts an integer to words in decimal context.\n *\n * By default, delegates to `integerToWords()`. Override this method\n * when decimal conversion requires different behavior than integer conversion.\n *\n * Called with:\n * - Single digits (0-9) when `usePerDigitDecimals = true`\n * - Grouped numbers when `usePerDigitDecimals = false`\n *\n * **Use cases for overriding:**\n * - Romanian: Decimals always use masculine forms regardless of gender option\n * - Languages with different plural/gender rules for decimal vs integer parts\n *\n * @protected\n * @param {bigint} integerPart The integer to convert (single digit or grouped)\n * @returns {string} The word representation for use in decimal context\n */\n decimalIntegerToWords (integerPart) {\n return this.integerToWords(integerPart)\n }\n\n /**\n * Converts decimal fractional digits into an array of words.\n *\n * Leading zeros are always preserved individually. The remaining digits\n * are converted based on `usePerDigitDecimals`:\n *\n * - `false` (default): Remaining digits grouped as a single number\n * - \"05\" → [\"zero\", \"five\"], \"14\" → [\"fourteen\"]\n *\n * - `true`: Each remaining digit converted separately\n * - \"05\" → [\"zero\", \"five\"], \"14\" → [\"one\", \"four\"]\n *\n * @protected\n * @param {string} decimalPart Decimal digits as string (e.g., \"05\" for 3.05)\n * @returns {string[]} Array of word tokens for the fractional part\n */\n decimalDigitsToWords (decimalPart) {\n const words = []\n\n // Always preserve leading zeros individually\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n words.push(this.zeroWord)\n i++\n }\n\n const remainder = decimalPart.slice(i)\n if (!remainder) return words\n\n // Convert remainder: per-digit or as single number\n if (this.usePerDigitDecimals) {\n for (const char of remainder) {\n words.push(this.decimalIntegerToWords(BigInt(char)))\n }\n } else {\n words.push(this.decimalIntegerToWords(BigInt(remainder)))\n }\n\n return words\n }\n}\n","import { AbstractLanguage } from './abstract-language.js'\n\n/**\n * Greedy scale language converter implementing the \"highest-matching scale word\" algorithm.\n *\n * Responsibilities:\n * - Decompose an integer into a sequence of word-sets using greedy matching.\n * - Provide helpers to reduce and post-process matched word-sets.\n * - Inherits decimal handling from AbstractLanguage (supports grouped and per-digit\n * modes via the `usePerDigitDecimals` class property).\n *\n * Subclass requirements:\n * - Define `scaleWords` (ordered descending) as `[bigint, string]` tuples.\n * - Implement 'combineWordSets(preceding, following)' to combine adjacent word-sets\n * per language grammar.\n *\n * Scale words specification:\n * - `scaleWords` is an Array of 2-tuples: `[bigint, string]` where the first element\n * is the numeric scale value and the second is the word for that value.\n * - Scale words MUST be ordered from largest to smallest (descending) for the algorithm\n * to function correctly.\n *\n * Terminology:\n * - **Scale**: A magnitude value (100n, 1000n, 1000000n)\n * - **Word-set**: An object `{ word: bigint }` representing a partial result\n * - **Scale word**: The word for a scale value (\"hundred\", \"thousand\")\n *\n * @abstract\n * @extends AbstractLanguage\n */\n\nexport class GreedyScaleLanguage extends AbstractLanguage {\n // ============================================================================\n // Required Properties (subclasses must define)\n // ============================================================================\n\n /**\n * Array of scale words mapping numeric values to their word representations.\n *\n * Each element is a 2-tuple: `[bigint, string]` where the first element is the\n * numeric scale value and the second is the word for that value. The array MUST be\n * ordered from largest to smallest (descending) for the greedy algorithm to work correctly.\n *\n * @type {Array<[bigint, string]>}\n * @example\n * // English scale words (descending order):\n * // [[1000000000n, 'billion'], [1000000n, 'million'], [1000n, 'thousand'], [100n, 'hundred'], ..., [1n, 'one']]\n */\n scaleWords\n\n // ============================================================================\n // Public Methods\n // ============================================================================\n\n /**\n * Returns the word for an exact scale value.\n *\n * @param {bigint} scale The scale value to look up (prefer BigInt for exact matching).\n * @returns {string|undefined} The word for the provided scale, or `undefined`.\n */\n wordForScale (scale) {\n const match = this.scaleWords.find((pair) => pair[0] === scale)\n return match?.[1]\n }\n\n // ============================================================================\n // Protected Methods (subclasses may call or override)\n // ============================================================================\n\n /**\n * Decomposes an integer into a sequence of word-sets.\n *\n * This internal helper returns a nested structure that represents quantities and\n * their matching scale words (e.g. `[{ 'one': 1n }, { 'hundred': 100n }, ...]`).\n * The result is designed to be reduced by `reduceWordSets()` using language-specific `combineWordSets()`.\n *\n * For quantities > 1, the multiplier is recursively decomposed. For quantity = 1,\n * the implicit \"one\" is represented with `{ 'one': 1n }` and typically omitted during combineWordSets().\n *\n * @protected\n * @param {bigint} integerPart The integer to decompose.\n * @returns {Array<Object|Array>} An array of word-set objects and possibly nested arrays.\n */\n decomposeInteger (integerPart) {\n const wordSets = []\n let remaining = integerPart\n\n do {\n const match = this.scaleWords.find((pair) => remaining >= pair[0])\n if (!match) break\n\n const multiplier = remaining === 0n ? 1n : remaining / match[0]\n\n if (multiplier === 1n) {\n wordSets.push({ [this.wordForScale(1n)]: 1n })\n } else {\n wordSets.push(this.decomposeInteger(multiplier))\n }\n\n wordSets.push({ [match[1]]: match[0] })\n\n remaining = remaining === 0n ? 0n : remaining % match[0]\n } while (remaining > 0n)\n\n return wordSets\n }\n\n /**\n * Reduces a nested array of word-sets into a single word-set object.\n *\n * This method repeatedly applies the subclass `combineWordSets()` operation until a single\n * object remains. It normalizes nested arrays by recursively reducing them.\n *\n * @protected\n * @param {Array<Object|Array>} wordSets Array of word-set objects and nested arrays.\n * @returns {Object} Reduced word-set where the single object key is the language string\n * and its value is the numeric BigInt result for that string.\n */\n reduceWordSets (wordSets) {\n while (wordSets.length > 1) {\n const [first, second, ...rest] = wordSets\n\n if (!Array.isArray(first) && !Array.isArray(second)) {\n const combined = this.combineWordSets(first, second)\n wordSets = rest.length > 0 ? [combined, rest] : [combined]\n continue\n }\n\n const normalized = wordSets.map((element) => {\n if (!Array.isArray(element)) return element\n return element.length === 1 ? element[0] : this.reduceWordSets(element)\n })\n\n wordSets = normalized\n }\n\n return wordSets[0]\n }\n\n // ============================================================================\n // Abstract Methods (subclasses must implement)\n // ============================================================================\n\n /**\n * Combines two adjacent word-sets into a single word-set.\n *\n * This is the core language-specific method that must be implemented by subclasses\n * to define how adjacent word-sets are combined according to the language's grammar.\n * For example, English combines \"twenty\" + \"three\" → \"twenty-three\", while\n * French might combine \"quatre-vingts\" + \"dix\" → \"quatre-vingt-dix\".\n *\n * @abstract\n * @protected\n * @param {Object} preceding Preceding word-set as `{ word: bigint }`.\n * @param {Object} following Following word-set as `{ word: bigint }`.\n * @returns {Object} Combined word-set with merged text and resulting numeric value.\n *\n * @example\n * // English implementation might handle:\n * // combineWordSets({ 'twenty': 20n }, { 'three': 3n }) → { 'twenty-three': 23n }\n * // combineWordSets({ 'one': 1n }, { 'hundred': 100n }) → { 'one hundred': 100n }\n */\n combineWordSets (preceding, following) {\n throw new Error('combineWordSets() must be implemented by subclass')\n }\n\n // ============================================================================\n // Optional Methods (subclasses may override)\n // ============================================================================\n\n /**\n * Final string post-processing hook.\n *\n * Subclasses may override to apply language-specific whitespace, punctuation or\n * orthographic corrections.\n *\n * @protected\n * @param {string} output Language string produced by the conversion flow.\n * @returns {string} Final formatted string.\n */\n finalizeWords (output) {\n return output.trimEnd()\n }\n\n /**\n * Converts an integer to its language-specific cardinal words.\n *\n * This method orchestrates decomposition, reduction, and final formatting. It does\n * not handle decimals or sign; those concerns are implemented in\n * `AbstractLanguage.toWords` which calls this method for the integer part.\n *\n * @param {bigint} integerPart The integer to convert.\n * @returns {string} The cardinal representation for the integer in the language.\n */\n integerToWords (integerPart) {\n const wordSets = this.decomposeInteger(integerPart)\n const reduced = this.reduceWordSets(wordSets)\n const result = Object.keys(reduced)[0]\n return this.finalizeWords(result)\n }\n}\n","/**\n * n2words - Convert numbers to words in multiple languages\n *\n * This file is the main entry point for the n2words library.\n * It exports converter functions for all supported languages.\n *\n * ## For Contributors\n *\n * When adding a new language, this file must be updated in THREE sections:\n * 1. Language Imports - Add import statement (alphabetically sorted)\n * 2. Language Converters - Create converter with makeConverter() (alphabetically sorted)\n * 3. Exports - Add to export list (alphabetically sorted)\n *\n * Use the scaffolding tool to automate this process:\n * npm run lang:add <language-code>\n *\n * ## Public API Structure\n *\n * Each language exports a converter function:\n * - Name: `{LanguageName}Converter` (e.g., EnglishConverter, SpanishConverter)\n * - Signature: `(value: NumericValue, options?: Options) => string`\n * - Input: number, bigint, or string (numeric strings only)\n * - Output: Words representing the number in the target language\n *\n * Languages without options use signature: `(value: NumericValue) => string`\n * Languages with options define a typedef (e.g., ArabicOptions) and use: `(value: NumericValue, options?: ArabicOptions) => string`\n *\n * @module n2words\n */\n\n// ============================================================================\n// Language Imports\n// ============================================================================\n\nimport { Arabic } from './languages/ar.js'\nimport { Azerbaijani } from './languages/az.js'\nimport { Bangla } from './languages/bn.js'\nimport { Czech } from './languages/cs.js'\nimport { Danish } from './languages/da.js'\nimport { German } from './languages/de.js'\nimport { Greek } from './languages/el.js'\nimport { English } from './languages/en.js'\nimport { Spanish } from './languages/es.js'\nimport { Persian } from './languages/fa.js'\nimport { Filipino } from './languages/fil.js'\nimport { French } from './languages/fr.js'\nimport { FrenchBelgium } from './languages/fr-BE.js'\nimport { Gujarati } from './languages/gu.js'\nimport { Hebrew } from './languages/he.js'\nimport { BiblicalHebrew } from './languages/hbo.js'\nimport { Hindi } from './languages/hi.js'\nimport { Croatian } from './languages/hr.js'\nimport { Hungarian } from './languages/hu.js'\nimport { Indonesian } from './languages/id.js'\nimport { Italian } from './languages/it.js'\nimport { Japanese } from './languages/ja.js'\nimport { Kannada } from './languages/kn.js'\nimport { Korean } from './languages/ko.js'\nimport { Lithuanian } from './languages/lt.js'\nimport { Latvian } from './languages/lv.js'\nimport { Marathi } from './languages/mr.js'\nimport { Malay } from './languages/ms.js'\nimport { Dutch } from './languages/nl.js'\nimport { NorwegianBokmal } from './languages/nb.js'\nimport { Punjabi } from './languages/pa.js'\nimport { Polish } from './languages/pl.js'\nimport { Portuguese } from './languages/pt.js'\nimport { Romanian } from './languages/ro.js'\nimport { Russian } from './languages/ru.js'\nimport { SerbianCyrillic } from './languages/sr-Cyrl.js'\nimport { SerbianLatin } from './languages/sr-Latn.js'\nimport { Swedish } from './languages/sv.js'\nimport { Swahili } from './languages/sw.js'\nimport { Tamil } from './languages/ta.js'\nimport { Telugu } from './languages/te.js'\nimport { Thai } from './languages/th.js'\nimport { Turkish } from './languages/tr.js'\nimport { Ukrainian } from './languages/uk.js'\nimport { Urdu } from './languages/ur.js'\nimport { Vietnamese } from './languages/vi.js'\nimport { SimplifiedChinese } from './languages/zh-Hans.js'\nimport { TraditionalChinese } from './languages/zh-Hant.js'\n\n// ============================================================================\n// Type Definitions\n// ============================================================================\n//\n// This section defines TypeScript-compatible JSDoc types for:\n// - NumericValue: The input types accepted by all converters\n// - {Language}Options: Optional configuration for languages that support it\n//\n// Keep options typedefs alphabetically sorted for maintainability.\n// ============================================================================\n\n/**\n * Numeric value that can be converted to words.\n * Accepts number, bigint, or numeric string representations.\n * @typedef {number | bigint | string} NumericValue\n */\n\n/**\n * @typedef {Object} ArabicOptions\n * @property {string} [negativeWord] Word for negative numbers\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} BiblicalHebrewOptions\n * @property {string} [andWord='ו'] Conjunction character (typically 'ו' for and)\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} CroatianOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} CzechOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} DanishOptions\n * @property {boolean} [ordFlag=false] Enable ordinal number conversion\n */\n\n/**\n * @typedef {Object} DutchOptions\n * @property {boolean} [includeOptionalAnd=false] Include optional \"en\" separator\n * @property {boolean} [noHundredPairing=false] Disable hundred-pairing (e.g., \"twelve hundred\" becomes \"one thousand two hundred\")\n * @property {boolean} [accentOne=true] Use accented \"één\" for one\n */\n\n/**\n * @typedef {Object} FrenchOptions\n * @property {boolean} [withHyphenSeparator=false] Use hyphens (true) instead of spaces (false) in compounds\n */\n\n/**\n * @typedef {Object} FrenchBelgiumOptions\n * @property {boolean} [withHyphenSeparator=false] Use hyphens (true) instead of spaces (false) in compounds\n */\n\n/**\n * @typedef {Object} HebrewOptions\n * @property {string} [andWord='ו'] Conjunction character (typically 'ו' for and)\n */\n\n/**\n * @typedef {Object} LatvianOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} LithuanianOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} PolishOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} RomanianOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} RussianOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} SerbianCyrillicOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} SerbianLatinOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} SimplifiedChineseOptions\n * @property {boolean} [formal=true] Use formal/financial numerals (壹贰叁) vs. common numerals (一二三)\n */\n\n/**\n * @typedef {Object} SpanishOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} TraditionalChineseOptions\n * @property {boolean} [formal=true] Use formal/financial numerals (壹貳參) vs. common numerals (一二三)\n */\n\n/**\n * @typedef {Object} TurkishOptions\n * @property {boolean} [dropSpaces=false] Remove spaces between words if true\n */\n\n/**\n * @typedef {Object} UkrainianOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n// ============================================================================\n// Converter Factory\n// ============================================================================\n//\n// makeConverter() is a factory function that creates converter functions from\n// language classes. It provides a consistent functional API for all languages:\n//\n// const result = EnglishConverter(42) // \"forty-two\"\n// const result = ArabicConverter(1, {gender: 'feminine'}) // \"واحدة\"\n//\n// This design:\n// - Hides class instantiation details from public API\n// - Ensures each conversion uses a fresh instance\n// - Maintains immutability (no shared state between conversions)\n// ============================================================================\n\n/**\n * Creates a converter function for a language class.\n *\n * This factory handles all input validation and normalization at the public API\n * boundary, then delegates to the language class with pre-processed data.\n *\n * @template {Object} [TOptions={}]\n * @param {new (options?: TOptions) => { toWords: (isNegative: boolean, integerPart: bigint, decimalPart?: string) => string }} LanguageClass - Language class constructor\n * @returns {(value: NumericValue, options?: TOptions) => string} Converter function\n */\nfunction makeConverter (LanguageClass) {\n /**\n * @param {NumericValue} value\n * @param {TOptions} [options]\n * @returns {string}\n */\n return function convertToWords (value, options) {\n if (options !== undefined && !isPlainObject(options)) {\n throw new TypeError('options must be a plain object if provided')\n }\n\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n return new LanguageClass(options).toWords(isNegative, integerPart, decimalPart)\n }\n}\n\n// ============================================================================\n// Input Parsing Utilities\n// ============================================================================\n\n/**\n * @typedef {Object} ParsedNumericValue\n * @property {boolean} isNegative - Whether the value is negative\n * @property {bigint} integerPart - The absolute integer part\n * @property {string} [decimalPart] - The decimal digits (without the point)\n */\n\n/**\n * Parses and validates a numeric value into its components.\n * Handles number, string, and bigint inputs uniformly.\n *\n * @param {NumericValue} value The value to parse\n * @returns {ParsedNumericValue} The parsed components\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n */\nfunction parseNumericValue (value) {\n const type = typeof value\n\n // BigInt: simplest case - no decimals, no scientific notation\n if (type === 'bigint') {\n return value < 0n\n ? { isNegative: true, integerPart: -value }\n : { isNegative: false, integerPart: value }\n }\n\n // Reject invalid types early\n if (type !== 'number' && type !== 'string') {\n throw new TypeError(\n `Invalid value type: expected number, string, or bigint, received ${type}`\n )\n }\n\n // Convert to normalized string\n const str = type === 'number'\n ? numberToString(value)\n : stringToNormalizedForm(value)\n\n return parseNumericString(str)\n}\n\n/**\n * Converts a JavaScript number to a decimal string.\n * Handles special cases: Infinity, NaN, and scientific notation.\n *\n * @param {number} value The number to convert\n * @returns {string} Decimal string representation\n * @throws {Error} If value is not finite\n */\nfunction numberToString (value) {\n if (!Number.isFinite(value)) {\n throw new Error('Number must be finite (NaN and Infinity are not supported)')\n }\n\n const str = value.toString()\n\n // Expand scientific notation (used for values >= 1e21 or < 1e-6)\n if (str.includes('e') || str.includes('E')) {\n return expandScientificNotation(str)\n }\n\n return str\n}\n\n/**\n * Validates and normalizes a string numeric input.\n *\n * @param {string} value The string to validate\n * @returns {string} Trimmed and validated string\n * @throws {Error} If string is empty or not a valid number format\n */\nfunction stringToNormalizedForm (value) {\n const trimmed = value.trim()\n\n if (trimmed.length === 0) {\n throw new Error(`Invalid number format: \"${value}\"`)\n }\n\n // Validate by attempting conversion (handles edge cases like \"1e21\", \"-.5\", etc.)\n if (Number.isNaN(Number(trimmed))) {\n throw new Error(`Invalid number format: \"${value}\"`)\n }\n\n // Expand scientific notation if present\n if (trimmed.includes('e') || trimmed.includes('E')) {\n return expandScientificNotation(trimmed)\n }\n\n return trimmed\n}\n\n/**\n * Parses a normalized numeric string into its components.\n *\n * @param {string} str A normalized decimal string (no scientific notation)\n * @returns {ParsedNumericValue} The parsed components\n */\nfunction parseNumericString (str) {\n // Extract sign\n const isNegative = str[0] === '-'\n if (isNegative) {\n str = str.slice(1)\n }\n\n // Split into integer and decimal parts\n const dotIndex = str.indexOf('.')\n if (dotIndex === -1) {\n return { isNegative, integerPart: BigInt(str) }\n }\n\n const integerStr = str.slice(0, dotIndex) || '0'\n const decimalPart = str.slice(dotIndex + 1)\n\n return { isNegative, integerPart: BigInt(integerStr), decimalPart }\n}\n\n/**\n * Expands scientific notation to decimal form.\n * JavaScript uses scientific notation for values >= 1e21 or < 1e-6.\n *\n * @param {string} str String possibly in scientific notation (e.g., \"1e+21\")\n * @returns {string} Decimal form (e.g., \"1000000000000000000000\")\n */\nfunction expandScientificNotation (str) {\n const [mantissa, expStr] = str.toLowerCase().split('e')\n const exp = parseInt(expStr, 10)\n\n // Extract digits and determine original decimal position\n const dotIndex = mantissa.indexOf('.')\n const digits = dotIndex === -1\n ? mantissa\n : mantissa.slice(0, dotIndex) + mantissa.slice(dotIndex + 1)\n const integerLength = dotIndex === -1 ? mantissa.length : dotIndex\n\n // Calculate new decimal position after applying exponent\n const newDotPosition = integerLength + exp\n\n if (newDotPosition >= digits.length) {\n // Pure integer: pad with trailing zeros\n return digits + '0'.repeat(newDotPosition - digits.length)\n }\n\n if (newDotPosition <= 0) {\n // Pure decimal: pad with leading zeros after decimal point\n return '0.' + '0'.repeat(-newDotPosition) + digits\n }\n\n // Mixed: insert decimal point at new position\n return digits.slice(0, newDotPosition) + '.' + digits.slice(newDotPosition)\n}\n\n/**\n * Checks if a value is a plain object (not null, array, or other object types).\n *\n * @param {*} value Value to check\n * @returns {boolean} True if value is a plain object\n */\nfunction isPlainObject (value) {\n if (value === null || typeof value !== 'object') return false\n const proto = Object.getPrototypeOf(value)\n return proto === Object.prototype || proto === null\n}\n\n// ============================================================================\n// Language Converters\n// ============================================================================\n//\n// Each converter is created using makeConverter() with explicit type annotations.\n//\n// Pattern for languages WITHOUT options:\n// const LanguageConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Language))\n//\n// Pattern for languages WITH options:\n// const LanguageConverter = /** @type {(value: NumericValue, options?: LanguageOptions) => string} */ (makeConverter(Language))\n//\n// IMPORTANT: Keep converters alphabetically sorted by converter name.\n// ============================================================================\n\nconst ArabicConverter = /** @type {(value: NumericValue, options?: ArabicOptions) => string} */ (makeConverter(Arabic))\nconst AzerbaijaniConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Azerbaijani))\nconst BanglaConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Bangla))\nconst BiblicalHebrewConverter = /** @type {(value: NumericValue, options?: BiblicalHebrewOptions) => string} */ (makeConverter(BiblicalHebrew))\nconst CroatianConverter = /** @type {(value: NumericValue, options?: CroatianOptions) => string} */ (makeConverter(Croatian))\nconst CzechConverter = /** @type {(value: NumericValue, options?: CzechOptions) => string} */ (makeConverter(Czech))\nconst DanishConverter = /** @type {(value: NumericValue, options?: DanishOptions) => string} */ (makeConverter(Danish))\nconst DutchConverter = /** @type {(value: NumericValue, options?: DutchOptions) => string} */ (makeConverter(Dutch))\nconst EnglishConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(English))\nconst FilipinoConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Filipino))\nconst FrenchConverter = /** @type {(value: NumericValue, options?: FrenchOptions) => string} */ (makeConverter(French))\nconst FrenchBelgiumConverter = /** @type {(value: NumericValue, options?: FrenchBelgiumOptions) => string} */ (makeConverter(FrenchBelgium))\nconst GermanConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(German))\nconst GreekConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Greek))\nconst GujaratiConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Gujarati))\nconst HebrewConverter = /** @type {(value: NumericValue, options?: HebrewOptions) => string} */ (makeConverter(Hebrew))\nconst HindiConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Hindi))\nconst HungarianConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Hungarian))\nconst IndonesianConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Indonesian))\nconst ItalianConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Italian))\nconst JapaneseConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Japanese))\nconst KannadaConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Kannada))\nconst KoreanConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Korean))\nconst LatvianConverter = /** @type {(value: NumericValue, options?: LatvianOptions) => string} */ (makeConverter(Latvian))\nconst LithuanianConverter = /** @type {(value: NumericValue, options?: LithuanianOptions) => string} */ (makeConverter(Lithuanian))\nconst MalayConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Malay))\nconst MarathiConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Marathi))\nconst NorwegianBokmalConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(NorwegianBokmal))\nconst PersianConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Persian))\nconst PolishConverter = /** @type {(value: NumericValue, options?: PolishOptions) => string} */ (makeConverter(Polish))\nconst PortugueseConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Portuguese))\nconst PunjabiConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Punjabi))\nconst RomanianConverter = /** @type {(value: NumericValue, options?: RomanianOptions) => string} */ (makeConverter(Romanian))\nconst RussianConverter = /** @type {(value: NumericValue, options?: RussianOptions) => string} */ (makeConverter(Russian))\nconst SerbianCyrillicConverter = /** @type {(value: NumericValue, options?: SerbianCyrillicOptions) => string} */ (makeConverter(SerbianCyrillic))\nconst SerbianLatinConverter = /** @type {(value: NumericValue, options?: SerbianLatinOptions) => string} */ (makeConverter(SerbianLatin))\nconst SimplifiedChineseConverter = /** @type {(value: NumericValue, options?: SimplifiedChineseOptions) => string} */ (makeConverter(SimplifiedChinese))\nconst SpanishConverter = /** @type {(value: NumericValue, options?: SpanishOptions) => string} */ (makeConverter(Spanish))\nconst SwahiliConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Swahili))\nconst SwedishConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Swedish))\nconst TamilConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Tamil))\nconst TeluguConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Telugu))\nconst ThaiConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Thai))\nconst TraditionalChineseConverter = /** @type {(value: NumericValue, options?: TraditionalChineseOptions) => string} */ (makeConverter(TraditionalChinese))\nconst TurkishConverter = /** @type {(value: NumericValue, options?: TurkishOptions) => string} */ (makeConverter(Turkish))\nconst UkrainianConverter = /** @type {(value: NumericValue, options?: UkrainianOptions) => string} */ (makeConverter(Ukrainian))\nconst UrduConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Urdu))\nconst VietnameseConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Vietnamese))\n\n// ============================================================================\n// Exports\n// ============================================================================\n//\n// All converter functions are exported for public use.\n//\n// IMPORTANT: Keep exports alphabetically sorted for maintainability.\n// ============================================================================\n\nexport {\n ArabicConverter,\n AzerbaijaniConverter,\n BanglaConverter,\n BiblicalHebrewConverter,\n CroatianConverter,\n CzechConverter,\n DanishConverter,\n DutchConverter,\n EnglishConverter,\n FilipinoConverter,\n FrenchConverter,\n FrenchBelgiumConverter,\n GermanConverter,\n GreekConverter,\n GujaratiConverter,\n HebrewConverter,\n HindiConverter,\n HungarianConverter,\n IndonesianConverter,\n ItalianConverter,\n JapaneseConverter,\n KannadaConverter,\n KoreanConverter,\n LatvianConverter,\n LithuanianConverter,\n MalayConverter,\n MarathiConverter,\n NorwegianBokmalConverter,\n PersianConverter,\n PolishConverter,\n PortugueseConverter,\n PunjabiConverter,\n RomanianConverter,\n RussianConverter,\n SerbianCyrillicConverter,\n SerbianLatinConverter,\n SimplifiedChineseConverter,\n SpanishConverter,\n SwahiliConverter,\n SwedishConverter,\n TamilConverter,\n TeluguConverter,\n ThaiConverter,\n TraditionalChineseConverter,\n TurkishConverter,\n UkrainianConverter,\n UrduConverter,\n VietnameseConverter\n}\n","import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js'\n\n/**\n * Danish language converter.\n *\n * Supports:\n * - Vigesimal (base-20) number system\n * - Units-before-tens ordering (e.g., \"tre-og-tyve\" = 23)\n * - Optional ordinal numbers via ordFlag option\n */\nexport class Danish extends GreedyScaleLanguage {\n negativeWord = 'minus'\n decimalSeparatorWord = 'komma'\n zeroWord = 'nul'\n\n scaleWords = [\n [1_000_000_000_000_000_000_000_000_000n, 'quadrillarder'],\n [1_000_000_000_000_000_000_000_000n, 'quadrillioner'],\n [1_000_000_000_000_000_000_000n, 'trillarder'],\n [1_000_000_000_000_000_000n, 'trillioner'],\n [1_000_000_000_000_000n, 'billarder'],\n [1_000_000_000_000n, 'billioner'],\n [1_000_000_000n, 'millarder'],\n [1_000_000n, 'millioner'],\n [1000n, 'tusind'],\n [100n, 'hundrede'],\n [90n, 'halvfems'],\n [80n, 'firs'],\n [70n, 'halvfjerds'],\n [60n, 'treds'],\n [50n, 'halvtreds'],\n [40n, 'fyrre'],\n [30n, 'tredive'],\n [20n, 'tyve'],\n [19n, 'nitten'],\n [18n, 'atten'],\n [17n, 'sytten'],\n [16n, 'seksten'],\n [15n, 'femten'],\n [14n, 'fjorten'],\n [13n, 'tretten'],\n [12n, 'tolv'],\n [11n, 'elleve'],\n [10n, 'ti'],\n [9n, 'ni'],\n [8n, 'otte'],\n [7n, 'syv'],\n [6n, 'seks'],\n [5n, 'fem'],\n [4n, 'fire'],\n [3n, 'tre'],\n [2n, 'to'],\n [1n, 'et'],\n [0n, 'nul']\n ]\n\n constructor (options = {}) {\n super()\n\n this.setOptions({\n ordFlag: false\n }, options)\n }\n\n /** Combines two word-sets with Danish vigesimal and reversal rules. */\n combineWordSets (preceding, following) {\n let precedingWord = Object.keys(preceding)[0]\n let followingWord = Object.keys(following)[0]\n const precedingValue = Object.values(preceding)[0] // BigInt (e.g., 1n, 100n, 1000n)\n const followingValue = Object.values(following)[0] // BigInt (e.g., magnitude level like 100n, 1000n)\n\n // Prepend \"et\" to hundreds and thousands (not \"en\") for proper Danish form\n if (followingValue === 100n || followingValue === 1000n) {\n following = { [`et${followingWord}`]: followingValue }\n }\n\n // Implicit '1' handling: omit '1' before most magnitudes (except millions/ordinals)\n if (precedingValue === 1n) {\n if (followingValue < 1_000_000n || this.options.ordFlag) {\n return following // Just the magnitude word (e.g., \"hundrede\" not \"en hundrede\")\n }\n precedingWord = 'en' // Explicit \"en\" (one) for millions and above\n }\n\n // Multiplication across magnitude boundaries\n if (followingValue > precedingValue) {\n // Space for million+ (e.g., \"en million\", \"to millioner\")\n if (followingValue >= 1_000_000n) {\n precedingWord += ' '\n }\n return { [`${precedingWord}${followingWord}`]: precedingValue * followingValue }\n }\n\n // Addition with separator rules:\n // \"og\" (and) for hundreds + smaller numbers\n if (precedingValue >= 100n && precedingValue < 1000n) {\n precedingWord += ' og '\n } else if (precedingValue >= 1000n && precedingValue <= 100_000n) {\n // Special \"e og\" for thousands (e.g., \"tusinde og tyve\")\n precedingWord += 'e og '\n }\n\n // Units-before-tens reversal (Danish vigesimal pattern):\n // For small units (< 10) with tens (10-99), swap order: \"tre og tyve\" (25)\n if (followingValue < 10n && precedingValue > 10n && precedingValue < 100n) {\n if (followingValue === 1n) {\n followingWord = 'en' // Convert 1 to \"en\" for vigesimal context\n }\n // Swap positions: units go after \"og\", tens go before\n const temporary = followingWord\n followingWord = precedingWord\n precedingWord = temporary + 'og'\n } else if (precedingValue >= 1_000_000n) {\n // Space for large magnitudes (millions+)\n precedingWord += ' '\n }\n\n return { [`${precedingWord}${followingWord}`]: precedingValue + followingValue }\n }\n}\n"],"names":["AbstractLanguage","options","negativeWord","decimalSeparatorWord","zeroWord","wordSeparator","usePerDigitDecimals","this","toWords","isNegative","integerPart","decimalPart","words","push","integerToWords","decimalDigitsToWords","join","Error","setOptions","defaults","userOptions","decimalIntegerToWords","i","length","remainder","slice","char","BigInt","GreedyScaleLanguage","scaleWords","wordForScale","scale","match","find","pair","decomposeInteger","wordSets","remaining","multiplier","reduceWordSets","first","second","rest","Array","isArray","combined","combineWordSets","normalized","map","element","preceding","following","finalizeWords","output","trimEnd","reduced","result","Object","keys","expandScientificNotation","str","mantissa","expStr","toLowerCase","split","exp","parseInt","dotIndex","indexOf","digits","newDotPosition","repeat","DanishConverter","LanguageClass","constructor","super","ordFlag","precedingWord","followingWord","precedingValue","values","followingValue","temporary","value","undefined","proto","getPrototypeOf","prototype","isPlainObject","TypeError","type","Number","isFinite","toString","includes","numberToString","trimmed","trim","isNaN","stringToNormalizedForm","integerStr","parseNumericString","parseNumericValue"],"mappings":"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"}
|
package/dist/DutchConverter.js
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
/*! n2words/DutchConverter v2.0.0 | MIT License | github.com/forzagreen/n2words */
|
|
2
|
-
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).n2words=e.n2words||{})}(this,function(e){"use strict";class n{#e={};negativeWord="";decimalSeparatorWord="";zeroWord="";wordSeparator=" ";usePerDigitDecimals=!1;get options(){return this.#e}toWords(e,n,t){const r=[];return e&&r.push(this.negativeWord),r.push(this.integerToWords(n)),t&&(r.push(this.decimalSeparatorWord),r.push(...this.decimalDigitsToWords(t))),r.join(this.wordSeparator)}integerToWords(e){throw new Error("integerToWords() must be implemented by subclass")}setOptions(e={},n={}){this.#e={...e,...n}}decimalIntegerToWords(e){return this.integerToWords(e)}decimalDigitsToWords(e){const n=[];let t=0;for(;t<e.length&&"0"===e[t];)n.push(this.zeroWord),t++;const r=e.slice(t);if(!r)return n;if(this.usePerDigitDecimals)for(const e of r)n.push(this.decimalIntegerToWords(BigInt(e)));else n.push(this.decimalIntegerToWords(BigInt(r)));return n}}class t extends n{scaleWords;wordForScale(e){const n=this.scaleWords.find(n=>n[0]===e);return n?.[1]}decomposeInteger(e){const n=[];let t=e;do{const e=this.scaleWords.find(e=>t>=e[0]);if(!e)break;const r=0n===t?1n:t/e[0];1n===r?n.push({[this.wordForScale(1n)]:1n}):n.push(this.decomposeInteger(r)),n.push({[e[1]]:e[0]}),t=0n===t?0n:t%e[0]}while(t>0n);return n}reduceWordSets(e){for(;e.length>1;){const[n,t,...r]=e;if(!Array.isArray(n)&&!Array.isArray(t)){const i=this.combineWordSets(n,t);e=r.length>0?[i,r]:[i];continue}const i=e.map(e=>Array.isArray(e)?1===e.length?e[0]:this.reduceWordSets(e):e);e=i}return e[0]}combineWordSets(e,n){throw new Error("combineWordSets() must be implemented by subclass")}finalizeWords(e){return e.trimEnd()}integerToWords(e){const n=this.decomposeInteger(e),t=this.reduceWordSets(n),r=Object.keys(t)[0];return this.finalizeWords(r)}}function r(e){const[n,t]=e.toLowerCase().split("e"),r=parseInt(t,10),i=n.indexOf("."),o=-1===i?n:n.slice(0,i)+n.slice(i+1),s=(-1===i?n.length:i)+r;return s>=o.length?o+"0".repeat(s-o.length):s<=0?"0."+"0".repeat(-s)+o:o.slice(0,s)+"."+o.slice(s)}const i=(o=class extends t{negativeWord="min";decimalSeparatorWord="komma";zeroWord="nul";scaleWords=[[1000000000000000000000000000n,"quadriljard"],[1000000000000000000000000n,"quadriljoen"],[1000000000000000000000n,"triljard"],[1000000000000000000n,"triljoen"],[1000000000000000n,"biljard"],[1000000000000n,"biljoen"],[1000000000n,"miljard"],[1000000n,"miljoen"],[1000n,"duizend"],[100n,"honderd"],[90n,"negentig"],[80n,"tachtig"],[70n,"zeventig"],[60n,"zestig"],[50n,"vijftig"],[40n,"veertig"],[30n,"dertig"],[20n,"twintig"],[19n,"negentien"],[18n,"achttien"],[17n,"zeventien"],[16n,"zestien"],[15n,"vijftien"],[14n,"veertien"],[13n,"dertien"],[12n,"twaalf"],[11n,"elf"],[10n,"tien"],[9n,"negen"],[8n,"acht"],[7n,"zeven"],[6n,"zes"],[5n,"vijf"],[4n,"vier"],[3n,"drie"],[2n,"twee"],[1n,"één"],[0n,"nul"]];constructor(e={}){super(),this.setOptions({includeOptionalAnd:!1,noHundredPairing:!1,accentOne:!0},e),this.options.accentOne||(this.scaleWords[this.scaleWords.length-2][1]="een")}combineWordSets(e,n){let t=Object.keys(e)[0],r=Object.keys(n)[0];const i=Object.values(e)[0],o=Object.values(n)[0];if(1n===i){if(o<1000000n)return n;t=this.options.accentOne?"één":"een"}if(o>i){let e=!1;return o>=1000000n?(t+=" ",e=!0):o>100n&&(r+=" ",e=!0),e&&this.options.accentOne||(t=t.replace(/één/g,"een"),r=r.replace(/één/g,"een")),{[`${t}${r}`]:i*o}}let s=!1;if(o<10n&&i>10n&&i<100n){const e=r;r=t;const n=e.endsWith("e")?"ën":"en";t=`${e}${n}`}else o<13n&&i<1000n&&this.options.includeOptionalAnd?t=`${t}en`:o<13n&&i>=1000n&&this.options.includeOptionalAnd?(r=` en ${r}`,s=!0):(i>=1000000n||1000n===i)&&(t+=" ",s=!0);return s&&this.options.accentOne||(t=t.replace(/één/g,"een"),r=r.replace(/één/g,"een")),{[`${t}${r}`]:i+o}}integerToWords(e){if(e>=1100n&&e<10000n&&!this.options.noHundredPairing){const n=e/100n,t=e%100n;if(n%10n!=0n){let e=super.integerToWords(n)+"honderd";return t&&(e+=(this.options.includeOptionalAnd?" en ":" ")+super.integerToWords(t)),e}}return super.integerToWords(e)}},function(e,n){if(void 0!==n&&!function(e){if(null===e||"object"!=typeof e)return!1;const n=Object.getPrototypeOf(e);return n===Object.prototype||null===n}(n))throw new TypeError("options must be a plain object if provided");const{isNegative:t,integerPart:i,decimalPart:s}=function(e){const n=typeof e;if("bigint"===n)return e<0n?{isNegative:!0,integerPart:-e}:{isNegative:!1,integerPart:e};if("number"!==n&&"string"!==n)throw new TypeError(`Invalid value type: expected number, string, or bigint, received ${n}`);const t="number"===n?function(e){if(!Number.isFinite(e))throw new Error("Number must be finite (NaN and Infinity are not supported)");const n=e.toString();return n.includes("e")||n.includes("E")?r(n):n}(e):function(e){const n=e.trim();if(0===n.length)throw new Error(`Invalid number format: "${e}"`);if(Number.isNaN(Number(n)))throw new Error(`Invalid number format: "${e}"`);return n.includes("e")||n.includes("E")?r(n):n}(e);return function(e){const n="-"===e[0];n&&(e=e.slice(1));const t=e.indexOf(".");if(-1===t)return{isNegative:n,integerPart:BigInt(e)};const r=e.slice(0,t)||"0",i=e.slice(t+1);return{isNegative:n,integerPart:BigInt(r),decimalPart:i}}(t)}(e);return new o(n).toWords(t,i,s)});var o;e.DutchConverter=i});
|
|
3
|
-
//# sourceMappingURL=DutchConverter.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"DutchConverter.js","sources":["../lib/classes/abstract-language.js","../lib/classes/greedy-scale-language.js","../lib/n2words.js","../lib/languages/nl.js"],"sourcesContent":["/**\n * Abstract base class for language converters.\n *\n * This class provides the framework for converting numbers to words in any language.\n * It handles the common conversion flow while delegating language-specific logic to subclasses.\n *\n * ## Responsibilities\n *\n * - Receives pre-validated and normalized input from the public API (n2words.js)\n * - Handles negative number prefixing via `negativeWord`\n * - Converts decimals via `decimalDigitsToWords()`, preserving leading zeros\n * - Delegates integer conversion to `integerToWords()` (implemented by subclasses)\n *\n * ## Required Subclass Implementation\n *\n * Subclasses MUST provide:\n * - `integerToWords(integerPart)` - Core conversion logic (abstract method)\n * - `negativeWord` - Word preceding negative numbers (e.g., \"minus\"))\n * - `zeroWord` - Word for the digit 0 (e.g., \"zero\")\n * - `decimalSeparatorWord` - Word between whole and decimal parts (e.g., \"point\")\n *\n * ## Optional Overrides\n *\n * Subclasses MAY override:\n * - `wordSeparator` - Character(s) between words (default: space, empty for CJK languages)\n * - `usePerDigitDecimals` - Enable per-digit decimal mode (default: false)\n * - `decimalIntegerToWords()` - Custom decimal conversion (e.g., Romanian masculine forms)\n * - `decimalDigitsToWords()` - Complete decimal conversion override\n * - `toWords()` - Override to capture integerPart for context-dependent rules (e.g., Czech)\n *\n * ## Input Contract\n *\n * Input validation and normalization happen at the public API boundary (n2words.js).\n * This class assumes it receives clean, pre-processed data via `toWords()`.\n *\n * @abstract\n */\nexport class AbstractLanguage {\n // ============================================================================\n // Private Fields\n // ============================================================================\n\n /**\n * Private storage for options.\n * @type {Object}\n */\n #options = {}\n\n // ============================================================================\n // Required Properties (subclasses must define)\n // ============================================================================\n\n /**\n * Word that precedes negative numbers (e.g., \"minus\", \"negative\", \"moins\").\n * @type {string}\n */\n negativeWord = ''\n\n /**\n * Word that separates integer and decimal parts (e.g., \"point\", \"virgule\", \"comma\").\n * @type {string}\n */\n decimalSeparatorWord = ''\n\n /**\n * Word representation for the digit 0 (e.g., \"zero\", \"zéro\", \"null\").\n * Used for zero values and leading zeros in decimals.\n * @type {string}\n */\n zeroWord = ''\n\n // ============================================================================\n // Optional Properties (subclasses may override)\n // ============================================================================\n\n /**\n * Character(s) used to separate words in the output.\n *\n * Defaults to a single space. Set to empty string for languages without\n * word separators (e.g., Japanese, Thai, Chinese).\n *\n * @type {string}\n */\n wordSeparator = ' '\n\n /**\n * Whether to convert decimal digits individually rather than grouped.\n *\n * - `false` (default): Leading zeros preserved, remaining digits grouped as a number\n * - Example: \"05\" → [\"zero\", \"five\"], \"14\" → [\"fourteen\"]\n * - Used by: English, Spanish, French, German, etc.\n *\n * - `true`: Each digit converted separately\n * - Example: \"05\" → [\"zero\", \"five\"], \"14\" → [\"one\", \"four\"]\n * - Used by: Japanese, Thai, Tamil, Telugu, Greek, Hebrew, Filipino\n *\n * @type {boolean}\n */\n usePerDigitDecimals = false\n\n // ============================================================================\n // Public Accessors\n // ============================================================================\n\n /**\n * Read-only access to options set via `setOptions()`.\n * @type {Object}\n */\n get options () {\n return this.#options\n }\n\n // ============================================================================\n // Public Methods\n // ============================================================================\n\n /**\n * Converts pre-normalized numeric components to words.\n *\n * This is the main entry point called by the public API (makeConverter in n2words.js).\n * It assembles the final word representation from the provided components.\n *\n * **Caller contract (enforced by makeConverter):**\n * - `integerPart` is a non-negative BigInt (>= 0n)\n * - `decimalPart` is a string of digits only (no sign, no decimal point)\n * - `isNegative` reflects the original input sign\n *\n * **Conversion flow:**\n * 1. Prepend negative word if applicable\n * 2. Convert integer part via `integerToWords()`\n * 3. If decimals present: append separator and decimal words\n * 4. Join all parts with `wordSeparator`\n *\n * Subclasses needing access to the integer part during decimal conversion\n * (e.g., for context-dependent separator words) should override this\n * method to cache the value before calling super.toWords().\n *\n * @public\n * @param {boolean} isNegative Whether the original number was negative\n * @param {bigint} integerPart The integer part (always non-negative)\n * @param {string} [decimalPart] - Decimal digits if present (e.g., \"14\" for 3.14)\n * @returns {string} The localized cardinal string\n */\n toWords (isNegative, integerPart, decimalPart) {\n const words = []\n\n if (isNegative) words.push(this.negativeWord)\n\n words.push(this.integerToWords(integerPart))\n\n if (decimalPart) {\n words.push(this.decimalSeparatorWord)\n words.push(...this.decimalDigitsToWords(decimalPart))\n }\n\n return words.join(this.wordSeparator)\n }\n\n // ============================================================================\n // Abstract Methods (subclasses must implement)\n // ============================================================================\n\n /**\n * Converts a BigInt integer part to its cardinal word representation.\n *\n * This is the core template method that subclasses MUST implement to provide\n * language-specific number conversion logic.\n *\n * @abstract\n * @param {bigint} integerPart The integer part to convert (always >= 0n)\n * @returns {string} The cardinal representation in the target language\n * @throws {Error} If not implemented by subclass\n */\n integerToWords (integerPart) {\n throw new Error('integerToWords() must be implemented by subclass')\n }\n\n // ============================================================================\n // Protected Methods (subclasses may override or call)\n // ============================================================================\n\n /**\n * Sets options by merging language defaults with user-provided options.\n *\n * Merges defaults first, then user options (later keys override earlier ones).\n * Stores the result in the private `#options` field, accessible via the\n * read-only `options` getter.\n *\n * @protected\n * @param {Object} [defaults={}] - Default option values for the language\n * @param {Object} [userOptions={}] - Runtime options supplied by the caller\n *\n * @example\n * constructor(options = {}) {\n * super()\n * this.setOptions({ gender: 'masculine' }, options)\n * }\n */\n setOptions (defaults = {}, userOptions = {}) {\n this.#options = {\n ...defaults,\n ...userOptions\n }\n }\n\n /**\n * Converts an integer to words in decimal context.\n *\n * By default, delegates to `integerToWords()`. Override this method\n * when decimal conversion requires different behavior than integer conversion.\n *\n * Called with:\n * - Single digits (0-9) when `usePerDigitDecimals = true`\n * - Grouped numbers when `usePerDigitDecimals = false`\n *\n * **Use cases for overriding:**\n * - Romanian: Decimals always use masculine forms regardless of gender option\n * - Languages with different plural/gender rules for decimal vs integer parts\n *\n * @protected\n * @param {bigint} integerPart The integer to convert (single digit or grouped)\n * @returns {string} The word representation for use in decimal context\n */\n decimalIntegerToWords (integerPart) {\n return this.integerToWords(integerPart)\n }\n\n /**\n * Converts decimal fractional digits into an array of words.\n *\n * Leading zeros are always preserved individually. The remaining digits\n * are converted based on `usePerDigitDecimals`:\n *\n * - `false` (default): Remaining digits grouped as a single number\n * - \"05\" → [\"zero\", \"five\"], \"14\" → [\"fourteen\"]\n *\n * - `true`: Each remaining digit converted separately\n * - \"05\" → [\"zero\", \"five\"], \"14\" → [\"one\", \"four\"]\n *\n * @protected\n * @param {string} decimalPart Decimal digits as string (e.g., \"05\" for 3.05)\n * @returns {string[]} Array of word tokens for the fractional part\n */\n decimalDigitsToWords (decimalPart) {\n const words = []\n\n // Always preserve leading zeros individually\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n words.push(this.zeroWord)\n i++\n }\n\n const remainder = decimalPart.slice(i)\n if (!remainder) return words\n\n // Convert remainder: per-digit or as single number\n if (this.usePerDigitDecimals) {\n for (const char of remainder) {\n words.push(this.decimalIntegerToWords(BigInt(char)))\n }\n } else {\n words.push(this.decimalIntegerToWords(BigInt(remainder)))\n }\n\n return words\n }\n}\n","import { AbstractLanguage } from './abstract-language.js'\n\n/**\n * Greedy scale language converter implementing the \"highest-matching scale word\" algorithm.\n *\n * Responsibilities:\n * - Decompose an integer into a sequence of word-sets using greedy matching.\n * - Provide helpers to reduce and post-process matched word-sets.\n * - Inherits decimal handling from AbstractLanguage (supports grouped and per-digit\n * modes via the `usePerDigitDecimals` class property).\n *\n * Subclass requirements:\n * - Define `scaleWords` (ordered descending) as `[bigint, string]` tuples.\n * - Implement 'combineWordSets(preceding, following)' to combine adjacent word-sets\n * per language grammar.\n *\n * Scale words specification:\n * - `scaleWords` is an Array of 2-tuples: `[bigint, string]` where the first element\n * is the numeric scale value and the second is the word for that value.\n * - Scale words MUST be ordered from largest to smallest (descending) for the algorithm\n * to function correctly.\n *\n * Terminology:\n * - **Scale**: A magnitude value (100n, 1000n, 1000000n)\n * - **Word-set**: An object `{ word: bigint }` representing a partial result\n * - **Scale word**: The word for a scale value (\"hundred\", \"thousand\")\n *\n * @abstract\n * @extends AbstractLanguage\n */\n\nexport class GreedyScaleLanguage extends AbstractLanguage {\n // ============================================================================\n // Required Properties (subclasses must define)\n // ============================================================================\n\n /**\n * Array of scale words mapping numeric values to their word representations.\n *\n * Each element is a 2-tuple: `[bigint, string]` where the first element is the\n * numeric scale value and the second is the word for that value. The array MUST be\n * ordered from largest to smallest (descending) for the greedy algorithm to work correctly.\n *\n * @type {Array<[bigint, string]>}\n * @example\n * // English scale words (descending order):\n * // [[1000000000n, 'billion'], [1000000n, 'million'], [1000n, 'thousand'], [100n, 'hundred'], ..., [1n, 'one']]\n */\n scaleWords\n\n // ============================================================================\n // Public Methods\n // ============================================================================\n\n /**\n * Returns the word for an exact scale value.\n *\n * @param {bigint} scale The scale value to look up (prefer BigInt for exact matching).\n * @returns {string|undefined} The word for the provided scale, or `undefined`.\n */\n wordForScale (scale) {\n const match = this.scaleWords.find((pair) => pair[0] === scale)\n return match?.[1]\n }\n\n // ============================================================================\n // Protected Methods (subclasses may call or override)\n // ============================================================================\n\n /**\n * Decomposes an integer into a sequence of word-sets.\n *\n * This internal helper returns a nested structure that represents quantities and\n * their matching scale words (e.g. `[{ 'one': 1n }, { 'hundred': 100n }, ...]`).\n * The result is designed to be reduced by `reduceWordSets()` using language-specific `combineWordSets()`.\n *\n * For quantities > 1, the multiplier is recursively decomposed. For quantity = 1,\n * the implicit \"one\" is represented with `{ 'one': 1n }` and typically omitted during combineWordSets().\n *\n * @protected\n * @param {bigint} integerPart The integer to decompose.\n * @returns {Array<Object|Array>} An array of word-set objects and possibly nested arrays.\n */\n decomposeInteger (integerPart) {\n const wordSets = []\n let remaining = integerPart\n\n do {\n const match = this.scaleWords.find((pair) => remaining >= pair[0])\n if (!match) break\n\n const multiplier = remaining === 0n ? 1n : remaining / match[0]\n\n if (multiplier === 1n) {\n wordSets.push({ [this.wordForScale(1n)]: 1n })\n } else {\n wordSets.push(this.decomposeInteger(multiplier))\n }\n\n wordSets.push({ [match[1]]: match[0] })\n\n remaining = remaining === 0n ? 0n : remaining % match[0]\n } while (remaining > 0n)\n\n return wordSets\n }\n\n /**\n * Reduces a nested array of word-sets into a single word-set object.\n *\n * This method repeatedly applies the subclass `combineWordSets()` operation until a single\n * object remains. It normalizes nested arrays by recursively reducing them.\n *\n * @protected\n * @param {Array<Object|Array>} wordSets Array of word-set objects and nested arrays.\n * @returns {Object} Reduced word-set where the single object key is the language string\n * and its value is the numeric BigInt result for that string.\n */\n reduceWordSets (wordSets) {\n while (wordSets.length > 1) {\n const [first, second, ...rest] = wordSets\n\n if (!Array.isArray(first) && !Array.isArray(second)) {\n const combined = this.combineWordSets(first, second)\n wordSets = rest.length > 0 ? [combined, rest] : [combined]\n continue\n }\n\n const normalized = wordSets.map((element) => {\n if (!Array.isArray(element)) return element\n return element.length === 1 ? element[0] : this.reduceWordSets(element)\n })\n\n wordSets = normalized\n }\n\n return wordSets[0]\n }\n\n // ============================================================================\n // Abstract Methods (subclasses must implement)\n // ============================================================================\n\n /**\n * Combines two adjacent word-sets into a single word-set.\n *\n * This is the core language-specific method that must be implemented by subclasses\n * to define how adjacent word-sets are combined according to the language's grammar.\n * For example, English combines \"twenty\" + \"three\" → \"twenty-three\", while\n * French might combine \"quatre-vingts\" + \"dix\" → \"quatre-vingt-dix\".\n *\n * @abstract\n * @protected\n * @param {Object} preceding Preceding word-set as `{ word: bigint }`.\n * @param {Object} following Following word-set as `{ word: bigint }`.\n * @returns {Object} Combined word-set with merged text and resulting numeric value.\n *\n * @example\n * // English implementation might handle:\n * // combineWordSets({ 'twenty': 20n }, { 'three': 3n }) → { 'twenty-three': 23n }\n * // combineWordSets({ 'one': 1n }, { 'hundred': 100n }) → { 'one hundred': 100n }\n */\n combineWordSets (preceding, following) {\n throw new Error('combineWordSets() must be implemented by subclass')\n }\n\n // ============================================================================\n // Optional Methods (subclasses may override)\n // ============================================================================\n\n /**\n * Final string post-processing hook.\n *\n * Subclasses may override to apply language-specific whitespace, punctuation or\n * orthographic corrections.\n *\n * @protected\n * @param {string} output Language string produced by the conversion flow.\n * @returns {string} Final formatted string.\n */\n finalizeWords (output) {\n return output.trimEnd()\n }\n\n /**\n * Converts an integer to its language-specific cardinal words.\n *\n * This method orchestrates decomposition, reduction, and final formatting. It does\n * not handle decimals or sign; those concerns are implemented in\n * `AbstractLanguage.toWords` which calls this method for the integer part.\n *\n * @param {bigint} integerPart The integer to convert.\n * @returns {string} The cardinal representation for the integer in the language.\n */\n integerToWords (integerPart) {\n const wordSets = this.decomposeInteger(integerPart)\n const reduced = this.reduceWordSets(wordSets)\n const result = Object.keys(reduced)[0]\n return this.finalizeWords(result)\n }\n}\n","/**\n * n2words - Convert numbers to words in multiple languages\n *\n * This file is the main entry point for the n2words library.\n * It exports converter functions for all supported languages.\n *\n * ## For Contributors\n *\n * When adding a new language, this file must be updated in THREE sections:\n * 1. Language Imports - Add import statement (alphabetically sorted)\n * 2. Language Converters - Create converter with makeConverter() (alphabetically sorted)\n * 3. Exports - Add to export list (alphabetically sorted)\n *\n * Use the scaffolding tool to automate this process:\n * npm run lang:add <language-code>\n *\n * ## Public API Structure\n *\n * Each language exports a converter function:\n * - Name: `{LanguageName}Converter` (e.g., EnglishConverter, SpanishConverter)\n * - Signature: `(value: NumericValue, options?: Options) => string`\n * - Input: number, bigint, or string (numeric strings only)\n * - Output: Words representing the number in the target language\n *\n * Languages without options use signature: `(value: NumericValue) => string`\n * Languages with options define a typedef (e.g., ArabicOptions) and use: `(value: NumericValue, options?: ArabicOptions) => string`\n *\n * @module n2words\n */\n\n// ============================================================================\n// Language Imports\n// ============================================================================\n\nimport { Arabic } from './languages/ar.js'\nimport { Azerbaijani } from './languages/az.js'\nimport { Bangla } from './languages/bn.js'\nimport { Czech } from './languages/cs.js'\nimport { Danish } from './languages/da.js'\nimport { German } from './languages/de.js'\nimport { Greek } from './languages/el.js'\nimport { English } from './languages/en.js'\nimport { Spanish } from './languages/es.js'\nimport { Persian } from './languages/fa.js'\nimport { Filipino } from './languages/fil.js'\nimport { French } from './languages/fr.js'\nimport { FrenchBelgium } from './languages/fr-BE.js'\nimport { Gujarati } from './languages/gu.js'\nimport { Hebrew } from './languages/he.js'\nimport { BiblicalHebrew } from './languages/hbo.js'\nimport { Hindi } from './languages/hi.js'\nimport { Croatian } from './languages/hr.js'\nimport { Hungarian } from './languages/hu.js'\nimport { Indonesian } from './languages/id.js'\nimport { Italian } from './languages/it.js'\nimport { Japanese } from './languages/ja.js'\nimport { Kannada } from './languages/kn.js'\nimport { Korean } from './languages/ko.js'\nimport { Lithuanian } from './languages/lt.js'\nimport { Latvian } from './languages/lv.js'\nimport { Marathi } from './languages/mr.js'\nimport { Malay } from './languages/ms.js'\nimport { Dutch } from './languages/nl.js'\nimport { NorwegianBokmal } from './languages/nb.js'\nimport { Punjabi } from './languages/pa.js'\nimport { Polish } from './languages/pl.js'\nimport { Portuguese } from './languages/pt.js'\nimport { Romanian } from './languages/ro.js'\nimport { Russian } from './languages/ru.js'\nimport { SerbianCyrillic } from './languages/sr-Cyrl.js'\nimport { SerbianLatin } from './languages/sr-Latn.js'\nimport { Swedish } from './languages/sv.js'\nimport { Swahili } from './languages/sw.js'\nimport { Tamil } from './languages/ta.js'\nimport { Telugu } from './languages/te.js'\nimport { Thai } from './languages/th.js'\nimport { Turkish } from './languages/tr.js'\nimport { Ukrainian } from './languages/uk.js'\nimport { Urdu } from './languages/ur.js'\nimport { Vietnamese } from './languages/vi.js'\nimport { SimplifiedChinese } from './languages/zh-Hans.js'\nimport { TraditionalChinese } from './languages/zh-Hant.js'\n\n// ============================================================================\n// Type Definitions\n// ============================================================================\n//\n// This section defines TypeScript-compatible JSDoc types for:\n// - NumericValue: The input types accepted by all converters\n// - {Language}Options: Optional configuration for languages that support it\n//\n// Keep options typedefs alphabetically sorted for maintainability.\n// ============================================================================\n\n/**\n * Numeric value that can be converted to words.\n * Accepts number, bigint, or numeric string representations.\n * @typedef {number | bigint | string} NumericValue\n */\n\n/**\n * @typedef {Object} ArabicOptions\n * @property {string} [negativeWord] Word for negative numbers\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} BiblicalHebrewOptions\n * @property {string} [andWord='ו'] Conjunction character (typically 'ו' for and)\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} CroatianOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} CzechOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} DanishOptions\n * @property {boolean} [ordFlag=false] Enable ordinal number conversion\n */\n\n/**\n * @typedef {Object} DutchOptions\n * @property {boolean} [includeOptionalAnd=false] Include optional \"en\" separator\n * @property {boolean} [noHundredPairing=false] Disable hundred-pairing (e.g., \"twelve hundred\" becomes \"one thousand two hundred\")\n * @property {boolean} [accentOne=true] Use accented \"één\" for one\n */\n\n/**\n * @typedef {Object} FrenchOptions\n * @property {boolean} [withHyphenSeparator=false] Use hyphens (true) instead of spaces (false) in compounds\n */\n\n/**\n * @typedef {Object} FrenchBelgiumOptions\n * @property {boolean} [withHyphenSeparator=false] Use hyphens (true) instead of spaces (false) in compounds\n */\n\n/**\n * @typedef {Object} HebrewOptions\n * @property {string} [andWord='ו'] Conjunction character (typically 'ו' for and)\n */\n\n/**\n * @typedef {Object} LatvianOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} LithuanianOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} PolishOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} RomanianOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} RussianOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} SerbianCyrillicOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} SerbianLatinOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} SimplifiedChineseOptions\n * @property {boolean} [formal=true] Use formal/financial numerals (壹贰叁) vs. common numerals (一二三)\n */\n\n/**\n * @typedef {Object} SpanishOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} TraditionalChineseOptions\n * @property {boolean} [formal=true] Use formal/financial numerals (壹貳參) vs. common numerals (一二三)\n */\n\n/**\n * @typedef {Object} TurkishOptions\n * @property {boolean} [dropSpaces=false] Remove spaces between words if true\n */\n\n/**\n * @typedef {Object} UkrainianOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n// ============================================================================\n// Converter Factory\n// ============================================================================\n//\n// makeConverter() is a factory function that creates converter functions from\n// language classes. It provides a consistent functional API for all languages:\n//\n// const result = EnglishConverter(42) // \"forty-two\"\n// const result = ArabicConverter(1, {gender: 'feminine'}) // \"واحدة\"\n//\n// This design:\n// - Hides class instantiation details from public API\n// - Ensures each conversion uses a fresh instance\n// - Maintains immutability (no shared state between conversions)\n// ============================================================================\n\n/**\n * Creates a converter function for a language class.\n *\n * This factory handles all input validation and normalization at the public API\n * boundary, then delegates to the language class with pre-processed data.\n *\n * @template {Object} [TOptions={}]\n * @param {new (options?: TOptions) => { toWords: (isNegative: boolean, integerPart: bigint, decimalPart?: string) => string }} LanguageClass - Language class constructor\n * @returns {(value: NumericValue, options?: TOptions) => string} Converter function\n */\nfunction makeConverter (LanguageClass) {\n /**\n * @param {NumericValue} value\n * @param {TOptions} [options]\n * @returns {string}\n */\n return function convertToWords (value, options) {\n if (options !== undefined && !isPlainObject(options)) {\n throw new TypeError('options must be a plain object if provided')\n }\n\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n return new LanguageClass(options).toWords(isNegative, integerPart, decimalPart)\n }\n}\n\n// ============================================================================\n// Input Parsing Utilities\n// ============================================================================\n\n/**\n * @typedef {Object} ParsedNumericValue\n * @property {boolean} isNegative - Whether the value is negative\n * @property {bigint} integerPart - The absolute integer part\n * @property {string} [decimalPart] - The decimal digits (without the point)\n */\n\n/**\n * Parses and validates a numeric value into its components.\n * Handles number, string, and bigint inputs uniformly.\n *\n * @param {NumericValue} value The value to parse\n * @returns {ParsedNumericValue} The parsed components\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n */\nfunction parseNumericValue (value) {\n const type = typeof value\n\n // BigInt: simplest case - no decimals, no scientific notation\n if (type === 'bigint') {\n return value < 0n\n ? { isNegative: true, integerPart: -value }\n : { isNegative: false, integerPart: value }\n }\n\n // Reject invalid types early\n if (type !== 'number' && type !== 'string') {\n throw new TypeError(\n `Invalid value type: expected number, string, or bigint, received ${type}`\n )\n }\n\n // Convert to normalized string\n const str = type === 'number'\n ? numberToString(value)\n : stringToNormalizedForm(value)\n\n return parseNumericString(str)\n}\n\n/**\n * Converts a JavaScript number to a decimal string.\n * Handles special cases: Infinity, NaN, and scientific notation.\n *\n * @param {number} value The number to convert\n * @returns {string} Decimal string representation\n * @throws {Error} If value is not finite\n */\nfunction numberToString (value) {\n if (!Number.isFinite(value)) {\n throw new Error('Number must be finite (NaN and Infinity are not supported)')\n }\n\n const str = value.toString()\n\n // Expand scientific notation (used for values >= 1e21 or < 1e-6)\n if (str.includes('e') || str.includes('E')) {\n return expandScientificNotation(str)\n }\n\n return str\n}\n\n/**\n * Validates and normalizes a string numeric input.\n *\n * @param {string} value The string to validate\n * @returns {string} Trimmed and validated string\n * @throws {Error} If string is empty or not a valid number format\n */\nfunction stringToNormalizedForm (value) {\n const trimmed = value.trim()\n\n if (trimmed.length === 0) {\n throw new Error(`Invalid number format: \"${value}\"`)\n }\n\n // Validate by attempting conversion (handles edge cases like \"1e21\", \"-.5\", etc.)\n if (Number.isNaN(Number(trimmed))) {\n throw new Error(`Invalid number format: \"${value}\"`)\n }\n\n // Expand scientific notation if present\n if (trimmed.includes('e') || trimmed.includes('E')) {\n return expandScientificNotation(trimmed)\n }\n\n return trimmed\n}\n\n/**\n * Parses a normalized numeric string into its components.\n *\n * @param {string} str A normalized decimal string (no scientific notation)\n * @returns {ParsedNumericValue} The parsed components\n */\nfunction parseNumericString (str) {\n // Extract sign\n const isNegative = str[0] === '-'\n if (isNegative) {\n str = str.slice(1)\n }\n\n // Split into integer and decimal parts\n const dotIndex = str.indexOf('.')\n if (dotIndex === -1) {\n return { isNegative, integerPart: BigInt(str) }\n }\n\n const integerStr = str.slice(0, dotIndex) || '0'\n const decimalPart = str.slice(dotIndex + 1)\n\n return { isNegative, integerPart: BigInt(integerStr), decimalPart }\n}\n\n/**\n * Expands scientific notation to decimal form.\n * JavaScript uses scientific notation for values >= 1e21 or < 1e-6.\n *\n * @param {string} str String possibly in scientific notation (e.g., \"1e+21\")\n * @returns {string} Decimal form (e.g., \"1000000000000000000000\")\n */\nfunction expandScientificNotation (str) {\n const [mantissa, expStr] = str.toLowerCase().split('e')\n const exp = parseInt(expStr, 10)\n\n // Extract digits and determine original decimal position\n const dotIndex = mantissa.indexOf('.')\n const digits = dotIndex === -1\n ? mantissa\n : mantissa.slice(0, dotIndex) + mantissa.slice(dotIndex + 1)\n const integerLength = dotIndex === -1 ? mantissa.length : dotIndex\n\n // Calculate new decimal position after applying exponent\n const newDotPosition = integerLength + exp\n\n if (newDotPosition >= digits.length) {\n // Pure integer: pad with trailing zeros\n return digits + '0'.repeat(newDotPosition - digits.length)\n }\n\n if (newDotPosition <= 0) {\n // Pure decimal: pad with leading zeros after decimal point\n return '0.' + '0'.repeat(-newDotPosition) + digits\n }\n\n // Mixed: insert decimal point at new position\n return digits.slice(0, newDotPosition) + '.' + digits.slice(newDotPosition)\n}\n\n/**\n * Checks if a value is a plain object (not null, array, or other object types).\n *\n * @param {*} value Value to check\n * @returns {boolean} True if value is a plain object\n */\nfunction isPlainObject (value) {\n if (value === null || typeof value !== 'object') return false\n const proto = Object.getPrototypeOf(value)\n return proto === Object.prototype || proto === null\n}\n\n// ============================================================================\n// Language Converters\n// ============================================================================\n//\n// Each converter is created using makeConverter() with explicit type annotations.\n//\n// Pattern for languages WITHOUT options:\n// const LanguageConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Language))\n//\n// Pattern for languages WITH options:\n// const LanguageConverter = /** @type {(value: NumericValue, options?: LanguageOptions) => string} */ (makeConverter(Language))\n//\n// IMPORTANT: Keep converters alphabetically sorted by converter name.\n// ============================================================================\n\nconst ArabicConverter = /** @type {(value: NumericValue, options?: ArabicOptions) => string} */ (makeConverter(Arabic))\nconst AzerbaijaniConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Azerbaijani))\nconst BanglaConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Bangla))\nconst BiblicalHebrewConverter = /** @type {(value: NumericValue, options?: BiblicalHebrewOptions) => string} */ (makeConverter(BiblicalHebrew))\nconst CroatianConverter = /** @type {(value: NumericValue, options?: CroatianOptions) => string} */ (makeConverter(Croatian))\nconst CzechConverter = /** @type {(value: NumericValue, options?: CzechOptions) => string} */ (makeConverter(Czech))\nconst DanishConverter = /** @type {(value: NumericValue, options?: DanishOptions) => string} */ (makeConverter(Danish))\nconst DutchConverter = /** @type {(value: NumericValue, options?: DutchOptions) => string} */ (makeConverter(Dutch))\nconst EnglishConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(English))\nconst FilipinoConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Filipino))\nconst FrenchConverter = /** @type {(value: NumericValue, options?: FrenchOptions) => string} */ (makeConverter(French))\nconst FrenchBelgiumConverter = /** @type {(value: NumericValue, options?: FrenchBelgiumOptions) => string} */ (makeConverter(FrenchBelgium))\nconst GermanConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(German))\nconst GreekConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Greek))\nconst GujaratiConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Gujarati))\nconst HebrewConverter = /** @type {(value: NumericValue, options?: HebrewOptions) => string} */ (makeConverter(Hebrew))\nconst HindiConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Hindi))\nconst HungarianConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Hungarian))\nconst IndonesianConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Indonesian))\nconst ItalianConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Italian))\nconst JapaneseConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Japanese))\nconst KannadaConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Kannada))\nconst KoreanConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Korean))\nconst LatvianConverter = /** @type {(value: NumericValue, options?: LatvianOptions) => string} */ (makeConverter(Latvian))\nconst LithuanianConverter = /** @type {(value: NumericValue, options?: LithuanianOptions) => string} */ (makeConverter(Lithuanian))\nconst MalayConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Malay))\nconst MarathiConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Marathi))\nconst NorwegianBokmalConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(NorwegianBokmal))\nconst PersianConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Persian))\nconst PolishConverter = /** @type {(value: NumericValue, options?: PolishOptions) => string} */ (makeConverter(Polish))\nconst PortugueseConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Portuguese))\nconst PunjabiConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Punjabi))\nconst RomanianConverter = /** @type {(value: NumericValue, options?: RomanianOptions) => string} */ (makeConverter(Romanian))\nconst RussianConverter = /** @type {(value: NumericValue, options?: RussianOptions) => string} */ (makeConverter(Russian))\nconst SerbianCyrillicConverter = /** @type {(value: NumericValue, options?: SerbianCyrillicOptions) => string} */ (makeConverter(SerbianCyrillic))\nconst SerbianLatinConverter = /** @type {(value: NumericValue, options?: SerbianLatinOptions) => string} */ (makeConverter(SerbianLatin))\nconst SimplifiedChineseConverter = /** @type {(value: NumericValue, options?: SimplifiedChineseOptions) => string} */ (makeConverter(SimplifiedChinese))\nconst SpanishConverter = /** @type {(value: NumericValue, options?: SpanishOptions) => string} */ (makeConverter(Spanish))\nconst SwahiliConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Swahili))\nconst SwedishConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Swedish))\nconst TamilConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Tamil))\nconst TeluguConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Telugu))\nconst ThaiConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Thai))\nconst TraditionalChineseConverter = /** @type {(value: NumericValue, options?: TraditionalChineseOptions) => string} */ (makeConverter(TraditionalChinese))\nconst TurkishConverter = /** @type {(value: NumericValue, options?: TurkishOptions) => string} */ (makeConverter(Turkish))\nconst UkrainianConverter = /** @type {(value: NumericValue, options?: UkrainianOptions) => string} */ (makeConverter(Ukrainian))\nconst UrduConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Urdu))\nconst VietnameseConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Vietnamese))\n\n// ============================================================================\n// Exports\n// ============================================================================\n//\n// All converter functions are exported for public use.\n//\n// IMPORTANT: Keep exports alphabetically sorted for maintainability.\n// ============================================================================\n\nexport {\n ArabicConverter,\n AzerbaijaniConverter,\n BanglaConverter,\n BiblicalHebrewConverter,\n CroatianConverter,\n CzechConverter,\n DanishConverter,\n DutchConverter,\n EnglishConverter,\n FilipinoConverter,\n FrenchConverter,\n FrenchBelgiumConverter,\n GermanConverter,\n GreekConverter,\n GujaratiConverter,\n HebrewConverter,\n HindiConverter,\n HungarianConverter,\n IndonesianConverter,\n ItalianConverter,\n JapaneseConverter,\n KannadaConverter,\n KoreanConverter,\n LatvianConverter,\n LithuanianConverter,\n MalayConverter,\n MarathiConverter,\n NorwegianBokmalConverter,\n PersianConverter,\n PolishConverter,\n PortugueseConverter,\n PunjabiConverter,\n RomanianConverter,\n RussianConverter,\n SerbianCyrillicConverter,\n SerbianLatinConverter,\n SimplifiedChineseConverter,\n SpanishConverter,\n SwahiliConverter,\n SwedishConverter,\n TamilConverter,\n TeluguConverter,\n ThaiConverter,\n TraditionalChineseConverter,\n TurkishConverter,\n UkrainianConverter,\n UrduConverter,\n VietnameseConverter\n}\n","import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js'\n\n/**\n * Dutch language converter.\n *\n * Supports:\n * - Optional \"en\" (and) separator for tens\n * - Optional comma before hundreds\n * - Compound word formation without hyphenation\n */\nexport class Dutch extends GreedyScaleLanguage {\n negativeWord = 'min'\n decimalSeparatorWord = 'komma'\n zeroWord = 'nul'\n\n scaleWords = [\n [1_000_000_000_000_000_000_000_000_000n, 'quadriljard'],\n [1_000_000_000_000_000_000_000_000n, 'quadriljoen'],\n [1_000_000_000_000_000_000_000n, 'triljard'],\n [1_000_000_000_000_000_000n, 'triljoen'],\n [1_000_000_000_000_000n, 'biljard'],\n [1_000_000_000_000n, 'biljoen'],\n [1_000_000_000n, 'miljard'],\n [1_000_000n, 'miljoen'],\n [1000n, 'duizend'],\n [100n, 'honderd'],\n [90n, 'negentig'],\n [80n, 'tachtig'],\n [70n, 'zeventig'],\n [60n, 'zestig'],\n [50n, 'vijftig'],\n [40n, 'veertig'],\n [30n, 'dertig'],\n [20n, 'twintig'],\n [19n, 'negentien'],\n [18n, 'achttien'],\n [17n, 'zeventien'],\n [16n, 'zestien'],\n [15n, 'vijftien'],\n [14n, 'veertien'],\n [13n, 'dertien'],\n [12n, 'twaalf'],\n [11n, 'elf'],\n [10n, 'tien'],\n [9n, 'negen'],\n [8n, 'acht'],\n [7n, 'zeven'],\n [6n, 'zes'],\n [5n, 'vijf'],\n [4n, 'vier'],\n [3n, 'drie'],\n [2n, 'twee'],\n [1n, 'één'],\n [0n, 'nul']\n ]\n\n constructor (options = {}) {\n super()\n\n this.setOptions({\n includeOptionalAnd: false,\n noHundredPairing: false,\n accentOne: true\n }, options)\n\n if (!this.options.accentOne) {\n this.scaleWords[this.scaleWords.length - 2][1] = 'een'\n }\n }\n\n /** Combines two word-sets according to Dutch grammar rules. */\n combineWordSets (preceding, following) {\n let precedingWord = Object.keys(preceding)[0]\n let followingWord = Object.keys(following)[0]\n const precedingValue = Object.values(preceding)[0] // BigInt\n const followingValue = Object.values(following)[0] // BigInt\n\n // Implicit \"een\": omit before large multipliers (\"miljoen\" not \"een miljoen\")\n if (precedingValue === 1n) {\n if (followingValue < 1_000_000n) {\n return following\n }\n precedingWord = this.options.accentOne ? 'één' : 'een'\n }\n\n // Handle multiplication and spacing\n if (followingValue > precedingValue) {\n let hadSpace = false\n // Large scale words (millions+): add space before multiplier\n if (followingValue >= 1_000_000n) {\n precedingWord += ' '\n hadSpace = true\n } else if (followingValue > 100n) {\n // Hundreds and above: add space after multiplier\n followingWord += ' '\n hadSpace = true\n }\n // Convert 'één' to 'een' in compounds (when no space or accentOne disabled)\n if (!hadSpace || !this.options.accentOne) {\n precedingWord = precedingWord.replace(/één/g, 'een')\n followingWord = followingWord.replace(/één/g, 'een')\n }\n return { [`${precedingWord}${followingWord}`]: precedingValue * followingValue }\n }\n\n // Track if we're adding a space (which keeps words separate)\n let hasSpace = false\n\n if (followingValue < 10n && precedingValue > 10n && precedingValue < 100n) {\n const temporary = followingWord\n followingWord = precedingWord\n const andTxt = temporary.endsWith('e') ? 'ën' : 'en'\n precedingWord = `${temporary}${andTxt}`\n } else if (followingValue < 13n && precedingValue < 1000n && this.options.includeOptionalAnd) {\n precedingWord = `${precedingWord}en`\n } else if (followingValue < 13n && precedingValue >= 1000n && this.options.includeOptionalAnd) {\n followingWord = ` en ${followingWord}`\n hasSpace = true\n } else if (precedingValue >= 1_000_000n) {\n precedingWord += ' '\n hasSpace = true\n } else if (precedingValue === 1000n) {\n precedingWord += ' '\n hasSpace = true\n }\n\n // Convert 'één' to 'een' in direct compounds (no space)\n // Keep 'één' only if there's a space AND accentOne=true\n if (!hasSpace) {\n precedingWord = precedingWord.replace(/één/g, 'een')\n followingWord = followingWord.replace(/één/g, 'een')\n } else if (!this.options.accentOne) {\n precedingWord = precedingWord.replace(/één/g, 'een')\n followingWord = followingWord.replace(/één/g, 'een')\n }\n\n return { [`${precedingWord}${followingWord}`]: precedingValue + followingValue }\n }\n\n integerToWords (integerPart) {\n if (integerPart >= 1100n && integerPart < 10_000n && !this.options.noHundredPairing) {\n const high = integerPart / 100n\n const low = integerPart % 100n\n if (high % 10n !== 0n) {\n let result = super.integerToWords(high) + 'honderd'\n if (low) {\n result +=\n (this.options.includeOptionalAnd ? ' en ' : ' ') + super.integerToWords(low)\n }\n return result\n }\n }\n return super.integerToWords(integerPart)\n }\n}\n"],"names":["AbstractLanguage","options","negativeWord","decimalSeparatorWord","zeroWord","wordSeparator","usePerDigitDecimals","this","toWords","isNegative","integerPart","decimalPart","words","push","integerToWords","decimalDigitsToWords","join","Error","setOptions","defaults","userOptions","decimalIntegerToWords","i","length","remainder","slice","char","BigInt","GreedyScaleLanguage","scaleWords","wordForScale","scale","match","find","pair","decomposeInteger","wordSets","remaining","multiplier","reduceWordSets","first","second","rest","Array","isArray","combined","combineWordSets","normalized","map","element","preceding","following","finalizeWords","output","trimEnd","reduced","result","Object","keys","expandScientificNotation","str","mantissa","expStr","toLowerCase","split","exp","parseInt","dotIndex","indexOf","digits","newDotPosition","repeat","DutchConverter","LanguageClass","constructor","super","includeOptionalAnd","noHundredPairing","accentOne","precedingWord","followingWord","precedingValue","values","followingValue","hadSpace","replace","hasSpace","temporary","andTxt","endsWith","high","low","value","undefined","proto","getPrototypeOf","prototype","isPlainObject","TypeError","type","Number","isFinite","toString","includes","numberToString","trimmed","trim","isNaN","stringToNormalizedForm","integerStr","parseNumericString","parseNumericValue"],"mappings":"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"}
|
package/dist/EnglishConverter.js
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
/*! n2words/EnglishConverter v2.0.0 | MIT License | github.com/forzagreen/n2words */
|
|
2
|
-
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).n2words=e.n2words||{})}(this,function(e){"use strict";class n{#e={};negativeWord="";decimalSeparatorWord="";zeroWord="";wordSeparator=" ";usePerDigitDecimals=!1;get options(){return this.#e}toWords(e,n,t){const r=[];return e&&r.push(this.negativeWord),r.push(this.integerToWords(n)),t&&(r.push(this.decimalSeparatorWord),r.push(...this.decimalDigitsToWords(t))),r.join(this.wordSeparator)}integerToWords(e){throw new Error("integerToWords() must be implemented by subclass")}setOptions(e={},n={}){this.#e={...e,...n}}decimalIntegerToWords(e){return this.integerToWords(e)}decimalDigitsToWords(e){const n=[];let t=0;for(;t<e.length&&"0"===e[t];)n.push(this.zeroWord),t++;const r=e.slice(t);if(!r)return n;if(this.usePerDigitDecimals)for(const e of r)n.push(this.decimalIntegerToWords(BigInt(e)));else n.push(this.decimalIntegerToWords(BigInt(r)));return n}}class t extends n{scaleWords;wordForScale(e){const n=this.scaleWords.find(n=>n[0]===e);return n?.[1]}decomposeInteger(e){const n=[];let t=e;do{const e=this.scaleWords.find(e=>t>=e[0]);if(!e)break;const r=0n===t?1n:t/e[0];1n===r?n.push({[this.wordForScale(1n)]:1n}):n.push(this.decomposeInteger(r)),n.push({[e[1]]:e[0]}),t=0n===t?0n:t%e[0]}while(t>0n);return n}reduceWordSets(e){for(;e.length>1;){const[n,t,...r]=e;if(!Array.isArray(n)&&!Array.isArray(t)){const i=this.combineWordSets(n,t);e=r.length>0?[i,r]:[i];continue}const i=e.map(e=>Array.isArray(e)?1===e.length?e[0]:this.reduceWordSets(e):e);e=i}return e[0]}combineWordSets(e,n){throw new Error("combineWordSets() must be implemented by subclass")}finalizeWords(e){return e.trimEnd()}integerToWords(e){const n=this.decomposeInteger(e),t=this.reduceWordSets(n),r=Object.keys(t)[0];return this.finalizeWords(r)}}function r(e){const[n,t]=e.toLowerCase().split("e"),r=parseInt(t,10),i=n.indexOf("."),o=-1===i?n:n.slice(0,i)+n.slice(i+1),s=(-1===i?n.length:i)+r;return s>=o.length?o+"0".repeat(s-o.length):s<=0?"0."+"0".repeat(-s)+o:o.slice(0,s)+"."+o.slice(s)}const i=(o=class extends t{negativeWord="minus";decimalSeparatorWord="point";zeroWord="zero";scaleWords=[[1000000000000000000000000000n,"octillion"],[1000000000000000000000000n,"septillion"],[1000000000000000000000n,"sextillion"],[1000000000000000000n,"quintillion"],[1000000000000000n,"quadrillion"],[1000000000000n,"trillion"],[1000000000n,"billion"],[1000000n,"million"],[1000n,"thousand"],[100n,"hundred"],[90n,"ninety"],[80n,"eighty"],[70n,"seventy"],[60n,"sixty"],[50n,"fifty"],[40n,"forty"],[30n,"thirty"],[20n,"twenty"],[19n,"nineteen"],[18n,"eighteen"],[17n,"seventeen"],[16n,"sixteen"],[15n,"fifteen"],[14n,"fourteen"],[13n,"thirteen"],[12n,"twelve"],[11n,"eleven"],[10n,"ten"],[9n,"nine"],[8n,"eight"],[7n,"seven"],[6n,"six"],[5n,"five"],[4n,"four"],[3n,"three"],[2n,"two"],[1n,"one"],[0n,"zero"]];combineWordSets(e,n){const t=Object.keys(e)[0],r=Object.values(e)[0],i=Object.keys(n)[0],o=Object.values(n)[0];return 1n===r&&o<100n?{[i]:o}:r<100n&&r>o?{[`${t}-${i}`]:r+o}:r>=100n&&o<100n?{[`${t} and ${i}`]:r+o}:o>r?{[`${t} ${i}`]:r*o}:{[`${t} ${i}`]:r+o}}},function(e,n){if(void 0!==n&&!function(e){if(null===e||"object"!=typeof e)return!1;const n=Object.getPrototypeOf(e);return n===Object.prototype||null===n}(n))throw new TypeError("options must be a plain object if provided");const{isNegative:t,integerPart:i,decimalPart:s}=function(e){const n=typeof e;if("bigint"===n)return e<0n?{isNegative:!0,integerPart:-e}:{isNegative:!1,integerPart:e};if("number"!==n&&"string"!==n)throw new TypeError(`Invalid value type: expected number, string, or bigint, received ${n}`);const t="number"===n?function(e){if(!Number.isFinite(e))throw new Error("Number must be finite (NaN and Infinity are not supported)");const n=e.toString();return n.includes("e")||n.includes("E")?r(n):n}(e):function(e){const n=e.trim();if(0===n.length)throw new Error(`Invalid number format: "${e}"`);if(Number.isNaN(Number(n)))throw new Error(`Invalid number format: "${e}"`);return n.includes("e")||n.includes("E")?r(n):n}(e);return function(e){const n="-"===e[0];n&&(e=e.slice(1));const t=e.indexOf(".");if(-1===t)return{isNegative:n,integerPart:BigInt(e)};const r=e.slice(0,t)||"0",i=e.slice(t+1);return{isNegative:n,integerPart:BigInt(r),decimalPart:i}}(t)}(e);return new o(n).toWords(t,i,s)});var o;e.EnglishConverter=i});
|
|
3
|
-
//# sourceMappingURL=EnglishConverter.js.map
|