Files
skyview.astronomiemuseum.de/public/js/number_to_german_words.js
T

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);