n2words
Version:
n2words converts a numerical number into a written one, supports 28 languages and has zero dependencies.
210 lines (180 loc) • 7.09 kB
JavaScript
import AbstractLanguage from '../classes/abstract-language.js';
export class Arabic extends AbstractLanguage {
number;
feminine;
ones = {
masculine: [
'واحد',
'اثنان',
'ثلاثة',
'أربعة',
'خمسة',
'ستة',
'سبعة',
'ثمانية',
'تسعة',
'عشرة',
'أحد عشر',
'اثنا عشر',
'ثلاثة عشر',
'أربعة عشر',
'خمسة عشر',
'ستة عشر',
'سبعة عشر',
'ثمانية عشر',
'تسعة عشر',
],
feminine: [
'واحدة',
'اثنتان',
'ثلاث',
'أربع',
'خمس',
'ست',
'سبع',
'ثمان',
'تسع',
'عشر',
'إحدى عشرة',
'اثنتا عشرة',
'ثلاث عشرة',
'أربع عشرة',
'خمس عشرة',
'ست عشرة',
'سبع عشرة',
'ثماني عشرة',
'تسع عشرة',
]
};
arabicTens = ['عشرون', 'ثلاثون', 'أربعون', 'خمسون', 'ستون', 'سبعون', 'ثمانون', 'تسعون'];
arabicHundreds = ['', 'مائة', 'مئتان', 'ثلاثمائة', 'أربعمائة', 'خمسمائة', 'ستمائة', 'سبعمائة', 'ثمانمائة', 'تسعمائة'];
arabicAppendedTwos = ['مئتا', 'ألفا', 'مليونا', 'مليارا', 'تريليونا', 'كوادريليونا', 'كوينتليونا', 'سكستيليونا'];
arabicTwos = ['مئتان', 'ألفان', 'مليونان', 'ملياران', 'تريليونان', 'كوادريليونان', 'كوينتليونان', 'سكستيليونان'];
arabicGroup = ['مائة', 'ألف', 'مليون', 'مليار', 'تريليون', 'كوادريليون', 'كوينتليون', 'سكستيليون'];
arabicAppendedGroup = ['', 'ألفاً', 'مليوناً', 'ملياراً', 'تريليوناً', 'كوادريليوناً', 'كوينتليوناً', 'سكستيليوناً'];
arabicPluralGroups = ['', 'آلاف', 'ملايين', 'مليارات', 'تريليونات', 'كوادريليونات', 'كوينتليونات', 'سكستيليونات'];
constructor(options) {
options = Object.assign({
negativeWord: 'ناقص',
separatorWord: 'فاصلة',
zero: 'صفر',
feminine: false,
}, options);
super(options);
this.feminine = options.feminine;
}
digitFeminineStatus(digit) {
return this.ones[this.feminine ? 'feminine' : 'masculine'][digit - 1];
}
/**
* Processes the Arabic group number and returns the corresponding Arabic representation.
* @param {number} groupNumber - The number to process. (Range: 1-999)
* @param {number} groupLevel - Group level to process. (See example)
* @returns {string} The Arabic representation of the group number.
* @example 12345678 is processed in blocks of 3: '678' (group 0), '345' (group 1), '12' (group 2).
*/
processArabicGroup(groupNumber, groupLevel) {
let tens = groupNumber % 100;
const hundreds = groupNumber / 100;
let returnValue = '';
if (Math.trunc(hundreds) > 0) {
if (tens == 0 && Math.trunc(hundreds) == 2) {
returnValue = this.arabicTwos[0];
} else {
returnValue = this.arabicHundreds[Math.trunc(hundreds)];
if (returnValue != '' && tens != 0) {
returnValue += ' و';
}
}
}
if (tens > 0 && tens < 20) { // 1 -> 19
if (tens === 2 && Math.trunc(hundreds) === 0 && groupLevel > 0) {
const pow = Math.trunc(Math.log10(Number(this.number)));
if (pow % 3 === 0 && this.number === 2 * Math.pow(10, pow)) {
returnValue = groupNumber === 2 ? this.arabicTwos[Math.trunc(groupLevel)] : this.arabicAppendedTwos[Math.trunc(groupLevel)];
} else {
returnValue = this.arabicTwos[Math.trunc(groupLevel)];
}
} else if (tens === 1 && groupLevel > 0) {
returnValue += this.arabicGroup[Math.trunc(groupLevel)];
} else {
returnValue += this.digitFeminineStatus(Math.trunc(tens), groupLevel);
}
} else if (tens >= 20) { // 20 -> 99
const ones = tens % 10;
const tensIndex = (tens / 10) - 2;
if (ones > 0) {
returnValue += this.digitFeminineStatus(ones, groupLevel);
}
if (returnValue != '' && ones != 0) {
returnValue += ' و';
}
returnValue += this.arabicTens[Math.trunc(tensIndex)];
}
return returnValue;
}
/**
* Converts a number to its cardinal representation in Arabic.
* It process by blocks of 3 digits.
* @param {number} number - The number to convert.
* @returns {string} The cardinal representation of the number in Arabic.
*/
toCardinal(number) {
if (number == 0) {
return this.zero;
}
this.number = number;
let temporaryNumber = number;
let temporaryNumberDec;
let group = 0;
let returnValue = '';
// Loop until number has been reduced to zero or less
while (temporaryNumber > 0) {
temporaryNumberDec = temporaryNumber;
// Get the remaining value after dividing by 1000
const numberToProcess = Number(temporaryNumberDec % 1000n); // Maximum: 999
temporaryNumber = temporaryNumberDec / 1000n;
// Process "group"
const groupDescription = this.processArabicGroup(numberToProcess, group);
// Did the group return anything?
if (groupDescription != '') {
// Is this after the first "group" ? Because nothing is appeded after group 0.
if (group > 0) { // hundreds, thousands, etc...
// Is the return value still empty ?
if (returnValue != '') {
returnValue = ' و' + returnValue;
}
if (numberToProcess > 2) {
if (numberToProcess % 100 == 1) {
returnValue = this.arabicGroup[group] + ' ' + returnValue;
} else {
if (numberToProcess >= 3 && numberToProcess <= 10) {
returnValue = this.arabicPluralGroups[group] + ' ' + returnValue;
} else {
returnValue = returnValue == '' ? this.arabicGroup[group] + ' ' + returnValue : this.arabicAppendedGroup[group] + ' ' + returnValue;
}
}
}
}
// Add "group" return to output variable
// NOTE: Language is right-to-left
returnValue = groupDescription + ' ' + returnValue;
}
// Increase group level/number
group++;
}
// Replace multiple spaces with one space
returnValue = returnValue.replaceAll(/ +/g, ' ');
return returnValue.trim();
}
}
/**
* Converts a value to cardinal (written) form.
* @param {number|string|bigint} value Number to be convert.
* @param {object} [options] Options for class.
* @returns {string} Value in cardinal (written) format.
* @throws {Error} Value cannot be invalid.
*/
export default function floatToCardinal (value, options = {}) {
return new Arabic(options).floatToCardinal(value);
}