79 lines
2.3 KiB
TypeScript
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)}%`;
|
|
}
|