151 lines
4.9 KiB
JavaScript
151 lines
4.9 KiB
JavaScript
(function (global) {
|
|
const ONES = ['', 'eins', 'zwei', 'drei', 'vier', 'fuenf', 'sechs', 'sieben', 'acht', 'neun'];
|
|
const ONES_COMPOUND = ['', 'ein', 'zwei', 'drei', 'vier', 'fuenf', 'sechs', 'sieben', 'acht', 'neun'];
|
|
const TEENS = ['zehn', 'elf', 'zwoelf', 'dreizehn', 'vierzehn', 'fuenfzehn', 'sechzehn', 'siebzehn', 'achtzehn', 'neunzehn'];
|
|
const TENS = ['', '', 'zwanzig', 'dreissig', 'vierzig', 'fuenfzig', 'sechzig', 'siebzig', 'achtzig', 'neunzig'];
|
|
const DIGIT_WORDS = ['null', 'eins', 'zwei', 'drei', 'vier', 'fuenf', 'sechs', 'sieben', 'acht', 'neun'];
|
|
const SCALES = [
|
|
{ singular: '', plural: '' },
|
|
{ singular: 'Tausend', plural: 'Tausend' },
|
|
{ singular: 'Million', plural: 'Millionen' },
|
|
{ singular: 'Milliarde', plural: 'Milliarden' },
|
|
{ singular: 'Billion', plural: 'Billionen' },
|
|
{ singular: 'Billiarde', plural: 'Billiarden' },
|
|
{ singular: 'Trillion', plural: 'Trillionen' },
|
|
{ singular: 'Trilliarde', plural: 'Trilliarden' },
|
|
{ singular: 'Quadrillion', plural: 'Quadrillionen' }
|
|
];
|
|
|
|
function chunkToWords(value) {
|
|
if (value === 0) {
|
|
return '';
|
|
}
|
|
|
|
if (value === 1) {
|
|
return 'eins';
|
|
}
|
|
|
|
let text = '';
|
|
const hundreds = Math.floor(value / 100);
|
|
const remainder = value % 100;
|
|
|
|
if (hundreds > 0) {
|
|
text += (hundreds === 1 ? 'ein' : ONES_COMPOUND[hundreds]) + 'hundert';
|
|
}
|
|
|
|
if (remainder > 0) {
|
|
if (remainder < 10) {
|
|
text += ONES[remainder];
|
|
} else if (remainder < 20) {
|
|
text += TEENS[remainder - 10];
|
|
} else {
|
|
const ones = remainder % 10;
|
|
const tens = Math.floor(remainder / 10);
|
|
if (ones > 0) {
|
|
text += ONES_COMPOUND[ones] + 'und';
|
|
}
|
|
text += TENS[tens];
|
|
}
|
|
}
|
|
|
|
return text;
|
|
}
|
|
|
|
function normalizeGermanNumberString(value) {
|
|
if (typeof value === 'number') {
|
|
if (!Number.isFinite(value)) {
|
|
return '';
|
|
}
|
|
|
|
return String(value).replace('.', ',');
|
|
}
|
|
|
|
if (typeof value !== 'string') {
|
|
return '';
|
|
}
|
|
|
|
return value.trim().replace(/\s+/g, '');
|
|
}
|
|
|
|
function splitNormalizedNumber(value) {
|
|
const normalized = normalizeGermanNumberString(value);
|
|
if (normalized === '') {
|
|
return null;
|
|
}
|
|
|
|
const sign = normalized.startsWith('-') ? '-' : '';
|
|
const unsigned = sign ? normalized.slice(1) : normalized;
|
|
const cleaned = unsigned.replace(/\./g, '');
|
|
const parts = cleaned.split(',');
|
|
|
|
if (parts.length > 2) {
|
|
return null;
|
|
}
|
|
|
|
const integerPart = (parts[0] || '0').replace(/^0+(?=\d)/, '') || '0';
|
|
const fractionalPart = parts[1] || '';
|
|
|
|
if (!/^\d+$/.test(integerPart) || (fractionalPart !== '' && !/^\d+$/.test(fractionalPart))) {
|
|
return null;
|
|
}
|
|
|
|
return { sign, integerPart, fractionalPart };
|
|
}
|
|
|
|
function integerStringToWords(integerString, sign) {
|
|
if (integerString === '0') {
|
|
return 'null';
|
|
}
|
|
|
|
const groups = [];
|
|
for (let index = integerString.length; index > 0; index -= 3) {
|
|
groups.push(integerString.slice(Math.max(0, index - 3), index));
|
|
}
|
|
|
|
const parts = [];
|
|
for (let i = groups.length - 1; i >= 0; i -= 1) {
|
|
const groupValue = Number(groups[i]);
|
|
if (groupValue === 0) {
|
|
continue;
|
|
}
|
|
|
|
const scale = SCALES[i] || { singular: '', plural: '' };
|
|
if (i >= 2) {
|
|
const prefix = groupValue === 1 ? 'eine' : String(groupValue);
|
|
const label = groupValue === 1 ? scale.singular : scale.plural;
|
|
parts.push(prefix + ' ' + label);
|
|
} else if (i === 1) {
|
|
parts.push(groupValue === 1 ? 'ein ' + scale.singular : String(groupValue) + ' ' + scale.singular);
|
|
} else {
|
|
parts.push(String(groupValue));
|
|
}
|
|
}
|
|
|
|
const result = parts.join('\n');
|
|
return sign === '-' ? 'minus ' + result : result;
|
|
}
|
|
|
|
function numberToGermanText(value) {
|
|
const parsed = splitNormalizedNumber(value);
|
|
if (!parsed) {
|
|
return '';
|
|
}
|
|
|
|
const integerText = integerStringToWords(parsed.integerPart, parsed.sign);
|
|
if (!parsed.fractionalPart || /^0+$/.test(parsed.fractionalPart)) {
|
|
return integerText;
|
|
}
|
|
|
|
const decimalDigits = parsed.fractionalPart
|
|
.split('')
|
|
.map(function (digit) {
|
|
return DIGIT_WORDS[Number(digit)];
|
|
})
|
|
.join(' ');
|
|
|
|
return integerText + '\nKomma ' + decimalDigits;
|
|
}
|
|
|
|
global.numberToGermanText = numberToGermanText;
|
|
})(window);
|