Initial public release

Aurum — a self-hosted personal finance operating system: cash flow,
net worth tracking, budgets, goals, recurring payments, reports,
proactive alerts, and a compound-interest returns calculator, all
bilingual (RU/EN) and mobile-first. Ships as a three-container Docker
Compose stack (Postgres + FastAPI + nginx-served React frontend).

See README.md for setup and CONTRIBUTING.md for development.
This commit is contained in:
ZProger
2026-08-16 18:39:52 +03:00
commit fe9b079c12
199 changed files with 14410 additions and 0 deletions

View File

@@ -0,0 +1,46 @@
import { getCurrency, getLanguage, type Language } from "@/lib/i18n";
/** Maps our app language to the Intl locale used for number/date formatting. */
export function getIntlLocale(language: Language = getLanguage()): string {
return language === "ru" ? "ru-RU" : "en-US";
}
// `currency` defaults to the app's primary currency setting (Settings page)
// — call sites only need to pass it explicitly when formatting a value known
// to be in a *different* currency than that setting.
export function formatCurrency(amount: number | string, currency: string = getCurrency()): string {
const value = typeof amount === "string" ? Number(amount) : amount;
return new Intl.NumberFormat(getIntlLocale(), {
style: "currency",
currency,
maximumFractionDigits: 0,
}).format(value);
}
export function formatSignedCurrency(amount: number | string, currency: string = getCurrency()): string {
const value = typeof amount === "string" ? Number(amount) : amount;
const sign = value > 0 ? "+" : "";
return `${sign}${formatCurrency(value, currency)}`;
}
const MONTH_LABELS_EN = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] as const;
const MONTH_LABELS_RU = ["Янв", "Фев", "Мар", "Апр", "Май", "Июн", "Июл", "Авг", "Сен", "Окт", "Ноя", "Дек"] as const;
export function getMonthLabels(language: Language): readonly string[] {
return language === "ru" ? MONTH_LABELS_RU : MONTH_LABELS_EN;
}
export function formatTransactionDate(isoDate: string): string {
const date = new Date(`${isoDate}T00:00:00`);
return new Intl.DateTimeFormat(getIntlLocale(), { month: "short", day: "numeric" }).format(date);
}
/** Russian noun pluralization: pick the right form for 1/2-4/5+ (with the
* 11-14 exception), e.g. pluralizeRu(3, "актив", "актива", "активов"). */
export function pluralizeRu(count: number, one: string, few: string, many: string): string {
const mod10 = count % 10;
const mod100 = count % 100;
if (mod10 === 1 && mod100 !== 11) return one;
if (mod10 >= 2 && mod10 <= 4 && (mod100 < 12 || mod100 > 14)) return few;
return many;
}