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/fr.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fr.js","sources":["../../lib/utils/parse-numeric.js","../../lib/languages/fr.js","../../lib/utils/validate-options.js","../../lib/utils/is-plain-object.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 * French 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-999) at module load.\n * This eliminates all per-call string manipulation for segment conversion.\n *\n * French-specific rules (handled in precomputation):\n * - Vigesimal patterns: 70 = soixante-dix, 80 = quatre-vingts, 90 = quatre-vingt-dix\n * - \"et\" conjunction: vingt et un (21), soixante et onze (71), but NOT quatre-vingt-un\n * - Pluralization: \"cents\" loses 's' when followed by more digits\n * - Long scale with -ard forms: milliard, billiard, trilliard\n * - Omit \"un\" before mille\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\nimport { validateOptions } from '../utils/validate-options.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\nconst ONES = ['', 'un', 'deux', 'trois', 'quatre', 'cinq', 'six', 'sept', 'huit', 'neuf']\nconst TEENS = ['dix', 'onze', 'douze', 'treize', 'quatorze', 'quinze', 'seize', 'dix-sept', 'dix-huit', 'dix-neuf']\nconst TENS = ['', '', 'vingt', 'trente', 'quarante', 'cinquante', 'soixante']\n\n// Scale words (even indices: million, billion, trillion, quadrillion)\nconst SCALES = ['million', 'billion', 'trillion', 'quadrillion']\nconst SCALES_ARD = ['milliard', 'billiard', 'trilliard', 'quadrilliard']\n\nconst THOUSAND = 'mille'\nconst HUNDRED = 'cent'\nconst ZERO = 'zéro'\nconst NEGATIVE = 'moins'\nconst DECIMAL_SEP = 'virgule'\n\n// ============================================================================\n// Precomputed Lookup Tables (built once at module load)\n// ============================================================================\n\n/**\n * Builds segment word for 0-999.\n * Returns object with { word, endsWithCents, endsWithVingts } for pluralization handling.\n */\nfunction buildSegment (n) {\n if (n === 0) return { word: '', endsWithCents: false, endsWithVingts: false }\n\n const tensOnes = n % 100\n const hundreds = Math.floor(n / 100)\n\n const parts = []\n let endsWithCents = false\n let endsWithVingts = false\n\n // Hundreds\n if (hundreds > 0) {\n if (hundreds === 1) {\n if (tensOnes === 0) {\n parts.push(HUNDRED)\n } else {\n parts.push(HUNDRED)\n }\n } else {\n if (tensOnes === 0) {\n // \"deux cents\", \"trois cents\" (with 's')\n parts.push(ONES[hundreds] + ' ' + HUNDRED + 's')\n endsWithCents = true\n } else {\n // \"deux cent\", \"trois cent\" (no 's' when followed by more)\n parts.push(ONES[hundreds] + ' ' + HUNDRED)\n }\n }\n }\n\n // Tens and ones - vigesimal pattern\n if (tensOnes === 0) {\n // Just hundreds, nothing more\n } else if (tensOnes < 10) {\n // Single digit\n parts.push(ONES[tensOnes])\n } else if (tensOnes < 17) {\n // 10-16: regular teens\n parts.push(TEENS[tensOnes - 10])\n } else if (tensOnes < 20) {\n // 17-19: dix-sept, dix-huit, dix-neuf\n parts.push(TEENS[tensOnes - 10])\n } else if (tensOnes < 70) {\n // 20-69: standard tens + ones\n const t = Math.floor(tensOnes / 10)\n const o = tensOnes % 10\n if (o === 0) {\n parts.push(TENS[t])\n } else if (o === 1) {\n // \"et un\" for 21, 31, 41, 51, 61\n parts.push(TENS[t] + ' et ' + ONES[1])\n } else {\n parts.push(TENS[t] + '-' + ONES[o])\n }\n } else if (tensOnes < 80) {\n // 70-79: soixante-dix, soixante et onze, soixante-douze...\n const remainder = tensOnes - 60\n if (remainder === 11) {\n // 71: soixante et onze\n parts.push('soixante et onze')\n } else {\n // 70, 72-79: soixante-dix, soixante-douze...\n parts.push('soixante-' + TEENS[remainder - 10])\n }\n } else if (tensOnes === 80) {\n // 80: quatre-vingts (with 's')\n parts.push('quatre-vingts')\n endsWithVingts = true\n } else if (tensOnes < 100) {\n // 81-99: quatre-vingt-un, quatre-vingt-dix...\n const remainder = tensOnes - 80\n if (remainder < 10) {\n // 81-89\n parts.push('quatre-vingt-' + ONES[remainder])\n } else {\n // 90-99\n parts.push('quatre-vingt-' + TEENS[remainder - 10])\n }\n }\n\n // Join parts with space (between hundreds and rest)\n return { word: parts.join(' '), endsWithCents, endsWithVingts }\n}\n\n// Precompute all 1000 segment words (0-999)\nconst SEGMENTS = new Array(1000)\nconst SEGMENTS_ENDS_CENTS = new Array(1000)\nconst SEGMENTS_ENDS_VINGTS = new Array(1000)\n\nfor (let i = 0; i < 1000; i++) {\n const result = buildSegment(i)\n SEGMENTS[i] = result.word\n SEGMENTS_ENDS_CENTS[i] = result.endsWithCents\n SEGMENTS_ENDS_VINGTS[i] = result.endsWithVingts\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Gets scale word for French long scale with -ard pattern.\n *\n * @param {number} scaleIndex - Scale level (1 = thousand, 2 = million, etc.)\n * @param {bigint} segment - Segment value for pluralization\n * @returns {string} Scale word\n */\nfunction getScaleWord (scaleIndex, segment) {\n if (scaleIndex === 1) return THOUSAND\n\n // Even indices (2, 4, 6, 8): million, billion, trillion, quadrillion\n // Odd indices > 1 (3, 5, 7, 9): milliard, billiard, trilliard, quadrilliard\n if (scaleIndex % 2 === 0) {\n const arrayIndex = (scaleIndex / 2) - 1\n const baseWord = SCALES[arrayIndex]\n if (!baseWord) return ''\n return segment > 1n ? baseWord + 's' : baseWord\n } else {\n const arrayIndex = ((scaleIndex - 1) / 2) - 1\n const ardWord = SCALES_ARD[arrayIndex]\n if (!ardWord) return THOUSAND\n return segment > 1n ? ardWord + 's' : ardWord\n }\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to French words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @param {boolean} withHyphen - Whether to use hyphen separators\n * @returns {string} French words\n */\nfunction integerToWords (n, withHyphen = false) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000 (direct lookup)\n if (n < 1000n) {\n const word = SEGMENTS[Number(n)]\n return withHyphen ? word.replace(/ /g, '-') : word\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 // \"mille\" not \"un mille\"\n result = THOUSAND\n } else {\n // Check if segment ends with \"cents\" or \"vingts\" - need to strip 's' before mille\n let thousandsWord = SEGMENTS[thousands]\n if (SEGMENTS_ENDS_CENTS[thousands] || SEGMENTS_ENDS_VINGTS[thousands]) {\n thousandsWord = thousandsWord.slice(0, -1) // Remove trailing 's'\n }\n result = thousandsWord + (withHyphen ? '-' : ' ') + THOUSAND\n }\n\n if (remainder > 0) {\n result += (withHyphen ? '-' : ' ') + SEGMENTS[remainder]\n }\n\n if (withHyphen) {\n result = result.replace(/ /g, '-')\n }\n\n return result\n }\n\n // For numbers >= 1,000,000, use scale decomposition\n return buildLargeNumberWords(n, withHyphen)\n}\n\n/**\n * Builds words for numbers >= 1,000,000.\n *\n * @param {bigint} n - Number >= 1,000,000\n * @param {boolean} withHyphen - Whether to use hyphen separators\n * @returns {string} French words\n */\nfunction buildLargeNumberWords (n, withHyphen) {\n const numStr = n.toString()\n const len = numStr.length\n\n // Build segments of 3 digits from right to left\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 const scaleWord = scaleIndex > 0 ? getScaleWord(scaleIndex, BigInt(segment)) : ''\n\n if (scaleIndex === 0) {\n // Units segment\n parts.push(SEGMENTS[segment])\n } else if (scaleIndex === 1) {\n // Thousands: \"mille\" not \"un mille\"\n if (segment === 1) {\n parts.push(THOUSAND)\n } else {\n let segWords = SEGMENTS[segment]\n // Strip 's' from cents/vingts before mille\n if (SEGMENTS_ENDS_CENTS[segment] || SEGMENTS_ENDS_VINGTS[segment]) {\n segWords = segWords.slice(0, -1)\n }\n parts.push(segWords)\n parts.push(scaleWord)\n }\n } else {\n // Million and above\n parts.push(SEGMENTS[segment])\n parts.push(scaleWord)\n }\n }\n\n scaleIndex--\n }\n\n const sep = withHyphen ? '-' : ' '\n let result = parts.join(sep)\n\n if (withHyphen) {\n result = result.replace(/ /g, '-')\n }\n\n return result\n}\n\n/**\n * Converts decimal digits to French words.\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @param {boolean} withHyphen - Whether to use hyphen separators\n * @returns {string} French words for decimal part\n */\nfunction decimalPartToWords (decimalPart, withHyphen) {\n let result = ''\n const sep = withHyphen ? '-' : ' '\n\n // Handle leading zeros\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += sep\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 += sep\n result += integerToWords(BigInt(remainder), withHyphen)\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to French 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 * @param {Object} [options] - Optional configuration\n * @param {boolean} [options.withHyphenSeparator=false] - Use hyphens between all words\n * @returns {string} The number in French 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) // 'vingt et un'\n * toWords(80) // 'quatre-vingts'\n * toWords(1000000) // 'un million'\n */\nfunction toWords (value, options) {\n options = validateOptions(options)\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n const withHyphen = options.withHyphenSeparator || false\n\n let result = ''\n const sep = withHyphen ? '-' : ' '\n\n if (isNegative) {\n result = NEGATIVE + sep\n }\n\n result += integerToWords(integerPart, withHyphen)\n\n if (decimalPart) {\n result += sep + DECIMAL_SEP + sep + decimalPartToWords(decimalPart, withHyphen)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n","import { isPlainObject } from './is-plain-object.js'\n\n/**\n * Validates and normalizes the options parameter.\n *\n * @param {*} options The options value to validate\n * @returns {Object} A valid options object (empty object if undefined)\n * @throws {TypeError} If options is not undefined or a plain object\n */\nexport function validateOptions (options) {\n if (options === undefined) return {}\n if (isPlainObject(options)) return options\n throw new TypeError(\n `Invalid options: expected plain object or undefined, got ${typeof options}`\n )\n}\n","/**\n * Checks if a value is a plain object (not null, array, or other object types).\n *\n * A plain object is one created by:\n * - Object literal: `{}`\n * - Object.create(null): null-prototype object\n *\n * This excludes arrays, class instances, Map, Set, and other object types.\n *\n * @param {*} value Value to check\n * @returns {boolean} True if value is a plain object\n */\nexport function isPlainObject (value) {\n if (value === null || typeof value !== 'object') return false\n const proto = Object.getPrototypeOf(value)\n return proto === null || proto === Object.prototype\n}\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","SCALES","SCALES_ARD","THOUSAND","HUNDRED","ZERO","buildSegment","n","word","endsWithCents","endsWithVingts","tensOnes","hundreds","Math","floor","parts","push","t","o","remainder","join","SEGMENTS","Array","SEGMENTS_ENDS_CENTS","SEGMENTS_ENDS_VINGTS","i","result","getScaleWord","scaleIndex","segment","baseWord","ardWord","integerToWords","withHyphen","Number","replace","thousands","thousandsWord","numStr","toString","len","segments","remainderLen","pos","scaleWord","segWords","buildLargeNumberWords","value","options","undefined","proto","Object","getPrototypeOf","prototype","isPlainObject","TypeError","validateOptions","type","isFinite","Error","isSafeInteger","includes","numberToString","trimmed","trim","isNaN","normalizeString","parseNumericValue","withHyphenSeparator","sep","decimalPartToWords"],"mappings":";0CAwEA,SAASA,EAAoBC,GAC3B,MAAMC,EAAwB,MAAXD,EAAI,GACnBC,IAAYD,EAAMA,EAAIE,MAAM,IAEhC,MAAMC,EAAWH,EAAII,QAAQ,KAC7B,IAAiB,IAAbD,EACF,MAAO,CAAEF,aAAYI,YAAaC,OAAON,IAG3C,MAAMO,EAAaP,EAAIE,MAAM,EAAGC,IAAa,IACvCK,EAAcR,EAAIE,MAAMC,EAAW,GACzC,MAAO,CAAEF,aAAYI,YAAaC,OAAOC,GAAaC,cACxD,CAKA,SAASC,EAA0BT,GACjC,MAAOU,EAAUC,GAAUX,EAAIY,cAAcC,MAAM,KAC7CC,EAAMC,SAASJ,EAAQ,IAEvBR,EAAWO,EAASN,QAAQ,KAC5BY,OAASb,EACXO,EACAA,EAASR,MAAM,EAAGC,GAAYO,EAASR,MAAMC,EAAW,GAEtDc,IAD6B,IAAbd,EAAkBO,EAASQ,OAASf,GACnBW,EAEvC,OAAIG,GAAkBD,EAAOE,OACpBF,EAAS,IAAIG,OAAOF,EAAiBD,EAAOE,QAEjDD,GAAkB,EACb,KAAO,IAAIE,QAAQF,GAAkBD,EAEvCA,EAAOd,MAAM,EAAGe,GAAkB,IAAMD,EAAOd,MAAMe,EAC9D,CCnFA,MAAMG,EAAO,CAAC,GAAI,KAAM,OAAQ,QAAS,SAAU,OAAQ,MAAO,OAAQ,OAAQ,QAC5EC,EAAQ,CAAC,MAAO,OAAQ,QAAS,SAAU,WAAY,SAAU,QAAS,WAAY,WAAY,YAClGC,EAAO,CAAC,GAAI,GAAI,QAAS,SAAU,WAAY,YAAa,YAG5DC,EAAS,CAAC,UAAW,UAAW,WAAY,eAC5CC,EAAa,CAAC,WAAY,WAAY,YAAa,gBAEnDC,EAAW,QACXC,EAAU,OACVC,EAAO,OAYb,SAASC,EAAcC,GACrB,GAAU,IAANA,EAAS,MAAO,CAAEC,KAAM,GAAIC,eAAe,EAAOC,gBAAgB,GAEtE,MAAMC,EAAWJ,EAAI,IACfK,EAAWC,KAAKC,MAAMP,EAAI,KAE1BQ,EAAQ,GACd,IAAIN,GAAgB,EAChBC,GAAiB,EAuBrB,GApBIE,EAAW,IACI,IAAbA,EAEAG,EAAMC,KAAKZ,GAKI,IAAbO,GAEFI,EAAMC,KAAKlB,EAAKc,GAAY,IAAMR,EAAU,KAC5CK,GAAgB,GAGhBM,EAAMC,KAAKlB,EAAKc,GAAY,IAAMR,IAMvB,IAAbO,QAEG,GAAIA,EAAW,GAEpBI,EAAMC,KAAKlB,EAAKa,SACX,GAAIA,EAAW,GAEpBI,EAAMC,KAAKjB,EAAMY,EAAW,UACvB,GAAIA,EAAW,GAEpBI,EAAMC,KAAKjB,EAAMY,EAAW,UACvB,GAAIA,EAAW,GAAI,CAExB,MAAMM,EAAIJ,KAAKC,MAAMH,EAAW,IAC1BO,EAAIP,EAAW,GAEnBI,EAAMC,KADE,IAANE,EACSlB,EAAKiB,GACD,IAANC,EAEElB,EAAKiB,GAAK,OAASnB,EAAK,GAExBE,EAAKiB,GAAK,IAAMnB,EAAKoB,GAEpC,MAAO,GAAIP,EAAW,GAAI,CAExB,MAAMQ,EAAYR,EAAW,GAG3BI,EAAMC,KAFU,KAAdG,EAES,mBAGA,YAAcpB,EAAMoB,EAAY,IAE/C,MAAO,GAAiB,KAAbR,EAETI,EAAMC,KAAK,iBACXN,GAAiB,OACZ,GAAIC,EAAW,IAAK,CAEzB,MAAMQ,EAAYR,EAAW,GAG3BI,EAAMC,KAFJG,EAAY,GAEH,gBAAkBrB,EAAKqB,GAGvB,gBAAkBpB,EAAMoB,EAAY,IAEnD,CAGA,MAAO,CAAEX,KAAMO,EAAMK,KAAK,KAAMX,gBAAeC,iBACjD,CAGA,MAAMW,EAAW,IAAIC,MAAM,KACrBC,EAAsB,IAAID,MAAM,KAChCE,EAAuB,IAAIF,MAAM,KAEvC,IAAK,IAAIG,EAAI,EAAGA,EAAI,IAAMA,IAAK,CAC7B,MAAMC,EAASpB,EAAamB,GAC5BJ,EAASI,GAAKC,EAAOlB,KACrBe,EAAoBE,GAAKC,EAAOjB,cAChCe,EAAqBC,GAAKC,EAAOhB,cACnC,CAaA,SAASiB,EAAcC,EAAYC,GACjC,GAAmB,IAAfD,EAAkB,OAAOzB,EAI7B,GAAIyB,EAAa,GAAM,EAAG,CACxB,MACME,EAAW7B,EADG2B,EAAa,EAAK,GAEtC,OAAKE,EACED,EAAU,GAAKC,EAAW,IAAMA,EADjB,EAExB,CAAO,CACL,MACMC,EAAU7B,GADK0B,EAAa,GAAK,EAAK,GAE5C,OAAKG,EACEF,EAAU,GAAKE,EAAU,IAAMA,EADjB5B,CAEvB,CACF,CAaA,SAAS6B,EAAgBzB,EAAG0B,GAAa,GACvC,GAAU,KAAN1B,EAAU,OAAOF,EAGrB,GAAIE,EAAI,MAAO,CACb,MAAMC,EAAOa,EAASa,OAAO3B,IAC7B,OAAO0B,EAAazB,EAAK2B,QAAQ,KAAM,KAAO3B,CAChD,CAGA,GAAID,EAAI,SAAY,CAClB,MAAM6B,EAAYF,OAAO3B,EAAI,OACvBY,EAAYe,OAAO3B,EAAI,OAE7B,IAAImB,EACJ,GAAkB,IAAdU,EAEFV,EAASvB,MACJ,CAEL,IAAIkC,EAAgBhB,EAASe,IACzBb,EAAoBa,IAAcZ,EAAqBY,MACzDC,EAAgBA,EAAczD,MAAM,GAAG,IAEzC8C,EAASW,GAAiBJ,EAAa,IAAM,KAAO9B,CACtD,CAUA,OARIgB,EAAY,IACdO,IAAWO,EAAa,IAAM,KAAOZ,EAASF,IAG5Cc,IACFP,EAASA,EAAOS,QAAQ,KAAM,MAGzBT,CACT,CAGA,OAUF,SAAgCnB,EAAG0B,GACjC,MAAMK,EAAS/B,EAAEgC,WACXC,EAAMF,EAAO1C,OAGb6C,EAAW,GAGXC,EAAeF,EAFD,EAGpB,IAAIG,EAAM,EAKV,IAJID,EAAe,IACjBD,EAASzB,KAAKkB,OAAOI,EAAO1D,MAAM,EAAG8D,KACrCC,EAAMD,GAEDC,EAAMH,GACXC,EAASzB,KAAKkB,OAAOI,EAAO1D,MAAM+D,EAAKA,EATrB,KAUlBA,GAVkB,EAcpB,MAAM5B,EAAQ,GACd,IAAIa,EAAaa,EAAS7C,OAAS,EAEnC,IAAK,IAAI6B,EAAI,EAAGA,EAAIgB,EAAS7C,OAAQ6B,IAAK,CACxC,MAAMI,EAAUY,EAAShB,GAEzB,GAAgB,IAAZI,EAAe,CACjB,MAAMe,EAAYhB,EAAa,EAAID,EAAaC,EAAY5C,OAAO6C,IAAY,GAE/E,GAAmB,IAAfD,EAEFb,EAAMC,KAAKK,EAASQ,SACf,GAAmB,IAAfD,EAET,GAAgB,IAAZC,EACFd,EAAMC,KAAKb,OACN,CACL,IAAI0C,EAAWxB,EAASQ,IAEpBN,EAAoBM,IAAYL,EAAqBK,MACvDgB,EAAWA,EAASjE,MAAM,GAAG,IAE/BmC,EAAMC,KAAK6B,GACX9B,EAAMC,KAAK4B,EACb,MAGA7B,EAAMC,KAAKK,EAASQ,IACpBd,EAAMC,KAAK4B,EAEf,CAEAhB,GACF,CAGA,IAAIF,EAASX,EAAMK,KADPa,EAAa,IAAM,KAO/B,OAJIA,IACFP,EAASA,EAAOS,QAAQ,KAAM,MAGzBT,CACT,CAzESoB,CAAsBvC,EAAG0B,EAClC,MAyHA,SAAkBc,EAAOC,GACvBA,EC/UK,SAA0BA,GAC/B,QAAgBC,IAAZD,EAAuB,MAAO,CAAA,EAClC,GCCK,SAAwBD,GAC7B,GAAc,OAAVA,GAAmC,iBAAVA,EAAoB,OAAO,EACxD,MAAMG,EAAQC,OAAOC,eAAeL,GACpC,OAAiB,OAAVG,GAAkBA,IAAUC,OAAOE,SAC5C,CDLMC,CAAcN,GAAU,OAAOA,EACnC,MAAM,IAAIO,UACR,mEAAmEP,EAEvE,CDyUYQ,CAAgBR,GAC1B,MAAMrE,WAAEA,EAAUI,YAAEA,EAAWG,YAAEA,GD5U5B,SAA4B6D,GACjC,MAAMU,SAAcV,EAGpB,GAAa,WAATU,EACF,OAAOV,EAAQ,GACX,CAAEpE,YAAY,EAAMI,aAAcgE,GAClC,CAAEpE,YAAY,EAAOI,YAAagE,GAIxC,GAAa,WAATU,EAAmB,CACrB,IAAKvB,OAAOwB,SAASX,GACnB,MAAM,IAAIY,MAAM,8DAElB,OAAIzB,OAAO0B,cAAcb,GAChBA,EAAQ,EACX,CAAEpE,YAAY,EAAMI,YAAaC,QAAQ+D,IACzC,CAAEpE,YAAY,EAAOI,YAAaC,OAAO+D,IAExCtE,EAgBX,SAAyBsE,GACvB,MAAMrE,EAAMqE,EAAMR,WAClB,OAAQ7D,EAAImF,SAAS,MAAQnF,EAAImF,SAAS,KACtC1E,EAAyBT,GACzBA,CACN,CArB8BoF,CAAef,GAC3C,CAGA,GAAa,WAATU,EACF,OAAOhF,EAqBX,SAA0BsE,GACxB,MAAMgB,EAAUhB,EAAMiB,OACtB,GAAuB,IAAnBD,EAAQnE,QAAgBsC,OAAO+B,MAAM/B,OAAO6B,IAC9C,MAAM,IAAIJ,MAAM,2BAA2BZ,MAE7C,OAAQgB,EAAQF,SAAS,MAAQE,EAAQF,SAAS,KAC9C1E,EAAyB4E,GACzBA,CACN,CA7B8BG,CAAgBnB,IAG5C,MAAM,IAAIQ,UACR,oEAAoEE,IAExE,CC6SmDU,CAAkBpB,GAC7Dd,EAAae,EAAQoB,sBAAuB,EAElD,IAAI1C,EAAS,GACb,MAAM2C,EAAMpC,EAAa,IAAM,IAY/B,OAVItD,IACF+C,EA7Ta,QA6TO2C,GAGtB3C,GAAUM,EAAejD,EAAakD,GAElC/C,IACFwC,GAAU2C,EAlUM,UAkUcA,EAvDlC,SAA6BnF,EAAa+C,GACxC,IAAIP,EAAS,GACb,MAAM2C,EAAMpC,EAAa,IAAM,IAG/B,IAAIR,EAAI,EACR,KAAOA,EAAIvC,EAAYU,QAA6B,MAAnBV,EAAYuC,IACvCC,IAAQA,GAAU2C,GACtB3C,GAAUrB,EACVoB,IAIF,MAAMN,EAAYjC,EAAYN,MAAM6C,GAMpC,OALIN,IACEO,IAAQA,GAAU2C,GACtB3C,GAAUM,EAAehD,OAAOmC,GAAYc,IAGvCP,CACT,CAmCwC4C,CAAmBpF,EAAa+C,IAG/DP,CACT"}
|
|
1
|
+
{"version":3,"file":"fr.js","sources":["../../lib/utils/parse-numeric.js","../../lib/languages/fr.js","../../lib/utils/validate-options.js","../../lib/utils/is-plain-object.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 * French language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * French-specific rules:\n * - Vigesimal patterns: 70 = soixante-dix, 80 = quatre-vingts, 90 = quatre-vingt-dix\n * - \"et\" conjunction: vingt et un (21), soixante et onze (71), but NOT quatre-vingt-un\n * - Pluralization: \"cents\" loses 's' when followed by more digits\n * - Long scale with -ard forms: milliard, billiard, trilliard\n * - Omit \"un\" before mille\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\nimport { validateOptions } from '../utils/validate-options.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\nconst ONES = ['', 'un', 'deux', 'trois', 'quatre', 'cinq', 'six', 'sept', 'huit', 'neuf']\nconst TEENS = ['dix', 'onze', 'douze', 'treize', 'quatorze', 'quinze', 'seize', 'dix-sept', 'dix-huit', 'dix-neuf']\nconst TENS = ['', '', 'vingt', 'trente', 'quarante', 'cinquante', 'soixante']\n\n// Scale words (even indices: million, billion, trillion, quadrillion)\nconst SCALES = ['million', 'billion', 'trillion', 'quadrillion']\nconst SCALES_ARD = ['milliard', 'billiard', 'trilliard', 'quadrilliard']\n\nconst THOUSAND = 'mille'\nconst HUNDRED = 'cent'\nconst ZERO = 'zéro'\nconst NEGATIVE = 'moins'\nconst DECIMAL_SEP = 'virgule'\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds segment word for 0-999.\n * Returns object with { word, endsWithCents, endsWithVingts } for pluralization handling.\n */\nfunction buildSegment (n) {\n if (n === 0) return { word: '', endsWithCents: false, endsWithVingts: false }\n\n const tensOnes = n % 100\n const hundreds = Math.floor(n / 100)\n\n const parts = []\n let endsWithCents = false\n let endsWithVingts = false\n\n // Hundreds\n if (hundreds > 0) {\n if (hundreds === 1) {\n if (tensOnes === 0) {\n parts.push(HUNDRED)\n } else {\n parts.push(HUNDRED)\n }\n } else {\n if (tensOnes === 0) {\n // \"deux cents\", \"trois cents\" (with 's')\n parts.push(ONES[hundreds] + ' ' + HUNDRED + 's')\n endsWithCents = true\n } else {\n // \"deux cent\", \"trois cent\" (no 's' when followed by more)\n parts.push(ONES[hundreds] + ' ' + HUNDRED)\n }\n }\n }\n\n // Tens and ones - vigesimal pattern\n if (tensOnes === 0) {\n // Just hundreds, nothing more\n } else if (tensOnes < 10) {\n // Single digit\n parts.push(ONES[tensOnes])\n } else if (tensOnes < 17) {\n // 10-16: regular teens\n parts.push(TEENS[tensOnes - 10])\n } else if (tensOnes < 20) {\n // 17-19: dix-sept, dix-huit, dix-neuf\n parts.push(TEENS[tensOnes - 10])\n } else if (tensOnes < 70) {\n // 20-69: standard tens + ones\n const t = Math.floor(tensOnes / 10)\n const o = tensOnes % 10\n if (o === 0) {\n parts.push(TENS[t])\n } else if (o === 1) {\n // \"et un\" for 21, 31, 41, 51, 61\n parts.push(TENS[t] + ' et ' + ONES[1])\n } else {\n parts.push(TENS[t] + '-' + ONES[o])\n }\n } else if (tensOnes < 80) {\n // 70-79: soixante-dix, soixante et onze, soixante-douze...\n const remainder = tensOnes - 60\n if (remainder === 11) {\n // 71: soixante et onze\n parts.push('soixante et onze')\n } else {\n // 70, 72-79: soixante-dix, soixante-douze...\n parts.push('soixante-' + TEENS[remainder - 10])\n }\n } else if (tensOnes === 80) {\n // 80: quatre-vingts (with 's')\n parts.push('quatre-vingts')\n endsWithVingts = true\n } else if (tensOnes < 100) {\n // 81-99: quatre-vingt-un, quatre-vingt-dix...\n const remainder = tensOnes - 80\n if (remainder < 10) {\n // 81-89\n parts.push('quatre-vingt-' + ONES[remainder])\n } else {\n // 90-99\n parts.push('quatre-vingt-' + TEENS[remainder - 10])\n }\n }\n\n // Join parts with space (between hundreds and rest)\n return { word: parts.join(' '), endsWithCents, endsWithVingts }\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Gets scale word for French long scale with -ard pattern.\n *\n * @param {number} scaleIndex - Scale level (1 = thousand, 2 = million, etc.)\n * @param {bigint} segment - Segment value for pluralization\n * @returns {string} Scale word\n */\nfunction getScaleWord (scaleIndex, segment) {\n if (scaleIndex === 1) return THOUSAND\n\n // Even indices (2, 4, 6, 8): million, billion, trillion, quadrillion\n // Odd indices > 1 (3, 5, 7, 9): milliard, billiard, trilliard, quadrilliard\n if (scaleIndex % 2 === 0) {\n const arrayIndex = (scaleIndex / 2) - 1\n const baseWord = SCALES[arrayIndex]\n if (!baseWord) return ''\n return segment > 1n ? baseWord + 's' : baseWord\n } else {\n const arrayIndex = ((scaleIndex - 1) / 2) - 1\n const ardWord = SCALES_ARD[arrayIndex]\n if (!ardWord) return THOUSAND\n return segment > 1n ? ardWord + 's' : ardWord\n }\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to French words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @param {boolean} withHyphen - Whether to use hyphen separators\n * @returns {string} French words\n */\nfunction integerToWords (n, withHyphen = false) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000\n if (n < 1000n) {\n const { word } = buildSegment(Number(n))\n return withHyphen ? word.replace(/ /g, '-') : word\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 // \"mille\" not \"un mille\"\n result = THOUSAND\n } else {\n // Check if segment ends with \"cents\" or \"vingts\" - need to strip 's' before mille\n const { word: thousandsWord, endsWithCents, endsWithVingts } = buildSegment(thousands)\n let adjustedWord = thousandsWord\n if (endsWithCents || endsWithVingts) {\n adjustedWord = thousandsWord.slice(0, -1) // Remove trailing 's'\n }\n result = adjustedWord + (withHyphen ? '-' : ' ') + THOUSAND\n }\n\n if (remainder > 0) {\n const { word: remainderWord } = buildSegment(remainder)\n result += (withHyphen ? '-' : ' ') + remainderWord\n }\n\n if (withHyphen) {\n result = result.replace(/ /g, '-')\n }\n\n return result\n }\n\n // For numbers >= 1,000,000, use scale decomposition\n return buildLargeNumberWords(n, withHyphen)\n}\n\n/**\n * Builds words for numbers >= 1,000,000.\n *\n * @param {bigint} n - Number >= 1,000,000\n * @param {boolean} withHyphen - Whether to use hyphen separators\n * @returns {string} French words\n */\nfunction buildLargeNumberWords (n, withHyphen) {\n const numStr = n.toString()\n const len = numStr.length\n\n // Build segments of 3 digits from right to left\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 const scaleWord = scaleIndex > 0 ? getScaleWord(scaleIndex, BigInt(segment)) : ''\n const { word: segWords, endsWithCents, endsWithVingts } = buildSegment(segment)\n\n if (scaleIndex === 0) {\n // Units segment\n parts.push(segWords)\n } else if (scaleIndex === 1) {\n // Thousands: \"mille\" not \"un mille\"\n if (segment === 1) {\n parts.push(THOUSAND)\n } else {\n // Strip 's' from cents/vingts before mille\n let adjustedWord = segWords\n if (endsWithCents || endsWithVingts) {\n adjustedWord = segWords.slice(0, -1)\n }\n parts.push(adjustedWord)\n parts.push(scaleWord)\n }\n } else {\n // Million and above\n parts.push(segWords)\n parts.push(scaleWord)\n }\n }\n\n scaleIndex--\n }\n\n const sep = withHyphen ? '-' : ' '\n let result = parts.join(sep)\n\n if (withHyphen) {\n result = result.replace(/ /g, '-')\n }\n\n return result\n}\n\n/**\n * Converts decimal digits to French words.\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @param {boolean} withHyphen - Whether to use hyphen separators\n * @returns {string} French words for decimal part\n */\nfunction decimalPartToWords (decimalPart, withHyphen) {\n let result = ''\n const sep = withHyphen ? '-' : ' '\n\n // Handle leading zeros\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += sep\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 += sep\n result += integerToWords(BigInt(remainder), withHyphen)\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to French 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 * @param {Object} [options] - Optional configuration\n * @param {boolean} [options.withHyphenSeparator=false] - Use hyphens between all words\n * @returns {string} The number in French 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) // 'vingt et un'\n * toWords(80) // 'quatre-vingts'\n * toWords(1000000) // 'un million'\n */\nfunction toWords (value, options) {\n options = validateOptions(options)\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n const withHyphen = options.withHyphenSeparator || false\n\n let result = ''\n const sep = withHyphen ? '-' : ' '\n\n if (isNegative) {\n result = NEGATIVE + sep\n }\n\n result += integerToWords(integerPart, withHyphen)\n\n if (decimalPart) {\n result += sep + DECIMAL_SEP + sep + decimalPartToWords(decimalPart, withHyphen)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n","import { isPlainObject } from './is-plain-object.js'\n\n/**\n * Validates and normalizes the options parameter.\n *\n * @param {*} options The options value to validate\n * @returns {Object} A valid options object (empty object if undefined)\n * @throws {TypeError} If options is not undefined or a plain object\n */\nexport function validateOptions (options) {\n if (options === undefined) return {}\n if (isPlainObject(options)) return options\n throw new TypeError(\n `Invalid options: expected plain object or undefined, got ${typeof options}`\n )\n}\n","/**\n * Checks if a value is a plain object (not null, array, or other object types).\n *\n * A plain object is one created by:\n * - Object literal: `{}`\n * - Object.create(null): null-prototype object\n *\n * This excludes arrays, class instances, Map, Set, and other object types.\n *\n * @param {*} value Value to check\n * @returns {boolean} True if value is a plain object\n */\nexport function isPlainObject (value) {\n if (value === null || typeof value !== 'object') return false\n const proto = Object.getPrototypeOf(value)\n return proto === null || proto === Object.prototype\n}\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","SCALES","SCALES_ARD","THOUSAND","HUNDRED","ZERO","buildSegment","n","word","endsWithCents","endsWithVingts","tensOnes","hundreds","Math","floor","parts","push","t","o","remainder","join","getScaleWord","scaleIndex","segment","baseWord","ardWord","integerToWords","withHyphen","Number","replace","thousands","result","thousandsWord","adjustedWord","remainderWord","numStr","toString","len","segments","remainderLen","pos","i","scaleWord","segWords","buildLargeNumberWords","value","options","undefined","proto","Object","getPrototypeOf","prototype","isPlainObject","TypeError","validateOptions","type","isFinite","Error","isSafeInteger","includes","numberToString","trimmed","trim","isNaN","normalizeString","parseNumericValue","withHyphenSeparator","sep","decimalPartToWords"],"mappings":"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"}
|
package/dist/languages/gu.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
/*! n2words/gu v3.
|
|
2
|
-
var e,
|
|
1
|
+
/*! n2words/gu 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=["","હજાર","લાખ","કરોડ","અબજ","ખરબ","નીલ","પદ્મ","શંખ"];function o(e){if(0===e)return"";if(e<100)return i[e];const n=Math.trunc(e/100),t=e%100;return 0===t?i[n]+" સો":i[n]+" સો "+i[t]}e.gu=function(e){const{isNegative:u,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 u&&(a="ઋણ "),a+=function(e){if(0n===e)return r;if(e<1000n)return o(Number(e));const n=[];n.push(Number(e%1000n));let t=e/1000n;for(;t>0n;)n.push(Number(t%100n)),t/=100n;const u=[];for(let e=n.length-1;e>=0;e--){const t=n[e];0!==t&&(u.push(0===e?o(t):i[t]),e>0&&s[e]&&u.push(s[e]))}return u.join(" ")}(c),f&&(a+=" દશાંશ "+function(e){const n=[];for(const t of e){const e=parseInt(t,10);n.push(0===e?r:i[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=gu.js.map
|
package/dist/languages/gu.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gu.js","sources":["../../lib/utils/parse-numeric.js","../../lib/languages/gu.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 * Gujarati language converter - Functional Implementation\n *\n * Self-contained converter for South Asian numbering.\n *\n * Key features:\n * - Indian numbering system (હજાર, લાખ, કરોડ)\n * - Gujarati 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 Gujarati words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Gujarati 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","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":"gu.js","sources":["../../lib/utils/parse-numeric.js","../../lib/languages/gu.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 * Gujarati 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 * - Gujarati 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 Gujarati 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} Gujarati 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 Gujarati words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Gujarati 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","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/ha.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
/*! n2words/ha v3.
|
|
2
|
-
var e
|
|
1
|
+
/*! n2words/ha v3.1.0 | MIT License | github.com/forzagreen/n2words */
|
|
2
|
+
var t,e;t=this,e=function(t){"use strict";function e(t){const e="-"===t[0];e&&(t=t.slice(1));const n=t.indexOf(".");if(-1===n)return{isNegative:e,integerPart:BigInt(t)};const i=t.slice(0,n)||"0",r=t.slice(n+1);return{isNegative:e,integerPart:BigInt(i),decimalPart:r}}function n(t){const[e,n]=t.toLowerCase().split("e"),i=parseInt(n,10),r=e.indexOf("."),s=-1===r?e:e.slice(0,r)+e.slice(r+1),a=(-1===r?e.length:r)+i;return a>=s.length?s+"0".repeat(a-s.length):a<=0?"0."+"0".repeat(-a)+s:s.slice(0,a)+"."+s.slice(a)}const i=["","ɗaya","biyu","uku","huɗu","biyar","shida","bakwai","takwas","tara"],r=["goma","sha ɗaya","sha biyu","sha uku","sha huɗu","sha biyar","sha shida","sha bakwai","sha takwas","sha tara"],s=["","","ashirin","talatin","arba'in","hamsin","sittin","saba'in","tamanin","tis'in"],a="ɗari",o="dubu",u="sifiri",c=["",o,"miliyan","biliyan"];function l(t){if(0===t)return"";const e=t%10,n=Math.floor(t/10)%10,o=Math.floor(t/100),u=[];o>0&&u.push(1===o?a:i[o]+" "+a);const c=t%100;return 0===c||u.push(c<10?o>0?"da "+i[e]:i[e]:c<20?r[e]:0===e?s[n]:s[n]+" da "+i[e]),u.join(" ")}function f(t){return i.slice(1).includes(t)}t.ha=function(t){const{isNegative:r,integerPart:s,decimalPart:h}=function(t){const i=typeof t;if("bigint"===i)return t<0n?{isNegative:!0,integerPart:-t}:{isNegative:!1,integerPart:t};if("number"===i){if(!Number.isFinite(t))throw new Error("Number must be finite (NaN and Infinity are not supported)");return Number.isSafeInteger(t)?t<0?{isNegative:!0,integerPart:BigInt(-t)}:{isNegative:!1,integerPart:BigInt(t)}:e(function(t){const e=t.toString();return e.includes("e")||e.includes("E")?n(e):e}(t))}if("string"===i)return e(function(t){const e=t.trim();if(0===e.length||Number.isNaN(Number(e)))throw new Error(`Invalid number format: "${t}"`);return e.includes("e")||e.includes("E")?n(e):e}(t));throw new TypeError(`Invalid value type: expected number, string, or bigint, received ${i}`)}(t);let g="";return r&&(g="babu "),g+=0n===(d=s)?u:d<1000n?l(Number(d)):function(t){const e=t.toString(),n=e.length,i=[],r=n%3;let s=0;for(r>0&&(i.push(Number(e.slice(0,r))),s=r);s<n;)i.push(Number(e.slice(s,s+3))),s+=3;const u=[];let h=i.length-1;for(let t=0;t<i.length;t++){const e=i[t];if(0!==e){const t=c[h]||"";0===h?u.push(l(e)):(u.push(l(e)),u.push(t))}h--}const g=[];for(let t=0;t<u.length;t++){const e=u[t],n=u[t+1];("ɗaya"!==e||!n||n!==a&&n!==o)&&g.push(e)}const d=[];for(let t=0;t<g.length;t++){const e=g[t],n=t>0?g[t-1]:null;n&&f(e)&&(n===o||n===a||c.includes(n))?d.push(" da "):t>0&&d.push(" "),d.push(e)}return d.join("")}(d),h&&(g+=" digo "+function(t){const e=[];for(const n of t){const t=parseInt(n,10);e.push(0===t?u:i[t])}return e.join(" ")}(h)),g;var d}},"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).n2words=t.n2words||{});
|
|
3
3
|
//# sourceMappingURL=ha.js.map
|
package/dist/languages/ha.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ha.js","sources":["../../lib/utils/parse-numeric.js","../../lib/languages/ha.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 * Hausa language converter - Functional Implementation\n *\n * Self-contained converter with precomputed lookup tables.\n *\n * Key features:\n * - Authentic Boko orthography with ɗ (hooked d) and ' (glottal stop)\n * - Teens with \"sha\" prefix (sha ɗaya = 11)\n * - Compound numbers with \"da\" connector (ashirin da ɗaya = 21)\n * - Arabic loanwords for tens (ashirin, talatin, arba'in, etc.)\n * - Reversed multiplier order: \"biyu ɗari\" (200), \"biyu dubu\" (2000)\n * - Implicit one before ɗari and dubu\n * - Per-digit decimal reading\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ONES = ['', 'ɗaya', 'biyu', 'uku', 'huɗu', 'biyar', 'shida', 'bakwai', 'takwas', 'tara']\nconst TEENS = ['goma', 'sha ɗaya', 'sha biyu', 'sha uku', 'sha huɗu', 'sha biyar', 'sha shida', 'sha bakwai', 'sha takwas', 'sha tara']\n// Arabic loanwords for tens\nconst TENS = ['', '', 'ashirin', 'talatin', \"arba'in\", 'hamsin', 'sittin', \"saba'in\", 'tamanin', \"tis'in\"]\n\nconst HUNDRED = 'ɗari'\nconst THOUSAND = 'dubu'\n\nconst ZERO = 'sifiri'\nconst NEGATIVE = 'babu'\nconst DECIMAL_SEP = 'digo'\n\n// Short scale\nconst SCALE_WORDS = ['', THOUSAND, 'miliyan', 'biliyan']\n\n// ============================================================================\n// Precomputed Lookup Table\n// ============================================================================\n\n/**\n * Build segment for 0-999 with Hausa patterns.\n * Hausa uses reversed order for hundreds: \"biyu ɗari\" (200)\n * And \"da\" connector for ones: \"ashirin da ɗaya\" (21)\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tensDigit = Math.floor(n / 10) % 10\n const hundredsDigit = Math.floor(n / 100)\n\n const parts = []\n\n // Hundreds: implicit one, or \"biyu ɗari\" (reversed order)\n if (hundredsDigit > 0) {\n if (hundredsDigit === 1) {\n parts.push(HUNDRED)\n } else {\n // Reversed: multiplier + hundredWord\n parts.push(ONES[hundredsDigit] + ' ' + HUNDRED)\n }\n }\n\n // Tens and ones\n const tensOnes = n % 100\n\n if (tensOnes === 0) {\n // Just hundreds\n } else if (tensOnes < 10) {\n // Single digit - with \"da\" connector if after hundreds\n if (hundredsDigit > 0) {\n parts.push('da ' + ONES[ones])\n } else {\n parts.push(ONES[ones])\n }\n } else if (tensOnes < 20) {\n // Teens (10-19): \"sha X\"\n parts.push(TEENS[ones])\n } else if (ones === 0) {\n // Even tens (20, 30, 40, etc.)\n parts.push(TENS[tensDigit])\n } else {\n // Tens + ones with \"da\" connector\n parts.push(TENS[tensDigit] + ' da ' + ONES[ones])\n }\n\n return parts.join(' ')\n}\n\nconst SEGMENTS = new Array(1000)\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS[i] = buildSegment(i)\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n if (n < 1000n) {\n return SEGMENTS[Number(n)]\n }\n\n return buildLargeNumberWords(n)\n}\n\n/**\n * Checks if a word is a single digit (1-9).\n */\nfunction isSingleDigit (word) {\n return ONES.slice(1).includes(word)\n}\n\nfunction buildLargeNumberWords (n) {\n const numStr = n.toString()\n const len = numStr.length\n\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 // Build raw parts (segment words and scale words)\n const rawParts = []\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 const scaleWord = SCALE_WORDS[scaleIndex] || ''\n\n if (scaleIndex === 0) {\n rawParts.push(SEGMENTS[segment])\n } else {\n rawParts.push(SEGMENTS[segment])\n rawParts.push(scaleWord)\n }\n }\n\n scaleIndex--\n }\n\n // Filter out implicit \"ɗaya\" before ɗari or dubu\n const filtered = []\n for (let i = 0; i < rawParts.length; i++) {\n const part = rawParts[i]\n const nextPart = rawParts[i + 1]\n\n // Skip \"ɗaya\" before ɗari or dubu (implicit one)\n if (part === 'ɗaya' && nextPart && (nextPart === HUNDRED || nextPart === THOUSAND)) {\n continue\n }\n\n filtered.push(part)\n }\n\n // Join with correct separators\n const result = []\n for (let i = 0; i < filtered.length; i++) {\n const part = filtered[i]\n const prevPart = i > 0 ? filtered[i - 1] : null\n\n // Determine if we need \"da\" connector\n // Use \"da\" when current is a single digit following a scale word\n if (prevPart && isSingleDigit(part) &&\n (prevPart === THOUSAND || prevPart === HUNDRED ||\n SCALE_WORDS.includes(prevPart))) {\n result.push(' da ')\n } else if (i > 0) {\n result.push(' ')\n }\n\n result.push(part)\n }\n\n return result.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 : ONES[d])\n }\n return digits.join(' ')\n}\n\n/**\n * Converts a numeric value to Hausa words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Hausa 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","ONES","TEENS","TENS","HUNDRED","THOUSAND","ZERO","SCALE_WORDS","buildSegment","n","ones","tensDigit","Math","floor","hundredsDigit","parts","push","tensOnes","join","SEGMENTS","Array","i","isSingleDigit","word","includes","value","type","Number","isFinite","Error","isSafeInteger","toString","numberToString","trimmed","trim","isNaN","normalizeString","TypeError","parseNumericValue","result","NEGATIVE","numStr","len","segments","remainderLen","pos","rawParts","scaleIndex","segment","scaleWord","filtered","part","nextPart","prevPart","buildLargeNumberWords","char","d","decimalPartToWords"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ha.js","sources":["../../lib/utils/parse-numeric.js","../../lib/languages/ha.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 * Hausa language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Authentic Boko orthography with ɗ (hooked d) and ' (glottal stop)\n * - Teens with \"sha\" prefix (sha ɗaya = 11)\n * - Compound numbers with \"da\" connector (ashirin da ɗaya = 21)\n * - Arabic loanwords for tens (ashirin, talatin, arba'in, etc.)\n * - Reversed multiplier order: \"biyu ɗari\" (200), \"biyu dubu\" (2000)\n * - Implicit one before ɗari and dubu\n * - Per-digit decimal reading\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ONES = ['', 'ɗaya', 'biyu', 'uku', 'huɗu', 'biyar', 'shida', 'bakwai', 'takwas', 'tara']\nconst TEENS = ['goma', 'sha ɗaya', 'sha biyu', 'sha uku', 'sha huɗu', 'sha biyar', 'sha shida', 'sha bakwai', 'sha takwas', 'sha tara']\n// Arabic loanwords for tens\nconst TENS = ['', '', 'ashirin', 'talatin', \"arba'in\", 'hamsin', 'sittin', \"saba'in\", 'tamanin', \"tis'in\"]\n\nconst HUNDRED = 'ɗari'\nconst THOUSAND = 'dubu'\n\nconst ZERO = 'sifiri'\nconst NEGATIVE = 'babu'\nconst DECIMAL_SEP = 'digo'\n\n// Short scale\nconst SCALE_WORDS = ['', THOUSAND, 'miliyan', 'biliyan']\n\n// ============================================================================\n// Precomputed Lookup Table\n// ============================================================================\n\n/**\n * Build segment for 0-999 with Hausa patterns.\n * Hausa uses reversed order for hundreds: \"biyu ɗari\" (200)\n * And \"da\" connector for ones: \"ashirin da ɗaya\" (21)\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tensDigit = Math.floor(n / 10) % 10\n const hundredsDigit = Math.floor(n / 100)\n\n const parts = []\n\n // Hundreds: implicit one, or \"biyu ɗari\" (reversed order)\n if (hundredsDigit > 0) {\n if (hundredsDigit === 1) {\n parts.push(HUNDRED)\n } else {\n // Reversed: multiplier + hundredWord\n parts.push(ONES[hundredsDigit] + ' ' + HUNDRED)\n }\n }\n\n // Tens and ones\n const tensOnes = n % 100\n\n if (tensOnes === 0) {\n // Just hundreds\n } else if (tensOnes < 10) {\n // Single digit - with \"da\" connector if after hundreds\n if (hundredsDigit > 0) {\n parts.push('da ' + ONES[ones])\n } else {\n parts.push(ONES[ones])\n }\n } else if (tensOnes < 20) {\n // Teens (10-19): \"sha X\"\n parts.push(TEENS[ones])\n } else if (ones === 0) {\n // Even tens (20, 30, 40, etc.)\n parts.push(TENS[tensDigit])\n } else {\n // Tens + ones with \"da\" connector\n parts.push(TENS[tensDigit] + ' da ' + ONES[ones])\n }\n\n return parts.join(' ')\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n if (n < 1000n) {\n return buildSegment(Number(n))\n }\n\n return buildLargeNumberWords(n)\n}\n\n/**\n * Checks if a word is a single digit (1-9).\n */\nfunction isSingleDigit (word) {\n return ONES.slice(1).includes(word)\n}\n\nfunction buildLargeNumberWords (n) {\n const numStr = n.toString()\n const len = numStr.length\n\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 // Build raw parts (segment words and scale words)\n const rawParts = []\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 const scaleWord = SCALE_WORDS[scaleIndex] || ''\n\n if (scaleIndex === 0) {\n rawParts.push(buildSegment(segment))\n } else {\n rawParts.push(buildSegment(segment))\n rawParts.push(scaleWord)\n }\n }\n\n scaleIndex--\n }\n\n // Filter out implicit \"ɗaya\" before ɗari or dubu\n const filtered = []\n for (let i = 0; i < rawParts.length; i++) {\n const part = rawParts[i]\n const nextPart = rawParts[i + 1]\n\n // Skip \"ɗaya\" before ɗari or dubu (implicit one)\n if (part === 'ɗaya' && nextPart && (nextPart === HUNDRED || nextPart === THOUSAND)) {\n continue\n }\n\n filtered.push(part)\n }\n\n // Join with correct separators\n const result = []\n for (let i = 0; i < filtered.length; i++) {\n const part = filtered[i]\n const prevPart = i > 0 ? filtered[i - 1] : null\n\n // Determine if we need \"da\" connector\n // Use \"da\" when current is a single digit following a scale word\n if (prevPart && isSingleDigit(part) &&\n (prevPart === THOUSAND || prevPart === HUNDRED ||\n SCALE_WORDS.includes(prevPart))) {\n result.push(' da ')\n } else if (i > 0) {\n result.push(' ')\n }\n\n result.push(part)\n }\n\n return result.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 : ONES[d])\n }\n return digits.join(' ')\n}\n\n/**\n * Converts a numeric value to Hausa words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Hausa 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","ONES","TEENS","TENS","HUNDRED","THOUSAND","ZERO","SCALE_WORDS","buildSegment","n","ones","tensDigit","Math","floor","hundredsDigit","parts","push","tensOnes","join","isSingleDigit","word","includes","value","type","Number","isFinite","Error","isSafeInteger","toString","numberToString","trimmed","trim","isNaN","normalizeString","TypeError","parseNumericValue","result","NEGATIVE","numStr","len","segments","remainderLen","pos","rawParts","scaleIndex","i","segment","scaleWord","filtered","part","nextPart","prevPart","buildLargeNumberWords","char","d","decimalPartToWords"],"mappings":";0CAwEA,SAASA,EAAoBC,GAC3B,MAAMC,EAAwB,MAAXD,EAAI,GACnBC,IAAYD,EAAMA,EAAIE,MAAM,IAEhC,MAAMC,EAAWH,EAAII,QAAQ,KAC7B,IAAiB,IAAbD,EACF,MAAO,CAAEF,aAAYI,YAAaC,OAAON,IAG3C,MAAMO,EAAaP,EAAIE,MAAM,EAAGC,IAAa,IACvCK,EAAcR,EAAIE,MAAMC,EAAW,GACzC,MAAO,CAAEF,aAAYI,YAAaC,OAAOC,GAAaC,cACxD,CAKA,SAASC,EAA0BT,GACjC,MAAOU,EAAUC,GAAUX,EAAIY,cAAcC,MAAM,KAC7CC,EAAMC,SAASJ,EAAQ,IAEvBR,EAAWO,EAASN,QAAQ,KAC5BY,OAASb,EACXO,EACAA,EAASR,MAAM,EAAGC,GAAYO,EAASR,MAAMC,EAAW,GAEtDc,IAD6B,IAAbd,EAAkBO,EAASQ,OAASf,GACnBW,EAEvC,OAAIG,GAAkBD,EAAOE,OACpBF,EAAS,IAAIG,OAAOF,EAAiBD,EAAOE,QAEjDD,GAAkB,EACb,KAAO,IAAIE,QAAQF,GAAkBD,EAEvCA,EAAOd,MAAM,EAAGe,GAAkB,IAAMD,EAAOd,MAAMe,EAC9D,CCtFA,MAAMG,EAAO,CAAC,GAAI,OAAQ,OAAQ,MAAO,OAAQ,QAAS,QAAS,SAAU,SAAU,QACjFC,EAAQ,CAAC,OAAQ,WAAY,WAAY,UAAW,WAAY,YAAa,YAAa,aAAc,aAAc,YAEtHC,EAAO,CAAC,GAAI,GAAI,UAAW,UAAW,UAAW,SAAU,SAAU,UAAW,UAAW,UAE3FC,EAAU,OACVC,EAAW,OAEXC,EAAO,SAKPC,EAAc,CAAC,GAAIF,EAAU,UAAW,WAW9C,SAASG,EAAcC,GACrB,GAAU,IAANA,EAAS,MAAO,GAEpB,MAAMC,EAAOD,EAAI,GACXE,EAAYC,KAAKC,MAAMJ,EAAI,IAAM,GACjCK,EAAgBF,KAAKC,MAAMJ,EAAI,KAE/BM,EAAQ,GAGVD,EAAgB,GAEhBC,EAAMC,KADc,IAAlBF,EACSV,EAGAH,EAAKa,GAAiB,IAAMV,GAK3C,MAAMa,EAAWR,EAAI,IAsBrB,OApBiB,IAAbQ,GAKAF,EAAMC,KAHCC,EAAW,GAEhBH,EAAgB,EACP,MAAQb,EAAKS,GAEbT,EAAKS,GAETO,EAAW,GAETf,EAAMQ,GACC,IAATA,EAEEP,EAAKQ,GAGLR,EAAKQ,GAAa,OAASV,EAAKS,IAGtCK,EAAMG,KAAK,IACpB,CAmBA,SAASC,EAAeC,GACtB,OAAOnB,EAAKlB,MAAM,GAAGsC,SAASD,EAChC,MA6FA,SAAkBE,GAChB,MAAMxC,WAAEA,EAAUI,YAAEA,EAAWG,YAAEA,GD9L5B,SAA4BiC,GACjC,MAAMC,SAAcD,EAGpB,GAAa,WAATC,EACF,OAAOD,EAAQ,GACX,CAAExC,YAAY,EAAMI,aAAcoC,GAClC,CAAExC,YAAY,EAAOI,YAAaoC,GAIxC,GAAa,WAATC,EAAmB,CACrB,IAAKC,OAAOC,SAASH,GACnB,MAAM,IAAII,MAAM,8DAElB,OAAIF,OAAOG,cAAcL,GAChBA,EAAQ,EACX,CAAExC,YAAY,EAAMI,YAAaC,QAAQmC,IACzC,CAAExC,YAAY,EAAOI,YAAaC,OAAOmC,IAExC1C,EAgBX,SAAyB0C,GACvB,MAAMzC,EAAMyC,EAAMM,WAClB,OAAQ/C,EAAIwC,SAAS,MAAQxC,EAAIwC,SAAS,KACtC/B,EAAyBT,GACzBA,CACN,CArB8BgD,CAAeP,GAC3C,CAGA,GAAa,WAATC,EACF,OAAO3C,EAqBX,SAA0B0C,GACxB,MAAMQ,EAAUR,EAAMS,OACtB,GAAuB,IAAnBD,EAAQ/B,QAAgByB,OAAOQ,MAAMR,OAAOM,IAC9C,MAAM,IAAIJ,MAAM,2BAA2BJ,MAE7C,OAAQQ,EAAQT,SAAS,MAAQS,EAAQT,SAAS,KAC9C/B,EAAyBwC,GACzBA,CACN,CA7B8BG,CAAgBX,IAG5C,MAAM,IAAIY,UACR,oEAAoEX,IAExE,CC+JmDY,CAAkBb,GAEnE,IAAIc,EAAS,GAYb,OAVItD,IACFsD,EAASC,SAGXD,GApHU,MADa3B,EAqHEvB,GApHJoB,EAEjBG,EAAI,MACCD,EAAagB,OAAOf,IAa/B,SAAgCA,GAC9B,MAAM6B,EAAS7B,EAAEmB,WACXW,EAAMD,EAAOvC,OAEbyC,EAAW,GAGXC,EAAeF,EAFD,EAGpB,IAAIG,EAAM,EAKV,IAJID,EAAe,IACjBD,EAASxB,KAAKQ,OAAOc,EAAOvD,MAAM,EAAG0D,KACrCC,EAAMD,GAEDC,EAAMH,GACXC,EAASxB,KAAKQ,OAAOc,EAAOvD,MAAM2D,EAAKA,EATrB,KAUlBA,GAVkB,EAcpB,MAAMC,EAAW,GACjB,IAAIC,EAAaJ,EAASzC,OAAS,EAEnC,IAAK,IAAI8C,EAAI,EAAGA,EAAIL,EAASzC,OAAQ8C,IAAK,CACxC,MAAMC,EAAUN,EAASK,GAEzB,GAAgB,IAAZC,EAAe,CACjB,MAAMC,EAAYxC,EAAYqC,IAAe,GAE1B,IAAfA,EACFD,EAAS3B,KAAKR,EAAasC,KAE3BH,EAAS3B,KAAKR,EAAasC,IAC3BH,EAAS3B,KAAK+B,GAElB,CAEAH,GACF,CAGA,MAAMI,EAAW,GACjB,IAAK,IAAIH,EAAI,EAAGA,EAAIF,EAAS5C,OAAQ8C,IAAK,CACxC,MAAMI,EAAON,EAASE,GAChBK,EAAWP,EAASE,EAAI,IAGjB,SAATI,IAAmBC,GAAaA,IAAa9C,GAAW8C,IAAa7C,IAIzE2C,EAAShC,KAAKiC,EAChB,CAGA,MAAMb,EAAS,GACf,IAAK,IAAIS,EAAI,EAAGA,EAAIG,EAASjD,OAAQ8C,IAAK,CACxC,MAAMI,EAAOD,EAASH,GAChBM,EAAWN,EAAI,EAAIG,EAASH,EAAI,GAAK,KAIvCM,GAAYhC,EAAc8B,KACzBE,IAAa9C,GAAY8C,IAAa/C,GACtCG,EAAYc,SAAS8B,IACxBf,EAAOpB,KAAK,QACH6B,EAAI,GACbT,EAAOpB,KAAK,KAGdoB,EAAOpB,KAAKiC,EACd,CAEA,OAAOb,EAAOlB,KAAK,GACrB,CAnFSkC,CAAsB3C,GAgHzBpB,IACF+C,GAAU,SA5Bd,SAA6B/C,GAE3B,MAAMQ,EAAS,GACf,IAAK,MAAMwD,KAAQhE,EAAa,CAC9B,MAAMiE,EAAI1D,SAASyD,EAAM,IACzBxD,EAAOmB,KAAW,IAANsC,EAAUhD,EAAOL,EAAKqD,GACpC,CACA,OAAOzD,EAAOqB,KAAK,IACrB,CAoBwCqC,CAAmBlE,IAGlD+C,EA3HT,IAAyB3B,CA4HzB"}
|
package/dist/languages/hbo.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
/*! n2words/hbo 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=["עשרה","אחד עשר","שנים עשר","שלשה עשר","ארבעה עשר","חמשה עשר","ששה עשר","שבעה עשר","שמונה עשר","תשעה עשר"],o=["","אלף","אלפיים","שלשה אלפים","ארבעה אלפים","חמשה אלפים","ששה אלפים","שבעה אלפים","שמונה אלפים","תשעה אלפים"],s=["","אחת","שתים","שלש","ארבע","חמש","שש","שבע","שמונה","תשע"],u=["עשר","אחת עשרה","שתים עשרה","שלש עשרה","ארבע עשרה","חמש עשרה","שש עשרה","שבע עשרה","שמונה עשרה","תשע עשרה"],f=["","אלף","אלפיים","שלשת אלפים","ארבעת אלפים","חמשת אלפים","ששת אלפים","שבעת אלפים","שמונת אלפים","תשעת אלפים"],c=["","","עשרים","שלשים","ארבעים","חמישים","ששים","שבעים","שמונים","תשעים"],l=["","מאה","מאתיים","שלשה מאות","ארבעה מאות","חמשה מאות","ששה מאות","שבעה מאות","שמונה מאות","תשעה מאות"],a=["","מאה","מאתיים","שלש מאות","ארבע מאות","חמש מאות","שש מאות","שבע מאות","שמונה מאות","תשע מאות"],g=["","אלף","מיליון","מיליארד","טריליון","קוודרליון","קווינטיליון"],d=["","אלפים","מיליונים","מיליארדים","טריליונים","קוודרליונים","קווינטיליונים"];function p(e,t,n,r,i){if(0===e)return"";const o=e%10,s=Math.floor(e/10)%10,u=Math.floor(e/100);let f="";if(u>0&&(f=i[u]),1===s){const e=r[o];f?f+=" "+t+e:f=e}else s>=2&&(f?f+=" "+t+c[s]:f=c[s]),o>0&&(f?f+=" "+t+n[o]:f=n[o]);return f}function b(e,t,n,r,i){if(0===e)return"";const o=e%10,s=Math.floor(e/10)%10,u=Math.floor(e/100);let f="";return u>0&&(f=i[u]),1===s?f?f+=" "+r[o]:f=r[o]:(s>=2&&(f?f+=" "+c[s]:f=c[s]),o>0&&(f?f+=" "+t+n[o]:f=n[o])),f}
|
|
1
|
+
/*! n2words/hbo 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=["עשרה","אחד עשר","שנים עשר","שלשה עשר","ארבעה עשר","חמשה עשר","ששה עשר","שבעה עשר","שמונה עשר","תשעה עשר"],o=["","אלף","אלפיים","שלשה אלפים","ארבעה אלפים","חמשה אלפים","ששה אלפים","שבעה אלפים","שמונה אלפים","תשעה אלפים"],s=["","אחת","שתים","שלש","ארבע","חמש","שש","שבע","שמונה","תשע"],u=["עשר","אחת עשרה","שתים עשרה","שלש עשרה","ארבע עשרה","חמש עשרה","שש עשרה","שבע עשרה","שמונה עשרה","תשע עשרה"],f=["","אלף","אלפיים","שלשת אלפים","ארבעת אלפים","חמשת אלפים","ששת אלפים","שבעת אלפים","שמונת אלפים","תשעת אלפים"],c=["","","עשרים","שלשים","ארבעים","חמישים","ששים","שבעים","שמונים","תשעים"],l=["","מאה","מאתיים","שלשה מאות","ארבעה מאות","חמשה מאות","ששה מאות","שבעה מאות","שמונה מאות","תשעה מאות"],a=["","מאה","מאתיים","שלש מאות","ארבע מאות","חמש מאות","שש מאות","שבע מאות","שמונה מאות","תשע מאות"],g=["","אלף","מיליון","מיליארד","טריליון","קוודרליון","קווינטיליון"],d=["","אלפים","מיליונים","מיליארדים","טריליונים","קוודרליונים","קווינטיליונים"];function p(e,t,n,r,i){if(0===e)return"";const o=e%10,s=Math.floor(e/10)%10,u=Math.floor(e/100);let f="";if(u>0&&(f=i[u]),1===s){const e=r[o];f?f+=" "+t+e:f=e}else s>=2&&(f?f+=" "+t+c[s]:f=c[s]),o>0&&(f?f+=" "+t+n[o]:f=n[o]);return f}function b(e,t,n,r,i){if(0===e)return"";const o=e%10,s=Math.floor(e/10)%10,u=Math.floor(e/100);let f="";return u>0&&(f=i[u]),1===s?f?f+=" "+r[o]:f=r[o]:(s>=2&&(f?f+=" "+c[s]:f=c[s]),o>0&&(f?f+=" "+t+n[o]:f=n[o])),f}e.hbo=function(e,c){c=function(e){if(void 0===e)return{};if(function(e){if(null===e||"object"!=typeof e)return!1;const t=Object.getPrototypeOf(e);return null===t||t===Object.prototype}(e))return e;throw new TypeError("Invalid options: expected plain object or undefined, got "+typeof e)}(c);const{isNegative:m,integerPart:h,decimalPart:N}=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 v="";return m&&(v="מינוס "),v+=function(e,t){if(0n===e)return"אפס";const n=t.andWord??"ו",c="feminine"===(t.gender||"masculine"),m=c?s:r,h=c?u:i,N=c?f:o,v=c?a:l;if(e<1000n)return b(Number(e),n,m,h,v);const y=[];let w=e;for(;w>0n;)y.push(Number(w%1000n)),w/=1000n;let I="";for(let e=y.length-1;e>=0;e--){const t=y[e];if(0!==t)if(0===e){const e=b(t,n,m,h,v);I?I+=t<=9?" "+n+e:" "+e:I=e}else 1===e?t<=9?(I&&(I+=" "),I+=N[t]):(I&&(I+=" "),I+=p(t,n,m,h,v)+" "+g[1]):1===t?(I&&(I+=" "),I+=g[e]):(I&&(I+=" "),I+=p(t,n,m,h,v)+" "+d[e])}return I}(h,c),N&&(v+=" נקודה "+function(e,t){const n="feminine"===(t.gender||"masculine")?s:r;let i="";for(let t=0;t<e.length;t++){const r=parseInt(e[t],10);i&&(i+=" "),i+=0===r?"אפס":n[r]}return i}(N,c)),v}},"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=hbo.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hbo.js","sources":["../../lib/utils/parse-numeric.js","../../lib/languages/hbo.js","../../lib/utils/validate-options.js","../../lib/utils/is-plain-object.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 * Biblical Hebrew language converter - Functional Implementation\n *\n * Self-contained converter with segment-based decomposition.\n *\n * Key features:\n * - Gender agreement (masculine default, feminine via option)\n * - Dual forms for 2, 200, 2000\n * - Special 1-9 thousands construct state\n * - \"ו\" (ve) conjunction rules vary by position\n * - Per-digit decimal reading\n *\n * Optimization: Precomputed segment lookup tables for both genders.\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\nimport { validateOptions } from '../utils/validate-options.js'\n\n// ============================================================================\n// Vocabulary (arrays for indexed access - faster than object property lookup)\n// ============================================================================\n\n// Masculine forms (default in Biblical Hebrew) - index 0 unused\nconst ONES_MASC = ['', 'אחד', 'שניים', 'שלשה', 'ארבעה', 'חמשה', 'ששה', 'שבעה', 'שמונה', 'תשעה']\nconst TEENS_MASC = ['עשרה', 'אחד עשר', 'שנים עשר', 'שלשה עשר', 'ארבעה עשר', 'חמשה עשר', 'ששה עשר', 'שבעה עשר', 'שמונה עשר', 'תשעה עשר']\nconst THOUSANDS_MASC = ['', 'אלף', 'אלפיים', 'שלשה אלפים', 'ארבעה אלפים', 'חמשה אלפים', 'ששה אלפים', 'שבעה אלפים', 'שמונה אלפים', 'תשעה אלפים']\n\n// Feminine forms - index 0 unused\nconst ONES_FEM = ['', 'אחת', 'שתים', 'שלש', 'ארבע', 'חמש', 'שש', 'שבע', 'שמונה', 'תשע']\nconst TEENS_FEM = ['עשר', 'אחת עשרה', 'שתים עשרה', 'שלש עשרה', 'ארבע עשרה', 'חמש עשרה', 'שש עשרה', 'שבע עשרה', 'שמונה עשרה', 'תשע עשרה']\nconst THOUSANDS_FEM = ['', 'אלף', 'אלפיים', 'שלשת אלפים', 'ארבעת אלפים', 'חמשת אלפים', 'ששת אלפים', 'שבעת אלפים', 'שמונת אלפים', 'תשעת אלפים']\n\n// Shared vocabulary\nconst TENS = ['', '', 'עשרים', 'שלשים', 'ארבעים', 'חמישים', 'ששים', 'שבעים', 'שמונים', 'תשעים']\nconst HUNDREDS = ['', 'מאה', 'מאתיים', 'שלשה מאות', 'ארבעה מאות', 'חמשה מאות', 'ששה מאות', 'שבעה מאות', 'שמונה מאות', 'תשעה מאות']\nconst HUNDREDS_FEM = ['', 'מאה', 'מאתיים', 'שלש מאות', 'ארבע מאות', 'חמש מאות', 'שש מאות', 'שבע מאות', 'שמונה מאות', 'תשע מאות']\n\n// Scale words (index 1 = thousands, 2 = millions, etc.)\nconst SCALE = ['', 'אלף', 'מיליון', 'מיליארד', 'טריליון', 'קוודרליון', 'קווינטיליון']\nconst SCALE_PLURAL = ['', 'אלפים', 'מיליונים', 'מיליארדים', 'טריליונים', 'קוודרליונים', 'קווינטיליונים']\n\nconst ZERO = 'אפס'\nconst NEGATIVE = 'מינוס'\nconst DECIMAL_SEP = 'נקודה'\n\n// ============================================================================\n// Precomputed Lookup Tables (built once at module load)\n// ============================================================================\n\n/**\n * Builds segment word for scale segments (thousands, millions, etc.).\n * \"ו\" is added before tens and ones when following hundreds.\n */\nfunction buildScaleSegment (n, andWord, ONES, TEENS, HUNDREDS_ARR) {\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_ARR[hundreds]\n }\n\n // Tens and ones\n if (tens === 1) {\n // Teens (10-19)\n const teenWord = TEENS[ones]\n if (result) {\n result += ' ' + andWord + teenWord\n } else {\n result = teenWord\n }\n } else {\n // Tens (20-90)\n if (tens >= 2) {\n if (result) {\n result += ' ' + andWord + TENS[tens]\n } else {\n result = TENS[tens]\n }\n }\n\n // Ones\n if (ones > 0) {\n if (result) {\n result += ' ' + andWord + ONES[ones]\n } else {\n result = ONES[ones]\n }\n }\n }\n\n return result\n}\n\n/**\n * Builds segment word for units segment (no scale word).\n * \"ו\" is only added before the final ones digit.\n */\nfunction buildUnitsSegment (n, andWord, ONES, TEENS, HUNDREDS_ARR) {\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_ARR[hundreds]\n }\n\n // Tens (no conjunction)\n if (tens === 1) {\n // Teens (10-19)\n if (result) {\n result += ' ' + TEENS[ones]\n } else {\n result = TEENS[ones]\n }\n } else {\n if (tens >= 2) {\n if (result) {\n result += ' ' + TENS[tens]\n } else {\n result = TENS[tens]\n }\n }\n\n // Ones - conjunction only here\n if (ones > 0) {\n if (result) {\n result += ' ' + andWord + ONES[ones]\n } else {\n result = ONES[ones]\n }\n }\n }\n\n return result\n}\n\n// Precompute all 1000 segment words for masculine (default) with default conjunction\nconst SCALE_SEGMENTS_MASC = new Array(1000)\nconst UNITS_SEGMENTS_MASC = new Array(1000)\nconst SCALE_SEGMENTS_FEM = new Array(1000)\nconst UNITS_SEGMENTS_FEM = new Array(1000)\n\nfor (let i = 0; i < 1000; i++) {\n SCALE_SEGMENTS_MASC[i] = buildScaleSegment(i, 'ו', ONES_MASC, TEENS_MASC, HUNDREDS)\n UNITS_SEGMENTS_MASC[i] = buildUnitsSegment(i, 'ו', ONES_MASC, TEENS_MASC, HUNDREDS)\n SCALE_SEGMENTS_FEM[i] = buildScaleSegment(i, 'ו', ONES_FEM, TEENS_FEM, HUNDREDS_FEM)\n UNITS_SEGMENTS_FEM[i] = buildUnitsSegment(i, 'ו', ONES_FEM, TEENS_FEM, HUNDREDS_FEM)\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Biblical Hebrew words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @param {Object} options - Conversion options\n * @returns {string} Biblical Hebrew words\n */\nfunction integerToWords (n, options) {\n if (n === 0n) return ZERO\n\n const andWord = options.andWord ?? 'ו'\n const gender = options.gender || 'masculine'\n const isFeminine = gender === 'feminine'\n const usePrecomputed = andWord === 'ו'\n\n // Select vocabulary based on gender\n const ONES = isFeminine ? ONES_FEM : ONES_MASC\n const TEENS = isFeminine ? TEENS_FEM : TEENS_MASC\n const THOUSANDS_SPECIAL = isFeminine ? THOUSANDS_FEM : THOUSANDS_MASC\n const HUNDREDS_ARR = isFeminine ? HUNDREDS_FEM : HUNDREDS\n const SCALE_SEGS = isFeminine ? SCALE_SEGMENTS_FEM : SCALE_SEGMENTS_MASC\n const UNITS_SEGS = isFeminine ? UNITS_SEGMENTS_FEM : UNITS_SEGMENTS_MASC\n\n // Fast path: numbers < 1000 (direct lookup)\n if (n < 1000n) {\n return usePrecomputed ? UNITS_SEGS[Number(n)] : buildUnitsSegment(Number(n), andWord, ONES, TEENS, HUNDREDS_ARR)\n }\n\n // Extract segments using BigInt modulo\n const segments = []\n let temp = n\n while (temp > 0n) {\n segments.push(Number(temp % 1000n))\n temp = temp / 1000n\n }\n\n // Build result string directly\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 // Units segment (no scale word)\n const segmentWord = usePrecomputed ? UNITS_SEGS[segment] : buildUnitsSegment(segment, andWord, ONES, TEENS, HUNDREDS_ARR)\n if (result) {\n // Add \"ו\" before single-digit units when following scale words\n if (segment <= 9) {\n result += ' ' + andWord + segmentWord\n } else {\n result += ' ' + segmentWord\n }\n } else {\n result = segmentWord\n }\n } else if (i === 1) {\n // Thousands - special handling for 1-9\n if (segment <= 9) {\n if (result) result += ' '\n result += THOUSANDS_SPECIAL[segment]\n } else {\n const segmentWord = usePrecomputed ? SCALE_SEGS[segment] : buildScaleSegment(segment, andWord, ONES, TEENS, HUNDREDS_ARR)\n if (result) result += ' '\n result += segmentWord + ' ' + SCALE[1]\n }\n } else {\n // Millions and above\n if (segment === 1) {\n if (result) result += ' '\n result += SCALE[i]\n } else {\n const segmentWord = usePrecomputed ? SCALE_SEGS[segment] : buildScaleSegment(segment, andWord, ONES, TEENS, HUNDREDS_ARR)\n if (result) result += ' '\n result += segmentWord + ' ' + SCALE_PLURAL[i]\n }\n }\n }\n\n return result\n}\n\n/**\n * Converts decimal digits to Biblical Hebrew words (digit by digit).\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @param {Object} options - Conversion options\n * @returns {string} Biblical Hebrew words for decimal part\n */\nfunction decimalPartToWords (decimalPart, options) {\n const gender = options.gender || 'masculine'\n const ONES = gender === 'feminine' ? ONES_FEM : ONES_MASC\n\n let result = ''\n for (let i = 0; i < decimalPart.length; i++) {\n const d = parseInt(decimalPart[i], 10)\n if (result) result += ' '\n result += d === 0 ? ZERO : ONES[d]\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Biblical Hebrew words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @param {Object} [options] - Optional configuration\n * @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender\n * @param {string} [options.andWord] - Custom conjunction word\n * @returns {string} The number in Biblical Hebrew words\n */\nfunction toWords (value, options) {\n options = validateOptions(options)\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart, options)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart, options)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","import { isPlainObject } from './is-plain-object.js'\n\n/**\n * Validates and normalizes the options parameter.\n *\n * @param {*} options The options value to validate\n * @returns {Object} A valid options object (empty object if undefined)\n * @throws {TypeError} If options is not undefined or a plain object\n */\nexport function validateOptions (options) {\n if (options === undefined) return {}\n if (isPlainObject(options)) return options\n throw new TypeError(\n `Invalid options: expected plain object or undefined, got ${typeof options}`\n )\n}\n","/**\n * Checks if a value is a plain object (not null, array, or other object types).\n *\n * A plain object is one created by:\n * - Object literal: `{}`\n * - Object.create(null): null-prototype object\n *\n * This excludes arrays, class instances, Map, Set, and other object types.\n *\n * @param {*} value Value to check\n * @returns {boolean} True if value is a plain object\n */\nexport function isPlainObject (value) {\n if (value === null || typeof value !== 'object') return false\n const proto = Object.getPrototypeOf(value)\n return proto === null || proto === Object.prototype\n}\n"],"names":["parseNumericString","str","isNegative","slice","dotIndex","indexOf","integerPart","BigInt","integerStr","decimalPart","expandScientificNotation","mantissa","expStr","toLowerCase","split","exp","parseInt","digits","newDotPosition","length","repeat","ONES_MASC","TEENS_MASC","THOUSANDS_MASC","ONES_FEM","TEENS_FEM","THOUSANDS_FEM","TENS","HUNDREDS","HUNDREDS_FEM","SCALE","SCALE_PLURAL","buildScaleSegment","n","andWord","ONES","TEENS","HUNDREDS_ARR","ones","tens","Math","floor","hundreds","result","teenWord","buildUnitsSegment","SCALE_SEGMENTS_MASC","Array","UNITS_SEGMENTS_MASC","SCALE_SEGMENTS_FEM","UNITS_SEGMENTS_FEM","i","value","options","undefined","proto","Object","getPrototypeOf","prototype","isPlainObject","TypeError","validateOptions","type","Number","isFinite","Error","isSafeInteger","toString","includes","numberToString","trimmed","trim","isNaN","normalizeString","parseNumericValue","NEGATIVE","isFeminine","gender","usePrecomputed","THOUSANDS_SPECIAL","SCALE_SEGS","UNITS_SEGS","segments","temp","push","segment","segmentWord","integerToWords","d","decimalPartToWords"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"hbo.js","sources":["../../lib/utils/parse-numeric.js","../../lib/languages/hbo.js","../../lib/utils/validate-options.js","../../lib/utils/is-plain-object.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 * Biblical Hebrew language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Gender agreement (masculine default, feminine via option)\n * - Dual forms for 2, 200, 2000\n * - Special 1-9 thousands construct state\n * - \"ו\" (ve) conjunction rules vary by position\n * - Per-digit decimal reading\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\nimport { validateOptions } from '../utils/validate-options.js'\n\n// ============================================================================\n// Vocabulary (arrays for indexed access - faster than object property lookup)\n// ============================================================================\n\n// Masculine forms (default in Biblical Hebrew) - index 0 unused\nconst ONES_MASC = ['', 'אחד', 'שניים', 'שלשה', 'ארבעה', 'חמשה', 'ששה', 'שבעה', 'שמונה', 'תשעה']\nconst TEENS_MASC = ['עשרה', 'אחד עשר', 'שנים עשר', 'שלשה עשר', 'ארבעה עשר', 'חמשה עשר', 'ששה עשר', 'שבעה עשר', 'שמונה עשר', 'תשעה עשר']\nconst THOUSANDS_MASC = ['', 'אלף', 'אלפיים', 'שלשה אלפים', 'ארבעה אלפים', 'חמשה אלפים', 'ששה אלפים', 'שבעה אלפים', 'שמונה אלפים', 'תשעה אלפים']\n\n// Feminine forms - index 0 unused\nconst ONES_FEM = ['', 'אחת', 'שתים', 'שלש', 'ארבע', 'חמש', 'שש', 'שבע', 'שמונה', 'תשע']\nconst TEENS_FEM = ['עשר', 'אחת עשרה', 'שתים עשרה', 'שלש עשרה', 'ארבע עשרה', 'חמש עשרה', 'שש עשרה', 'שבע עשרה', 'שמונה עשרה', 'תשע עשרה']\nconst THOUSANDS_FEM = ['', 'אלף', 'אלפיים', 'שלשת אלפים', 'ארבעת אלפים', 'חמשת אלפים', 'ששת אלפים', 'שבעת אלפים', 'שמונת אלפים', 'תשעת אלפים']\n\n// Shared vocabulary\nconst TENS = ['', '', 'עשרים', 'שלשים', 'ארבעים', 'חמישים', 'ששים', 'שבעים', 'שמונים', 'תשעים']\nconst HUNDREDS = ['', 'מאה', 'מאתיים', 'שלשה מאות', 'ארבעה מאות', 'חמשה מאות', 'ששה מאות', 'שבעה מאות', 'שמונה מאות', 'תשעה מאות']\nconst HUNDREDS_FEM = ['', 'מאה', 'מאתיים', 'שלש מאות', 'ארבע מאות', 'חמש מאות', 'שש מאות', 'שבע מאות', 'שמונה מאות', 'תשע מאות']\n\n// Scale words (index 1 = thousands, 2 = millions, etc.)\nconst SCALE = ['', 'אלף', 'מיליון', 'מיליארד', 'טריליון', 'קוודרליון', 'קווינטיליון']\nconst SCALE_PLURAL = ['', 'אלפים', 'מיליונים', 'מיליארדים', 'טריליונים', 'קוודרליונים', 'קווינטיליונים']\n\nconst ZERO = 'אפס'\nconst NEGATIVE = 'מינוס'\nconst DECIMAL_SEP = 'נקודה'\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds segment word for scale segments (thousands, millions, etc.).\n * \"ו\" is added before tens and ones when following hundreds.\n */\nfunction buildScaleSegment (n, andWord, ONES, TEENS, HUNDREDS_ARR) {\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_ARR[hundreds]\n }\n\n // Tens and ones\n if (tens === 1) {\n // Teens (10-19)\n const teenWord = TEENS[ones]\n if (result) {\n result += ' ' + andWord + teenWord\n } else {\n result = teenWord\n }\n } else {\n // Tens (20-90)\n if (tens >= 2) {\n if (result) {\n result += ' ' + andWord + TENS[tens]\n } else {\n result = TENS[tens]\n }\n }\n\n // Ones\n if (ones > 0) {\n if (result) {\n result += ' ' + andWord + ONES[ones]\n } else {\n result = ONES[ones]\n }\n }\n }\n\n return result\n}\n\n/**\n * Builds segment word for units segment (no scale word).\n * \"ו\" is only added before the final ones digit.\n */\nfunction buildUnitsSegment (n, andWord, ONES, TEENS, HUNDREDS_ARR) {\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_ARR[hundreds]\n }\n\n // Tens (no conjunction)\n if (tens === 1) {\n // Teens (10-19)\n if (result) {\n result += ' ' + TEENS[ones]\n } else {\n result = TEENS[ones]\n }\n } else {\n if (tens >= 2) {\n if (result) {\n result += ' ' + TENS[tens]\n } else {\n result = TENS[tens]\n }\n }\n\n // Ones - conjunction only here\n if (ones > 0) {\n if (result) {\n result += ' ' + andWord + ONES[ones]\n } else {\n result = ONES[ones]\n }\n }\n }\n\n return result\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Biblical Hebrew words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @param {Object} options - Conversion options\n * @returns {string} Biblical Hebrew words\n */\nfunction integerToWords (n, options) {\n if (n === 0n) return ZERO\n\n const andWord = options.andWord ?? 'ו'\n const gender = options.gender || 'masculine'\n const isFeminine = gender === 'feminine'\n\n // Select vocabulary based on gender\n const ONES = isFeminine ? ONES_FEM : ONES_MASC\n const TEENS = isFeminine ? TEENS_FEM : TEENS_MASC\n const THOUSANDS_SPECIAL = isFeminine ? THOUSANDS_FEM : THOUSANDS_MASC\n const HUNDREDS_ARR = isFeminine ? HUNDREDS_FEM : HUNDREDS\n\n // Fast path: numbers < 1000\n if (n < 1000n) {\n return buildUnitsSegment(Number(n), andWord, ONES, TEENS, HUNDREDS_ARR)\n }\n\n // Extract segments using BigInt modulo\n const segments = []\n let temp = n\n while (temp > 0n) {\n segments.push(Number(temp % 1000n))\n temp = temp / 1000n\n }\n\n // Build result string directly\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 // Units segment (no scale word)\n const segmentWord = buildUnitsSegment(segment, andWord, ONES, TEENS, HUNDREDS_ARR)\n if (result) {\n // Add \"ו\" before single-digit units when following scale words\n if (segment <= 9) {\n result += ' ' + andWord + segmentWord\n } else {\n result += ' ' + segmentWord\n }\n } else {\n result = segmentWord\n }\n } else if (i === 1) {\n // Thousands - special handling for 1-9\n if (segment <= 9) {\n if (result) result += ' '\n result += THOUSANDS_SPECIAL[segment]\n } else {\n const segmentWord = buildScaleSegment(segment, andWord, ONES, TEENS, HUNDREDS_ARR)\n if (result) result += ' '\n result += segmentWord + ' ' + SCALE[1]\n }\n } else {\n // Millions and above\n if (segment === 1) {\n if (result) result += ' '\n result += SCALE[i]\n } else {\n const segmentWord = buildScaleSegment(segment, andWord, ONES, TEENS, HUNDREDS_ARR)\n if (result) result += ' '\n result += segmentWord + ' ' + SCALE_PLURAL[i]\n }\n }\n }\n\n return result\n}\n\n/**\n * Converts decimal digits to Biblical Hebrew words (digit by digit).\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @param {Object} options - Conversion options\n * @returns {string} Biblical Hebrew words for decimal part\n */\nfunction decimalPartToWords (decimalPart, options) {\n const gender = options.gender || 'masculine'\n const ONES = gender === 'feminine' ? ONES_FEM : ONES_MASC\n\n let result = ''\n for (let i = 0; i < decimalPart.length; i++) {\n const d = parseInt(decimalPart[i], 10)\n if (result) result += ' '\n result += d === 0 ? ZERO : ONES[d]\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Biblical Hebrew words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @param {Object} [options] - Optional configuration\n * @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender\n * @param {string} [options.andWord] - Custom conjunction word\n * @returns {string} The number in Biblical Hebrew words\n */\nfunction toWords (value, options) {\n options = validateOptions(options)\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart, options)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart, options)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","import { isPlainObject } from './is-plain-object.js'\n\n/**\n * Validates and normalizes the options parameter.\n *\n * @param {*} options The options value to validate\n * @returns {Object} A valid options object (empty object if undefined)\n * @throws {TypeError} If options is not undefined or a plain object\n */\nexport function validateOptions (options) {\n if (options === undefined) return {}\n if (isPlainObject(options)) return options\n throw new TypeError(\n `Invalid options: expected plain object or undefined, got ${typeof options}`\n )\n}\n","/**\n * Checks if a value is a plain object (not null, array, or other object types).\n *\n * A plain object is one created by:\n * - Object literal: `{}`\n * - Object.create(null): null-prototype object\n *\n * This excludes arrays, class instances, Map, Set, and other object types.\n *\n * @param {*} value Value to check\n * @returns {boolean} True if value is a plain object\n */\nexport function isPlainObject (value) {\n if (value === null || typeof value !== 'object') return false\n const proto = Object.getPrototypeOf(value)\n return proto === null || proto === Object.prototype\n}\n"],"names":["parseNumericString","str","isNegative","slice","dotIndex","indexOf","integerPart","BigInt","integerStr","decimalPart","expandScientificNotation","mantissa","expStr","toLowerCase","split","exp","parseInt","digits","newDotPosition","length","repeat","ONES_MASC","TEENS_MASC","THOUSANDS_MASC","ONES_FEM","TEENS_FEM","THOUSANDS_FEM","TENS","HUNDREDS","HUNDREDS_FEM","SCALE","SCALE_PLURAL","buildScaleSegment","n","andWord","ONES","TEENS","HUNDREDS_ARR","ones","tens","Math","floor","hundreds","result","teenWord","buildUnitsSegment","value","options","undefined","proto","Object","getPrototypeOf","prototype","isPlainObject","TypeError","validateOptions","type","Number","isFinite","Error","isSafeInteger","toString","includes","numberToString","trimmed","trim","isNaN","normalizeString","parseNumericValue","NEGATIVE","isFeminine","gender","THOUSANDS_SPECIAL","segments","temp","push","i","segment","segmentWord","integerToWords","d","decimalPartToWords"],"mappings":"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"}
|
package/dist/languages/he.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
/*! n2words/he 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=["עשר","אחת עשרה","שתים עשרה","שלש עשרה","ארבע עשרה","חמש עשרה","שש עשרה","שבע עשרה","שמונה עשרה","תשע עשרה"],o=["","","עשרים","שלשים","ארבעים","חמישים","ששים","שבעים","שמונים","תשעים"],s=["","מאה","מאתיים","שלש מאות","ארבע מאות","חמש מאות","שש מאות","שבע מאות","שמונה מאות","תשע מאות"],u=["","אלף","אלפיים","שלשת אלפים","ארבעת אלפים","חמשת אלפים","ששת אלפים","שבעת אלפים","שמונת אלפים","תשעת אלפים"],f=["","אלף","מיליון","מיליארד","טריליון","קוודרליון","קווינטיליון"],c=["","אלפים","מיליונים","מיליארדים","טריליונים","קוודרליונים","קווינטיליונים"];function l(e,t){if(0===e)return"";const n=e%10,u=Math.floor(e/10)%10,f=Math.floor(e/100);let c="";if(f>0&&(c=s[f]),1===u){const e=i[n];c?c+=" "+t+e:c=e}else u>=2&&(c?c+=" "+t+o[u]:c=o[u]),n>0&&(c?c+=" "+t+r[n]:c=r[n]);return c}function a(e,t){if(0===e)return"";const n=e%10,u=Math.floor(e/10)%10,f=Math.floor(e/100);let c="";return f>0&&(c=s[f]),1===u?c?c+=" "+i[n]:c=i[n]:(u>=2&&(c?c+=" "+o[u]:c=o[u]),n>0&&(c?c+=" "+t+r[n]:c=r[n])),c}
|
|
1
|
+
/*! n2words/he 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=["עשר","אחת עשרה","שתים עשרה","שלש עשרה","ארבע עשרה","חמש עשרה","שש עשרה","שבע עשרה","שמונה עשרה","תשע עשרה"],o=["","","עשרים","שלשים","ארבעים","חמישים","ששים","שבעים","שמונים","תשעים"],s=["","מאה","מאתיים","שלש מאות","ארבע מאות","חמש מאות","שש מאות","שבע מאות","שמונה מאות","תשע מאות"],u=["","אלף","אלפיים","שלשת אלפים","ארבעת אלפים","חמשת אלפים","ששת אלפים","שבעת אלפים","שמונת אלפים","תשעת אלפים"],f=["","אלף","מיליון","מיליארד","טריליון","קוודרליון","קווינטיליון"],c=["","אלפים","מיליונים","מיליארדים","טריליונים","קוודרליונים","קווינטיליונים"];function l(e,t){if(0===e)return"";const n=e%10,u=Math.floor(e/10)%10,f=Math.floor(e/100);let c="";if(f>0&&(c=s[f]),1===u){const e=i[n];c?c+=" "+t+e:c=e}else u>=2&&(c?c+=" "+t+o[u]:c=o[u]),n>0&&(c?c+=" "+t+r[n]:c=r[n]);return c}function a(e,t){if(0===e)return"";const n=e%10,u=Math.floor(e/10)%10,f=Math.floor(e/100);let c="";return f>0&&(c=s[f]),1===u?c?c+=" "+i[n]:c=i[n]:(u>=2&&(c?c+=" "+o[u]:c=o[u]),n>0&&(c?c+=" "+t+r[n]:c=r[n])),c}e.he=function(e,i){i=function(e){if(void 0===e)return{};if(function(e){if(null===e||"object"!=typeof e)return!1;const t=Object.getPrototypeOf(e);return null===t||t===Object.prototype}(e))return e;throw new TypeError("Invalid options: expected plain object or undefined, got "+typeof e)}(i);const{isNegative:o,integerPart:s,decimalPart:g}=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 d="";return o&&(d="מינוס "),d+=function(e,t){if(0n===e)return"אפס";const n=t.andWord??"ו";if(e<1000n)return a(Number(e),n);const r=[];let i=e;for(;i>0n;)r.push(Number(i%1000n)),i/=1000n;let o="";for(let e=r.length-1;e>=0;e--){const t=r[e];if(0!==t)if(0===e){const e=a(t,n);o?o+=t<=9?" "+n+e:" "+e:o=e}else 1===e?t<=9?(o&&(o+=" "),o+=u[t]):(o&&(o+=" "),o+=l(t,n)+" "+f[1]):1===t?(o&&(o+=" "),o+=f[e]):(o&&(o+=" "),o+=l(t,n)+" "+c[e])}return o}(s,i),g&&(d+=" נקודה "+function(e){let t="";for(let n=0;n<e.length;n++){const i=parseInt(e[n],10);t&&(t+=" "),t+=0===i?"אפס":r[i]}return t}(g)),d}},"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=he.js.map
|