Skip to content

Instantly share code, notes, and snippets.

@olragon
Last active September 14, 2019 03:33
Show Gist options
  • Save olragon/ffb05cf617f7d1d86d45261c3a71a175 to your computer and use it in GitHub Desktop.
Save olragon/ffb05cf617f7d1d86d45261c3a71a175 to your computer and use it in GitHub Desktop.
Vietnamese number to text @ MIT
/**
* Convert number to text
*
* @param number
* @param prefix
*/
function numberToText(number, prefix = '') {
// số, tên thường, tên hàng chục > 20, tên hàng chục > 10, =< 20
let range1 = [
[0, 'không', '', ''],
[1, 'một', 'mốt'],
[2, 'hai'],
[3, 'ba'],
[4, 'bốn'],
[5, 'năm', 'lăm', 'lăm'],
[6, 'sáu'],
[7, 'bảy'],
[8, 'tám'],
[9, 'chín'],
[10, 'mười']
];
let range2 = [
[1000000000, 'tỷ'],
[1000000, 'triệu'],
[1000, 'ngàn'],
[100, 'trăm'],
];
if (number < 0) {
number *= -1;
return numberToText(number, 'âm ');
}
let decPart = Number.isInteger(number) ? null : ('' + number).match(/\.([0-9]+)/)[1];
let decTxt = !decPart ? '' : ' phẩy ' + decPart.split('').map(n => range1.find(r => r[0] == n)[1]).join(' ');
let intPart = Math.floor(number);
// không -> mười
if (intPart >= 0 && intPart <= 10) {
let r = range1.find(r => r[0] === intPart);
return prefix + r[1] + decTxt;
}
// mười -> mười chín
else if (intPart > 10 && intPart < 20) {
let r = range1.find(r => r[0] === intPart - 10);
let n = r[1];
if (typeof r[3] === 'string') {
n = r[3];
}
return prefix + 'mười ' + n + decTxt;
}
// hai mươi, ba mươi lăm, ...
else if (intPart >= 20 && intPart < 100) {
let result = prefix + ('' + intPart)
.split('')
.map(p => Number(p))
.map((p, pIdx) => {
let r = range1.find(r => r[0] === p);
// ba mươi [mốt] <-- không phải một
if (pIdx > 0 && typeof r[2] === 'string') {
return r[2];
}
return r[1] + (pIdx === 0 ? ' mươi' : '');
}).join(' ');
return (result + decTxt).replace(/[ ]+/g, ' ');
}
// 100 -> ...
else {
let result = '';
for (let i = 0; i < range2.length; i++) {
let r = range2[i];
let roundedDiv = Math.floor(intPart / Number(r[0]));
if (roundedDiv >= 1) {
let remainder = intPart % Number(r[0]);
result += numberToText(roundedDiv);
result += ' ' + r[1];
if (remainder) {
if (remainder < 10) {
result += ' lẻ';
}
result += ' ' + numberToText(remainder);
}
break;
}
}
return prefix + result + decTxt;
}
}
const assert = require('assert');
describe("numberToText", function () {
let testCases = [
[0.000009, 'không phẩy không không không không không chín'],
[0, 'không'],
[10, 'mười'],
[11, 'mười một'],
[21, 'hai mươi mốt'],
[40.5, 'bốn mươi phẩy năm'], // not "bốn mươi không phẩy năm"
[10.0412, 'mười phẩy không bốn một hai'],
[99.9999, 'chín mươi chín phẩy chín chín chín chín'],
[103.05, 'một trăm lẻ ba phẩy không năm'],
[515, 'năm trăm mười lăm'],
[567241, 'năm trăm sáu mươi bảy ngàn hai trăm bốn mươi mốt'],
[1567241, 'một triệu năm trăm sáu mươi bảy ngàn hai trăm bốn mươi mốt'],
[21396541323.005, 'hai mươi mốt tỷ ba trăm chín mươi sáu triệu năm trăm bốn mươi mốt ngàn ba trăm hai mươi ba phẩy không không năm'],
[1000000000000, 'một ngàn tỷ']
];
testCases.forEach((testCase) => {
it(`can read ${testCase[0]} as ${testCase[1]}`, function () {
assert.strictEqual(numberToText(testCase[0] as number), testCase[1]);
});
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment