UNPKG

digit-to-words-nepali

Version:

A comprehensive TypeScript library for converting numbers to words in English and Nepali languages. Supports numbers up to 10^39 (Adanta Singhar), currency formatting, decimal handling with individual digit pronunciation, and BigInt. Zero dependencies, fu

188 lines (187 loc) 8.51 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.digitToNepaliWords = exports.NepaliConverter = void 0; const baseConverter_1 = require("./baseConverter"); const validationUtils_1 = require("../utils/validationUtils"); const scaleMappings_1 = require("../mappings/scaleMappings"); const numberMappings_1 = require("../mappings/numberMappings"); const englishConverter_1 = require("./englishConverter"); const cacheUtils_1 = require("../utils/cacheUtils"); const converterFactory_1 = require("../utils/converterFactory"); /** * NepaliConverter * Converts numbers to Nepali words with support for currency, decimals, and custom mappings. * Optimized for readability, maintainability, and performance. */ class NepaliConverter extends baseConverter_1.BaseConverter { constructor() { super(); this.wordCache = (0, numberMappings_1.createNumberWordMap)(); this.scales = scaleMappings_1.nepaliScaleMappings; } /** * Convert decimal digits individually to Nepali words. * @param decimal - The decimal string (e.g., "33") * @param config - Conversion configuration * @returns Array of individual digit words */ convertDecimalDigitsIndividually(decimal, config) { const digits = decimal.split(''); const words = []; for (const digit of digits) { const digitNum = parseInt(digit); words.push(this.getWordMapping(digitNum, config.lang)); } return words; } /** * Convert a number to Nepali words. * @param num - The number to convert * @param config - Conversion configuration (all fields required) */ convert(num, config) { this.setCustomMappings(config); // Validate input if (!(0, validationUtils_1.isValidNumber)(num)) { throw new Error('Input must contain only valid digits'); } // Use cache if no custom mappings const hasCustomMappings = Object.keys(config.units).length > 0 || Object.keys(config.scales).length > 0; if (!hasCustomMappings) { const cacheKey = { value: num.toString(), lang: config.lang, isCurrency: config.isCurrency, includeDecimal: config.includeDecimal, individualDecimalDigits: config.individualDecimalDigits, currency: config.currency, decimalSuffix: config.decimalSuffix, currencyDecimalSuffix: config.currencyDecimalSuffix }; const cachedResult = cacheUtils_1.conversionCache.get(cacheKey); if (cachedResult) return cachedResult; } // Split number into integer and decimal parts const { integer, decimal } = (0, validationUtils_1.splitNumber)(num); // Enforce maximum supported value (10^39 - 1) and max length (39 digits) const MAX_SUPPORTED = BigInt('9'.repeat(39)); const intStr = integer.toString().replace(/^0+/, '') || '0'; if (integer > MAX_SUPPORTED || intStr.length > 39) { throw new Error('Input exceeds maximum supported value (10^39 - 1)'); } const words = []; // Determine if input is zero or a fraction (e.g., 0.01) const isZeroOrFraction = (typeof num === 'bigint' ? num === 0n : parseFloat(num.toString()) < 1 && parseFloat(num.toString()) > -1); // Integer part conversion if (integer === 0n) { // Add "zero" if the original number was effectively zero before the decimal, // or if there's no decimal part. if (isZeroOrFraction || !decimal) { // Only add zero if the decimal part isn't going to represent the only value (e.g., 0.01) if (!decimal || parseInt(decimal || '0') === 0) { words.push(this.getWordMapping(0, config.lang)); } } } else { this.processLargeNumber(integer, words, config); } // Decimal part conversion if (config.includeDecimal && decimal) { const decimalNum = parseInt(decimal || '0'); // Only add decimal suffix and value if the decimal part is non-zero if (decimalNum !== 0) { // If the integer part was zero, add it now before the decimal. if (integer === 0n && words.length === 0) { words.push(this.getWordMapping(0, config.lang)); } words.push(config.isCurrency ? config.currencyDecimalSuffix : config.decimalSuffix); // Use individual digits for non-currency or when explicitly configured if (config.individualDecimalDigits) { const individualDigits = this.convertDecimalDigitsIndividually(decimal, config); words.push(...individualDigits); } else { words.push(this.getWordMapping(decimalNum, config.lang)); } } else if (integer === 0n && words.length === 0) { // If integer was 0 and decimal rounded to 0, ensure "zero" is output words.push(this.getWordMapping(0, config.lang)); } } // Add currency prefix if needed and if currency string is not empty if (config.isCurrency && config.currency) { // Always add currency prefix if isCurrency is true, unless the result is empty (which shouldn't happen now) if (words.length > 0) { words.unshift(config.currency); } else { // If somehow words is empty, output "Currency Zero" words.push(config.currency, this.getWordMapping(0, config.lang)); } } // Final check: if words array is empty (e.g., input was 0.000), add zero. // This might be redundant now but serves as a safeguard. if (words.length === 0) { words.push(this.getWordMapping(0, config.lang)); } const result = { words, meta: { originalNumber: num.toString(), language: config.lang, isCurrency: config.isCurrency } }; // Store in cache if no custom mappings if (!hasCustomMappings) { const cacheKey = { value: num.toString(), lang: config.lang, isCurrency: config.isCurrency, includeDecimal: config.includeDecimal, individualDecimalDigits: config.individualDecimalDigits, currency: config.currency, decimalSuffix: config.decimalSuffix, currencyDecimalSuffix: config.currencyDecimalSuffix }; cacheUtils_1.conversionCache.set(cacheKey, result); } return result; } } exports.NepaliConverter = NepaliConverter; const digitToNepaliWords = (num, config = {}) => { // If lang is 'en', delegate to EnglishConverter for API consistency if (config.lang === 'en') { return (0, englishConverter_1.digitToEnglishWords)(num, config); } // Use the converter factory to get a singleton instance const converter = converterFactory_1.ConverterFactory.getInstance('NepaliConverter', () => new NepaliConverter()); // Set default language-specific config before merging user config const langDefaults = { currency: 'रुपैयाँ', decimalSuffix: 'दशमलव', currencyDecimalSuffix: 'पैसा', }; const defaultConfig = { lang: 'ne', isCurrency: false, includeDecimal: true, individualDecimalDigits: true, // Default to individual digits units: {}, scales: {}, ...langDefaults, // Apply language defaults ...config // User config overrides defaults }; // Override individualDecimalDigits based on currency setting if not explicitly set if (config.individualDecimalDigits === undefined) { defaultConfig.individualDecimalDigits = !defaultConfig.isCurrency; } const result = converter.convert(num, defaultConfig); // Filter out empty strings that might result from empty currency/decimal suffixes return result.words.filter((word) => word !== '').join(' ').trim(); }; exports.digitToNepaliWords = digitToNepaliWords;