@gratcy/angka-terbilang-indonesia
Version:
Angka Terbilang in NodeJS for Converting Decimal to an Indonesian Wording format
248 lines (239 loc) • 7.59 kB
text/typescript
// Definisi Array Angka dan Satuan
type LangKey = 'id' | 'en';
interface LangArr {
[key: string]: {
angka: string[];
satuan: string[];
sepuluh: string;
sebelas: string;
belas: string;
seratus: string;
seribu: string;
puluh: string;
ratus: string;
nol: string;
koma: string;
two?: string;
three?: string;
four?: string;
five?: string;
six?: string;
seven?: string;
eight?: string;
nine?: string;
duabelas?: string;
tigabelas?: string;
};
}
const langArr: LangArr = {
id: {
angka: ['', 'satu', 'dua', 'tiga', 'empat', 'lima', 'enam', 'tujuh', 'delapan', 'sembilan'],
satuan: ['', 'ribu', 'juta', 'milyar', 'triliun', 'quadriliun', 'quintiliun', 'sextiliun', 'septiliun', 'oktiliun', 'noniliun', 'desiliun', 'undesiliun', 'duodesiliun', 'tredesiliun', 'quattuordesiliun', 'quindesiliun', 'sexdesiliun', 'septendesiliun', 'oktodesiliun', 'novemdesiliun', 'vigintiliun'],
sepuluh: 'sepuluh',
sebelas: 'sebelas',
belas: 'belas',
seratus: 'seratus',
seribu: 'seribu',
puluh: 'puluh',
ratus: 'ratus',
nol: 'nol',
koma: 'koma',
},
en: {
angka: ['', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'],
satuan: ['', 'thousand', 'million', 'billion', 'trillion', 'quadrillion', 'quintillion', 'sextillion', 'septillion', 'octillion', 'nonillion', 'decillion', 'undecillion', 'duodecillion', 'tredecilion', 'quattuordecilion', 'quindecilion', 'sexdecilion', 'septendecilion', 'octodecilion', 'novemdesiliun', 'vigintillion'],
sepuluh: 'ten',
sebelas: 'eleven',
belas: 'teen',
seratus: 'one hundred',
seribu: 'one thousand',
puluh: 'ty',
ratus: 'hundred',
nol: 'zero',
koma: 'and',
two: 'twen',
three: 'thir',
four: 'for',
five: 'fif',
six: 'six',
seven: 'seven',
eight: 'eight',
nine: 'nine',
duabelas: 'twelve',
tigabelas: 'thirteen',
},
};
interface ToTerbilangConfig {
dec?: string;
lang?: LangKey;
}
let arrAngka: string[] = langArr.id.angka;
let arrSatuan: string[] = langArr.id.satuan;
let lang: LangKey = 'id';
export function toTerbilang(strAngka: string | number, config: ToTerbilangConfig = { dec: '.', lang: 'id' }): string {
strAngka = String(strAngka);
const angkaArr = strAngka.split(config.dec || '.');
if (config.lang === 'en') {
arrAngka = langArr.en.angka;
arrSatuan = langArr.en.satuan;
lang = 'en';
} else {
arrAngka = langArr.id.angka;
arrSatuan = langArr.id.satuan;
lang = 'id';
}
let strA = '';
for (let i = 0; i < angkaArr.length; ++i) {
if (i === 1) {
strA += ` ${langArr[lang].koma} ` + terbilangDecimal(angkaArr[i]);
} else {
strA += terbilang(angkaArr[i]);
}
}
return strA.trim();
}
function replaceString(str: string, rep: string): string {
return str.replace(rep, '');
}
function terbilangDecimal(strAngka: string): string {
let angkaArr = strAngka.split('')
let str = ''
for (let i = 0; i < angkaArr.length; ++i) {
if (angkaArr[i] === '0') {
str += ' ' + langArr[lang].nol
} else {
str += ' ' + langArr[lang].angka[parseInt(angkaArr[i])]
}
}
return str.trim()
}
function terbilang(strAngka: string): string {
if (isNaN(Number(strAngka))) {
return 'Error, input is not a valid number!';
}
const lenAngka = strAngka.length - 1;
if (lenAngka === 0 && Number(strAngka[0]) === 0) {
return langArr[lang].nol;
}
if (lenAngka / 3 > arrSatuan.length) {
return 'Error, number is to big!';
}
let resTerbilang = '';
let cntZero = 0;
for (let i = 0; i <= lenAngka; i++) {
let tmpTerbilang = '';
let posDigit = lenAngka - i;
let grpDigit = posDigit % 3;
let intAngka = Number(strAngka[i]);
switch (intAngka) {
case 1:
switch (grpDigit) {
case 2:
tmpTerbilang += langArr[lang].seratus;
break;
case 1: {
let nextIntAngka = Number(strAngka[i + 1]);
switch (nextIntAngka) {
case 1:
tmpTerbilang += langArr[lang].sebelas;
break;
case 0:
tmpTerbilang += langArr[lang].sepuluh;
break;
default:
if (lang === 'en') {
if (arrAngka[nextIntAngka] === 'two') {
tmpTerbilang += langArr[lang].duabelas;
} else if (arrAngka[nextIntAngka] === 'three') {
tmpTerbilang += langArr[lang].tigabelas;
} else {
// Type-safe teen prefix lookup
const teenPrefixes: Record<string, string | undefined> = {
one: '',
two: langArr[lang].two,
three: langArr[lang].three,
four: langArr[lang].four,
five: langArr[lang].five,
six: langArr[lang].six,
seven: langArr[lang].seven,
eight: langArr[lang].eight,
nine: langArr[lang].nine,
};
const prefix = teenPrefixes[arrAngka[nextIntAngka]] ?? '';
tmpTerbilang += prefix + langArr[lang].belas;
}
} else {
tmpTerbilang += arrAngka[nextIntAngka] + ` ${langArr[lang].belas}`;
}
break;
}
i++;
posDigit--;
grpDigit--;
break;
}
case 0:
if ((intAngka === 1 && posDigit === 3) && (cntZero === 2 || lenAngka === 3)) {
if (resTerbilang !== '') {
resTerbilang += ' ';
}
resTerbilang += langArr[lang].seribu;
cntZero = 0;
continue;
} else {
tmpTerbilang += arrAngka[intAngka];
}
break;
}
break;
case 0:
cntZero++;
break;
default:
tmpTerbilang += arrAngka[intAngka];
switch (grpDigit) {
case 2:
tmpTerbilang += ` ${langArr[lang].ratus}`;
break;
case 1:
if (lang === 'en') {
// Type-safe puluh prefix lookup
const puluhPrefixes: Record<string, string | undefined> = {
one: '',
two: langArr[lang].two,
three: langArr[lang].three,
four: langArr[lang].four,
five: langArr[lang].five,
six: langArr[lang].six,
seven: langArr[lang].seven,
eight: langArr[lang].eight,
nine: langArr[lang].nine,
};
const prefix = puluhPrefixes[arrAngka[intAngka]] ?? '';
tmpTerbilang += prefix + langArr[lang].puluh;
tmpTerbilang = replaceString(tmpTerbilang, arrAngka[intAngka]);
} else {
tmpTerbilang += ` ${langArr[lang].puluh}`;
}
break;
}
break;
}
if (tmpTerbilang !== '') {
if (resTerbilang !== '') {
resTerbilang += ' ' + tmpTerbilang;
} else {
resTerbilang += tmpTerbilang;
}
}
let posSatuan = posDigit / 3;
if (grpDigit === 0 && posSatuan !== 0) {
if (cntZero !== 3) {
resTerbilang += ' ' + arrSatuan[posSatuan];
}
cntZero = 0;
}
}
return resTerbilang;
}
export default toTerbilang;