Files
Gerhard Scheikl 5b2aa5d62b first version
2026-04-28 21:56:11 +02:00

79 lines
2.3 KiB
TypeScript

/**
* Per-locale formatters used in PDF rendering. We pin these to specific
* locales (de-AT for German invoices) so that the output is deterministic
* regardless of the runtime's default locale.
*/
const MONEY_FORMATTERS = new Map<string, Intl.NumberFormat>();
const QTY_FORMATTERS = new Map<string, Intl.NumberFormat>();
const DATE_FORMATTERS = new Map<string, Intl.DateTimeFormat>();
function localeFor(language: string): string {
return language === "en" ? "en-GB" : "de-AT";
}
export function formatMoney(
amount: number | string,
currency: string,
language: string,
): string {
const num = typeof amount === "string" ? Number(amount) : amount;
const key = `${language}|${currency}`;
let f = MONEY_FORMATTERS.get(key);
if (!f) {
f = new Intl.NumberFormat(localeFor(language), {
style: "decimal",
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
MONEY_FORMATTERS.set(key, f);
}
return `${f.format(Number.isFinite(num) ? num : 0)} ${currency}`;
}
export function formatQuantity(qty: number, unit: string, language: string): string {
let f = QTY_FORMATTERS.get(language);
if (!f) {
f = new Intl.NumberFormat(localeFor(language), {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
QTY_FORMATTERS.set(language, f);
}
return `${f.format(qty)} ${unit}`;
}
export function formatDate(date: Date | string, language: string): string {
const d = typeof date === "string" ? new Date(date) : date;
let f = DATE_FORMATTERS.get(language);
if (!f) {
f = new Intl.DateTimeFormat(localeFor(language), {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
DATE_FORMATTERS.set(language, f);
}
return f.format(d);
}
/** Adds days to a date, returning a new Date. */
export function addDays(date: Date, days: number): Date {
const d = new Date(date);
d.setDate(d.getDate() + days);
return d;
}
/**
* Formats a percentage. Tax rates from Shopify can be either 0.20 or 20 — we
* accept both shapes via heuristics.
*/
export function formatTaxRate(rate: number, language: string): string {
const pct = rate <= 1 ? rate * 100 : rate;
const f = new Intl.NumberFormat(localeFor(language), {
minimumFractionDigits: 0,
maximumFractionDigits: 2,
});
return `${f.format(pct)}%`;
}