read-vietnamese-number
Version:
Đọc số thành chữ trong Tiếng Việt
170 lines (169 loc) • 6.29 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.readLastTwoDigits = readLastTwoDigits;
exports.readThreeDigits = readThreeDigits;
exports.removeThousandsSeparators = removeThousandsSeparators;
exports.trimRedundantZeros = trimRedundantZeros;
exports.addLeadingZerosToFitPeriod = addLeadingZerosToFitPeriod;
exports.zipIntegralPeriods = zipIntegralPeriods;
exports.parseNumberData = parseNumberData;
exports.readIntegralPart = readIntegralPart;
exports.readFractionalPart = readFractionalPart;
exports.readNumber = readNumber;
exports.doReadNumber = doReadNumber;
const type_js_1 = require("./type.js");
const util_js_1 = require("./util.js");
function readLastTwoDigits(config, b, c, readZeroTen) {
const output = [];
switch (b) {
case 0: {
if (readZeroTen && c !== 0) {
output.push(config.digits[b]);
}
output.push(config.digits[c]);
break;
}
case 1: {
output.push(config.tenText);
if (c === 5) {
output.push(config.fiveToneText);
}
else if (c !== 0) {
output.push(config.digits[c]);
}
break;
}
default: {
output.push(config.digits[b], config.tenToneText);
if (c === 1) {
output.push(config.oneToneText);
}
else if (c === 4) {
output.push(config.fourToneText);
}
else if (c === 5) {
output.push(config.fiveToneText);
}
else if (c !== 0) {
output.push(config.digits[c]);
}
break;
}
}
return output;
}
function readThreeDigits(config, a, b, c, readZeroHundred) {
const output = [];
const hasHundred = a !== 0 || readZeroHundred;
if (hasHundred) {
output.push(config.digits[a], config.hundredText);
}
if (hasHundred && b === 0) {
if (c === 0) {
return output;
}
output.push(config.oddText);
}
output.push(...readLastTwoDigits(config, b, c, false));
return output;
}
function removeThousandsSeparators(config, number) {
const regex = new RegExp(config.thousandSign, 'g');
return number.replace(regex, '');
}
function trimRedundantZeros(config, number) {
return number.includes(config.pointSign)
? (0, util_js_1.trimLeft)((0, util_js_1.trimRight)(number, config.filledDigit), config.filledDigit)
: (0, util_js_1.trimLeft)(number, config.filledDigit);
}
function addLeadingZerosToFitPeriod(config, number) {
const newLength = Math.ceil(number.length / config.periodSize) * config.periodSize;
return number.padStart(newLength, config.filledDigit);
}
function zipIntegralPeriods(config, digits) {
const output = [];
const periodCount = Math.ceil(digits.length / config.periodSize);
for (let i = 0; i < periodCount; i++) {
const [a, b, c] = digits.slice(i * config.periodSize, (i + 1) * config.periodSize);
output.push([a, b, c]);
}
return output;
}
function parseNumberData(config, number) {
let numberString = removeThousandsSeparators(config, number);
const isNegative = numberString.startsWith(config.negativeSign);
numberString = isNegative ? numberString.substring(config.negativeSign.length) : numberString;
numberString = trimRedundantZeros(config, numberString);
const pointPos = numberString.indexOf(config.pointSign);
let integralString = pointPos === -1 ? numberString : numberString.substring(0, pointPos);
const fractionalString = pointPos === -1 ? '' : numberString.substring(pointPos + 1);
integralString = addLeadingZerosToFitPeriod(config, integralString);
const integralDigits = (0, util_js_1.splitToDigits)(integralString);
const fractionalDigits = (0, util_js_1.splitToDigits)(fractionalString);
if (integralDigits === null) {
throw new type_js_1.InvalidNumberError('Invalid integral part');
}
if (fractionalDigits === null) {
throw new type_js_1.InvalidNumberError('Invalid fractional part');
}
const integralPart = zipIntegralPeriods(config, integralDigits);
if (integralPart.length === 0) {
integralPart.push([0, 0, 0]);
}
else if (integralPart.length > config.units.length) {
throw new type_js_1.NotEnoughUnitError('Unit not enough');
}
const fractionalPart = fractionalDigits;
return { isNegative, integralPart, fractionalPart };
}
function readIntegralPart(config, periods) {
const output = [];
const isSinglePeriod = periods.length === 1;
for (const [index, period] of periods.entries()) {
const isFirstPeriod = index === 0;
const [a, b, c] = period;
if (a !== 0 || b !== 0 || c !== 0 || isSinglePeriod) {
output.push(...readThreeDigits(config, a, b, c, !isFirstPeriod), ...config.units[periods.length - 1 - index]);
}
}
return output;
}
function readFractionalPart(config, digits) {
const output = [];
switch (digits.length) {
case 2: {
const [b, c] = digits;
output.push(...readLastTwoDigits(config, b, c, true));
break;
}
case 3: {
const [a, b, c] = digits;
output.push(...readThreeDigits(config, a, b, c, true));
break;
}
default: {
for (const digit of digits) {
output.push(config.digits[digit]);
}
break;
}
}
return output;
}
function readNumber(config, numberData) {
const output = [];
output.push(...readIntegralPart(config, numberData.integralPart));
if (numberData.fractionalPart.length !== 0) {
output.push(config.pointText, ...readFractionalPart(config, numberData.fractionalPart));
}
if (numberData.isNegative) {
output.unshift(config.negativeText);
}
output.push(...config.unit);
return output.join(config.separator);
}
function doReadNumber(config, number) {
const validatedNumber = (0, util_js_1.validateNumber)(number);
const numberData = parseNumberData(config, validatedNumber);
return readNumber(config, numberData);
}