n2words 3.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 +15 -0
- package/dist/languages/am-Latn.js +2 -2
- package/dist/languages/am-Latn.js.map +1 -1
- package/dist/languages/am.js +2 -2
- package/dist/languages/am.js.map +1 -1
- package/dist/languages/ar.js +1 -1
- package/dist/languages/az.js +2 -2
- package/dist/languages/az.js.map +1 -1
- package/dist/languages/bn.js +2 -2
- package/dist/languages/bn.js.map +1 -1
- package/dist/languages/cs.js +2 -2
- package/dist/languages/cs.js.map +1 -1
- package/dist/languages/da.js +2 -2
- package/dist/languages/da.js.map +1 -1
- package/dist/languages/de.js +2 -2
- package/dist/languages/de.js.map +1 -1
- package/dist/languages/el.js +2 -2
- package/dist/languages/el.js.map +1 -1
- package/dist/languages/en.js +2 -2
- package/dist/languages/en.js.map +1 -1
- package/dist/languages/es.js +2 -2
- package/dist/languages/es.js.map +1 -1
- package/dist/languages/fa.js +1 -1
- package/dist/languages/fi.js +2 -2
- package/dist/languages/fi.js.map +1 -1
- package/dist/languages/fil.js +2 -2
- package/dist/languages/fil.js.map +1 -1
- package/dist/languages/fr-BE.js +2 -2
- package/dist/languages/fr-BE.js.map +1 -1
- package/dist/languages/fr.js +2 -2
- package/dist/languages/fr.js.map +1 -1
- package/dist/languages/gu.js +2 -2
- package/dist/languages/gu.js.map +1 -1
- package/dist/languages/ha.js +2 -2
- package/dist/languages/ha.js.map +1 -1
- package/dist/languages/hbo.js +2 -2
- package/dist/languages/hbo.js.map +1 -1
- package/dist/languages/he.js +2 -2
- package/dist/languages/he.js.map +1 -1
- package/dist/languages/hi.js +2 -2
- package/dist/languages/hi.js.map +1 -1
- package/dist/languages/hr.js +2 -2
- package/dist/languages/hr.js.map +1 -1
- package/dist/languages/hu.js +1 -1
- package/dist/languages/id.js +2 -2
- package/dist/languages/id.js.map +1 -1
- package/dist/languages/it.js +2 -2
- package/dist/languages/it.js.map +1 -1
- package/dist/languages/ja.js +2 -2
- package/dist/languages/ja.js.map +1 -1
- package/dist/languages/ka.js +3 -0
- package/dist/languages/ka.js.map +1 -0
- package/dist/languages/kn.js +2 -2
- package/dist/languages/kn.js.map +1 -1
- package/dist/languages/ko.js +2 -2
- package/dist/languages/ko.js.map +1 -1
- package/dist/languages/lt.js +2 -2
- package/dist/languages/lt.js.map +1 -1
- package/dist/languages/lv.js +2 -2
- package/dist/languages/lv.js.map +1 -1
- package/dist/languages/mr.js +2 -2
- package/dist/languages/mr.js.map +1 -1
- package/dist/languages/ms.js +2 -2
- package/dist/languages/ms.js.map +1 -1
- package/dist/languages/nb.js +2 -2
- package/dist/languages/nb.js.map +1 -1
- package/dist/languages/nl.js +2 -2
- package/dist/languages/nl.js.map +1 -1
- package/dist/languages/pa.js +2 -2
- package/dist/languages/pa.js.map +1 -1
- package/dist/languages/pl.js +2 -2
- package/dist/languages/pl.js.map +1 -1
- package/dist/languages/pt.js +2 -2
- package/dist/languages/pt.js.map +1 -1
- package/dist/languages/ro.js +1 -1
- package/dist/languages/ru.js +2 -2
- package/dist/languages/ru.js.map +1 -1
- package/dist/languages/sr-Cyrl.js +2 -2
- package/dist/languages/sr-Cyrl.js.map +1 -1
- package/dist/languages/sr-Latn.js +2 -2
- package/dist/languages/sr-Latn.js.map +1 -1
- package/dist/languages/sv.js +2 -2
- package/dist/languages/sv.js.map +1 -1
- package/dist/languages/sw.js +1 -1
- package/dist/languages/ta.js +2 -2
- package/dist/languages/ta.js.map +1 -1
- package/dist/languages/te.js +2 -2
- package/dist/languages/te.js.map +1 -1
- package/dist/languages/th.js +1 -1
- package/dist/languages/tr.js +2 -2
- package/dist/languages/tr.js.map +1 -1
- package/dist/languages/uk.js +2 -2
- package/dist/languages/uk.js.map +1 -1
- package/dist/languages/ur.js +2 -2
- package/dist/languages/ur.js.map +1 -1
- package/dist/languages/vi.js +2 -2
- package/dist/languages/vi.js.map +1 -1
- package/dist/languages/yo.js +3 -0
- package/dist/languages/yo.js.map +1 -0
- package/dist/languages/zh-Hans.js +1 -1
- package/dist/languages/zh-Hant.js +1 -1
- package/dist/n2words.js +2 -2
- package/dist/n2words.js.map +1 -1
- package/lib/languages/am-Latn.js +4 -9
- package/lib/languages/am.js +4 -9
- package/lib/languages/az.js +4 -9
- package/lib/languages/bn.js +51 -30
- package/lib/languages/cs.js +9 -26
- package/lib/languages/da.js +6 -13
- package/lib/languages/de.js +21 -33
- package/lib/languages/el.js +6 -13
- package/lib/languages/en.js +44 -58
- package/lib/languages/es.js +10 -30
- package/lib/languages/fi.js +6 -13
- package/lib/languages/fil.js +6 -17
- package/lib/languages/fr-BE.js +17 -26
- package/lib/languages/fr.js +18 -31
- package/lib/languages/gu.js +43 -30
- package/lib/languages/ha.js +4 -9
- package/lib/languages/hbo.js +7 -25
- package/lib/languages/he.js +7 -18
- package/lib/languages/hi.js +55 -30
- package/lib/languages/hr.js +61 -55
- package/lib/languages/id.js +8 -13
- package/lib/languages/it.js +64 -99
- package/lib/languages/ja.js +8 -20
- package/lib/languages/ka.d.ts +17 -0
- package/lib/languages/ka.js +291 -0
- package/lib/languages/kn.js +43 -30
- package/lib/languages/ko.js +6 -13
- package/lib/languages/lt.js +6 -15
- package/lib/languages/lv.js +6 -15
- package/lib/languages/mr.js +43 -30
- package/lib/languages/ms.js +8 -13
- package/lib/languages/nb.js +13 -23
- package/lib/languages/nl.js +11 -29
- package/lib/languages/pa.js +32 -37
- package/lib/languages/pl.js +7 -20
- package/lib/languages/pt.js +17 -31
- package/lib/languages/ru.js +64 -59
- package/lib/languages/sr-Cyrl.js +58 -52
- package/lib/languages/sr-Latn.js +58 -52
- package/lib/languages/sv.js +14 -24
- package/lib/languages/ta.js +55 -42
- package/lib/languages/te.js +33 -41
- package/lib/languages/tr.js +7 -18
- package/lib/languages/uk.js +61 -55
- package/lib/languages/ur.js +32 -37
- package/lib/languages/vi.js +23 -43
- package/lib/languages/yo.d.ts +7 -0
- package/lib/languages/yo.js +303 -0
- package/lib/n2words.d.ts +3 -1
- package/lib/n2words.js +4 -0
- package/package.json +1 -1
package/dist/languages/it.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"it.js","sources":["../../lib/utils/parse-numeric.js","../../lib/languages/it.js"],"sourcesContent":["/**\n * Numeric value parsing utility.\n * Transforms user input (number, string, or bigint) into normalized components.\n * @module parse-numeric\n */\n\n/**\n * Parses a numeric value into its components.\n * @param {number|string|bigint} value\n * @returns {{isNegative: boolean, integerPart: bigint, decimalPart?: string}}\n * @throws {TypeError} If value is not number, string, or bigint\n * @throws {Error} If value is not a valid number format\n */\nexport function parseNumericValue (value) {\n const type = typeof value\n\n // BigInt: simplest case\n if (type === 'bigint') {\n return value < 0n\n ? { isNegative: true, integerPart: -value }\n : { isNegative: false, integerPart: value }\n }\n\n // Number: fast path for safe integers\n if (type === 'number') {\n if (!Number.isFinite(value)) {\n throw new Error('Number must be finite (NaN and Infinity are not supported)')\n }\n if (Number.isSafeInteger(value)) {\n return value < 0\n ? { isNegative: true, integerPart: BigInt(-value) }\n : { isNegative: false, integerPart: BigInt(value) }\n }\n return parseNumericString(numberToString(value))\n }\n\n // String input\n if (type === 'string') {\n return parseNumericString(normalizeString(value))\n }\n\n throw new TypeError(\n `Invalid value type: expected number, string, or bigint, received ${type}`\n )\n}\n\n/**\n * Converts a number to decimal string, expanding scientific notation if needed.\n */\nfunction numberToString (value) {\n const str = value.toString()\n return (str.includes('e') || str.includes('E'))\n ? expandScientificNotation(str)\n : str\n}\n\n/**\n * Validates and normalizes a string numeric input.\n */\nfunction normalizeString (value) {\n const trimmed = value.trim()\n if (trimmed.length === 0 || Number.isNaN(Number(trimmed))) {\n throw new Error(`Invalid number format: \"${value}\"`)\n }\n return (trimmed.includes('e') || trimmed.includes('E'))\n ? expandScientificNotation(trimmed)\n : trimmed\n}\n\n/**\n * Parses a normalized numeric string into components.\n */\nfunction parseNumericString (str) {\n const isNegative = str[0] === '-'\n if (isNegative) str = str.slice(1)\n\n const dotIndex = str.indexOf('.')\n if (dotIndex === -1) {\n return { isNegative, integerPart: BigInt(str) }\n }\n\n const integerStr = str.slice(0, dotIndex) || '0'\n const decimalPart = str.slice(dotIndex + 1)\n return { isNegative, integerPart: BigInt(integerStr), decimalPart }\n}\n\n/**\n * Expands scientific notation to decimal form (e.g., \"1e21\" → \"1000...\").\n */\nfunction expandScientificNotation (str) {\n const [mantissa, expStr] = str.toLowerCase().split('e')\n const exp = parseInt(expStr, 10)\n\n const dotIndex = mantissa.indexOf('.')\n const digits = dotIndex === -1\n ? mantissa\n : mantissa.slice(0, dotIndex) + mantissa.slice(dotIndex + 1)\n const integerLength = dotIndex === -1 ? mantissa.length : dotIndex\n const newDotPosition = integerLength + exp\n\n if (newDotPosition >= digits.length) {\n return digits + '0'.repeat(newDotPosition - digits.length)\n }\n if (newDotPosition <= 0) {\n return '0.' + '0'.repeat(-newDotPosition) + digits\n }\n return digits.slice(0, newDotPosition) + '.' + digits.slice(newDotPosition)\n}\n","/**\n * Italian language converter - Functional Implementation v2\n *\n * A performance-optimized number-to-words converter using precomputed lookup tables.\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key optimization: Precompute all 1000 segment values (0-999) at module load.\n * This eliminates all per-call string manipulation for segment conversion.\n *\n * Italian-specific rules (handled in precomputation):\n * - Concatenation without spaces within segments (\"ventotto\" not \"venti otto\")\n * - Phonetic vowel elision: \"venti\" + \"otto\" → \"ventotto\"\n * - Accent on final \"tre\" in compounds: \"ventitré\"\n * - mille/mila alternation for thousands\n * - Scale words: milione/milioni, miliardo/miliardi, etc.\n * - \"e\" connector before simple final remainder\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\n// Base vocabulary for building lookup tables\nconst ONES = ['', 'uno', 'due', 'tre', 'quattro', 'cinque', 'sei', 'sette', 'otto', 'nove']\nconst TEENS = ['dieci', 'undici', 'dodici', 'tredici', 'quattordici', 'quindici', 'sedici', 'diciassette', 'diciotto', 'diciannove']\nconst TENS = ['', '', 'venti', 'trenta', 'quaranta', 'cinquanta', 'sessanta', 'settanta', 'ottanta', 'novanta']\nconst HUNDREDS = ['', 'cento', 'duecento', 'trecento', 'quattrocento', 'cinquecento', 'seicento', 'settecento', 'ottocento', 'novecento']\n\nconst ZERO = 'zero'\nconst NEGATIVE = 'meno'\nconst DECIMAL_SEP = 'virgola'\n\n// Thousands\nconst THOUSAND_SINGULAR = 'mille'\nconst THOUSAND_PLURAL_SUFFIX = 'mila'\n\n// Scale word generation\nconst SCALE_PREFIXES = ['m', 'b', 'tr', 'quadr', 'quint', 'sest', 'sett', 'ott', 'nov', 'dec']\n\n// ============================================================================\n// Precomputed Lookup Tables (built once at module load)\n// ============================================================================\n\n/**\n * Applies Italian phonetic vowel elision rules.\n * Only used during table construction.\n */\nfunction applyPhoneticRules (str) {\n return str\n .replace(/io/g, 'o')\n .replace(/ao/g, 'o')\n .replace(/oo/g, 'o')\n .replace(/iu/g, 'u')\n .replace(/au/g, 'u')\n}\n\n/**\n * Adds accent to final \"tre\" in a word.\n * Only used during table construction.\n */\nfunction accentuateTre (word) {\n if (word.length > 3 && word.slice(-3) === 'tre') {\n return word.slice(0, -3) + 'tré'\n }\n return word\n}\n\n/**\n * Builds the segment word for a number 0-999.\n * Only used during table construction.\n */\nfunction buildSegmentWord (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100)\n\n let result = ''\n\n // Hundreds\n if (hundreds > 0) {\n result = HUNDREDS[hundreds]\n }\n\n // Tens and ones\n if (tens === 0 && ones === 0) {\n // Nothing more\n } else if (tens === 1) {\n // Teens: 10-19\n result += TEENS[ones]\n } else if (tens >= 2) {\n // 20-99\n result += TENS[tens]\n if (ones > 0) {\n result += ONES[ones]\n }\n } else if (ones > 0) {\n // 1-9 (tens === 0)\n result += ONES[ones]\n }\n\n // Apply phonetic rules and accent\n return accentuateTre(applyPhoneticRules(result))\n}\n\n/**\n * Builds segment word with \"un\" for scale context (millions, billions).\n * Only used during table construction.\n */\nfunction buildSegmentWordForScale (n) {\n if (n === 0) return ''\n if (n === 1) return 'un' // \"un milione\" not \"uno milione\"\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100)\n\n let result = ''\n\n if (hundreds > 0) {\n result = HUNDREDS[hundreds]\n }\n\n if (tens === 0 && ones === 0) {\n // Nothing more\n } else if (tens === 1) {\n result += TEENS[ones]\n } else if (tens >= 2) {\n result += TENS[tens]\n if (ones > 0) {\n result += ONES[ones]\n }\n } else if (ones > 0) {\n // 1-9 with tens === 0\n // \"un\" only for exactly 1, others normal\n result += ONES[ones]\n }\n\n return accentuateTre(applyPhoneticRules(result))\n}\n\n// Precompute all 1000 segment words (0-999)\n// SEGMENTS[n] gives the Italian word for n within a segment\nconst SEGMENTS = new Array(1000)\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS[i] = buildSegmentWord(i)\n}\n\n// Precompute segment words for scale context (uses \"un\" for 1)\nconst SEGMENTS_SCALE = new Array(1000)\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS_SCALE[i] = buildSegmentWordForScale(i)\n}\n\n// Precompute thousands words (1-999) + \"mila\" suffix\n// THOUSANDS[n] gives the word for n thousand (e.g., THOUSANDS[2] = \"duemila\")\nconst THOUSANDS = new Array(1000)\nTHOUSANDS[0] = ''\nTHOUSANDS[1] = THOUSAND_SINGULAR // \"mille\"\nfor (let i = 2; i < 1000; i++) {\n THOUSANDS[i] = applyPhoneticRules(SEGMENTS[i] + THOUSAND_PLURAL_SUFFIX)\n}\n\n// ============================================================================\n// Scale Word Functions\n// ============================================================================\n\n/**\n * Gets singular scale word for index.\n * @param {number} scaleIndex - 2=million, 3=billion, etc.\n */\nfunction getScaleWordSingular (scaleIndex) {\n if (scaleIndex < 2) return ''\n const prefixIndex = Math.floor((scaleIndex - 2) / 2)\n const isIardo = (scaleIndex - 2) % 2 === 1\n const prefix = SCALE_PREFIXES[prefixIndex]\n if (!prefix) return ''\n return prefix + (isIardo ? 'iliardo' : 'ilione')\n}\n\n/**\n * Gets plural scale word for index.\n * @param {number} scaleIndex - 2=million, 3=billion, etc.\n */\nfunction getScaleWordPlural (scaleIndex) {\n if (scaleIndex < 2) return ''\n const prefixIndex = Math.floor((scaleIndex - 2) / 2)\n const isIardo = (scaleIndex - 2) % 2 === 1\n const prefix = SCALE_PREFIXES[prefixIndex]\n if (!prefix) return ''\n return prefix + (isIardo ? 'iliardi' : 'ilioni')\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Italian words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Italian words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000 (direct lookup)\n if (n < 1000n) {\n return SEGMENTS[Number(n)]\n }\n\n // Fast path: numbers < 1,000,000 (thousands)\n if (n < 1_000_000n) {\n const thousands = Number(n / 1000n)\n const remainder = Number(n % 1000n)\n\n if (remainder === 0) {\n return THOUSANDS[thousands]\n }\n\n // Concatenate thousands + remainder\n return THOUSANDS[thousands] + SEGMENTS[remainder]\n }\n\n // For numbers >= 1,000,000, use scale decomposition\n return buildLargeNumberWords(n)\n}\n\n/**\n * Builds words for numbers >= 1,000,000.\n *\n * @param {bigint} n - Number >= 1,000,000\n * @returns {string} Italian words\n */\nfunction buildLargeNumberWords (n) {\n const parts = []\n let remaining = n\n\n // Find the highest scale\n let maxScale = 2\n let testValue = 1_000_000n\n while (testValue * 1000n <= remaining) {\n testValue *= 1000n\n maxScale++\n }\n\n // Process from highest scale down\n for (let scaleIndex = maxScale; scaleIndex >= 0; scaleIndex--) {\n const divisor = 1000n ** BigInt(scaleIndex)\n const segment = remaining / divisor\n remaining = remaining % divisor\n\n if (segment === 0n) continue\n\n const segNum = Number(segment)\n\n if (scaleIndex >= 2) {\n // Millions and above: \"segment scaleWord\"\n const segmentWords = SEGMENTS_SCALE[segNum]\n const scaleWord = segment === 1n\n ? getScaleWordSingular(scaleIndex)\n : getScaleWordPlural(scaleIndex)\n parts.push(segmentWords + ' ' + scaleWord)\n } else if (scaleIndex === 1) {\n // Thousands: use precomputed table\n parts.push(THOUSANDS[segNum])\n } else {\n // Units (scaleIndex === 0): just the segment\n parts.push(SEGMENTS[segNum])\n }\n }\n\n return joinPartsWithConnector(parts)\n}\n\n/**\n * Joins parts with Italian connector rules.\n * Uses \"e\" before simple (non-compound) final segment.\n *\n * @param {string[]} parts - Parts to join\n * @returns {string} Joined string\n */\nfunction joinPartsWithConnector (parts) {\n const len = parts.length\n if (len === 0) return ''\n if (len === 1) return parts[0]\n\n // Check if last part is \"simple\" (no space = no scale word)\n const lastPart = parts[len - 1]\n if (lastPart.indexOf(' ') === -1) {\n // Join all but last with space, then add \"e\" connector\n let result = parts[0]\n for (let i = 1; i < len - 1; i++) {\n result += ' ' + parts[i]\n }\n return result + ' e ' + lastPart\n }\n\n // Join with spaces\n let result = parts[0]\n for (let i = 1; i < len; i++) {\n result += ' ' + parts[i]\n }\n return result\n}\n\n/**\n * Converts decimal digits to Italian words.\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @returns {string} Italian words for decimal part\n */\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n\n // Handle leading zeros\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n // Convert remainder as a single number\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder))\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Italian words.\n *\n * This is the main public API. It accepts any valid numeric input\n * (number, string, or bigint) and handles parsing internally.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Italian words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(28) // 'ventotto'\n * toWords(23) // 'ventitré'\n * toWords(1000) // 'mille'\n * toWords(2000) // 'duemila'\n * toWords(1000000) // 'un milione'\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n"],"names":["parseNumericString","str","isNegative","slice","dotIndex","indexOf","integerPart","BigInt","integerStr","decimalPart","expandScientificNotation","mantissa","expStr","toLowerCase","split","exp","parseInt","digits","newDotPosition","length","repeat","ONES","TEENS","TENS","HUNDREDS","ZERO","SCALE_PREFIXES","applyPhoneticRules","replace","accentuateTre","word","buildSegmentWord","n","ones","tens","Math","floor","hundreds","result","buildSegmentWordForScale","SEGMENTS","Array","i","SEGMENTS_SCALE","THOUSANDS","getScaleWordSingular","scaleIndex","prefixIndex","prefix","getScaleWordPlural","integerToWords","Number","thousands","remainder","parts","remaining","maxScale","testValue","divisor","segment","segNum","segmentWords","scaleWord","push","len","lastPart","joinPartsWithConnector","buildLargeNumberWords","value","type","isFinite","Error","isSafeInteger","toString","includes","numberToString","trimmed","trim","isNaN","normalizeString","TypeError","parseNumericValue","NEGATIVE","decimalPartToWords"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"it.js","sources":["../../lib/utils/parse-numeric.js","../../lib/languages/it.js"],"sourcesContent":["/**\n * Numeric value parsing utility.\n * Transforms user input (number, string, or bigint) into normalized components.\n * @module parse-numeric\n */\n\n/**\n * Parses a numeric value into its components.\n * @param {number|string|bigint} value\n * @returns {{isNegative: boolean, integerPart: bigint, decimalPart?: string}}\n * @throws {TypeError} If value is not number, string, or bigint\n * @throws {Error} If value is not a valid number format\n */\nexport function parseNumericValue (value) {\n const type = typeof value\n\n // BigInt: simplest case\n if (type === 'bigint') {\n return value < 0n\n ? { isNegative: true, integerPart: -value }\n : { isNegative: false, integerPart: value }\n }\n\n // Number: fast path for safe integers\n if (type === 'number') {\n if (!Number.isFinite(value)) {\n throw new Error('Number must be finite (NaN and Infinity are not supported)')\n }\n if (Number.isSafeInteger(value)) {\n return value < 0\n ? { isNegative: true, integerPart: BigInt(-value) }\n : { isNegative: false, integerPart: BigInt(value) }\n }\n return parseNumericString(numberToString(value))\n }\n\n // String input\n if (type === 'string') {\n return parseNumericString(normalizeString(value))\n }\n\n throw new TypeError(\n `Invalid value type: expected number, string, or bigint, received ${type}`\n )\n}\n\n/**\n * Converts a number to decimal string, expanding scientific notation if needed.\n */\nfunction numberToString (value) {\n const str = value.toString()\n return (str.includes('e') || str.includes('E'))\n ? expandScientificNotation(str)\n : str\n}\n\n/**\n * Validates and normalizes a string numeric input.\n */\nfunction normalizeString (value) {\n const trimmed = value.trim()\n if (trimmed.length === 0 || Number.isNaN(Number(trimmed))) {\n throw new Error(`Invalid number format: \"${value}\"`)\n }\n return (trimmed.includes('e') || trimmed.includes('E'))\n ? expandScientificNotation(trimmed)\n : trimmed\n}\n\n/**\n * Parses a normalized numeric string into components.\n */\nfunction parseNumericString (str) {\n const isNegative = str[0] === '-'\n if (isNegative) str = str.slice(1)\n\n const dotIndex = str.indexOf('.')\n if (dotIndex === -1) {\n return { isNegative, integerPart: BigInt(str) }\n }\n\n const integerStr = str.slice(0, dotIndex) || '0'\n const decimalPart = str.slice(dotIndex + 1)\n return { isNegative, integerPart: BigInt(integerStr), decimalPart }\n}\n\n/**\n * Expands scientific notation to decimal form (e.g., \"1e21\" → \"1000...\").\n */\nfunction expandScientificNotation (str) {\n const [mantissa, expStr] = str.toLowerCase().split('e')\n const exp = parseInt(expStr, 10)\n\n const dotIndex = mantissa.indexOf('.')\n const digits = dotIndex === -1\n ? mantissa\n : mantissa.slice(0, dotIndex) + mantissa.slice(dotIndex + 1)\n const integerLength = dotIndex === -1 ? mantissa.length : dotIndex\n const newDotPosition = integerLength + exp\n\n if (newDotPosition >= digits.length) {\n return digits + '0'.repeat(newDotPosition - digits.length)\n }\n if (newDotPosition <= 0) {\n return '0.' + '0'.repeat(-newDotPosition) + digits\n }\n return digits.slice(0, newDotPosition) + '.' + digits.slice(newDotPosition)\n}\n","/**\n * Italian language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Italian-specific rules:\n * - Concatenation without spaces within segments (\"ventotto\" not \"venti otto\")\n * - Phonetic vowel elision: \"venti\" + \"otto\" → \"ventotto\"\n * - Accent on final \"tre\" in compounds: \"ventitré\"\n * - mille/mila alternation for thousands\n * - Scale words: milione/milioni, miliardo/miliardi, etc.\n * - \"e\" connector before simple final remainder\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\n// Base vocabulary\nconst ONES = ['', 'uno', 'due', 'tre', 'quattro', 'cinque', 'sei', 'sette', 'otto', 'nove']\nconst TEENS = ['dieci', 'undici', 'dodici', 'tredici', 'quattordici', 'quindici', 'sedici', 'diciassette', 'diciotto', 'diciannove']\nconst TENS = ['', '', 'venti', 'trenta', 'quaranta', 'cinquanta', 'sessanta', 'settanta', 'ottanta', 'novanta']\nconst HUNDREDS = ['', 'cento', 'duecento', 'trecento', 'quattrocento', 'cinquecento', 'seicento', 'settecento', 'ottocento', 'novecento']\n\n// Pre-elided tens stems (drop final vowel before 'uno'/'otto')\n// vent- (from venti), trent- (from trenta), etc.\nconst TENS_STEM = ['', '', 'vent', 'trent', 'quarant', 'cinquant', 'sessant', 'settant', 'ottant', 'novant']\n\nconst ZERO = 'zero'\nconst NEGATIVE = 'meno'\nconst DECIMAL_SEP = 'virgola'\n\n// Thousands\nconst THOUSAND_SINGULAR = 'mille'\nconst THOUSAND_PLURAL_SUFFIX = 'mila'\n\n// Scale word generation\nconst SCALE_PREFIXES = ['m', 'b', 'tr', 'quadr', 'quint', 'sest', 'sett', 'ott', 'nov', 'dec']\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds the segment word for a number 0-999.\n * Handles Italian phonetic elision inline (no regex).\n *\n * Elision rules:\n * - Tens ending in vowel + uno/otto → drop tens vowel: ventuno, ventotto\n * - Hundreds cento + otto/ottanta → centotto, centottanta (drop 'o')\n * - Final 'tre' in compounds becomes 'tré': ventitré, trentatré\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100)\n\n let result = ''\n\n // Hundreds\n if (hundreds > 0) {\n // Elision: *cento + otto/ottanta → *centotto/centottanta (drop final 'o')\n // Only applies when tens = 8 (ottanta) or tens = 0 and ones = 8 (otto)\n if (tens === 8 || (tens === 0 && ones === 8)) {\n // Remove final 'o' from hundreds: cento→cent, duecento→duecent, etc.\n result = HUNDREDS[hundreds].slice(0, -1)\n } else {\n result = HUNDREDS[hundreds]\n }\n }\n\n // Tens and ones\n if (tens === 0 && ones === 0) {\n // Nothing more (just hundreds)\n } else if (tens === 1) {\n // Teens: 10-19\n result += TEENS[ones]\n } else if (tens >= 2) {\n // 20-99: handle elision for uno (1) and otto (8)\n if (ones === 1 || ones === 8) {\n // Use stem form: vent + uno = ventuno, vent + otto = ventotto\n result += TENS_STEM[tens] + ONES[ones]\n } else if (ones === 3) {\n // Final tre becomes tré\n result += TENS[tens] + 'tré'\n } else if (ones > 0) {\n result += TENS[tens] + ONES[ones]\n } else {\n result += TENS[tens]\n }\n } else if (ones > 0) {\n // 1-9 (tens === 0)\n if (ones === 3 && hundreds > 0) {\n // centotré, duecentotré, etc.\n result += 'tré'\n } else {\n result += ONES[ones]\n }\n }\n\n return result\n}\n\n/**\n * Builds segment word with \"un\" for scale context (millions, billions).\n * Same as buildSegment but returns \"un\" for 1 instead of \"uno\".\n */\nfunction buildSegmentForScale (n) {\n if (n === 0) return ''\n if (n === 1) return 'un' // \"un milione\" not \"uno milione\"\n return buildSegment(n)\n}\n\n/**\n * Builds thousands word for 1-999 thousand.\n * Handles elision: tre + mila = tremila (no accent), otto + mila = ottomila\n */\nfunction buildThousands (n) {\n if (n === 0) return ''\n if (n === 1) return THOUSAND_SINGULAR // \"mille\"\n\n // Build segment and append \"mila\"\n // Note: elision of segment ending vowel + 'mila' not needed (mila starts with 'm')\n // But we need to handle cases like \"ottomila\" (no double-o issue since we build directly)\n return buildSegment(n) + THOUSAND_PLURAL_SUFFIX\n}\n\n// ============================================================================\n// Scale Word Functions\n// ============================================================================\n\n/**\n * Gets singular scale word for index.\n * @param {number} scaleIndex - 2=million, 3=billion, etc.\n */\nfunction getScaleWordSingular (scaleIndex) {\n if (scaleIndex < 2) return ''\n const prefixIndex = Math.floor((scaleIndex - 2) / 2)\n const isIardo = (scaleIndex - 2) % 2 === 1\n const prefix = SCALE_PREFIXES[prefixIndex]\n if (!prefix) return ''\n return prefix + (isIardo ? 'iliardo' : 'ilione')\n}\n\n/**\n * Gets plural scale word for index.\n * @param {number} scaleIndex - 2=million, 3=billion, etc.\n */\nfunction getScaleWordPlural (scaleIndex) {\n if (scaleIndex < 2) return ''\n const prefixIndex = Math.floor((scaleIndex - 2) / 2)\n const isIardo = (scaleIndex - 2) % 2 === 1\n const prefix = SCALE_PREFIXES[prefixIndex]\n if (!prefix) return ''\n return prefix + (isIardo ? 'iliardi' : 'ilioni')\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Italian words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Italian words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000\n if (n < 1000n) {\n return buildSegment(Number(n))\n }\n\n // Fast path: numbers < 1,000,000 (thousands)\n if (n < 1_000_000n) {\n const thousands = Number(n / 1000n)\n const remainder = Number(n % 1000n)\n\n if (remainder === 0) {\n return buildThousands(thousands)\n }\n\n // Concatenate thousands + remainder\n return buildThousands(thousands) + buildSegment(remainder)\n }\n\n // For numbers >= 1,000,000, use scale decomposition\n return buildLargeNumberWords(n)\n}\n\n/**\n * Builds words for numbers >= 1,000,000.\n *\n * @param {bigint} n - Number >= 1,000,000\n * @returns {string} Italian words\n */\nfunction buildLargeNumberWords (n) {\n const parts = []\n let remaining = n\n\n // Find the highest scale\n let maxScale = 2\n let testValue = 1_000_000n\n while (testValue * 1000n <= remaining) {\n testValue *= 1000n\n maxScale++\n }\n\n // Process from highest scale down\n for (let scaleIndex = maxScale; scaleIndex >= 0; scaleIndex--) {\n const divisor = 1000n ** BigInt(scaleIndex)\n const segment = remaining / divisor\n remaining = remaining % divisor\n\n if (segment === 0n) continue\n\n const segNum = Number(segment)\n\n if (scaleIndex >= 2) {\n // Millions and above: \"segment scaleWord\"\n const segmentWords = buildSegmentForScale(segNum)\n const scaleWord = segment === 1n\n ? getScaleWordSingular(scaleIndex)\n : getScaleWordPlural(scaleIndex)\n parts.push(segmentWords + ' ' + scaleWord)\n } else if (scaleIndex === 1) {\n // Thousands\n parts.push(buildThousands(segNum))\n } else {\n // Units (scaleIndex === 0): just the segment\n parts.push(buildSegment(segNum))\n }\n }\n\n return joinPartsWithConnector(parts)\n}\n\n/**\n * Joins parts with Italian connector rules.\n * Uses \"e\" before simple (non-compound) final segment.\n *\n * @param {string[]} parts - Parts to join\n * @returns {string} Joined string\n */\nfunction joinPartsWithConnector (parts) {\n const len = parts.length\n if (len === 0) return ''\n if (len === 1) return parts[0]\n\n // Check if last part is \"simple\" (no space = no scale word)\n const lastPart = parts[len - 1]\n if (lastPart.indexOf(' ') === -1) {\n // Join all but last with space, then add \"e\" connector\n let result = parts[0]\n for (let i = 1; i < len - 1; i++) {\n result += ' ' + parts[i]\n }\n return result + ' e ' + lastPart\n }\n\n // Join with spaces\n let result = parts[0]\n for (let i = 1; i < len; i++) {\n result += ' ' + parts[i]\n }\n return result\n}\n\n/**\n * Converts decimal digits to Italian words.\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @returns {string} Italian words for decimal part\n */\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n\n // Handle leading zeros\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n // Convert remainder as a single number\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder))\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Italian words.\n *\n * This is the main public API. It accepts any valid numeric input\n * (number, string, or bigint) and handles parsing internally.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Italian words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(28) // 'ventotto'\n * toWords(23) // 'ventitré'\n * toWords(1000) // 'mille'\n * toWords(2000) // 'duemila'\n * toWords(1000000) // 'un milione'\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n"],"names":["parseNumericString","str","isNegative","slice","dotIndex","indexOf","integerPart","BigInt","integerStr","decimalPart","expandScientificNotation","mantissa","expStr","toLowerCase","split","exp","parseInt","digits","newDotPosition","length","repeat","ONES","TEENS","TENS","HUNDREDS","TENS_STEM","ZERO","SCALE_PREFIXES","buildSegment","n","ones","tens","Math","floor","hundreds","result","buildSegmentForScale","buildThousands","getScaleWordSingular","scaleIndex","prefixIndex","prefix","getScaleWordPlural","integerToWords","Number","thousands","remainder","parts","remaining","maxScale","testValue","divisor","segment","segNum","segmentWords","scaleWord","push","len","lastPart","i","joinPartsWithConnector","buildLargeNumberWords","value","type","isFinite","Error","isSafeInteger","toString","includes","numberToString","trimmed","trim","isNaN","normalizeString","TypeError","parseNumericValue","NEGATIVE","decimalPartToWords"],"mappings":"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"}
|
package/dist/languages/ja.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
/*! n2words/ja v3.
|
|
2
|
-
var e,t;e=this,t=function(e){"use strict";function t(e){const t="-"===e[0];t&&(e=e.slice(1));const n=e.indexOf(".");if(-1===n)return{isNegative:t,integerPart:BigInt(e)};const r=e.slice(0,n)||"0",i=e.slice(n+1);return{isNegative:t,integerPart:BigInt(r),decimalPart:i}}function n(e){const[t,n]=e.toLowerCase().split("e"),r=parseInt(n,10),i=t.indexOf("."),o=-1===i?t:t.slice(0,i)+t.slice(i+1),s=(-1===i?t.length:i)+r;return s>=o.length?o+"0".repeat(s-o.length):s<=0?"0."+"0".repeat(-s)+o:o.slice(0,s)+"."+o.slice(s)}const r=["","一","二","三","四","五","六","七","八","九"],i=["万","億","兆","京","垓","秭","穣","溝","澗","正","載","極","恒河沙","阿僧祇","那由他","不可思議","無量大数"];function o(e){if(0===e)return"";const t=e%10,n=Math.floor(e/10)%10,i=Math.floor(e/100)%10,o=Math.floor(e/1e3);let s="";return o>0&&(s+=1===o?"千":r[o]+"千"),i>0&&(s+=1===i?"百":r[i]+"百"),n>0&&(s+=1===n?"十":r[n]+"十"),t>0&&(s+=r[t]),s}
|
|
1
|
+
/*! n2words/ja v3.1.0 | MIT License | github.com/forzagreen/n2words */
|
|
2
|
+
var e,t;e=this,t=function(e){"use strict";function t(e){const t="-"===e[0];t&&(e=e.slice(1));const n=e.indexOf(".");if(-1===n)return{isNegative:t,integerPart:BigInt(e)};const r=e.slice(0,n)||"0",i=e.slice(n+1);return{isNegative:t,integerPart:BigInt(r),decimalPart:i}}function n(e){const[t,n]=e.toLowerCase().split("e"),r=parseInt(n,10),i=t.indexOf("."),o=-1===i?t:t.slice(0,i)+t.slice(i+1),s=(-1===i?t.length:i)+r;return s>=o.length?o+"0".repeat(s-o.length):s<=0?"0."+"0".repeat(-s)+o:o.slice(0,s)+"."+o.slice(s)}const r=["","一","二","三","四","五","六","七","八","九"],i=["万","億","兆","京","垓","秭","穣","溝","澗","正","載","極","恒河沙","阿僧祇","那由他","不可思議","無量大数"];function o(e){if(0===e)return"";const t=e%10,n=Math.floor(e/10)%10,i=Math.floor(e/100)%10,o=Math.floor(e/1e3);let s="";return o>0&&(s+=1===o?"千":r[o]+"千"),i>0&&(s+=1===i?"百":r[i]+"百"),n>0&&(s+=1===n?"十":r[n]+"十"),t>0&&(s+=r[t]),s}e.ja=function(e){const{isNegative:s,integerPart:u,decimalPart:f}=function(e){const r=typeof e;if("bigint"===r)return e<0n?{isNegative:!0,integerPart:-e}:{isNegative:!1,integerPart:e};if("number"===r){if(!Number.isFinite(e))throw new Error("Number must be finite (NaN and Infinity are not supported)");return Number.isSafeInteger(e)?e<0?{isNegative:!0,integerPart:BigInt(-e)}:{isNegative:!1,integerPart:BigInt(e)}:t(function(e){const t=e.toString();return t.includes("e")||t.includes("E")?n(t):t}(e))}if("string"===r)return t(function(e){const t=e.trim();if(0===t.length||Number.isNaN(Number(t)))throw new Error(`Invalid number format: "${e}"`);return t.includes("e")||t.includes("E")?n(t):t}(e));throw new TypeError(`Invalid value type: expected number, string, or bigint, received ${r}`)}(e);let c="";return s&&(c="マイナス"),c+=function(e){if(0n===e)return"零";if(e<10000n)return o(Number(e));if(e<100000000n){const t=Number(e/10000n),n=Number(e%10000n);let r;return r=1===t?"一"+i[0]:o(t)+i[0],n>0&&(r+=o(n)),r}return function(e){const t=[];let n=e;for(;n>0n;)t.push(Number(n%10000n)),n/=10000n;let r="";for(let e=t.length-1;e>=0;e--){const n=t[e];0!==n&&(r+=e>0?1===n?"一"+i[e-1]:o(n)+i[e-1]:o(n))}return r||"零"}(e)}(u),f&&(c+="点"+function(e){let t="";for(let n=0;n<e.length;n++){const i=parseInt(e[n],10);t+=0===i?"零":r[i]}return t}(f)),c}},"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).n2words=e.n2words||{});
|
|
3
3
|
//# sourceMappingURL=ja.js.map
|
package/dist/languages/ja.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ja.js","sources":["../../lib/utils/parse-numeric.js","../../lib/languages/ja.js"],"sourcesContent":["/**\n * Numeric value parsing utility.\n * Transforms user input (number, string, or bigint) into normalized components.\n * @module parse-numeric\n */\n\n/**\n * Parses a numeric value into its components.\n * @param {number|string|bigint} value\n * @returns {{isNegative: boolean, integerPart: bigint, decimalPart?: string}}\n * @throws {TypeError} If value is not number, string, or bigint\n * @throws {Error} If value is not a valid number format\n */\nexport function parseNumericValue (value) {\n const type = typeof value\n\n // BigInt: simplest case\n if (type === 'bigint') {\n return value < 0n\n ? { isNegative: true, integerPart: -value }\n : { isNegative: false, integerPart: value }\n }\n\n // Number: fast path for safe integers\n if (type === 'number') {\n if (!Number.isFinite(value)) {\n throw new Error('Number must be finite (NaN and Infinity are not supported)')\n }\n if (Number.isSafeInteger(value)) {\n return value < 0\n ? { isNegative: true, integerPart: BigInt(-value) }\n : { isNegative: false, integerPart: BigInt(value) }\n }\n return parseNumericString(numberToString(value))\n }\n\n // String input\n if (type === 'string') {\n return parseNumericString(normalizeString(value))\n }\n\n throw new TypeError(\n `Invalid value type: expected number, string, or bigint, received ${type}`\n )\n}\n\n/**\n * Converts a number to decimal string, expanding scientific notation if needed.\n */\nfunction numberToString (value) {\n const str = value.toString()\n return (str.includes('e') || str.includes('E'))\n ? expandScientificNotation(str)\n : str\n}\n\n/**\n * Validates and normalizes a string numeric input.\n */\nfunction normalizeString (value) {\n const trimmed = value.trim()\n if (trimmed.length === 0 || Number.isNaN(Number(trimmed))) {\n throw new Error(`Invalid number format: \"${value}\"`)\n }\n return (trimmed.includes('e') || trimmed.includes('E'))\n ? expandScientificNotation(trimmed)\n : trimmed\n}\n\n/**\n * Parses a normalized numeric string into components.\n */\nfunction parseNumericString (str) {\n const isNegative = str[0] === '-'\n if (isNegative) str = str.slice(1)\n\n const dotIndex = str.indexOf('.')\n if (dotIndex === -1) {\n return { isNegative, integerPart: BigInt(str) }\n }\n\n const integerStr = str.slice(0, dotIndex) || '0'\n const decimalPart = str.slice(dotIndex + 1)\n return { isNegative, integerPart: BigInt(integerStr), decimalPart }\n}\n\n/**\n * Expands scientific notation to decimal form (e.g., \"1e21\" → \"1000...\").\n */\nfunction expandScientificNotation (str) {\n const [mantissa, expStr] = str.toLowerCase().split('e')\n const exp = parseInt(expStr, 10)\n\n const dotIndex = mantissa.indexOf('.')\n const digits = dotIndex === -1\n ? mantissa\n : mantissa.slice(0, dotIndex) + mantissa.slice(dotIndex + 1)\n const integerLength = dotIndex === -1 ? mantissa.length : dotIndex\n const newDotPosition = integerLength + exp\n\n if (newDotPosition >= digits.length) {\n return digits + '0'.repeat(newDotPosition - digits.length)\n }\n if (newDotPosition <= 0) {\n return '0.' + '0'.repeat(-newDotPosition) + digits\n }\n return digits.slice(0, newDotPosition) + '.' + digits.slice(newDotPosition)\n}\n","/**\n * Japanese language converter - Functional Implementation\n *\n * A performance-optimized number-to-words converter using precomputed lookup tables.\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key optimization: Precompute all segment values (0-9999) at module load.\n * This eliminates all per-call string manipulation for segment conversion.\n *\n * Japanese-specific rules (handled in precomputation):\n * - Myriad (万-based) grouping: 4 digits per segment instead of 3\n * - 一 omission: Omit \"一\" before 十, 百, 千 but NOT before 万 and higher scales\n * - Kanji numerals: 零一二三四五六七八九\n * - No spaces between characters\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\n// Ones words (1-9), index 0 unused\nconst ONES = ['', '一', '二', '三', '四', '五', '六', '七', '八', '九']\n\n// Scale words for powers of 10,000 (万-based system)\n// Index 0 = 万 (10^4), 1 = 億 (10^8), 2 = 兆 (10^12), etc.\nconst SCALES = [\n '万', // 10^4 (man)\n '億', // 10^8 (oku)\n '兆', // 10^12 (chō)\n '京', // 10^16 (kei)\n '垓', // 10^20 (gai)\n '秭', // 10^24 (jo/shi)\n '穣', // 10^28 (jō)\n '溝', // 10^32 (kō)\n '澗', // 10^36 (kan)\n '正', // 10^40 (sei)\n '載', // 10^44 (sai)\n '極', // 10^48 (goku)\n '恒河沙', // 10^52 (gōgasha)\n '阿僧祇', // 10^56 (asōgi)\n '那由他', // 10^60 (nayuta)\n '不可思議', // 10^64 (fukashigi)\n '無量大数' // 10^68 (muryōtaisū)\n]\n\nconst ZERO = '零'\nconst NEGATIVE = 'マイナス'\nconst DECIMAL_SEP = '点'\n\n// Internal scale words (within 4-digit segments)\nconst TEN = '十'\nconst HUNDRED = '百'\nconst THOUSAND = '千'\n\n// ============================================================================\n// Precomputed Lookup Tables (built once at module load)\n// ============================================================================\n\n/**\n * Builds segment word for 0-9999 with 一 omission rules.\n * - Omit 一 before 十, 百, 千\n * Only used during table construction.\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100) % 10\n const thousands = Math.floor(n / 1000)\n\n let result = ''\n\n // Thousands (千) - omit 一 when 1\n if (thousands > 0) {\n if (thousands === 1) {\n result += THOUSAND\n } else {\n result += ONES[thousands] + THOUSAND\n }\n }\n\n // Hundreds (百) - omit 一 when 1\n if (hundreds > 0) {\n if (hundreds === 1) {\n result += HUNDRED\n } else {\n result += ONES[hundreds] + HUNDRED\n }\n }\n\n // Tens (十) - omit 一 when 1\n if (tens > 0) {\n if (tens === 1) {\n result += TEN\n } else {\n result += ONES[tens] + TEN\n }\n }\n\n // Ones\n if (ones > 0) {\n result += ONES[ones]\n }\n\n return result\n}\n\n// Precompute all 10000 segment words (0-9999)\n// SEGMENTS[n] gives the Japanese word for n within a segment\nconst SEGMENTS = new Array(10000)\nfor (let i = 0; i < 10000; i++) {\n SEGMENTS[i] = buildSegment(i)\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Japanese words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Japanese kanji words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 10000 (direct lookup)\n if (n < 10000n) {\n return SEGMENTS[Number(n)]\n }\n\n // Fast path: numbers < 100,000,000 (万 range)\n if (n < 100_000_000n) {\n const man = Number(n / 10000n)\n const remainder = Number(n % 10000n)\n\n // For 万 and above, we need 一 before the scale word when segment is 1\n let result\n if (man === 1) {\n result = '一' + SCALES[0] // 一万\n } else {\n result = SEGMENTS[man] + SCALES[0]\n }\n\n if (remainder > 0) {\n result += SEGMENTS[remainder]\n }\n\n return result\n }\n\n // For numbers >= 100,000,000, use scale decomposition\n return buildLargeNumberWords(n)\n}\n\n/**\n * Builds words for numbers >= 100,000,000.\n * Uses BigInt modulo for 4-digit (myriad) segment extraction.\n *\n * @param {bigint} n - Number >= 100,000,000\n * @returns {string} Japanese kanji words\n */\nfunction buildLargeNumberWords (n) {\n // Extract segments using BigInt modulo (faster than string slicing)\n // Segments stored least-significant first (index 0 = units, 1 = 万, etc.)\n const segments = []\n let temp = n\n while (temp > 0n) {\n segments.push(Number(temp % 10000n))\n temp = temp / 10000n\n }\n\n // Build result string directly (process from most-significant to least)\n let result = ''\n\n for (let i = segments.length - 1; i >= 0; i--) {\n const segment = segments[i]\n if (segment === 0) continue\n\n if (i > 0) {\n // For scales >= 万, we need 一 before scale word when segment is 1\n if (segment === 1) {\n result += '一' + SCALES[i - 1]\n } else {\n result += SEGMENTS[segment] + SCALES[i - 1]\n }\n } else {\n // Units segment (no scale word)\n result += SEGMENTS[segment]\n }\n }\n\n return result || ZERO\n}\n\n/**\n * Converts decimal digits to Japanese words (digit by digit).\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @returns {string} Japanese kanji words for decimal part\n */\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n\n for (let i = 0; i < decimalPart.length; i++) {\n const digit = parseInt(decimalPart[i], 10)\n if (digit === 0) {\n result += ZERO\n } else {\n result += ONES[digit]\n }\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Japanese words.\n *\n * This is the main public API. It accepts any valid numeric input\n * (number, string, or bigint) and handles parsing internally.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Japanese kanji words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(42) // '四十二'\n * toWords(10000) // '一万'\n * toWords(100000000) // '一億'\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += DECIMAL_SEP + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n"],"names":["parseNumericString","str","isNegative","slice","dotIndex","indexOf","integerPart","BigInt","integerStr","decimalPart","expandScientificNotation","mantissa","expStr","toLowerCase","split","exp","parseInt","digits","newDotPosition","length","repeat","ONES","SCALES","buildSegment","n","ones","tens","Math","floor","hundreds","thousands","result","SEGMENTS","Array","i","value","type","Number","isFinite","Error","isSafeInteger","toString","includes","numberToString","trimmed","trim","isNaN","normalizeString","TypeError","parseNumericValue","man","remainder","segments","temp","push","segment","buildLargeNumberWords","integerToWords","digit","decimalPartToWords"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ja.js","sources":["../../lib/utils/parse-numeric.js","../../lib/languages/ja.js"],"sourcesContent":["/**\n * Numeric value parsing utility.\n * Transforms user input (number, string, or bigint) into normalized components.\n * @module parse-numeric\n */\n\n/**\n * Parses a numeric value into its components.\n * @param {number|string|bigint} value\n * @returns {{isNegative: boolean, integerPart: bigint, decimalPart?: string}}\n * @throws {TypeError} If value is not number, string, or bigint\n * @throws {Error} If value is not a valid number format\n */\nexport function parseNumericValue (value) {\n const type = typeof value\n\n // BigInt: simplest case\n if (type === 'bigint') {\n return value < 0n\n ? { isNegative: true, integerPart: -value }\n : { isNegative: false, integerPart: value }\n }\n\n // Number: fast path for safe integers\n if (type === 'number') {\n if (!Number.isFinite(value)) {\n throw new Error('Number must be finite (NaN and Infinity are not supported)')\n }\n if (Number.isSafeInteger(value)) {\n return value < 0\n ? { isNegative: true, integerPart: BigInt(-value) }\n : { isNegative: false, integerPart: BigInt(value) }\n }\n return parseNumericString(numberToString(value))\n }\n\n // String input\n if (type === 'string') {\n return parseNumericString(normalizeString(value))\n }\n\n throw new TypeError(\n `Invalid value type: expected number, string, or bigint, received ${type}`\n )\n}\n\n/**\n * Converts a number to decimal string, expanding scientific notation if needed.\n */\nfunction numberToString (value) {\n const str = value.toString()\n return (str.includes('e') || str.includes('E'))\n ? expandScientificNotation(str)\n : str\n}\n\n/**\n * Validates and normalizes a string numeric input.\n */\nfunction normalizeString (value) {\n const trimmed = value.trim()\n if (trimmed.length === 0 || Number.isNaN(Number(trimmed))) {\n throw new Error(`Invalid number format: \"${value}\"`)\n }\n return (trimmed.includes('e') || trimmed.includes('E'))\n ? expandScientificNotation(trimmed)\n : trimmed\n}\n\n/**\n * Parses a normalized numeric string into components.\n */\nfunction parseNumericString (str) {\n const isNegative = str[0] === '-'\n if (isNegative) str = str.slice(1)\n\n const dotIndex = str.indexOf('.')\n if (dotIndex === -1) {\n return { isNegative, integerPart: BigInt(str) }\n }\n\n const integerStr = str.slice(0, dotIndex) || '0'\n const decimalPart = str.slice(dotIndex + 1)\n return { isNegative, integerPart: BigInt(integerStr), decimalPart }\n}\n\n/**\n * Expands scientific notation to decimal form (e.g., \"1e21\" → \"1000...\").\n */\nfunction expandScientificNotation (str) {\n const [mantissa, expStr] = str.toLowerCase().split('e')\n const exp = parseInt(expStr, 10)\n\n const dotIndex = mantissa.indexOf('.')\n const digits = dotIndex === -1\n ? mantissa\n : mantissa.slice(0, dotIndex) + mantissa.slice(dotIndex + 1)\n const integerLength = dotIndex === -1 ? mantissa.length : dotIndex\n const newDotPosition = integerLength + exp\n\n if (newDotPosition >= digits.length) {\n return digits + '0'.repeat(newDotPosition - digits.length)\n }\n if (newDotPosition <= 0) {\n return '0.' + '0'.repeat(-newDotPosition) + digits\n }\n return digits.slice(0, newDotPosition) + '.' + digits.slice(newDotPosition)\n}\n","/**\n * Japanese language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Japanese-specific rules:\n * - Myriad (万-based) grouping: 4 digits per segment instead of 3\n * - 一 omission: Omit \"一\" before 十, 百, 千 but NOT before 万 and higher scales\n * - Kanji numerals: 零一二三四五六七八九\n * - No spaces between characters\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\n// Ones words (1-9), index 0 unused\nconst ONES = ['', '一', '二', '三', '四', '五', '六', '七', '八', '九']\n\n// Scale words for powers of 10,000 (万-based system)\n// Index 0 = 万 (10^4), 1 = 億 (10^8), 2 = 兆 (10^12), etc.\nconst SCALES = [\n '万', // 10^4 (man)\n '億', // 10^8 (oku)\n '兆', // 10^12 (chō)\n '京', // 10^16 (kei)\n '垓', // 10^20 (gai)\n '秭', // 10^24 (jo/shi)\n '穣', // 10^28 (jō)\n '溝', // 10^32 (kō)\n '澗', // 10^36 (kan)\n '正', // 10^40 (sei)\n '載', // 10^44 (sai)\n '極', // 10^48 (goku)\n '恒河沙', // 10^52 (gōgasha)\n '阿僧祇', // 10^56 (asōgi)\n '那由他', // 10^60 (nayuta)\n '不可思議', // 10^64 (fukashigi)\n '無量大数' // 10^68 (muryōtaisū)\n]\n\nconst ZERO = '零'\nconst NEGATIVE = 'マイナス'\nconst DECIMAL_SEP = '点'\n\n// Internal scale words (within 4-digit segments)\nconst TEN = '十'\nconst HUNDRED = '百'\nconst THOUSAND = '千'\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds segment word for 0-9999 with 一 omission rules.\n * - Omit 一 before 十, 百, 千\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100) % 10\n const thousands = Math.floor(n / 1000)\n\n let result = ''\n\n // Thousands (千) - omit 一 when 1\n if (thousands > 0) {\n if (thousands === 1) {\n result += THOUSAND\n } else {\n result += ONES[thousands] + THOUSAND\n }\n }\n\n // Hundreds (百) - omit 一 when 1\n if (hundreds > 0) {\n if (hundreds === 1) {\n result += HUNDRED\n } else {\n result += ONES[hundreds] + HUNDRED\n }\n }\n\n // Tens (十) - omit 一 when 1\n if (tens > 0) {\n if (tens === 1) {\n result += TEN\n } else {\n result += ONES[tens] + TEN\n }\n }\n\n // Ones\n if (ones > 0) {\n result += ONES[ones]\n }\n\n return result\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Japanese words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Japanese kanji words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 10000\n if (n < 10000n) {\n return buildSegment(Number(n))\n }\n\n // Fast path: numbers < 100,000,000 (万 range)\n if (n < 100_000_000n) {\n const man = Number(n / 10000n)\n const remainder = Number(n % 10000n)\n\n // For 万 and above, we need 一 before the scale word when segment is 1\n let result\n if (man === 1) {\n result = '一' + SCALES[0] // 一万\n } else {\n result = buildSegment(man) + SCALES[0]\n }\n\n if (remainder > 0) {\n result += buildSegment(remainder)\n }\n\n return result\n }\n\n // For numbers >= 100,000,000, use scale decomposition\n return buildLargeNumberWords(n)\n}\n\n/**\n * Builds words for numbers >= 100,000,000.\n * Uses BigInt modulo for 4-digit (myriad) segment extraction.\n *\n * @param {bigint} n - Number >= 100,000,000\n * @returns {string} Japanese kanji words\n */\nfunction buildLargeNumberWords (n) {\n // Extract segments using BigInt modulo (faster than string slicing)\n // Segments stored least-significant first (index 0 = units, 1 = 万, etc.)\n const segments = []\n let temp = n\n while (temp > 0n) {\n segments.push(Number(temp % 10000n))\n temp = temp / 10000n\n }\n\n // Build result string directly (process from most-significant to least)\n let result = ''\n\n for (let i = segments.length - 1; i >= 0; i--) {\n const segment = segments[i]\n if (segment === 0) continue\n\n if (i > 0) {\n // For scales >= 万, we need 一 before scale word when segment is 1\n if (segment === 1) {\n result += '一' + SCALES[i - 1]\n } else {\n result += buildSegment(segment) + SCALES[i - 1]\n }\n } else {\n // Units segment (no scale word)\n result += buildSegment(segment)\n }\n }\n\n return result || ZERO\n}\n\n/**\n * Converts decimal digits to Japanese words (digit by digit).\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @returns {string} Japanese kanji words for decimal part\n */\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n\n for (let i = 0; i < decimalPart.length; i++) {\n const digit = parseInt(decimalPart[i], 10)\n if (digit === 0) {\n result += ZERO\n } else {\n result += ONES[digit]\n }\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Japanese words.\n *\n * This is the main public API. It accepts any valid numeric input\n * (number, string, or bigint) and handles parsing internally.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Japanese kanji words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(42) // '四十二'\n * toWords(10000) // '一万'\n * toWords(100000000) // '一億'\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += DECIMAL_SEP + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n"],"names":["parseNumericString","str","isNegative","slice","dotIndex","indexOf","integerPart","BigInt","integerStr","decimalPart","expandScientificNotation","mantissa","expStr","toLowerCase","split","exp","parseInt","digits","newDotPosition","length","repeat","ONES","SCALES","buildSegment","n","ones","tens","Math","floor","hundreds","thousands","result","value","type","Number","isFinite","Error","isSafeInteger","toString","includes","numberToString","trimmed","trim","isNaN","normalizeString","TypeError","parseNumericValue","man","remainder","segments","temp","push","i","segment","buildLargeNumberWords","integerToWords","digit","decimalPartToWords"],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/*! n2words/ka v3.1.0 | MIT License | github.com/forzagreen/n2words */
|
|
2
|
+
var e,t;e=this,t=function(e){"use strict";function t(e){const t="-"===e[0];t&&(e=e.slice(1));const n=e.indexOf(".");if(-1===n)return{isNegative:t,integerPart:BigInt(e)};const i=e.slice(0,n)||"0",r=e.slice(n+1);return{isNegative:t,integerPart:BigInt(i),decimalPart:r}}function n(e){const[t,n]=e.toLowerCase().split("e"),i=parseInt(n,10),r=t.indexOf("."),s=-1===r?t:t.slice(0,r)+t.slice(r+1),o=(-1===r?t.length:r)+i;return o>=s.length?s+"0".repeat(o-s.length):o<=0?"0."+"0".repeat(-o)+s:s.slice(0,o)+"."+s.slice(o)}const i=["ნული","ერთი","ორი","სამი","ოთხი","ხუთი","ექვსი","შვიდი","რვა","ცხრა"],r=["ათი","თერთმეტი","თორმეტი","ცამეტი","თოთხმეტი","თხუთმეტი","თექვსმეტი","ჩვიდმეტი","თვრამეტი","ცხრამეტი"],s=["","ოცი","ორმოცი","სამოცი","ოთხმოცი"],o=["","ოცდა","ორმოცდა","სამოცდა","ოთხმოცდა"],u=["","","ორ","სამ","ოთხ","ხუთ","ექვს","შვიდ","რვა","ცხრა"],l="ათასი",c="ათას",f=["","","მილიონი","მილიარდი","ტრილიონი","კვადრილიონი","კვინტილიონი","სექსტილიონი"],a="ნული";function g(e){if(e<10)return i[e];if(e<20)return r[e-10];const t=Math.floor(e/20),n=e%20;if(0===n)return s[t];const u=o[t];return n<10?u+i[n]:u+r[n-10]}function m(e){if(0===e)return{full:"",stem:""};if(e<100){const t=g(e),n=t.slice(-1),i="ი"===n||"ა"===n?t.slice(0,-1):t;return{full:t,stem:i}}const t=Math.floor(e/100),n=e%100;let i;if(i=1===t?n>0?"ას":"ასი":u[t]+(n>0?"ას":"ასი"),n>0){const e=g(n),t=i+" "+e,r=e.slice(-1);return{full:t,stem:i+" "+("ი"===r||"ა"===r?e.slice(0,-1):e)}}return{full:i,stem:i.slice(0,-1)}}function d(e){if(0n===e)return a;if(e<1000n){const{full:t}=m(Number(e));return t}if(e<1000000n){const t=Number(e/1000n),n=Number(e%1000n);let i;if(1===t)i=n>0?c:l;else{const{stem:e}=m(t);i=e+" "+(n>0?c:l)}if(n>0){const{full:e}=m(n);i+=" "+e}return i}return function(e){const t=e.toString(),n=t.length,i=[],r=n%3;let s=0;for(r>0&&(i.push(Number(t.slice(0,r))),s=r);s<n;)i.push(Number(t.slice(s,s+3))),s+=3;const o=[];let u=i.length-1;for(let e=0;e<i.length;e++){const t=i[e];if(0!==t)if(0===u){const{full:e}=m(t);o.push(e)}else if(1===u){const n=i.slice(e+1).some(e=>0!==e)?c:l;if(1===t)o.push(n);else{const{stem:e}=m(t);o.push(e+" "+n)}}else{const e=f[u]||f[f.length-1];if(1===t)o.push("ერთი "+e);else{const{full:n}=m(t);o.push(n+" "+e)}}u--}return o.join(" ")}(e)}e.ka=function(e){const{isNegative:i,integerPart:r,decimalPart:s}=function(e){const i=typeof e;if("bigint"===i)return e<0n?{isNegative:!0,integerPart:-e}:{isNegative:!1,integerPart:e};if("number"===i){if(!Number.isFinite(e))throw new Error("Number must be finite (NaN and Infinity are not supported)");return Number.isSafeInteger(e)?e<0?{isNegative:!0,integerPart:BigInt(-e)}:{isNegative:!1,integerPart:BigInt(e)}:t(function(e){const t=e.toString();return t.includes("e")||t.includes("E")?n(t):t}(e))}if("string"===i)return t(function(e){const t=e.trim();if(0===t.length||Number.isNaN(Number(t)))throw new Error(`Invalid number format: "${e}"`);return t.includes("e")||t.includes("E")?n(t):t}(e));throw new TypeError(`Invalid value type: expected number, string, or bigint, received ${i}`)}(e);let o="";return i&&(o="მინუს "),o+=d(r),s&&(o+=" მთელი "+function(e){let t="",n=0;for(;n<e.length&&"0"===e[n];)t&&(t+=" "),t+=a,n++;const i=e.slice(n);return i&&(t&&(t+=" "),t+=d(BigInt(i))),t}(s)),o}},"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).n2words=e.n2words||{});
|
|
3
|
+
//# sourceMappingURL=ka.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ka.js","sources":["../../lib/utils/parse-numeric.js","../../lib/languages/ka.js"],"sourcesContent":["/**\n * Numeric value parsing utility.\n * Transforms user input (number, string, or bigint) into normalized components.\n * @module parse-numeric\n */\n\n/**\n * Parses a numeric value into its components.\n * @param {number|string|bigint} value\n * @returns {{isNegative: boolean, integerPart: bigint, decimalPart?: string}}\n * @throws {TypeError} If value is not number, string, or bigint\n * @throws {Error} If value is not a valid number format\n */\nexport function parseNumericValue (value) {\n const type = typeof value\n\n // BigInt: simplest case\n if (type === 'bigint') {\n return value < 0n\n ? { isNegative: true, integerPart: -value }\n : { isNegative: false, integerPart: value }\n }\n\n // Number: fast path for safe integers\n if (type === 'number') {\n if (!Number.isFinite(value)) {\n throw new Error('Number must be finite (NaN and Infinity are not supported)')\n }\n if (Number.isSafeInteger(value)) {\n return value < 0\n ? { isNegative: true, integerPart: BigInt(-value) }\n : { isNegative: false, integerPart: BigInt(value) }\n }\n return parseNumericString(numberToString(value))\n }\n\n // String input\n if (type === 'string') {\n return parseNumericString(normalizeString(value))\n }\n\n throw new TypeError(\n `Invalid value type: expected number, string, or bigint, received ${type}`\n )\n}\n\n/**\n * Converts a number to decimal string, expanding scientific notation if needed.\n */\nfunction numberToString (value) {\n const str = value.toString()\n return (str.includes('e') || str.includes('E'))\n ? expandScientificNotation(str)\n : str\n}\n\n/**\n * Validates and normalizes a string numeric input.\n */\nfunction normalizeString (value) {\n const trimmed = value.trim()\n if (trimmed.length === 0 || Number.isNaN(Number(trimmed))) {\n throw new Error(`Invalid number format: \"${value}\"`)\n }\n return (trimmed.includes('e') || trimmed.includes('E'))\n ? expandScientificNotation(trimmed)\n : trimmed\n}\n\n/**\n * Parses a normalized numeric string into components.\n */\nfunction parseNumericString (str) {\n const isNegative = str[0] === '-'\n if (isNegative) str = str.slice(1)\n\n const dotIndex = str.indexOf('.')\n if (dotIndex === -1) {\n return { isNegative, integerPart: BigInt(str) }\n }\n\n const integerStr = str.slice(0, dotIndex) || '0'\n const decimalPart = str.slice(dotIndex + 1)\n return { isNegative, integerPart: BigInt(integerStr), decimalPart }\n}\n\n/**\n * Expands scientific notation to decimal form (e.g., \"1e21\" → \"1000...\").\n */\nfunction expandScientificNotation (str) {\n const [mantissa, expStr] = str.toLowerCase().split('e')\n const exp = parseInt(expStr, 10)\n\n const dotIndex = mantissa.indexOf('.')\n const digits = dotIndex === -1\n ? mantissa\n : mantissa.slice(0, dotIndex) + mantissa.slice(dotIndex + 1)\n const integerLength = dotIndex === -1 ? mantissa.length : dotIndex\n const newDotPosition = integerLength + exp\n\n if (newDotPosition >= digits.length) {\n return digits + '0'.repeat(newDotPosition - digits.length)\n }\n if (newDotPosition <= 0) {\n return '0.' + '0'.repeat(-newDotPosition) + digits\n }\n return digits.slice(0, newDotPosition) + '.' + digits.slice(newDotPosition)\n}\n","/**\n * Georgian language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Georgian-specific rules:\n * - Vigesimal (base-20) system for 20-99\n * - 40 = ორმოცი (2×20), 60 = სამოცი (3×20), 80 = ოთხმოცი (4×20)\n * - 30/50/70/90 use \"და\" + \"ათი\": ოცდაათი (20+10), ორმოცდაათი (40+10)\n * - Compound numbers use \"და\" (da = \"and\") connector\n * - Hundreds: unit prefix + ას (ორასი = 200)\n * - Short scale for large numbers (მილიონი, მილიარდი, ტრილიონი)\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\n// Numbers 0-9 (primitives)\nconst ONES = ['ნული', 'ერთი', 'ორი', 'სამი', 'ოთხი', 'ხუთი', 'ექვსი', 'შვიდი', 'რვა', 'ცხრა']\n\n// Numbers 10-19\nconst TEENS = ['ათი', 'თერთმეტი', 'თორმეტი', 'ცამეტი', 'თოთხმეტი', 'თხუთმეტი', 'თექვსმეტი', 'ჩვიდმეტი', 'თვრამეტი', 'ცხრამეტი']\n\n// Vigesimal bases: 20, 40, 60, 80 (with და connector forms)\nconst VIGESIMAL = ['', 'ოცი', 'ორმოცი', 'სამოცი', 'ოთხმოცი']\nconst VIGESIMAL_DA = ['', 'ოცდა', 'ორმოცდა', 'სამოცდა', 'ოთხმოცდა']\n\n// Prefixes for hundreds (unit forms without final vowel)\nconst HUNDRED_PREFIXES = ['', '', 'ორ', 'სამ', 'ოთხ', 'ხუთ', 'ექვს', 'შვიდ', 'რვა', 'ცხრა']\n\nconst HUNDRED = 'ასი'\nconst HUNDRED_STEM = 'ას' // Without final vowel\nconst THOUSAND = 'ათასი'\nconst THOUSAND_STEM = 'ათას' // Without final vowel\n\n// Scale words (short scale) - indexed by segment position\nconst SCALES = ['', '', 'მილიონი', 'მილიარდი', 'ტრილიონი', 'კვადრილიონი', 'კვინტილიონი', 'სექსტილიონი']\n\nconst ZERO = 'ნული'\nconst NEGATIVE = 'მინუს'\nconst DECIMAL_SEP = 'მთელი'\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds segment word for 0-99 using vigesimal system.\n * @param {number} n - Number 0-99\n * @returns {string} Georgian word\n */\nfunction buildTens (n) {\n if (n < 10) return ONES[n]\n if (n < 20) return TEENS[n - 10]\n\n const vigesimalGroup = Math.floor(n / 20)\n const remainder = n % 20\n\n if (remainder === 0) {\n return VIGESIMAL[vigesimalGroup]\n }\n\n // Use და connector: ოცდა + remainder\n const base = VIGESIMAL_DA[vigesimalGroup]\n if (remainder < 10) {\n return base + ONES[remainder]\n }\n return base + TEENS[remainder - 10]\n}\n\n/**\n * Builds segment word for 0-999.\n * Returns object with full form and stem form (without final vowel).\n * @param {number} n - Number 0-999\n * @returns {{full: string, stem: string}} Georgian words\n */\nfunction buildSegment (n) {\n if (n === 0) return { full: '', stem: '' }\n if (n < 100) {\n const word = buildTens(n)\n // Remove final vowel for stem\n const lastChar = word.slice(-1)\n const stem = (lastChar === 'ი' || lastChar === 'ა') ? word.slice(0, -1) : word\n return { full: word, stem }\n }\n\n const hundreds = Math.floor(n / 100)\n const remainder = n % 100\n\n // Build hundreds: ასი (100), ორასი (200), etc.\n let hundredWord\n if (hundreds === 1) {\n hundredWord = remainder > 0 ? HUNDRED_STEM : HUNDRED\n } else {\n hundredWord = HUNDRED_PREFIXES[hundreds] + (remainder > 0 ? HUNDRED_STEM : HUNDRED)\n }\n\n if (remainder > 0) {\n const remainderWord = buildTens(remainder)\n const full = hundredWord + ' ' + remainderWord\n // Stem removes final vowel from remainder\n const lastChar = remainderWord.slice(-1)\n const remainderStem = (lastChar === 'ი' || lastChar === 'ა') ? remainderWord.slice(0, -1) : remainderWord\n return { full, stem: hundredWord + ' ' + remainderStem }\n }\n\n // Hundreds only - stem removes final ი\n return { full: hundredWord, stem: hundredWord.slice(0, -1) }\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Georgian words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Georgian words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000\n if (n < 1000n) {\n const { full } = buildSegment(Number(n))\n return full\n }\n\n // Fast path: numbers < 1,000,000 (thousands)\n if (n < 1_000_000n) {\n const thousands = Number(n / 1000n)\n const remainder = Number(n % 1000n)\n\n let result\n if (thousands === 1) {\n // \"ათასი\" not \"ერთი ათასი\"\n result = remainder > 0 ? THOUSAND_STEM : THOUSAND\n } else {\n // Use stem form before ათასი\n const { stem: thousandsPart } = buildSegment(thousands)\n result = thousandsPart + ' ' + (remainder > 0 ? THOUSAND_STEM : THOUSAND)\n }\n\n if (remainder > 0) {\n const { full: remainderWord } = buildSegment(remainder)\n result += ' ' + remainderWord\n }\n\n return result\n }\n\n // For numbers >= 1,000,000, use scale decomposition\n return buildLargeNumberWords(n)\n}\n\n/**\n * Builds words for numbers >= 1,000,000.\n *\n * @param {bigint} n - Number >= 1,000,000\n * @returns {string} Georgian words\n */\nfunction buildLargeNumberWords (n) {\n const numStr = n.toString()\n const len = numStr.length\n\n // Build segments of 3 digits from left to right\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n // Convert segments to words\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n if (scaleIndex === 0) {\n // Units (no scale)\n const { full } = buildSegment(segment)\n parts.push(full)\n } else if (scaleIndex === 1) {\n // Thousands - check if there's a remainder\n const hasRemainder = segments.slice(i + 1).some(s => s !== 0)\n const thousandWord = hasRemainder ? THOUSAND_STEM : THOUSAND\n\n if (segment === 1) {\n parts.push(thousandWord)\n } else {\n const { stem } = buildSegment(segment)\n parts.push(stem + ' ' + thousandWord)\n }\n } else {\n // Million and above\n const scaleWord = SCALES[scaleIndex] || SCALES[SCALES.length - 1]\n if (segment === 1) {\n parts.push('ერთი ' + scaleWord)\n } else {\n const { full } = buildSegment(segment)\n parts.push(full + ' ' + scaleWord)\n }\n }\n }\n\n scaleIndex--\n }\n\n return parts.join(' ')\n}\n\n/**\n * Converts decimal digits to Georgian words.\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @returns {string} Georgian words for decimal part\n */\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n\n // Handle leading zeros\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n // Convert remainder as a single number\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder))\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Georgian words.\n *\n * This is the main public API. It accepts any valid numeric input\n * (number, string, or bigint) and handles parsing internally.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Georgian words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(21) // 'ოცდაერთი'\n * toWords(100) // 'ასი'\n * toWords(1000) // 'ათასი'\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n"],"names":["parseNumericString","str","isNegative","slice","dotIndex","indexOf","integerPart","BigInt","integerStr","decimalPart","expandScientificNotation","mantissa","expStr","toLowerCase","split","exp","parseInt","digits","newDotPosition","length","repeat","ONES","TEENS","VIGESIMAL","VIGESIMAL_DA","HUNDRED_PREFIXES","THOUSAND","THOUSAND_STEM","SCALES","ZERO","buildTens","n","vigesimalGroup","Math","floor","remainder","base","buildSegment","full","stem","word","lastChar","hundreds","hundredWord","remainderWord","integerToWords","Number","thousands","result","thousandsPart","numStr","toString","len","segments","remainderLen","pos","push","parts","scaleIndex","i","segment","thousandWord","some","s","scaleWord","join","buildLargeNumberWords","value","type","isFinite","Error","isSafeInteger","includes","numberToString","trimmed","trim","isNaN","normalizeString","TypeError","parseNumericValue","NEGATIVE","decimalPartToWords"],"mappings":"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"}
|
package/dist/languages/kn.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
/*! n2words/kn v3.
|
|
2
|
-
var e,
|
|
1
|
+
/*! n2words/kn v3.1.0 | MIT License | github.com/forzagreen/n2words */
|
|
2
|
+
var e,n;e=this,n=function(e){"use strict";function n(e){const n="-"===e[0];n&&(e=e.slice(1));const t=e.indexOf(".");if(-1===t)return{isNegative:n,integerPart:BigInt(e)};const r=e.slice(0,t)||"0",i=e.slice(t+1);return{isNegative:n,integerPart:BigInt(r),decimalPart:i}}function t(e){const[n,t]=e.toLowerCase().split("e"),r=parseInt(t,10),i=n.indexOf("."),s=-1===i?n:n.slice(0,i)+n.slice(i+1),o=(-1===i?n.length:i)+r;return o>=s.length?s+"0".repeat(o-s.length):o<=0?"0."+"0".repeat(-o)+s:s.slice(0,o)+"."+s.slice(o)}const r="ಸೊನ್ನೆ",i="ನೂರು",s=["ಸೊನ್ನೆ","ಒಂದು","ಎರಡು","ಮೂರು","ನಾಲ್ಕು","ಐದು","ಆರು","ಏಳು","ಎಂಟು","ಒಂಬತ್ತು","ಹತ್ತು","ಹನ್ನೊಂದು","ಹನ್ನೆರಡು","ಹದಿಮೂರು","ಹದಿನಾಲ್ಕು","ಹದಿನೈದು","ಹದಿನಾರು","ಹದಿನೇಳು","ಹದಿನೆಂಟು","ಹತ್ತೊಂಬತ್ತು","ಇಪ್ಪತ್ತು","ಇಪ್ಪತ್ತೊಂದು","ಇಪ್ಪತ್ತೆರಡು","ಇಪ್ಪತ್ತಮೂರು","ಇಪ್ಪತ್ತನಾಲ್ಕು","ಇಪ್ಪತ್ತೈದು","ಇಪ್ಪತ್ತಾರು","ಇಪ್ಪತ್ತೇಳು","ಇಪ್ಪತ್ತೆಂಟು","ಇಪ್ಪತ್ತೊಂಬತ್ತು","ಮೂವತ್ತು","ಮೂವತ್ತೊಂದು","ಮೂವತ್ತೆರಡು","ಮೂವತ್ತಮೂರು","ಮೂವತ್ತನಾಲ್ಕು","ಮೂವತ್ತೈದು","ಮೂವತ್ತಾರು","ಮೂವತ್ತೇಳು","ಮೂವತ್ತೆಂಟು","ಮೂವತ್ತೊಂಬತ್ತು","ನಲವತ್ತು","ನಲವತ್ತೊಂದು","ನಲವತ್ತೆರಡು","ನಲವತ್ತಮೂರು","ನಲವತ್ತನಾಲ್ಕು","ನಲವತ್ತೈದು","ನಲವತ್ತಾರು","ನಲವತ್ತೇಳು","ನಲವತ್ತೆಂಟು","ನಲವತ್ತೊಂಬತ್ತು","ಐವತ್ತು","ಐವತ್ತೊಂದು","ಐವತ್ತೆರಡು","ಐವತ್ತಮೂರು","ಐವತ್ತನಾಲ್ಕು","ಐವತ್ತೈದು","ಐವತ್ತಾರು","ಐವತ್ತೇಳು","ಐವತ್ತೆಂಟು","ಐವತ್ತೊಂಬತ್ತು","ಅರವತ್ತು","ಅರವತ್ತೊಂದು","ಅರವತ್ತೆರಡು","ಅರವತ್ತಮೂರು","ಅರವತ್ತನಾಲ್ಕು","ಅರವತ್ತೈದು","ಅರವತ್ತಾರು","ಅರವತ್ತೇಳು","ಅರವತ್ತೆಂಟು","ಅರವತ್ತೊಂಬತ್ತು","ಎಪ್ಪತ್ತು","ಎಪ್ಪತ್ತೊಂದು","ಎಪ್ಪತ್ತೆರಡು","ಎಪ್ಪತ್ತಮೂರು","ಎಪ್ಪತ್ತನಾಲ್ಕು","ಎಪ್ಪತ್ತೈದು","ಎಪ್ಪತ್ತಾರು","ಎಪ್ಪತ್ತೇಳು","ಎಪ್ಪತ್ತೆಂಟು","ಎಪ್ಪತ್ತೊಂಬತ್ತು","ಎಂಬತ್ತು","ಎಂಬತ್ತೊಂದು","ಎಂಬತ್ತೆರಡು","ಎಂಬತ್ತಮೂರು","ಎಂಬತ್ತನಾಲ್ಕು","ಎಂಬತ್ತೈದು","ಎಂಬತ್ತಾರು","ಎಂಬತ್ತೇಳು","ಎಂಬತ್ತೆಂಟು","ಎಂಬತ್ತೊಂಬತ್ತು","ತೊಂಬತ್ತು","ತೊಂಬತ್ತೊಂದು","ತೊಂಬತ್ತೆರಡು","ತೊಂಬತ್ತಮೂರು","ತೊಂಬತ್ತನಾಲ್ಕು","ತೊಂಬತ್ತೈದು","ತೊಂಬತ್ತಾರು","ತೊಂಬತ್ತೇಳು","ತೊಂಬತ್ತೆಂಟು","ತೊಂಬತ್ತೊಂಬತ್ತು"],o=["","ಸಾವಿರ","ಲಕ್ಷ","ಕೋಟಿ","ಅಬ್ಜ","ಖರ್ವ","ನೀಲ","ಪದ್ಮ","ಶಂಖ"];function u(e){if(0===e)return"";if(e<100)return s[e];const n=Math.trunc(e/100),t=e%100;return 0===t?s[n]+" "+i:s[n]+" "+i+" "+s[t]}e.kn=function(e){const{isNegative:i,integerPart:c,decimalPart:f}=function(e){const r=typeof e;if("bigint"===r)return e<0n?{isNegative:!0,integerPart:-e}:{isNegative:!1,integerPart:e};if("number"===r){if(!Number.isFinite(e))throw new Error("Number must be finite (NaN and Infinity are not supported)");return Number.isSafeInteger(e)?e<0?{isNegative:!0,integerPart:BigInt(-e)}:{isNegative:!1,integerPart:BigInt(e)}:n(function(e){const n=e.toString();return n.includes("e")||n.includes("E")?t(n):n}(e))}if("string"===r)return n(function(e){const n=e.trim();if(0===n.length||Number.isNaN(Number(n)))throw new Error(`Invalid number format: "${e}"`);return n.includes("e")||n.includes("E")?t(n):n}(e));throw new TypeError(`Invalid value type: expected number, string, or bigint, received ${r}`)}(e);let a="";return i&&(a="ಋಣಾತ್ಮಕ "),a+=function(e){if(0n===e)return r;if(e<1000n)return u(Number(e));const n=[];n.push(Number(e%1000n));let t=e/1000n;for(;t>0n;)n.push(Number(t%100n)),t/=100n;const i=[];for(let e=n.length-1;e>=0;e--){const t=n[e];0!==t&&(i.push(0===e?u(t):s[t]),e>0&&o[e]&&i.push(o[e]))}return i.join(" ")}(c),f&&(a+=" ದಶಮಾಂಶ "+function(e){const n=[];for(const t of e){const e=parseInt(t,10);n.push(0===e?r:s[e])}return n.join(" ")}(f)),a}},"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).n2words=e.n2words||{});
|
|
3
3
|
//# sourceMappingURL=kn.js.map
|
package/dist/languages/kn.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kn.js","sources":["../../lib/utils/parse-numeric.js","../../lib/languages/kn.js"],"sourcesContent":["/**\n * Numeric value parsing utility.\n * Transforms user input (number, string, or bigint) into normalized components.\n * @module parse-numeric\n */\n\n/**\n * Parses a numeric value into its components.\n * @param {number|string|bigint} value\n * @returns {{isNegative: boolean, integerPart: bigint, decimalPart?: string}}\n * @throws {TypeError} If value is not number, string, or bigint\n * @throws {Error} If value is not a valid number format\n */\nexport function parseNumericValue (value) {\n const type = typeof value\n\n // BigInt: simplest case\n if (type === 'bigint') {\n return value < 0n\n ? { isNegative: true, integerPart: -value }\n : { isNegative: false, integerPart: value }\n }\n\n // Number: fast path for safe integers\n if (type === 'number') {\n if (!Number.isFinite(value)) {\n throw new Error('Number must be finite (NaN and Infinity are not supported)')\n }\n if (Number.isSafeInteger(value)) {\n return value < 0\n ? { isNegative: true, integerPart: BigInt(-value) }\n : { isNegative: false, integerPart: BigInt(value) }\n }\n return parseNumericString(numberToString(value))\n }\n\n // String input\n if (type === 'string') {\n return parseNumericString(normalizeString(value))\n }\n\n throw new TypeError(\n `Invalid value type: expected number, string, or bigint, received ${type}`\n )\n}\n\n/**\n * Converts a number to decimal string, expanding scientific notation if needed.\n */\nfunction numberToString (value) {\n const str = value.toString()\n return (str.includes('e') || str.includes('E'))\n ? expandScientificNotation(str)\n : str\n}\n\n/**\n * Validates and normalizes a string numeric input.\n */\nfunction normalizeString (value) {\n const trimmed = value.trim()\n if (trimmed.length === 0 || Number.isNaN(Number(trimmed))) {\n throw new Error(`Invalid number format: \"${value}\"`)\n }\n return (trimmed.includes('e') || trimmed.includes('E'))\n ? expandScientificNotation(trimmed)\n : trimmed\n}\n\n/**\n * Parses a normalized numeric string into components.\n */\nfunction parseNumericString (str) {\n const isNegative = str[0] === '-'\n if (isNegative) str = str.slice(1)\n\n const dotIndex = str.indexOf('.')\n if (dotIndex === -1) {\n return { isNegative, integerPart: BigInt(str) }\n }\n\n const integerStr = str.slice(0, dotIndex) || '0'\n const decimalPart = str.slice(dotIndex + 1)\n return { isNegative, integerPart: BigInt(integerStr), decimalPart }\n}\n\n/**\n * Expands scientific notation to decimal form (e.g., \"1e21\" → \"1000...\").\n */\nfunction expandScientificNotation (str) {\n const [mantissa, expStr] = str.toLowerCase().split('e')\n const exp = parseInt(expStr, 10)\n\n const dotIndex = mantissa.indexOf('.')\n const digits = dotIndex === -1\n ? mantissa\n : mantissa.slice(0, dotIndex) + mantissa.slice(dotIndex + 1)\n const integerLength = dotIndex === -1 ? mantissa.length : dotIndex\n const newDotPosition = integerLength + exp\n\n if (newDotPosition >= digits.length) {\n return digits + '0'.repeat(newDotPosition - digits.length)\n }\n if (newDotPosition <= 0) {\n return '0.' + '0'.repeat(-newDotPosition) + digits\n }\n return digits.slice(0, newDotPosition) + '.' + digits.slice(newDotPosition)\n}\n","/**\n * Kannada language converter - Functional Implementation\n *\n * Self-contained converter for South Asian numbering.\n *\n * Key features:\n * - Indian numbering system (ಸಾವಿರ, ಲಕ್ಷ, ಕೋಟಿ)\n * - Kannada script\n * - 3-2-2 grouping pattern (last 3 digits, then groups of 2)\n * - Complete word forms for 0-99\n * - Per-digit decimal reading\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ZERO = 'ಸೊನ್ನೆ'\nconst NEGATIVE = 'ಋಣಾತ್ಮಕ'\nconst DECIMAL_SEP = 'ದಶಮಾಂಶ'\nconst HUNDRED = 'ನೂರು'\n\nconst BELOW_HUNDRED = [\n 'ಸೊನ್ನೆ', 'ಒಂದು', 'ಎರಡು', 'ಮೂರು', 'ನಾಲ್ಕು', 'ಐದು', 'ಆರು', 'ಏಳು', 'ಎಂಟು', 'ಒಂಬತ್ತು',\n 'ಹತ್ತು', 'ಹನ್ನೊಂದು', 'ಹನ್ನೆರಡು', 'ಹದಿಮೂರು', 'ಹದಿನಾಲ್ಕು', 'ಹದಿನೈದು', 'ಹದಿನಾರು', 'ಹದಿನೇಳು', 'ಹದಿನೆಂಟು', 'ಹತ್ತೊಂಬತ್ತು',\n 'ಇಪ್ಪತ್ತು', 'ಇಪ್ಪತ್ತೊಂದು', 'ಇಪ್ಪತ್ತೆರಡು', 'ಇಪ್ಪತ್ತಮೂರು', 'ಇಪ್ಪತ್ತನಾಲ್ಕು', 'ಇಪ್ಪತ್ತೈದು', 'ಇಪ್ಪತ್ತಾರು', 'ಇಪ್ಪತ್ತೇಳು', 'ಇಪ್ಪತ್ತೆಂಟು', 'ಇಪ್ಪತ್ತೊಂಬತ್ತು',\n 'ಮೂವತ್ತು', 'ಮೂವತ್ತೊಂದು', 'ಮೂವತ್ತೆರಡು', 'ಮೂವತ್ತಮೂರು', 'ಮೂವತ್ತನಾಲ್ಕು', 'ಮೂವತ್ತೈದು', 'ಮೂವತ್ತಾರು', 'ಮೂವತ್ತೇಳು', 'ಮೂವತ್ತೆಂಟು', 'ಮೂವತ್ತೊಂಬತ್ತು',\n 'ನಲವತ್ತು', 'ನಲವತ್ತೊಂದು', 'ನಲವತ್ತೆರಡು', 'ನಲವತ್ತಮೂರು', 'ನಲವತ್ತನಾಲ್ಕು', 'ನಲವತ್ತೈದು', 'ನಲವತ್ತಾರು', 'ನಲವತ್ತೇಳು', 'ನಲವತ್ತೆಂಟು', 'ನಲವತ್ತೊಂಬತ್ತು',\n 'ಐವತ್ತು', 'ಐವತ್ತೊಂದು', 'ಐವತ್ತೆರಡು', 'ಐವತ್ತಮೂರು', 'ಐವತ್ತನಾಲ್ಕು', 'ಐವತ್ತೈದು', 'ಐವತ್ತಾರು', 'ಐವತ್ತೇಳು', 'ಐವತ್ತೆಂಟು', 'ಐವತ್ತೊಂಬತ್ತು',\n 'ಅರವತ್ತು', 'ಅರವತ್ತೊಂದು', 'ಅರವತ್ತೆರಡು', 'ಅರವತ್ತಮೂರು', 'ಅರವತ್ತನಾಲ್ಕು', 'ಅರವತ್ತೈದು', 'ಅರವತ್ತಾರು', 'ಅರವತ್ತೇಳು', 'ಅರವತ್ತೆಂಟು', 'ಅರವತ್ತೊಂಬತ್ತು',\n 'ಎಪ್ಪತ್ತು', 'ಎಪ್ಪತ್ತೊಂದು', 'ಎಪ್ಪತ್ತೆರಡು', 'ಎಪ್ಪತ್ತಮೂರು', 'ಎಪ್ಪತ್ತನಾಲ್ಕು', 'ಎಪ್ಪತ್ತೈದು', 'ಎಪ್ಪತ್ತಾರು', 'ಎಪ್ಪತ್ತೇಳು', 'ಎಪ್ಪತ್ತೆಂಟು', 'ಎಪ್ಪತ್ತೊಂಬತ್ತು',\n 'ಎಂಬತ್ತು', 'ಎಂಬತ್ತೊಂದು', 'ಎಂಬತ್ತೆರಡು', 'ಎಂಬತ್ತಮೂರು', 'ಎಂಬತ್ತನಾಲ್ಕು', 'ಎಂಬತ್ತೈದು', 'ಎಂಬತ್ತಾರು', 'ಎಂಬತ್ತೇಳು', 'ಎಂಬತ್ತೆಂಟು', 'ಎಂಬತ್ತೊಂಬತ್ತು',\n 'ತೊಂಬತ್ತು', 'ತೊಂಬತ್ತೊಂದು', 'ತೊಂಬತ್ತೆರಡು', 'ತೊಂಬತ್ತಮೂರು', 'ತೊಂಬತ್ತನಾಲ್ಕು', 'ತೊಂಬತ್ತೈದು', 'ತೊಂಬತ್ತಾರು', 'ತೊಂಬತ್ತೇಳು', 'ತೊಂಬತ್ತೆಂಟು', 'ತೊಂಬತ್ತೊಂಬತ್ತು'\n]\n\n// Scale words: index 0 = units (empty), 1 = thousand, 2 = lakh, 3 = crore, etc.\nconst SCALE_WORDS = ['', 'ಸಾವಿರ', 'ಲಕ್ಷ', 'ಕೋಟಿ', 'ಅಬ್ಜ', 'ಖರ್ವ', 'ನೀಲ', 'ಪದ್ಮ', 'ಶಂಖ']\n\n// ============================================================================\n// Segment Splitting (inlined for performance)\n// ============================================================================\n\nfunction groupByThreeThenTwos (n) {\n const numStr = n.toString()\n if (numStr.length <= 3) return [Number(numStr)]\n\n const segments = []\n segments.unshift(Number(numStr.slice(-3)))\n\n let remaining = numStr.slice(0, -3)\n while (remaining.length > 0) {\n segments.unshift(Number(remaining.slice(-2)))\n remaining = remaining.slice(0, -2)\n }\n\n return segments\n}\n\nfunction segmentToWords (n) {\n if (n === 0) return ''\n if (n < 100) return BELOW_HUNDRED[n]\n\n const hundreds = Math.trunc(n / 100)\n const remainder = n % 100\n\n if (remainder === 0) {\n return BELOW_HUNDRED[hundreds] + ' ' + HUNDRED\n }\n return BELOW_HUNDRED[hundreds] + ' ' + HUNDRED + ' ' + BELOW_HUNDRED[remainder]\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n const segments = groupByThreeThenTwos(n)\n const segmentCount = segments.length\n const words = []\n\n for (let i = 0; i < segmentCount; i++) {\n const segmentValue = segments[i]\n if (segmentValue === 0) continue\n\n const scaleIndex = segmentCount - i - 1\n words.push(segmentToWords(segmentValue))\n if (scaleIndex > 0 && SCALE_WORDS[scaleIndex]) {\n words.push(SCALE_WORDS[scaleIndex])\n }\n }\n\n return words.join(' ').trim()\n}\n\nfunction decimalPartToWords (decimalPart) {\n // Per-digit decimal reading\n const digits = []\n for (const char of decimalPart) {\n const d = parseInt(char, 10)\n digits.push(d === 0 ? ZERO : BELOW_HUNDRED[d])\n }\n return digits.join(' ')\n}\n\n/**\n * Converts a numeric value to Kannada words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Kannada words\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n"],"names":["parseNumericString","str","isNegative","slice","dotIndex","indexOf","integerPart","BigInt","integerStr","decimalPart","expandScientificNotation","mantissa","expStr","toLowerCase","split","exp","parseInt","digits","newDotPosition","length","repeat","ZERO","HUNDRED","BELOW_HUNDRED","SCALE_WORDS","segmentToWords","n","hundreds","Math","trunc","remainder","value","type","Number","isFinite","Error","isSafeInteger","toString","includes","numberToString","trimmed","trim","isNaN","normalizeString","TypeError","parseNumericValue","result","NEGATIVE","segments","numStr","unshift","remaining","groupByThreeThenTwos","segmentCount","words","i","segmentValue","scaleIndex","push","join","integerToWords","char","d","decimalPartToWords"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"kn.js","sources":["../../lib/utils/parse-numeric.js","../../lib/languages/kn.js"],"sourcesContent":["/**\n * Numeric value parsing utility.\n * Transforms user input (number, string, or bigint) into normalized components.\n * @module parse-numeric\n */\n\n/**\n * Parses a numeric value into its components.\n * @param {number|string|bigint} value\n * @returns {{isNegative: boolean, integerPart: bigint, decimalPart?: string}}\n * @throws {TypeError} If value is not number, string, or bigint\n * @throws {Error} If value is not a valid number format\n */\nexport function parseNumericValue (value) {\n const type = typeof value\n\n // BigInt: simplest case\n if (type === 'bigint') {\n return value < 0n\n ? { isNegative: true, integerPart: -value }\n : { isNegative: false, integerPart: value }\n }\n\n // Number: fast path for safe integers\n if (type === 'number') {\n if (!Number.isFinite(value)) {\n throw new Error('Number must be finite (NaN and Infinity are not supported)')\n }\n if (Number.isSafeInteger(value)) {\n return value < 0\n ? { isNegative: true, integerPart: BigInt(-value) }\n : { isNegative: false, integerPart: BigInt(value) }\n }\n return parseNumericString(numberToString(value))\n }\n\n // String input\n if (type === 'string') {\n return parseNumericString(normalizeString(value))\n }\n\n throw new TypeError(\n `Invalid value type: expected number, string, or bigint, received ${type}`\n )\n}\n\n/**\n * Converts a number to decimal string, expanding scientific notation if needed.\n */\nfunction numberToString (value) {\n const str = value.toString()\n return (str.includes('e') || str.includes('E'))\n ? expandScientificNotation(str)\n : str\n}\n\n/**\n * Validates and normalizes a string numeric input.\n */\nfunction normalizeString (value) {\n const trimmed = value.trim()\n if (trimmed.length === 0 || Number.isNaN(Number(trimmed))) {\n throw new Error(`Invalid number format: \"${value}\"`)\n }\n return (trimmed.includes('e') || trimmed.includes('E'))\n ? expandScientificNotation(trimmed)\n : trimmed\n}\n\n/**\n * Parses a normalized numeric string into components.\n */\nfunction parseNumericString (str) {\n const isNegative = str[0] === '-'\n if (isNegative) str = str.slice(1)\n\n const dotIndex = str.indexOf('.')\n if (dotIndex === -1) {\n return { isNegative, integerPart: BigInt(str) }\n }\n\n const integerStr = str.slice(0, dotIndex) || '0'\n const decimalPart = str.slice(dotIndex + 1)\n return { isNegative, integerPart: BigInt(integerStr), decimalPart }\n}\n\n/**\n * Expands scientific notation to decimal form (e.g., \"1e21\" → \"1000...\").\n */\nfunction expandScientificNotation (str) {\n const [mantissa, expStr] = str.toLowerCase().split('e')\n const exp = parseInt(expStr, 10)\n\n const dotIndex = mantissa.indexOf('.')\n const digits = dotIndex === -1\n ? mantissa\n : mantissa.slice(0, dotIndex) + mantissa.slice(dotIndex + 1)\n const integerLength = dotIndex === -1 ? mantissa.length : dotIndex\n const newDotPosition = integerLength + exp\n\n if (newDotPosition >= digits.length) {\n return digits + '0'.repeat(newDotPosition - digits.length)\n }\n if (newDotPosition <= 0) {\n return '0.' + '0'.repeat(-newDotPosition) + digits\n }\n return digits.slice(0, newDotPosition) + '.' + digits.slice(newDotPosition)\n}\n","/**\n * Kannada language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Indian numbering system (ಸಾವಿರ, ಲಕ್ಷ, ಕೋಟಿ)\n * - Kannada script\n * - 3-2-2 grouping pattern (last 3 digits, then groups of 2)\n * - Complete word forms for 0-99\n * - Per-digit decimal reading\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ZERO = 'ಸೊನ್ನೆ'\nconst NEGATIVE = 'ಋಣಾತ್ಮಕ'\nconst DECIMAL_SEP = 'ದಶಮಾಂಶ'\nconst HUNDRED = 'ನೂರು'\n\nconst BELOW_HUNDRED = [\n 'ಸೊನ್ನೆ', 'ಒಂದು', 'ಎರಡು', 'ಮೂರು', 'ನಾಲ್ಕು', 'ಐದು', 'ಆರು', 'ಏಳು', 'ಎಂಟು', 'ಒಂಬತ್ತು',\n 'ಹತ್ತು', 'ಹನ್ನೊಂದು', 'ಹನ್ನೆರಡು', 'ಹದಿಮೂರು', 'ಹದಿನಾಲ್ಕು', 'ಹದಿನೈದು', 'ಹದಿನಾರು', 'ಹದಿನೇಳು', 'ಹದಿನೆಂಟು', 'ಹತ್ತೊಂಬತ್ತು',\n 'ಇಪ್ಪತ್ತು', 'ಇಪ್ಪತ್ತೊಂದು', 'ಇಪ್ಪತ್ತೆರಡು', 'ಇಪ್ಪತ್ತಮೂರು', 'ಇಪ್ಪತ್ತನಾಲ್ಕು', 'ಇಪ್ಪತ್ತೈದು', 'ಇಪ್ಪತ್ತಾರು', 'ಇಪ್ಪತ್ತೇಳು', 'ಇಪ್ಪತ್ತೆಂಟು', 'ಇಪ್ಪತ್ತೊಂಬತ್ತು',\n 'ಮೂವತ್ತು', 'ಮೂವತ್ತೊಂದು', 'ಮೂವತ್ತೆರಡು', 'ಮೂವತ್ತಮೂರು', 'ಮೂವತ್ತನಾಲ್ಕು', 'ಮೂವತ್ತೈದು', 'ಮೂವತ್ತಾರು', 'ಮೂವತ್ತೇಳು', 'ಮೂವತ್ತೆಂಟು', 'ಮೂವತ್ತೊಂಬತ್ತು',\n 'ನಲವತ್ತು', 'ನಲವತ್ತೊಂದು', 'ನಲವತ್ತೆರಡು', 'ನಲವತ್ತಮೂರು', 'ನಲವತ್ತನಾಲ್ಕು', 'ನಲವತ್ತೈದು', 'ನಲವತ್ತಾರು', 'ನಲವತ್ತೇಳು', 'ನಲವತ್ತೆಂಟು', 'ನಲವತ್ತೊಂಬತ್ತು',\n 'ಐವತ್ತು', 'ಐವತ್ತೊಂದು', 'ಐವತ್ತೆರಡು', 'ಐವತ್ತಮೂರು', 'ಐವತ್ತನಾಲ್ಕು', 'ಐವತ್ತೈದು', 'ಐವತ್ತಾರು', 'ಐವತ್ತೇಳು', 'ಐವತ್ತೆಂಟು', 'ಐವತ್ತೊಂಬತ್ತು',\n 'ಅರವತ್ತು', 'ಅರವತ್ತೊಂದು', 'ಅರವತ್ತೆರಡು', 'ಅರವತ್ತಮೂರು', 'ಅರವತ್ತನಾಲ್ಕು', 'ಅರವತ್ತೈದು', 'ಅರವತ್ತಾರು', 'ಅರವತ್ತೇಳು', 'ಅರವತ್ತೆಂಟು', 'ಅರವತ್ತೊಂಬತ್ತು',\n 'ಎಪ್ಪತ್ತು', 'ಎಪ್ಪತ್ತೊಂದು', 'ಎಪ್ಪತ್ತೆರಡು', 'ಎಪ್ಪತ್ತಮೂರು', 'ಎಪ್ಪತ್ತನಾಲ್ಕು', 'ಎಪ್ಪತ್ತೈದು', 'ಎಪ್ಪತ್ತಾರು', 'ಎಪ್ಪತ್ತೇಳು', 'ಎಪ್ಪತ್ತೆಂಟು', 'ಎಪ್ಪತ್ತೊಂಬತ್ತು',\n 'ಎಂಬತ್ತು', 'ಎಂಬತ್ತೊಂದು', 'ಎಂಬತ್ತೆರಡು', 'ಎಂಬತ್ತಮೂರು', 'ಎಂಬತ್ತನಾಲ್ಕು', 'ಎಂಬತ್ತೈದು', 'ಎಂಬತ್ತಾರು', 'ಎಂಬತ್ತೇಳು', 'ಎಂಬತ್ತೆಂಟು', 'ಎಂಬತ್ತೊಂಬತ್ತು',\n 'ತೊಂಬತ್ತು', 'ತೊಂಬತ್ತೊಂದು', 'ತೊಂಬತ್ತೆರಡು', 'ತೊಂಬತ್ತಮೂರು', 'ತೊಂಬತ್ತನಾಲ್ಕು', 'ತೊಂಬತ್ತೈದು', 'ತೊಂಬತ್ತಾರು', 'ತೊಂಬತ್ತೇಳು', 'ತೊಂಬತ್ತೆಂಟು', 'ತೊಂಬತ್ತೊಂಬತ್ತು'\n]\n\n// Scale words: index 0 = units (empty), 1 = thousand, 2 = lakh, 3 = crore, etc.\nconst SCALE_WORDS = ['', 'ಸಾವಿರ', 'ಲಕ್ಷ', 'ಕೋಟಿ', 'ಅಬ್ಜ', 'ಖರ್ವ', 'ನೀಲ', 'ಪದ್ಮ', 'ಶಂಖ']\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds words for a 0-999 segment.\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n if (n < 100) return BELOW_HUNDRED[n]\n\n const hundreds = Math.trunc(n / 100)\n const remainder = n % 100\n\n if (remainder === 0) {\n return BELOW_HUNDRED[hundreds] + ' ' + HUNDRED\n }\n return BELOW_HUNDRED[hundreds] + ' ' + HUNDRED + ' ' + BELOW_HUNDRED[remainder]\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Kannada words.\n *\n * Uses BigInt modulo for segment extraction (faster than string slicing).\n * South Asian 3-2-2 grouping: first 3 digits, then groups of 2.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Kannada words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000 (direct lookup)\n if (n < 1000n) {\n return buildSegment(Number(n))\n }\n\n // Extract segments using BigInt modulo\n const segments = []\n segments.push(Number(n % 1000n))\n let temp = n / 1000n\n\n while (temp > 0n) {\n segments.push(Number(temp % 100n))\n temp = temp / 100n\n }\n\n // Build result string (process from most-significant to least)\n const words = []\n for (let i = segments.length - 1; i >= 0; i--) {\n const segment = segments[i]\n if (segment === 0) continue\n\n if (i === 0) {\n words.push(buildSegment(segment))\n } else {\n words.push(BELOW_HUNDRED[segment])\n }\n\n if (i > 0 && SCALE_WORDS[i]) {\n words.push(SCALE_WORDS[i])\n }\n }\n\n return words.join(' ')\n}\n\nfunction decimalPartToWords (decimalPart) {\n // Per-digit decimal reading\n const digits = []\n for (const char of decimalPart) {\n const d = parseInt(char, 10)\n digits.push(d === 0 ? ZERO : BELOW_HUNDRED[d])\n }\n return digits.join(' ')\n}\n\n/**\n * Converts a numeric value to Kannada words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Kannada words\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n"],"names":["parseNumericString","str","isNegative","slice","dotIndex","indexOf","integerPart","BigInt","integerStr","decimalPart","expandScientificNotation","mantissa","expStr","toLowerCase","split","exp","parseInt","digits","newDotPosition","length","repeat","ZERO","HUNDRED","BELOW_HUNDRED","SCALE_WORDS","buildSegment","n","hundreds","Math","trunc","remainder","value","type","Number","isFinite","Error","isSafeInteger","toString","includes","numberToString","trimmed","trim","isNaN","normalizeString","TypeError","parseNumericValue","result","NEGATIVE","segments","push","temp","words","i","segment","join","integerToWords","char","d","decimalPartToWords"],"mappings":"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"}
|
package/dist/languages/ko.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
/*! n2words/ko v3.
|
|
2
|
-
var e,t;e=this,t=function(e){"use strict";function t(e){const t="-"===e[0];t&&(e=e.slice(1));const n=e.indexOf(".");if(-1===n)return{isNegative:t,integerPart:BigInt(e)};const
|
|
1
|
+
/*! n2words/ko v3.1.0 | MIT License | github.com/forzagreen/n2words */
|
|
2
|
+
var e,t;e=this,t=function(e){"use strict";function t(e){const t="-"===e[0];t&&(e=e.slice(1));const n=e.indexOf(".");if(-1===n)return{isNegative:t,integerPart:BigInt(e)};const i=e.slice(0,n)||"0",r=e.slice(n+1);return{isNegative:t,integerPart:BigInt(i),decimalPart:r}}function n(e){const[t,n]=e.toLowerCase().split("e"),i=parseInt(n,10),r=t.indexOf("."),s=-1===r?t:t.slice(0,r)+t.slice(r+1),o=(-1===r?t.length:r)+i;return o>=s.length?s+"0".repeat(o-s.length):o<=0?"0."+"0".repeat(-o)+s:s.slice(0,o)+"."+s.slice(o)}const i=["","일","이","삼","사","오","육","칠","팔","구"],r=["만","억","조","경","해","자","양"];function s(e){if(0===e)return"";const t=e%10,n=Math.floor(e/10)%10,r=Math.floor(e/100)%10,s=Math.floor(e/1e3);let o="";return s>0&&(o+=1===s?"천":i[s]+"천"),r>0&&(o+=1===r?"백":i[r]+"백"),n>0&&(o+=1===n?"십":i[n]+"십"),t>0&&(o+=i[t]),o}function o(e){return 0n===e?"영":e<10000n?s(Number(e)):function(e){const t=e.toString(),n=t.length,i=[],o=n%4;let u=0;for(o>0&&(i.push(Number(t.slice(0,o))),u=o);u<n;)i.push(Number(t.slice(u,u+4))),u+=4;const c=[];let l=i.length-1;for(let e=0;e<i.length;e++){const t=i[e];if(0!==t)if(0===l)c.push({word:s(t),isScale:!1});else{const e=r[l-1];1===t||c.push({word:s(t),isScale:!1}),c.push({word:e,isScale:!0})}l--}return function(e){if(0===e.length)return"영";if(1===e.length)return e[0].word;const t=[];for(let n=0;n<e.length;n++){const i=e[n],r=n>0?e[n-1]:null;r&&r.isScale&&!i.isScale&&t.push(" "),t.push(i.word)}return t.join("")}(c)}(e)}e.ko=function(e){const{isNegative:i,integerPart:r,decimalPart:s}=function(e){const i=typeof e;if("bigint"===i)return e<0n?{isNegative:!0,integerPart:-e}:{isNegative:!1,integerPart:e};if("number"===i){if(!Number.isFinite(e))throw new Error("Number must be finite (NaN and Infinity are not supported)");return Number.isSafeInteger(e)?e<0?{isNegative:!0,integerPart:BigInt(-e)}:{isNegative:!1,integerPart:BigInt(e)}:t(function(e){const t=e.toString();return t.includes("e")||t.includes("E")?n(t):t}(e))}if("string"===i)return t(function(e){const t=e.trim();if(0===t.length||Number.isNaN(Number(t)))throw new Error(`Invalid number format: "${e}"`);return t.includes("e")||t.includes("E")?n(t):t}(e));throw new TypeError(`Invalid value type: expected number, string, or bigint, received ${i}`)}(e),u=[];return i&&u.push("마이너스"),u.push(o(r)),s&&(u.push("점"),u.push(function(e){const t=[];let n=0;for(;n<e.length&&"0"===e[n];)t.push("영"),n++;const i=e.slice(n);return i&&t.push(o(BigInt(i))),t.join(" ")}(s))),u.join(" ")}},"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).n2words=e.n2words||{});
|
|
3
3
|
//# sourceMappingURL=ko.js.map
|
package/dist/languages/ko.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ko.js","sources":["../../lib/utils/parse-numeric.js","../../lib/languages/ko.js"],"sourcesContent":["/**\n * Numeric value parsing utility.\n * Transforms user input (number, string, or bigint) into normalized components.\n * @module parse-numeric\n */\n\n/**\n * Parses a numeric value into its components.\n * @param {number|string|bigint} value\n * @returns {{isNegative: boolean, integerPart: bigint, decimalPart?: string}}\n * @throws {TypeError} If value is not number, string, or bigint\n * @throws {Error} If value is not a valid number format\n */\nexport function parseNumericValue (value) {\n const type = typeof value\n\n // BigInt: simplest case\n if (type === 'bigint') {\n return value < 0n\n ? { isNegative: true, integerPart: -value }\n : { isNegative: false, integerPart: value }\n }\n\n // Number: fast path for safe integers\n if (type === 'number') {\n if (!Number.isFinite(value)) {\n throw new Error('Number must be finite (NaN and Infinity are not supported)')\n }\n if (Number.isSafeInteger(value)) {\n return value < 0\n ? { isNegative: true, integerPart: BigInt(-value) }\n : { isNegative: false, integerPart: BigInt(value) }\n }\n return parseNumericString(numberToString(value))\n }\n\n // String input\n if (type === 'string') {\n return parseNumericString(normalizeString(value))\n }\n\n throw new TypeError(\n `Invalid value type: expected number, string, or bigint, received ${type}`\n )\n}\n\n/**\n * Converts a number to decimal string, expanding scientific notation if needed.\n */\nfunction numberToString (value) {\n const str = value.toString()\n return (str.includes('e') || str.includes('E'))\n ? expandScientificNotation(str)\n : str\n}\n\n/**\n * Validates and normalizes a string numeric input.\n */\nfunction normalizeString (value) {\n const trimmed = value.trim()\n if (trimmed.length === 0 || Number.isNaN(Number(trimmed))) {\n throw new Error(`Invalid number format: \"${value}\"`)\n }\n return (trimmed.includes('e') || trimmed.includes('E'))\n ? expandScientificNotation(trimmed)\n : trimmed\n}\n\n/**\n * Parses a normalized numeric string into components.\n */\nfunction parseNumericString (str) {\n const isNegative = str[0] === '-'\n if (isNegative) str = str.slice(1)\n\n const dotIndex = str.indexOf('.')\n if (dotIndex === -1) {\n return { isNegative, integerPart: BigInt(str) }\n }\n\n const integerStr = str.slice(0, dotIndex) || '0'\n const decimalPart = str.slice(dotIndex + 1)\n return { isNegative, integerPart: BigInt(integerStr), decimalPart }\n}\n\n/**\n * Expands scientific notation to decimal form (e.g., \"1e21\" → \"1000...\").\n */\nfunction expandScientificNotation (str) {\n const [mantissa, expStr] = str.toLowerCase().split('e')\n const exp = parseInt(expStr, 10)\n\n const dotIndex = mantissa.indexOf('.')\n const digits = dotIndex === -1\n ? mantissa\n : mantissa.slice(0, dotIndex) + mantissa.slice(dotIndex + 1)\n const integerLength = dotIndex === -1 ? mantissa.length : dotIndex\n const newDotPosition = integerLength + exp\n\n if (newDotPosition >= digits.length) {\n return digits + '0'.repeat(newDotPosition - digits.length)\n }\n if (newDotPosition <= 0) {\n return '0.' + '0'.repeat(-newDotPosition) + digits\n }\n return digits.slice(0, newDotPosition) + '.' + digits.slice(newDotPosition)\n}\n","/**\n * Korean language converter - Functional Implementation\n *\n * A performance-optimized number-to-words converter using precomputed lookup tables.\n *\n * Key features:\n * - Myriad-based (만) grouping - 4 digits\n * - Implicit '일' (one) omission before scale words\n * - Space separation after 만+ scales\n * - Hangul numerals\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\nconst ONES = ['', '일', '이', '삼', '사', '오', '육', '칠', '팔', '구']\n\nconst TEN = '십'\nconst HUNDRED = '백'\nconst THOUSAND = '천'\n\nconst ZERO = '영'\nconst NEGATIVE = '마이너스'\nconst DECIMAL_SEP = '점'\n\n// Myriad scale words (powers of 10,000)\n// 만 (10^4), 억 (10^8), 조 (10^12), 경 (10^16), etc.\nconst SCALES = ['만', '억', '조', '경', '해', '자', '양']\n\n// ============================================================================\n// Precomputed Lookup Tables (built once at module load)\n// ============================================================================\n\n/**\n * Builds segment word for 0-9999 (4-digit myriad segment).\n * Korean omits \"일\" before 십, 백, 천.\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100) % 10\n const thousands = Math.floor(n / 1000)\n\n let result = ''\n\n // Thousands\n if (thousands > 0) {\n if (thousands === 1) {\n result += THOUSAND\n } else {\n result += ONES[thousands] + THOUSAND\n }\n }\n\n // Hundreds\n if (hundreds > 0) {\n if (hundreds === 1) {\n result += HUNDRED\n } else {\n result += ONES[hundreds] + HUNDRED\n }\n }\n\n // Tens\n if (tens > 0) {\n if (tens === 1) {\n result += TEN\n } else {\n result += ONES[tens] + TEN\n }\n }\n\n // Ones\n if (ones > 0) {\n result += ONES[ones]\n }\n\n return result\n}\n\n// Precompute all 10000 segment words (0-9999) for myriad grouping\nconst SEGMENTS = new Array(10000)\n\nfor (let i = 0; i < 10000; i++) {\n SEGMENTS[i] = buildSegment(i)\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Korean words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Korean words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 10000 (direct lookup)\n if (n < 10000n) {\n return SEGMENTS[Number(n)]\n }\n\n // For numbers >= 10000, use myriad decomposition\n return buildLargeNumberWords(n)\n}\n\n/**\n * Builds words for numbers >= 10000.\n * Uses myriad (만) grouping - 4 digits per segment.\n *\n * @param {bigint} n - Number >= 10000\n * @returns {string} Korean words\n */\nfunction buildLargeNumberWords (n) {\n const numStr = n.toString()\n const len = numStr.length\n\n // Build segments of 4 digits from right to left\n const segments = []\n const segmentSize = 4\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n // Convert segments to words\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n if (scaleIndex === 0) {\n // Units segment (no scale word)\n parts.push({ word: SEGMENTS[segment], isScale: false })\n } else {\n // Segment with scale word\n const scaleWord = SCALES[scaleIndex - 1]\n\n // Korean omits segment when it's 1 before scale words\n if (segment === 1) {\n parts.push({ word: scaleWord, isScale: true })\n } else {\n parts.push({ word: SEGMENTS[segment], isScale: false })\n parts.push({ word: scaleWord, isScale: true })\n }\n }\n }\n\n scaleIndex--\n }\n\n // Join with Korean spacing rules\n return joinKoreanParts(parts)\n}\n\n/**\n * Joins parts with Korean spacing rules.\n * - Concatenate without spaces within segments\n * - Space after scale words before next number\n *\n * @param {Array} parts - Parts with isScale metadata\n * @returns {string} Joined string\n */\nfunction joinKoreanParts (parts) {\n if (parts.length === 0) return ZERO\n if (parts.length === 1) return parts[0].word\n\n const result = []\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i]\n const prevPart = i > 0 ? parts[i - 1] : null\n\n // Add space after scale words before next number\n if (prevPart && prevPart.isScale && !part.isScale) {\n result.push(' ')\n }\n\n result.push(part.word)\n }\n\n return result.join('')\n}\n\n/**\n * Converts decimal digits to Korean words.\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @returns {string} Korean words for decimal part (space-separated)\n */\nfunction decimalPartToWords (decimalPart) {\n const parts = []\n\n // Handle leading zeros\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n parts.push(ZERO)\n i++\n }\n\n // Convert remainder as a single number\n const remainder = decimalPart.slice(i)\n if (remainder) {\n parts.push(integerToWords(BigInt(remainder)))\n }\n\n return parts.join(' ')\n}\n\n/**\n * Converts a numeric value to Korean words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Korean words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(21) // '이십일'\n * toWords(10000) // '만'\n * toWords(1000000) // '백만'\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n const parts = []\n\n if (isNegative) {\n parts.push(NEGATIVE)\n }\n\n parts.push(integerToWords(integerPart))\n\n if (decimalPart) {\n parts.push(DECIMAL_SEP)\n parts.push(decimalPartToWords(decimalPart))\n }\n\n return parts.join(' ')\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n"],"names":["parseNumericString","str","isNegative","slice","dotIndex","indexOf","integerPart","BigInt","integerStr","decimalPart","expandScientificNotation","mantissa","expStr","toLowerCase","split","exp","parseInt","digits","newDotPosition","length","repeat","ONES","SCALES","buildSegment","n","ones","tens","Math","floor","hundreds","thousands","result","SEGMENTS","Array","i","integerToWords","Number","numStr","toString","len","segments","remainderLen","pos","push","parts","scaleIndex","segment","word","isScale","scaleWord","part","prevPart","join","joinKoreanParts","buildLargeNumberWords","value","type","isFinite","Error","isSafeInteger","includes","numberToString","trimmed","trim","isNaN","normalizeString","TypeError","parseNumericValue","remainder","decimalPartToWords"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ko.js","sources":["../../lib/utils/parse-numeric.js","../../lib/languages/ko.js"],"sourcesContent":["/**\n * Numeric value parsing utility.\n * Transforms user input (number, string, or bigint) into normalized components.\n * @module parse-numeric\n */\n\n/**\n * Parses a numeric value into its components.\n * @param {number|string|bigint} value\n * @returns {{isNegative: boolean, integerPart: bigint, decimalPart?: string}}\n * @throws {TypeError} If value is not number, string, or bigint\n * @throws {Error} If value is not a valid number format\n */\nexport function parseNumericValue (value) {\n const type = typeof value\n\n // BigInt: simplest case\n if (type === 'bigint') {\n return value < 0n\n ? { isNegative: true, integerPart: -value }\n : { isNegative: false, integerPart: value }\n }\n\n // Number: fast path for safe integers\n if (type === 'number') {\n if (!Number.isFinite(value)) {\n throw new Error('Number must be finite (NaN and Infinity are not supported)')\n }\n if (Number.isSafeInteger(value)) {\n return value < 0\n ? { isNegative: true, integerPart: BigInt(-value) }\n : { isNegative: false, integerPart: BigInt(value) }\n }\n return parseNumericString(numberToString(value))\n }\n\n // String input\n if (type === 'string') {\n return parseNumericString(normalizeString(value))\n }\n\n throw new TypeError(\n `Invalid value type: expected number, string, or bigint, received ${type}`\n )\n}\n\n/**\n * Converts a number to decimal string, expanding scientific notation if needed.\n */\nfunction numberToString (value) {\n const str = value.toString()\n return (str.includes('e') || str.includes('E'))\n ? expandScientificNotation(str)\n : str\n}\n\n/**\n * Validates and normalizes a string numeric input.\n */\nfunction normalizeString (value) {\n const trimmed = value.trim()\n if (trimmed.length === 0 || Number.isNaN(Number(trimmed))) {\n throw new Error(`Invalid number format: \"${value}\"`)\n }\n return (trimmed.includes('e') || trimmed.includes('E'))\n ? expandScientificNotation(trimmed)\n : trimmed\n}\n\n/**\n * Parses a normalized numeric string into components.\n */\nfunction parseNumericString (str) {\n const isNegative = str[0] === '-'\n if (isNegative) str = str.slice(1)\n\n const dotIndex = str.indexOf('.')\n if (dotIndex === -1) {\n return { isNegative, integerPart: BigInt(str) }\n }\n\n const integerStr = str.slice(0, dotIndex) || '0'\n const decimalPart = str.slice(dotIndex + 1)\n return { isNegative, integerPart: BigInt(integerStr), decimalPart }\n}\n\n/**\n * Expands scientific notation to decimal form (e.g., \"1e21\" → \"1000...\").\n */\nfunction expandScientificNotation (str) {\n const [mantissa, expStr] = str.toLowerCase().split('e')\n const exp = parseInt(expStr, 10)\n\n const dotIndex = mantissa.indexOf('.')\n const digits = dotIndex === -1\n ? mantissa\n : mantissa.slice(0, dotIndex) + mantissa.slice(dotIndex + 1)\n const integerLength = dotIndex === -1 ? mantissa.length : dotIndex\n const newDotPosition = integerLength + exp\n\n if (newDotPosition >= digits.length) {\n return digits + '0'.repeat(newDotPosition - digits.length)\n }\n if (newDotPosition <= 0) {\n return '0.' + '0'.repeat(-newDotPosition) + digits\n }\n return digits.slice(0, newDotPosition) + '.' + digits.slice(newDotPosition)\n}\n","/**\n * Korean language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Myriad-based (만) grouping - 4 digits\n * - Implicit '일' (one) omission before scale words\n * - Space separation after 만+ scales\n * - Hangul numerals\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\nconst ONES = ['', '일', '이', '삼', '사', '오', '육', '칠', '팔', '구']\n\nconst TEN = '십'\nconst HUNDRED = '백'\nconst THOUSAND = '천'\n\nconst ZERO = '영'\nconst NEGATIVE = '마이너스'\nconst DECIMAL_SEP = '점'\n\n// Myriad scale words (powers of 10,000)\n// 만 (10^4), 억 (10^8), 조 (10^12), 경 (10^16), etc.\nconst SCALES = ['만', '억', '조', '경', '해', '자', '양']\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds segment word for 0-9999 (4-digit myriad segment).\n * Korean omits \"일\" before 십, 백, 천.\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100) % 10\n const thousands = Math.floor(n / 1000)\n\n let result = ''\n\n // Thousands\n if (thousands > 0) {\n if (thousands === 1) {\n result += THOUSAND\n } else {\n result += ONES[thousands] + THOUSAND\n }\n }\n\n // Hundreds\n if (hundreds > 0) {\n if (hundreds === 1) {\n result += HUNDRED\n } else {\n result += ONES[hundreds] + HUNDRED\n }\n }\n\n // Tens\n if (tens > 0) {\n if (tens === 1) {\n result += TEN\n } else {\n result += ONES[tens] + TEN\n }\n }\n\n // Ones\n if (ones > 0) {\n result += ONES[ones]\n }\n\n return result\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Korean words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Korean words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 10000\n if (n < 10000n) {\n return buildSegment(Number(n))\n }\n\n // For numbers >= 10000, use myriad decomposition\n return buildLargeNumberWords(n)\n}\n\n/**\n * Builds words for numbers >= 10000.\n * Uses myriad (만) grouping - 4 digits per segment.\n *\n * @param {bigint} n - Number >= 10000\n * @returns {string} Korean words\n */\nfunction buildLargeNumberWords (n) {\n const numStr = n.toString()\n const len = numStr.length\n\n // Build segments of 4 digits from right to left\n const segments = []\n const segmentSize = 4\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n // Convert segments to words\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n if (scaleIndex === 0) {\n // Units segment (no scale word)\n parts.push({ word: buildSegment(segment), isScale: false })\n } else {\n // Segment with scale word\n const scaleWord = SCALES[scaleIndex - 1]\n\n // Korean omits segment when it's 1 before scale words\n if (segment === 1) {\n parts.push({ word: scaleWord, isScale: true })\n } else {\n parts.push({ word: buildSegment(segment), isScale: false })\n parts.push({ word: scaleWord, isScale: true })\n }\n }\n }\n\n scaleIndex--\n }\n\n // Join with Korean spacing rules\n return joinKoreanParts(parts)\n}\n\n/**\n * Joins parts with Korean spacing rules.\n * - Concatenate without spaces within segments\n * - Space after scale words before next number\n *\n * @param {Array} parts - Parts with isScale metadata\n * @returns {string} Joined string\n */\nfunction joinKoreanParts (parts) {\n if (parts.length === 0) return ZERO\n if (parts.length === 1) return parts[0].word\n\n const result = []\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i]\n const prevPart = i > 0 ? parts[i - 1] : null\n\n // Add space after scale words before next number\n if (prevPart && prevPart.isScale && !part.isScale) {\n result.push(' ')\n }\n\n result.push(part.word)\n }\n\n return result.join('')\n}\n\n/**\n * Converts decimal digits to Korean words.\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @returns {string} Korean words for decimal part (space-separated)\n */\nfunction decimalPartToWords (decimalPart) {\n const parts = []\n\n // Handle leading zeros\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n parts.push(ZERO)\n i++\n }\n\n // Convert remainder as a single number\n const remainder = decimalPart.slice(i)\n if (remainder) {\n parts.push(integerToWords(BigInt(remainder)))\n }\n\n return parts.join(' ')\n}\n\n/**\n * Converts a numeric value to Korean words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Korean words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(21) // '이십일'\n * toWords(10000) // '만'\n * toWords(1000000) // '백만'\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n const parts = []\n\n if (isNegative) {\n parts.push(NEGATIVE)\n }\n\n parts.push(integerToWords(integerPart))\n\n if (decimalPart) {\n parts.push(DECIMAL_SEP)\n parts.push(decimalPartToWords(decimalPart))\n }\n\n return parts.join(' ')\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n"],"names":["parseNumericString","str","isNegative","slice","dotIndex","indexOf","integerPart","BigInt","integerStr","decimalPart","expandScientificNotation","mantissa","expStr","toLowerCase","split","exp","parseInt","digits","newDotPosition","length","repeat","ONES","SCALES","buildSegment","n","ones","tens","Math","floor","hundreds","thousands","result","integerToWords","Number","numStr","toString","len","segments","remainderLen","pos","push","parts","scaleIndex","i","segment","word","isScale","scaleWord","part","prevPart","join","joinKoreanParts","buildLargeNumberWords","value","type","isFinite","Error","isSafeInteger","includes","numberToString","trimmed","trim","isNaN","normalizeString","TypeError","parseNumericValue","remainder","decimalPartToWords"],"mappings":"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"}
|
package/dist/languages/lt.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
/*! n2words/lt v3.
|
|
2
|
-
var i,
|
|
1
|
+
/*! n2words/lt v3.1.0 | MIT License | github.com/forzagreen/n2words */
|
|
2
|
+
var i,t;i=this,t=function(i){"use strict";function t(i){const t="-"===i[0];t&&(i=i.slice(1));const e=i.indexOf(".");if(-1===e)return{isNegative:t,integerPart:BigInt(i)};const n=i.slice(0,e)||"0",r=i.slice(e+1);return{isNegative:t,integerPart:BigInt(n),decimalPart:r}}function e(i){const[t,e]=i.toLowerCase().split("e"),n=parseInt(e,10),r=t.indexOf("."),o=-1===r?t:t.slice(0,r)+t.slice(r+1),s=(-1===r?t.length:r)+n;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 n=["","vienas","du","trys","keturi","penki","šeši","septyni","aštuoni","devyni"],r=["","viena","dvi","trys","keturios","penkios","šešios","septynios","aštuonios","devynios"],o=["dešimt","vienuolika","dvylika","trylika","keturiolika","penkiolika","šešiolika","septyniolika","aštuoniolika","devyniolika"],s=["","","dvidešimt","trisdešimt","keturiasdešimt","penkiasdešimt","šešiasdešimt","septyniasdešimt","aštuoniasdešimt","devyniasdešimt"],a="šimtas",u="šimtai",l="nulis",c=[["tūkstantis","tūkstančiai","tūkstančių"],["milijonas","milijonai","milijonų"],["milijardas","milijardai","milijardų"],["trilijonas","trilijonai","trilijonų"],["kvadrilijonas","kvadrilijonai","kvadrilijonų"],["kvintilijonas","kvintilijonai","kvintilijonų"],["sikstilijonas","sikstilijonai","sikstilijonų"],["septilijonas","septilijonai","septilijonų"],["oktilijonas","oktilijonai","oktilijonų"]];function f(i){if(0===i)return"";const t=i%10,e=Math.floor(i/10)%10,r=Math.floor(i/100),l=[];return r>0&&(l.push(n[r]),l.push(1===r?a:u)),e>1&&l.push(s[e]),1===e?l.push(o[t]):t>0&&l.push(n[t]),l.join(" ")}function d(i,t){if(0===i)return t[2];const e=i%10,n=i%100;return n>=10&&n<=19||0===e?t[2]:1===e?t[0]:t[1]}function p(i,t={}){if(0n===i)return l;if(i<1000n){const e=Number(i);return"feminine"===t.gender?function(i){if(0===i)return"";const t=i%10,e=Math.floor(i/10)%10,l=Math.floor(i/100),c=[];return l>0&&(c.push(n[l]),c.push(1===l?a:u)),e>1&&c.push(s[e]),1===e?c.push(o[t]):t>0&&c.push(r[t]),c.join(" ")}(e):f(e)}return function(i){const t=i.toString(),e=t.length,n=[],r=e%3;let o=0;for(r>0&&(n.push(Number(t.slice(0,r))),o=r);o<e;)n.push(Number(t.slice(o,o+3))),o+=3;const s=[];let a=n.length-1;for(let i=0;i<n.length;i++){const t=n[i];if(0!==t){const i=f(t);if(0===a)s.push(i);else{const e=d(t,c[a-1]);s.push(i+" "+e)}}a--}return s.join(" ")}(i)}i.lt=function(i,n){n=function(i){if(void 0===i)return{};if(function(i){if(null===i||"object"!=typeof i)return!1;const t=Object.getPrototypeOf(i);return null===t||t===Object.prototype}(i))return i;throw new TypeError("Invalid options: expected plain object or undefined, got "+typeof i)}(n);const{isNegative:r,integerPart:o,decimalPart:s}=function(i){const n=typeof i;if("bigint"===n)return i<0n?{isNegative:!0,integerPart:-i}:{isNegative:!1,integerPart:i};if("number"===n){if(!Number.isFinite(i))throw new Error("Number must be finite (NaN and Infinity are not supported)");return Number.isSafeInteger(i)?i<0?{isNegative:!0,integerPart:BigInt(-i)}:{isNegative:!1,integerPart:BigInt(i)}:t(function(i){const t=i.toString();return t.includes("e")||t.includes("E")?e(t):t}(i))}if("string"===n)return t(function(i){const t=i.trim();if(0===t.length||Number.isNaN(Number(t)))throw new Error(`Invalid number format: "${i}"`);return t.includes("e")||t.includes("E")?e(t):t}(i));throw new TypeError(`Invalid value type: expected number, string, or bigint, received ${n}`)}(i);let a="";return r&&(a="minus "),a+=p(o,n),s&&(a+=" kablelis "+function(i,t){let e="",n=0;for(;n<i.length&&"0"===i[n];)e&&(e+=" "),e+=l,n++;const r=i.slice(n);return r&&(e&&(e+=" "),e+=p(BigInt(r),t)),e}(s,n)),a}},"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((i="undefined"!=typeof globalThis?globalThis:i||self).n2words=i.n2words||{});
|
|
3
3
|
//# sourceMappingURL=lt.js.map
|