UNPKG

chinese-numbers-to-arabic

Version:
274 lines (265 loc) 7.83 kB
'use strict'; // src/utils/chars.ts var cnNumbers = { \u96F6: 0, "\u3007": 0, "\uFF10": 0, \u6D1E: 0, \u58F9: 1, \u4E00: 1, \u5E7A: 1, "\uFF11": 1, \u8CB3: 2, \u8D30: 2, \u4E8C: 2, \u4E24: 2, \u5169: 2, \u5006: 2, \u4FE9: 2, "\uFF12": 2, // Outdated financial number variant which is commonly used as a normal word, // which causes false positives: // '參': 3, \u53C1: 3, \u4E09: 3, \u4EE8: 3, "\uFF13": 3, \u8086: 4, \u56DB: 4, "\uFF14": 4, \u4F0D: 5, \u4E94: 5, "\uFF15": 5, \u9678: 6, \u9646: 6, \u516D: 6, "\uFF16": 6, \u67D2: 7, \u4E03: 7, \u62D0: 7, "\uFF17": 7, \u634C: 8, \u516B: 8, "\uFF18": 8, \u7396: 9, \u4E5D: 9, \u52FE: 9, "\uFF19": 9, \u62FE: "*10", \u5341: "*10", \u5EFF: "*20", \u5345: "*30", \u534C: "*40", \u4F70: "*100", \u767E: "*100", \u7695: "*200", \u4EDF: "*1000", \u5343: "*1000", \u842C: "*10000", \u4E07: "*10000", \u5104: "*100000000", \u4EBF: "*100000000" }; var characters = Object.keys(cnNumbers); var characterList = characters.join(""); var afterManMultipliers = ["\u842C", "\u4E07", "\u5104", "\u4EBF"]; var NUMBER_IN_STRING_REGEX = new RegExp( `(?![0]+)(?:(?:\\d+(?:[.,\\s]\\d+)*)*)(?:[\\d${characterList}]+)`, "gu" ); var reverseMultipliers = { "10": "\u5341", "100": "\u767E", "1000": "\u5343", "10000": "\u842C" }; var SINGLE_ARABIC_NUMBER_REGEX = /\d/gu; // src/utils/addMissingUnits.ts var addMissingUnits = (str) => { const characters2 = [...str]; let result = ""; const reverse = reverseMultipliers; characters2.forEach((character, i) => { if (i === 0) { result += character; } else { const arabic = Number.isNaN(character) ? cnNumbers[character] : Number.parseInt(character); const previousNumber = cnNumbers[characters2[i - 1]] || characters2[i - 1]; const previousCharacterAsMultiplier = reverse[previousNumber.toString().replace("*", "")] ? previousNumber.toString().replace("*", "") : void 0; const nextCharacterArabic = (cnNumbers[characters2[i + 1]] || 0).toString().replace("*", ""); if ( // not a multiplier like '*100': typeof arabic === "number" && // in the 1-9 range: arabic > 0 && arabic < 10 && // previous character is 10, 100, 1000 or 10000: previousCharacterAsMultiplier !== void 0 && // e.g. 1000 < 10000 for 8千3萬, or it's the last character in string: (Number.parseInt(previousCharacterAsMultiplier) < Number.parseInt(nextCharacterArabic) || characters2[i + 1] === void 0) && // For numbers like 十五, there are no other units to be appended at the end previousCharacterAsMultiplier !== "10" ) { const oneOrderSmaller = (Number.parseInt(previousCharacterAsMultiplier) / 10).toString(); const missingMultiplier = reverse[oneOrderSmaller]; result += character + missingMultiplier; } else { result += character; } } }); return result; }; // src/utils/isArabicNumber.ts var isArabicNumber = (character) => { if (!character || character.length !== 1) { throw new Error("Function isArabicNumber expects exactly one character."); } const arabicNumbers = [ "0123456789\uFF10", "\uFF11", "\uFF12", "\uFF13", "\uFF14", "\uFF15", "\uFF16", "\uFF17", "\uFF18", "\uFF19" ]; return arabicNumbers.includes(character); }; // src/utils/isChineseNumber.ts var isChineseNumber = (character) => { if (!character || character.length !== 1) { throw new Error("Function isChineseNumber expects exactly one character."); } return !!cnNumbers[character]; }; // src/utils/sourceStringEndsWithAfterManNumber.ts var sourceStringEndsWithAfterManNumber = (str) => { if (!str) { return str; } const characters2 = [...str].reverse(); for (const character of characters2) { if (SINGLE_ARABIC_NUMBER_REGEX.test(character)) ; if (afterManMultipliers.includes(character)) { return character; } } return null; }; // src/modules/toInteger.ts var toInteger = (source) => { let result = 0; let pairs = []; let str = source.toString(); let currentPair = []; let leadingNumber = Number.parseFloat(str); if (!str || str === "") { throw new Error("Empty strings cannot be converted."); } if (Number.parseFloat(str).toString() === str.trim()) { return Number.parseFloat(str) || 0; } let atLeastOneChineseNumber = false; for (const character of str) { if (characters.includes(character)) { atLeastOneChineseNumber = true; break; } } if (!atLeastOneChineseNumber) { return Number.parseFloat(str) || 0; } str = str.replace(/[,\s]/gu, ""); str = addMissingUnits(str); const maanLikeCharacterAtTheEnd = sourceStringEndsWithAfterManNumber(str); if (maanLikeCharacterAtTheEnd) { const maanLocation = str.lastIndexOf(maanLikeCharacterAtTheEnd); const stringBeforeMaan = str.slice(0, Math.max(0, maanLocation)); let convertedNumberBeforeMaan; if (stringBeforeMaan == null ? void 0 : stringBeforeMaan.trim()) { convertedNumberBeforeMaan = toInteger(stringBeforeMaan); } else { convertedNumberBeforeMaan = 1; } str = convertedNumberBeforeMaan.toString() + str.slice(maanLocation); leadingNumber = Number.parseFloat(str); if (!Number.isNaN(leadingNumber)) { str = str.replace(leadingNumber.toString(), ""); } } const len = str.length; for (let i = 0; i < len; i++) { if (SINGLE_ARABIC_NUMBER_REGEX.test(str[i])) { currentPair.push(Number.parseInt(str[i])); } if (cnNumbers[str[i]]) { let arabic = cnNumbers[str[i]]; if (typeof arabic === "number") { if (currentPair.length > 0) { const string = `${currentPair[0].toString()}${arabic}`.toString(); currentPair[0] = Number.parseInt(string); } else { currentPair.push(arabic); } } else { const [action] = arabic; arabic = Number.parseInt(arabic.replace("*", "")); currentPair.push(arabic); if (i === 0 && action === "*") { currentPair.push(1); pairs.push(currentPair); currentPair = []; } else { if (currentPair.length === 2) { pairs.push(currentPair); currentPair = []; } else { if (afterManMultipliers.includes(str[i])) { let numbersSoFar = 0; pairs.forEach((pair) => { numbersSoFar += pair[0] * pair[1]; }); if (!Number.isNaN(leadingNumber)) { numbersSoFar *= leadingNumber; leadingNumber = Number.NaN; } pairs = [[numbersSoFar, arabic]]; currentPair = []; } else { currentPair.push(1); pairs.push(currentPair); currentPair = []; } } } } } } if (currentPair.length === 1) { currentPair.push(1); pairs.push(currentPair); } if (pairs.length > 0 && !Number.isNaN(leadingNumber)) { pairs[0][0] *= leadingNumber; } pairs.forEach((pair) => { result += pair[0] * pair[1]; }); return result; }; // src/modules/toArabicString.ts var toArabicString = (source, minimumCharactersInNumber = 1) => { if (typeof source !== "string") { return source; } return source.replace(NUMBER_IN_STRING_REGEX, (match) => { if (match.length >= minimumCharactersInNumber) { return toInteger(match.replace(/[\s,_]/gu, "")).toString(); } return match; }); }; exports.addMissingUnits = addMissingUnits; exports.isArabicNumber = isArabicNumber; exports.isChineseNumber = isChineseNumber; exports.toArabicString = toArabicString; exports.toInteger = toInteger;