UNPKG

spell-vn-number

Version:
254 lines 9.73 kB
import { Idx, InvalidFormatError, InvalidNumberError, SpellerConfig } from './types'; /** * Number of magnitude groups in the system (billions, millions, thousands) * Số lượng nhóm đơn vị trong hệ thống (tỷ, triệu, nghìn) */ var NUM_GROUPS = 3; /** * Number of positions in each group of digits (hundreds, tens, units) * Số lượng vị trí trong mỗi nhóm chữ số (hàng trăm, hàng chục, hàng đơn vị) */ var NUM_POSITIONS = 3; /** * Process a section of the number by splitting it into groups and spelling each group * @param spelledParts * @param config SpellerConfig instance * @param numberStr The number string to process * @returns Array of spelled parts */ function processPart(spelledParts, config, numberStr) { if (numberStr === '') { return []; } var offset = numberStr.length % NUM_POSITIONS; // Add zeros to the beginning if length is not divisible by NUM_POSITIONS var paddedNumb = offset !== 0 ? '0'.repeat(NUM_POSITIONS - offset) + numberStr : numberStr; var totalThreeDigitSegments = paddedNumb.length / NUM_POSITIONS; // Not odd because already padded var magnitudeMod = totalThreeDigitSegments % NUM_GROUPS; var remainingGroups = magnitudeMod === 0 ? totalThreeDigitSegments / NUM_GROUPS : Math.floor(totalThreeDigitSegments / NUM_GROUPS) + 1; var currentMagnitudeIndex = magnitudeMod !== 0 ? NUM_GROUPS - magnitudeMod : Idx.BIL; var isFirst = true; var i = 0; while (remainingGroups > 0) { // THOUSAND/MILLION/BILLION (Unit of magnitude groups) if (!isFirst) { // Add unit for the group spelledParts.push(config.findUnit(currentMagnitudeIndex)); } // Group indices (chỉ số nhóm) for (; currentMagnitudeIndex < NUM_GROUPS; currentMagnitudeIndex++) { // Process three digits at a time var hundredsDigit = paddedNumb[i]; var tensDigit = paddedNumb[i + 1]; var unitsDigit = paddedNumb[i + 2]; i += 3; if (isFirst) { // Process hundreds if (hundredsDigit !== '0') { isFirst = false; spellHundreds(spelledParts, config, hundredsDigit, tensDigit, unitsDigit); } // Process tens if (!isFirst || tensDigit !== '0') { isFirst = false; spellTens(spelledParts, config, tensDigit, unitsDigit); } // Process units if (!isFirst || unitsDigit !== '0') { isFirst = false; spellUnits(spelledParts, config, hundredsDigit, tensDigit, unitsDigit, currentMagnitudeIndex); } // If all digits are zero, return "không" if (isFirst) { isFirst = false; spelledParts.push(config.getDigit('0')); } } else { // Process hundreds spellHundreds(spelledParts, config, hundredsDigit, tensDigit, unitsDigit); // Process tens spellTens(spelledParts, config, tensDigit, unitsDigit); // Process units spellUnits(spelledParts, config, hundredsDigit, tensDigit, unitsDigit, currentMagnitudeIndex); } } remainingGroups--; currentMagnitudeIndex = Idx.BIL; } return spelledParts; } /** * Spell a digit and its unit based on its position and context * @param spelledParts * @param config SpellerConfig instance * @param hundredsDigit Digit at hundreds position * @param tensDigit Digit at tens position * @param unitsDigit Digit at units position * @param currentMagnitudeIndex Index of the magnitude type (thousand, million, billion) * @returns Array of spelled parts */ function spellUnits(spelledParts, config, hundredsDigit, tensDigit, unitsDigit, currentMagnitudeIndex) { if (unitsDigit === '0') { if ((tensDigit !== '0' || hundredsDigit !== '0')) { // Unit digit is zero, but hundreds or tens are not both zero ⇒ still need to read the unit for correct group reading. if (currentMagnitudeIndex !== Idx.THO) { spelledParts.push(config.findMagUnit(currentMagnitudeIndex)); } } return; // return... } if (unitsDigit === '1') { if (tensDigit !== '0' && tensDigit !== '1') { spelledParts.push(config.oneToneText); } else { spelledParts.push(config.getDigit(unitsDigit)); } } else if (unitsDigit === '4') { if (tensDigit !== '0' && tensDigit !== '1') { spelledParts.push(config.fourToneText); } else { spelledParts.push(config.getDigit(unitsDigit)); } } else if (unitsDigit === '5') { if (tensDigit !== '0') { spelledParts.push(config.fiveToneText); } else { spelledParts.push(config.getDigit(unitsDigit)); } } else { spelledParts.push(config.getDigit(unitsDigit)); } // 2. Add unit if needed (spelled is not empty) if (currentMagnitudeIndex !== Idx.THO) { spelledParts.push(config.findMagUnit(currentMagnitudeIndex)); } } /** * Spell a digit and its unit based on its position and context * @param spelledParts * @param config SpellerConfig instance * @param tensDigit Digit at tens position * @param unitsDigit Digit at units position * @returns Array of spelled parts */ function spellTens(spelledParts, config, tensDigit, unitsDigit) { if (tensDigit === '0') { if (unitsDigit !== '0') { spelledParts.push(config.oddText); } } else if (tensDigit === '1') { spelledParts.push(config.tenText); } else { spelledParts.push(config.getDigit(tensDigit)); spelledParts.push(config.findUnit(Idx.TEN)); } } /** * Spell a digit and its unit based on its position and context * @param spelledParts * @param config SpellerConfig instance * @param hundredsDigit Digit at hundreds position * @param tensDigit Digit at tens position * @param unitsDigit Digit at units position * @returns Array of spelled parts */ function spellHundreds(spelledParts, config, hundredsDigit, tensDigit, unitsDigit) { if (hundredsDigit === '0') { if (!(tensDigit === '0' && unitsDigit === '0')) { spelledParts.push(config.getDigit(hundredsDigit)); spelledParts.push(config.findUnit(Idx.HUN)); } } else { spelledParts.push(config.getDigit(hundredsDigit)); spelledParts.push(config.findUnit(Idx.HUN)); } } /** * Main function to spell a Vietnamese number * @param config SpellerConfig instance * @param input Number to spell * @returns Vietnamese spelling of the number */ export function spellVnNumber(config, input) { // Parse the number using the configurable parser var numberData = config.parseNumberData(input); // Spell out each part var spelledParts = []; // Add negative sign if needed if (numberData.isNegative) { spelledParts.push(config.negativeText); } // Process integral part processPart(spelledParts, config, numberData.integralPart); // Process fractional part if exists if (numberData.fractionalPart.length > 0) { spelledParts.push(config.pointText); processPart(spelledParts, config, numberData.fractionalPart); } // Capitalize the first letter if capitalizeInitial is true if (config.capitalizeInitial) { var firstNumb = spelledParts[0]; spelledParts[0] = firstNumb.charAt(0).toUpperCase() + firstNumb.slice(1); } // Join all parts with the separator var result = spelledParts.join(config.separator); // After joining all parts, append the currency unit if provided if (config.currencyUnit) { result += " ".concat(config.currencyUnit); } return result; } /** * Convenience function to spell a Vietnamese number with default config * @param input Number to spell * @returns Vietnamese spelling of the number */ export function spell(input) { var config = new SpellerConfig(); return spellVnNumber(config, input); } /** * Convenience function to spell a Vietnamese number with default value on error * @param input Number to spell * @param subConfig Partial<SpellerConfig> to override default configuration * @param defaultOnError Default value to return when an error occurs. If undefined, the error will be thrown. * @returns Vietnamese spelling of the number or defaultOnError if an error occurs * @throws InvalidFormatError when input format is invalid * @throws InvalidNumberError when number is invalid * @throws Error for other unexpected errors */ export function spellOrDefault(input, subConfig, defaultOnError) { if (subConfig === void 0) { subConfig = {}; } var config = new SpellerConfig(subConfig); try { return spellVnNumber(config, input); } catch (err) { if (defaultOnError === undefined) { throw err; } if (err instanceof InvalidFormatError) { console.warn(err.name, err.message, '(', input, typeof input, ')'); } else if (err instanceof InvalidNumberError) { console.warn(err.name, err.message, '(', input, typeof input, ')'); } else { console.error('Unexpected error with input', '(', input, typeof input, '):', err); } return defaultOnError; } } //# sourceMappingURL=speller.js.map