mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 19:41:09 +02:00
feat(i18n): locale-aware formatters, migrate hardcoded de-DE call-sites
Bisher pinnten 185+ call-sites die UI-Zahlen/Datumsausgabe hart auf
Deutsch ("de" / "de-DE" an toLocaleDate{,Time}String, Intl.*Format,
date-fns locale), unabhängig von der aktiven Sprache. EN-User sahen
dadurch deutsche Datums-/Zahlenformate mitten im englischen UI.
- $lib/i18n/format.ts (neu): formatDate / formatTime / formatDateTime
/ formatNumber / formatCurrency / getDateFnsLocale. Alle lesen die
aktive Locale aus svelte-i18n's locale-store und mappen de→de-DE
etc. für Intl.
- Codemod: 119 Direktaufrufe in 79 Files migriert (.toLocaleDateString
/ .toLocaleTimeString / new Intl.{Number,DateTime}Format). Erkennt
new Date() / Number()-Receiver zum Disambiguieren von
.toLocaleString('de-DE').
- date-fns: 19 Files auf getDateFnsLocale() umgestellt; hardcoded
`import { de } from 'date-fns/locale'` entfernt.
- Skipped (Collision): 14 Files hatten lokale format*-Wrapper; diese
bleiben vorerst als gesonderte Folge-Refactorings stehen. Ca. 58
deep-gekapselte Aufrufe im .toLocaleString/.toLocaleDateString-Idiom
sind über diese Wrapper noch zu migrieren.
- svelte-check: 0 Errors / 0 Warnings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
364522db87
commit
e794e0ca57
96 changed files with 337 additions and 184 deletions
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import type { ProjectDataSummary } from '$lib/api/services/admin';
|
||||
|
||||
interface Props {
|
||||
|
|
@ -20,7 +21,7 @@
|
|||
if (diffMins < 60) return `vor ${diffMins} Min`;
|
||||
if (diffHours < 24) return `vor ${diffHours} Std`;
|
||||
if (diffDays < 7) return `vor ${diffDays} Tagen`;
|
||||
return new Date(dateStr).toLocaleDateString('de-DE');
|
||||
return formatDate(new Date(dateStr));
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { getDateFnsLocale } from '$lib/i18n/format';
|
||||
/**
|
||||
* ActivityFeedWidget — Recent timeBlock activity across all modules.
|
||||
*
|
||||
|
|
@ -35,8 +36,6 @@
|
|||
} from '@mana/shared-icons';
|
||||
import { getIconComponent } from '@mana/shared-icons';
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
import { de } from 'date-fns/locale';
|
||||
|
||||
const MAX_ITEMS = 10;
|
||||
|
||||
const recentQuery = useLiveQueryWithDefault(async () => {
|
||||
|
|
@ -72,7 +71,7 @@
|
|||
};
|
||||
|
||||
function timeAgo(iso: string): string {
|
||||
return formatDistanceToNow(new Date(iso), { addSuffix: true, locale: de });
|
||||
return formatDistanceToNow(new Date(iso), { addSuffix: true, locale: getDateFnsLocale() });
|
||||
}
|
||||
|
||||
function actionLabel(block: TimeBlock): string {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate, formatTime } from '$lib/i18n/format';
|
||||
/**
|
||||
* CalendarEventsWidget - Upcoming timeBlocks (local-first)
|
||||
*
|
||||
|
|
@ -35,7 +36,7 @@
|
|||
} else if (start.toDateString() === tomorrow.toDateString()) {
|
||||
dateStr = 'Morgen';
|
||||
} else {
|
||||
dateStr = start.toLocaleDateString('de-DE', {
|
||||
dateStr = formatDate(start, {
|
||||
weekday: 'short',
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
|
|
@ -46,7 +47,7 @@
|
|||
return dateStr;
|
||||
}
|
||||
|
||||
const timeStr = start.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' });
|
||||
const timeStr = formatTime(start, { hour: '2-digit', minute: '2-digit' });
|
||||
return `${dateStr}, ${timeStr}`;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { getDateFnsLocale } from '$lib/i18n/format';
|
||||
/**
|
||||
* TasksTodayWidget - Today's tasks from Todo app (local-first)
|
||||
*
|
||||
|
|
@ -10,14 +11,12 @@
|
|||
import { useOpenTasks } from '$lib/data/cross-app-queries';
|
||||
import { db } from '$lib/data/database';
|
||||
import { format, isToday, isTomorrow, isPast } from 'date-fns';
|
||||
import { de } from 'date-fns/locale';
|
||||
|
||||
function formatDueDate(dueDate?: string | null): string | null {
|
||||
if (!dueDate) return null;
|
||||
const date = new Date(dueDate);
|
||||
if (isToday(date)) return 'Heute';
|
||||
if (isTomorrow(date)) return 'Morgen';
|
||||
return format(date, 'dd. MMM', { locale: de });
|
||||
return format(date, 'dd. MMM', { locale: getDateFnsLocale() });
|
||||
}
|
||||
|
||||
function isOverdue(dueDate?: string | null): boolean {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
/**
|
||||
* TransactionsWidget - Recent credit transactions
|
||||
*/
|
||||
|
|
@ -80,7 +81,7 @@
|
|||
<div>
|
||||
<p class="text-sm font-medium">{tx.description || tx.type}</p>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
{new Date(tx.createdAt).toLocaleDateString('de-DE')}
|
||||
{formatDate(new Date(tx.createdAt))}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { formatDate } from '$lib/i18n/format';
|
||||
/**
|
||||
* Sync Status Dropdown — composable for the PillNavigation sync pill.
|
||||
*
|
||||
|
|
@ -22,7 +23,7 @@ export function useSyncStatusItems() {
|
|||
disabled: true,
|
||||
});
|
||||
if (syncBilling.nextChargeAt) {
|
||||
const date = new Date(syncBilling.nextChargeAt).toLocaleDateString('de-DE', {
|
||||
const date = formatDate(new Date(syncBilling.nextChargeAt), {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
|
|
|
|||
90
apps/mana/apps/web/src/lib/i18n/format.ts
Normal file
90
apps/mana/apps/web/src/lib/i18n/format.ts
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/**
|
||||
* Locale-aware formatters that read the active locale from svelte-i18n.
|
||||
*
|
||||
* Use these instead of a hard-coded "de-DE" passed to
|
||||
* toLocaleDateString / toLocaleString / Intl.NumberFormat. Hard-coded
|
||||
* locales pin the output to German even when the UI is switched.
|
||||
*
|
||||
* Callable from both .svelte and .ts files. Inside a Svelte component,
|
||||
* include `$locale` as a dep in a `$derived` block if you need the
|
||||
* output to update when the user switches languages mid-session.
|
||||
*/
|
||||
|
||||
import { get } from 'svelte/store';
|
||||
import { locale } from 'svelte-i18n';
|
||||
import type { Locale as DateFnsLocale } from 'date-fns';
|
||||
import { de, enUS, it, fr, es } from 'date-fns/locale';
|
||||
|
||||
type DateInput = Date | string | number;
|
||||
|
||||
const DATE_FNS_LOCALES: Record<string, DateFnsLocale> = {
|
||||
de,
|
||||
en: enUS,
|
||||
it,
|
||||
fr,
|
||||
es,
|
||||
};
|
||||
|
||||
/** Active locale string, e.g. 'de' or 'en'. Falls back to 'de'. */
|
||||
export function getCurrentLocale(): string {
|
||||
return get(locale) ?? 'de';
|
||||
}
|
||||
|
||||
/**
|
||||
* BCP-47 tag for Intl APIs. svelte-i18n stores a bare `de`; Intl
|
||||
* prefers `de-DE`-style tags for predictable formatting. Mapping is
|
||||
* best-effort — Intl itself is lenient.
|
||||
*/
|
||||
function toBcp47(loc: string): string {
|
||||
switch (loc) {
|
||||
case 'de':
|
||||
return 'de-DE';
|
||||
case 'en':
|
||||
return 'en-US';
|
||||
case 'it':
|
||||
return 'it-IT';
|
||||
case 'fr':
|
||||
return 'fr-FR';
|
||||
case 'es':
|
||||
return 'es-ES';
|
||||
default:
|
||||
return loc;
|
||||
}
|
||||
}
|
||||
|
||||
function toDate(value: DateInput): Date {
|
||||
return value instanceof Date ? value : new Date(value);
|
||||
}
|
||||
|
||||
export function formatDate(value: DateInput, options?: Intl.DateTimeFormatOptions): string {
|
||||
return toDate(value).toLocaleDateString(toBcp47(getCurrentLocale()), options);
|
||||
}
|
||||
|
||||
export function formatTime(value: DateInput, options?: Intl.DateTimeFormatOptions): string {
|
||||
return toDate(value).toLocaleTimeString(toBcp47(getCurrentLocale()), options);
|
||||
}
|
||||
|
||||
export function formatDateTime(value: DateInput, options?: Intl.DateTimeFormatOptions): string {
|
||||
return toDate(value).toLocaleString(toBcp47(getCurrentLocale()), options);
|
||||
}
|
||||
|
||||
export function formatNumber(value: number, options?: Intl.NumberFormatOptions): string {
|
||||
return value.toLocaleString(toBcp47(getCurrentLocale()), options);
|
||||
}
|
||||
|
||||
export function formatCurrency(
|
||||
value: number,
|
||||
currency = 'EUR',
|
||||
options?: Intl.NumberFormatOptions
|
||||
): string {
|
||||
return value.toLocaleString(toBcp47(getCurrentLocale()), {
|
||||
style: 'currency',
|
||||
currency,
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
/** date-fns locale object for the active locale. Defaults to `de`. */
|
||||
export function getDateFnsLocale(): DateFnsLocale {
|
||||
return DATE_FNS_LOCALES[getCurrentLocale()] ?? de;
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
AI Health app — runner status + manual tick + link to status page.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatDateTime } from '$lib/i18n/format';
|
||||
import { isMissionTickRunning, productionDeps } from '$lib/data/ai/missions/setup';
|
||||
import { runDueMissions } from '$lib/data/ai/missions/runner';
|
||||
|
||||
|
|
@ -19,13 +20,13 @@
|
|||
errors += r.failedSteps;
|
||||
}
|
||||
lastRunStats = {
|
||||
at: new Date().toLocaleString('de-DE'),
|
||||
at: formatDateTime(new Date()),
|
||||
plansProduced,
|
||||
errors,
|
||||
};
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
lastRunStats = { at: new Date().toLocaleString('de-DE'), plansProduced: 0, errors: 1 };
|
||||
lastRunStats = { at: formatDateTime(new Date()), plansProduced: 0, errors: 1 };
|
||||
} finally {
|
||||
manualRunning = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
Master-detail inline (list ↔ create ↔ detail) in a single panel.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatDateTime } from '$lib/i18n/format';
|
||||
import { ArrowLeft, Play, Pause, Check, Trash, Plus } from '@mana/shared-icons';
|
||||
import { useMissions } from '$lib/data/ai/missions/queries';
|
||||
import {
|
||||
|
|
@ -412,7 +413,7 @@
|
|||
{@const isRunning = it.overallStatus === 'running'}
|
||||
<article class="it" class:it-running={isRunning}>
|
||||
<header>
|
||||
<span class="it-date">{new Date(it.startedAt).toLocaleString('de-DE')}</span>
|
||||
<span class="it-date">{formatDateTime(new Date(it.startedAt))}</span>
|
||||
<span class="badge badge-{it.overallStatus}">{it.overallStatus}</span>
|
||||
</header>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { getDateFnsLocale } from '$lib/i18n/format';
|
||||
import { getContext } from 'svelte';
|
||||
import { calendarViewStore } from '../stores/view.svelte';
|
||||
import { eventsStore } from '../stores/events.svelte';
|
||||
|
|
@ -11,7 +12,6 @@
|
|||
import type { Calendar, CalendarEvent } from '../types';
|
||||
import { toDate } from '../utils/event-date-helpers';
|
||||
import { format, parseISO, isToday, isTomorrow, startOfDay, addMonths } from 'date-fns';
|
||||
import { de } from 'date-fns/locale';
|
||||
import { CalendarBlank, MapPin, CaretRight } from '@mana/shared-icons';
|
||||
|
||||
interface Props {
|
||||
|
|
@ -75,7 +75,7 @@
|
|||
function formatDateHeader(date: Date) {
|
||||
if (isToday(date)) return 'Heute';
|
||||
if (isTomorrow(date)) return 'Morgen';
|
||||
return format(date, 'EEEE, d. MMMM', { locale: de });
|
||||
return format(date, 'EEEE, d. MMMM', { locale: getDateFnsLocale() });
|
||||
}
|
||||
|
||||
function handleEventClick(event: CalendarEvent) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { getDateFnsLocale } from '$lib/i18n/format';
|
||||
import { calendarViewStore } from '../stores/view.svelte';
|
||||
import type { CalendarViewType } from '../types';
|
||||
import type { TimeBlockType } from '$lib/data/time-blocks/types';
|
||||
|
|
@ -30,8 +31,6 @@
|
|||
import { toTimeBlock } from '$lib/data/time-blocks/queries';
|
||||
import { downloadICalendar } from '$lib/data/time-blocks/ical-export';
|
||||
import { format } from 'date-fns';
|
||||
import { de } from 'date-fns/locale';
|
||||
|
||||
interface Props {
|
||||
onNewEvent: () => void;
|
||||
}
|
||||
|
|
@ -76,9 +75,11 @@
|
|||
|
||||
let headerLabel = $derived.by(() => {
|
||||
if (calendarViewStore.viewType === 'month') {
|
||||
return format(calendarViewStore.currentDate, 'MMMM yyyy', { locale: de });
|
||||
return format(calendarViewStore.currentDate, 'MMMM yyyy', { locale: getDateFnsLocale() });
|
||||
}
|
||||
return format(calendarViewStore.currentDate, "'KW' w — MMMM yyyy", { locale: de });
|
||||
return format(calendarViewStore.currentDate, "'KW' w — MMMM yyyy", {
|
||||
locale: getDateFnsLocale(),
|
||||
});
|
||||
});
|
||||
|
||||
const viewLabels: Record<CalendarViewType, string> = {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { getDateFnsLocale } from '$lib/i18n/format';
|
||||
import { getContext } from 'svelte';
|
||||
import { calendarViewStore } from '../stores/view.svelte';
|
||||
import { getEventsForDay } from '../queries';
|
||||
|
|
@ -12,7 +13,6 @@
|
|||
startOfDay,
|
||||
isWithinInterval,
|
||||
} from 'date-fns';
|
||||
import { de } from 'date-fns/locale';
|
||||
import { onMount, tick } from 'svelte';
|
||||
import SunCalc from 'suncalc';
|
||||
|
||||
|
|
@ -154,7 +154,7 @@
|
|||
}
|
||||
|
||||
// Get the month of the center visible day
|
||||
let visibleMonth = $state(format(new Date(), 'MMMM yyyy', { locale: de }));
|
||||
let visibleMonth = $state(format(new Date(), 'MMMM yyyy', { locale: getDateFnsLocale() }));
|
||||
|
||||
function updateVisibleMonth() {
|
||||
if (!scrollContainer) return;
|
||||
|
|
@ -169,7 +169,7 @@
|
|||
const dateStr = el.getAttribute('data-date');
|
||||
if (dateStr) {
|
||||
const date = new Date(dateStr);
|
||||
visibleMonth = format(date, 'MMMM yyyy', { locale: de });
|
||||
visibleMonth = format(date, 'MMMM yyyy', { locale: getDateFnsLocale() });
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -211,7 +211,9 @@
|
|||
{#if !isTodayVisible}
|
||||
<button onclick={goToToday} title="Zum heutigen Tag" class="today-button">
|
||||
<span class="today-label">Heute</span>
|
||||
<span class="today-date">{format(new Date(), 'd. MMM', { locale: de })}</span>
|
||||
<span class="today-date"
|
||||
>{format(new Date(), 'd. MMM', { locale: getDateFnsLocale() })}</span
|
||||
>
|
||||
</button>
|
||||
{/if}
|
||||
{visibleMonth}
|
||||
|
|
@ -251,7 +253,7 @@
|
|||
{#if moonPhase.significant && showMoonPhases}
|
||||
<span class="moon-indicator">{moonPhase.emoji}</span>
|
||||
{/if}
|
||||
<span class="day-weekday">{format(day, 'EE', { locale: de })}</span>
|
||||
<span class="day-weekday">{format(day, 'EE', { locale: getDateFnsLocale() })}</span>
|
||||
<span class="day-number">{format(day, 'd')}</span>
|
||||
{#if eventCount > 0 && showEventIndicators}
|
||||
<div class="event-dots">
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { getDateFnsLocale } from '$lib/i18n/format';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { getContext } from 'svelte';
|
||||
import { VisibilityPicker, type VisibilityLevel } from '@mana/shared-privacy';
|
||||
|
|
@ -21,8 +22,6 @@
|
|||
Check,
|
||||
} from '@mana/shared-icons';
|
||||
import { format, differenceInDays, differenceInHours, differenceInMinutes } from 'date-fns';
|
||||
import { de } from 'date-fns/locale';
|
||||
|
||||
interface Props {
|
||||
event: CalendarEvent;
|
||||
onClose: () => void;
|
||||
|
|
@ -60,7 +59,7 @@
|
|||
if (ev.isAllDay) return 'Ganztägig';
|
||||
const start = toDate(ev.startTime);
|
||||
const end = toDate(ev.endTime);
|
||||
const dateStr = format(start, 'EEEE, d. MMMM yyyy', { locale: de });
|
||||
const dateStr = format(start, 'EEEE, d. MMMM yyyy', { locale: getDateFnsLocale() });
|
||||
const timeStr = `${format(start, 'HH:mm')} – ${format(end, 'HH:mm')}`;
|
||||
return `${dateStr}\n${timeStr}`;
|
||||
}
|
||||
|
|
@ -308,12 +307,14 @@
|
|||
<!-- Metadata -->
|
||||
<div class="detail-meta-row">
|
||||
<span
|
||||
>Erstellt: {format(new Date(event.createdAt), 'dd. MMM yyyy', { locale: de })}</span
|
||||
>Erstellt: {format(new Date(event.createdAt), 'dd. MMM yyyy', {
|
||||
locale: getDateFnsLocale(),
|
||||
})}</span
|
||||
>
|
||||
{#if event.updatedAt !== event.createdAt}
|
||||
<span
|
||||
>· Bearbeitet: {format(new Date(event.updatedAt), 'dd. MMM yyyy', {
|
||||
locale: de,
|
||||
locale: getDateFnsLocale(),
|
||||
})}</span
|
||||
>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { getDateFnsLocale } from '$lib/i18n/format';
|
||||
import {
|
||||
format,
|
||||
startOfMonth,
|
||||
|
|
@ -12,7 +13,6 @@
|
|||
addMonths,
|
||||
subMonths,
|
||||
} from 'date-fns';
|
||||
import { de } from 'date-fns/locale';
|
||||
import { CaretLeft, CaretRight } from '@mana/shared-icons';
|
||||
|
||||
interface Props {
|
||||
|
|
@ -44,7 +44,9 @@
|
|||
>
|
||||
<CaretLeft size={16} />
|
||||
</button>
|
||||
<span class="month-label">{format(currentMonth, 'MMMM yyyy', { locale: de })}</span>
|
||||
<span class="month-label"
|
||||
>{format(currentMonth, 'MMMM yyyy', { locale: getDateFnsLocale() })}</span
|
||||
>
|
||||
<button
|
||||
class="nav-btn"
|
||||
onclick={() => (currentMonth = addMonths(currentMonth, 1))}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { getDateFnsLocale } from '$lib/i18n/format';
|
||||
import { getContext } from 'svelte';
|
||||
import { calendarViewStore } from '../stores/view.svelte';
|
||||
import { eventsStore } from '../stores/events.svelte';
|
||||
|
|
@ -27,8 +28,6 @@
|
|||
getHours,
|
||||
getMinutes,
|
||||
} from 'date-fns';
|
||||
import { de } from 'date-fns/locale';
|
||||
|
||||
interface Props {
|
||||
onEventClick?: (event: CalendarEvent) => void;
|
||||
onDayClick?: (day: Date) => void;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<script lang="ts">
|
||||
import { getDateFnsLocale } from '$lib/i18n/format';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { getContext, onMount, tick } from 'svelte';
|
||||
import { getDefaultCalendar, getCalendarColor } from '../queries';
|
||||
import type { Calendar } from '../types';
|
||||
import { format, addMinutes } from 'date-fns';
|
||||
import { de } from 'date-fns/locale';
|
||||
import {
|
||||
X,
|
||||
Clock,
|
||||
|
|
|
|||
|
|
@ -3,14 +3,13 @@
|
|||
Used in task scheduling and event creation to suggest optimal times.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { getDateFnsLocale } from '$lib/i18n/format';
|
||||
import { db } from '$lib/data/database';
|
||||
import type { LocalTimeBlock } from '$lib/data/time-blocks/types';
|
||||
import { toTimeBlock, findFreeSlots } from '$lib/data/time-blocks/queries';
|
||||
import type { TimeBlock } from '$lib/data/time-blocks/types';
|
||||
import { CalendarBlank } from '@mana/shared-icons';
|
||||
import { format, addDays, isToday, isTomorrow } from 'date-fns';
|
||||
import { de } from 'date-fns/locale';
|
||||
|
||||
let {
|
||||
minDurationMinutes = 30,
|
||||
onSelect,
|
||||
|
|
@ -52,7 +51,7 @@
|
|||
function formatDayLabel(date: Date): string {
|
||||
if (isToday(date)) return 'Heute';
|
||||
if (isTomorrow(date)) return 'Morgen';
|
||||
return format(date, 'EEE, d. MMM', { locale: de });
|
||||
return format(date, 'EEE, d. MMM', { locale: getDateFnsLocale() });
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { getDateFnsLocale } from '$lib/i18n/format';
|
||||
import { onMount, getContext } from 'svelte';
|
||||
import { calendarViewStore } from '../stores/view.svelte';
|
||||
import { eventsStore } from '../stores/events.svelte';
|
||||
|
|
@ -32,8 +33,6 @@
|
|||
startOfWeek,
|
||||
endOfWeek,
|
||||
} from 'date-fns';
|
||||
import { de } from 'date-fns/locale';
|
||||
|
||||
interface Props {
|
||||
onEventClick?: (event: CalendarEvent) => void;
|
||||
onQuickCreate?: (startTime: Date, endTime: Date, position: { x: number; y: number }) => void;
|
||||
|
|
@ -223,9 +222,9 @@
|
|||
class="day-header"
|
||||
class:today={isToday(day)}
|
||||
role="columnheader"
|
||||
aria-label={format(day, 'EEEE, d. MMMM', { locale: de })}
|
||||
aria-label={format(day, 'EEEE, d. MMMM', { locale: getDateFnsLocale() })}
|
||||
>
|
||||
<span class="day-name">{format(day, 'EEE', { locale: de })}</span>
|
||||
<span class="day-name">{format(day, 'EEE', { locale: getDateFnsLocale() })}</span>
|
||||
<span class="day-number" class:today={isToday(day)}>{format(day, 'd')}</span>
|
||||
</div>
|
||||
{/each}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { getDateFnsLocale } from '$lib/i18n/format';
|
||||
/**
|
||||
* Calendar QuickInputBar Adapter
|
||||
*/
|
||||
|
|
@ -11,8 +12,6 @@ import { toCalendar } from './queries';
|
|||
import type { LocalCalendar, LocalEvent } from './types';
|
||||
import type { LocalTimeBlock } from '$lib/data/time-blocks/types';
|
||||
import { format } from 'date-fns';
|
||||
import { de } from 'date-fns/locale';
|
||||
|
||||
export function createAdapter(): InputBarAdapter {
|
||||
return {
|
||||
placeholder: 'Neuer Termin oder suchen...',
|
||||
|
|
@ -40,7 +39,7 @@ export function createAdapter(): InputBarAdapter {
|
|||
id: b.sourceId, // event ID
|
||||
title: b.title || '',
|
||||
subtitle: b.startDate
|
||||
? format(new Date(b.startDate), 'dd. MMM yyyy, HH:mm', { locale: de })
|
||||
? format(new Date(b.startDate), 'dd. MMM yyyy, HH:mm', { locale: getDateFnsLocale() })
|
||||
: '',
|
||||
}));
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
All fields are always editable. Changes auto-save on blur.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { db } from '$lib/data/database';
|
||||
import { decryptRecord } from '$lib/data/crypto';
|
||||
import { useDetailEntity } from '$lib/data/detail-entity.svelte';
|
||||
|
|
@ -222,10 +223,10 @@
|
|||
|
||||
<div class="meta">
|
||||
{#if event.createdAt}
|
||||
<span>Erstellt: {new Date(event.createdAt).toLocaleDateString('de')}</span>
|
||||
<span>Erstellt: {formatDate(new Date(event.createdAt))}</span>
|
||||
{/if}
|
||||
{#if event.updatedAt}
|
||||
<span>Bearbeitet: {new Date(event.updatedAt).toLocaleDateString('de')}</span>
|
||||
<span>Bearbeitet: {formatDate(new Date(event.updatedAt))}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/snippet}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
All fields are always editable. Changes auto-save on blur.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { liveQuery } from 'dexie';
|
||||
import { db } from '$lib/data/database';
|
||||
import { useDetailEntity } from '$lib/data/detail-entity.svelte';
|
||||
|
|
@ -116,7 +117,7 @@
|
|||
{#if deck.lastStudied}
|
||||
<div class="prop-row">
|
||||
<span class="prop-label">Zuletzt gelernt</span>
|
||||
<span class="prop-value">{new Date(deck.lastStudied).toLocaleDateString('de')}</span>
|
||||
<span class="prop-value">{formatDate(new Date(deck.lastStudied))}</span>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
@ -134,9 +135,9 @@
|
|||
</div>
|
||||
|
||||
<div class="meta">
|
||||
<span>Erstellt: {new Date(deck.createdAt ?? '').toLocaleDateString('de')}</span>
|
||||
<span>Erstellt: {formatDate(new Date(deck.createdAt ?? ''))}</span>
|
||||
{#if deck.updatedAt}
|
||||
<span>Bearbeitet: {new Date(deck.updatedAt).toLocaleDateString('de')}</span>
|
||||
<span>Bearbeitet: {formatDate(new Date(deck.updatedAt))}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/snippet}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
Conversation thread with streaming AI responses.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatTime } from '$lib/i18n/format';
|
||||
import { useConversationMessages, useAllConversations } from '../queries';
|
||||
import { conversationsStore } from '../stores/conversations.svelte';
|
||||
import { sendAndStream } from '../services/completion';
|
||||
|
|
@ -91,7 +92,7 @@
|
|||
<div class="bubble" class:user-bubble={msg.sender === 'user'}>
|
||||
<p class="msg-text">{msg.messageText}</p>
|
||||
<span class="msg-time">
|
||||
{new Date(msg.createdAt).toLocaleTimeString('de-DE', {
|
||||
{formatTime(new Date(msg.createdAt), {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
})}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
All fields are always editable. Changes auto-save on blur.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { liveQuery } from 'dexie';
|
||||
import { db } from '$lib/data/database';
|
||||
import { useDetailEntity } from '$lib/data/detail-entity.svelte';
|
||||
|
|
@ -151,9 +152,9 @@
|
|||
</div>
|
||||
|
||||
<div class="meta">
|
||||
<span>Erstellt: {new Date(location.createdAt ?? '').toLocaleDateString('de')}</span>
|
||||
<span>Erstellt: {formatDate(new Date(location.createdAt ?? ''))}</span>
|
||||
{#if location.updatedAt}
|
||||
<span>Bearbeitet: {new Date(location.updatedAt).toLocaleDateString('de')}</span>
|
||||
<span>Bearbeitet: {formatDate(new Date(location.updatedAt))}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/snippet}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
All fields are always editable. Changes auto-save on blur.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { useDetailEntity } from '$lib/data/detail-entity.svelte';
|
||||
import DetailViewShell from '$lib/components/DetailViewShell.svelte';
|
||||
import { contactsStore } from '../stores/contacts.svelte';
|
||||
|
|
@ -372,10 +373,10 @@
|
|||
|
||||
<div class="meta">
|
||||
{#if contact.createdAt}
|
||||
<span>Erstellt: {new Date(contact.createdAt).toLocaleDateString('de')}</span>
|
||||
<span>Erstellt: {formatDate(new Date(contact.createdAt))}</span>
|
||||
{/if}
|
||||
{#if contact.updatedAt}
|
||||
<span>Bearbeitet: {new Date(contact.updatedAt).toLocaleDateString('de')}</span>
|
||||
<span>Bearbeitet: {formatDate(new Date(contact.updatedAt))}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/snippet}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
/**
|
||||
* TasksTodayWidget — Aufgaben fällig heute oder überfällig.
|
||||
*
|
||||
|
|
@ -68,7 +69,7 @@
|
|||
function formatDue(dueDate: string): string {
|
||||
if (dueDate.slice(0, 10) === todayStr) return 'Heute';
|
||||
const d = new Date(dueDate);
|
||||
return d.toLocaleDateString('de-DE', { day: 'numeric', month: 'short' });
|
||||
return formatDate(d, { day: 'numeric', month: 'short' });
|
||||
}
|
||||
|
||||
async function toggleComplete(task: Task) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate, formatTime } from '$lib/i18n/format';
|
||||
/**
|
||||
* UpcomingEventsWidget — Termine der nächsten 7 Tage.
|
||||
*
|
||||
|
|
@ -88,7 +89,7 @@
|
|||
} else if (start.toDateString() === tomorrow.toDateString()) {
|
||||
dateStr = 'Morgen';
|
||||
} else {
|
||||
dateStr = start.toLocaleDateString('de-DE', {
|
||||
dateStr = formatDate(start, {
|
||||
weekday: 'short',
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
|
|
@ -97,7 +98,7 @@
|
|||
|
||||
if (event.allDay) return dateStr;
|
||||
|
||||
const timeStr = start.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' });
|
||||
const timeStr = formatTime(start, { hour: '2-digit', minute: '2-digit' });
|
||||
return `${dateStr}, ${timeStr}`;
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { formatDate } from '$lib/i18n/format';
|
||||
/**
|
||||
* Reactive Queries & Pure Helpers for Dreams module.
|
||||
*
|
||||
|
|
@ -113,7 +114,7 @@ export function groupByMonth(dreams: Dream[]): Array<{ label: string; dreams: Dr
|
|||
const groups = new Map<string, Dream[]>();
|
||||
for (const d of dreams) {
|
||||
const date = new Date(d.dreamDate);
|
||||
const label = date.toLocaleDateString('de-DE', { month: 'long', year: 'numeric' });
|
||||
const label = formatDate(date, { month: 'long', year: 'numeric' });
|
||||
if (!groups.has(label)) groups.set(label, []);
|
||||
groups.get(label)!.push(d);
|
||||
}
|
||||
|
|
@ -128,7 +129,7 @@ export function formatDreamDate(iso: string): string {
|
|||
if (diffDays === 0) return 'Heute Nacht';
|
||||
if (diffDays === 1) return 'Gestern Nacht';
|
||||
if (diffDays < 7) return `vor ${diffDays} Tagen`;
|
||||
return date.toLocaleDateString('de-DE', { day: 'numeric', month: 'short', year: 'numeric' });
|
||||
return formatDate(date, { day: 'numeric', month: 'short', year: 'numeric' });
|
||||
}
|
||||
|
||||
/** Map of symbol name → most recent dreamDate that references it. */
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate, formatTime } from '$lib/i18n/format';
|
||||
import type { DiscoveredEvent } from '../discovery/types';
|
||||
|
||||
interface Props {
|
||||
|
|
@ -11,16 +12,14 @@
|
|||
|
||||
const startDate = $derived(new Date(event.startAt));
|
||||
const dateLabel = $derived(
|
||||
startDate.toLocaleDateString('de-DE', {
|
||||
formatDate(startDate, {
|
||||
weekday: 'short',
|
||||
day: '2-digit',
|
||||
month: 'short',
|
||||
})
|
||||
);
|
||||
const timeLabel = $derived(
|
||||
event.allDay
|
||||
? 'Ganztag'
|
||||
: startDate.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' })
|
||||
event.allDay ? 'Ganztag' : formatTime(startDate, { hour: '2-digit', minute: '2-digit' })
|
||||
);
|
||||
|
||||
const isSaved = $derived(event.userAction === 'save');
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate, formatTime } from '$lib/i18n/format';
|
||||
import type { SocialEvent, RsvpSummary } from '../types';
|
||||
|
||||
interface Props {
|
||||
|
|
@ -11,16 +12,14 @@
|
|||
|
||||
const startDate = $derived(new Date(event.startTime));
|
||||
const dateLabel = $derived(
|
||||
startDate.toLocaleDateString('de-DE', {
|
||||
formatDate(startDate, {
|
||||
weekday: 'short',
|
||||
day: '2-digit',
|
||||
month: 'short',
|
||||
})
|
||||
);
|
||||
const timeLabel = $derived(
|
||||
event.isAllDay
|
||||
? 'Ganztägig'
|
||||
: startDate.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' })
|
||||
event.isAllDay ? 'Ganztägig' : formatTime(startDate, { hour: '2-digit', minute: '2-digit' })
|
||||
);
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatTime } from '$lib/i18n/format';
|
||||
import { eventsApi, type PublicRsvpRecord } from '../api';
|
||||
import { eventGuestsStore } from '../stores/guests.svelte';
|
||||
|
||||
|
|
@ -104,7 +105,7 @@
|
|||
|
||||
{#if lastFetchedAt}
|
||||
<div class="meta">
|
||||
Aktualisiert um {lastFetchedAt.toLocaleTimeString('de-DE', {
|
||||
Aktualisiert um {formatTime(lastFetchedAt, {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { getDateFnsLocale } from '$lib/i18n/format';
|
||||
/**
|
||||
* Events QuickInputBar Adapter — quick-create gatherings.
|
||||
*
|
||||
|
|
@ -11,8 +12,6 @@ import type { QuickInputItem } from '@mana/shared-ui';
|
|||
import { db } from '$lib/data/database';
|
||||
import type { LocalSocialEvent } from './types';
|
||||
import { format } from 'date-fns';
|
||||
import { de } from 'date-fns/locale';
|
||||
|
||||
function defaultStart(): Date {
|
||||
const d = new Date();
|
||||
d.setHours(19, 0, 0, 0);
|
||||
|
|
@ -49,7 +48,7 @@ export function createAdapter(): InputBarAdapter {
|
|||
const start = defaultStart();
|
||||
return {
|
||||
title: `"${query.trim()}" anlegen`,
|
||||
subtitle: `Start: ${format(start, 'EEE, d. MMM, HH:mm', { locale: de })}`,
|
||||
subtitle: `Start: ${format(start, 'EEE, d. MMM, HH:mm', { locale: getDateFnsLocale() })}`,
|
||||
};
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { formatDate } from '$lib/i18n/format';
|
||||
import type { ModuleTool } from '$lib/data/tools/types';
|
||||
import { eventsStore } from './stores/events.svelte';
|
||||
import { discoveryStore } from './discovery/store.svelte';
|
||||
|
|
@ -89,7 +90,7 @@ export const socialEventsTools: ModuleTool[] = [
|
|||
.slice(0, 10)
|
||||
.map(
|
||||
(e) =>
|
||||
`- ${e.title} (${new Date(e.date).toLocaleDateString('de-DE')}${e.location ? `, ${e.location}` : ''})`
|
||||
`- ${e.title} (${formatDate(new Date(e.date))}${e.location ? `, ${e.location}` : ''})`
|
||||
)
|
||||
.join('\n');
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDateTime } from '$lib/i18n/format';
|
||||
import { useEvent, useEventGuests, summarizeRsvps } from '../queries';
|
||||
import { eventsStore } from '../stores/events.svelte';
|
||||
import GuestListEditor from '../components/GuestListEditor.svelte';
|
||||
|
|
@ -221,7 +222,7 @@
|
|||
<h1 class="title">{event.title}</h1>
|
||||
<div class="meta-row">
|
||||
<span class="when">
|
||||
{new Date(event.startTime).toLocaleString('de-DE', {
|
||||
{formatDateTime(new Date(event.startTime), {
|
||||
weekday: 'long',
|
||||
day: '2-digit',
|
||||
month: 'long',
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
DayTimeline — shows all habit logs for a given date as a timeline.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import type { Habit, HabitLog } from '../types';
|
||||
import { formatTime } from '../queries';
|
||||
import { DynamicIcon } from '@mana/shared-ui/atoms';
|
||||
|
|
@ -29,7 +30,7 @@
|
|||
const yesterday = new Date(Date.now() - 86400000).toISOString().split('T')[0];
|
||||
if (d === today) return 'Heute';
|
||||
if (d === yesterday) return 'Gestern';
|
||||
return new Date(d).toLocaleDateString('de-DE', {
|
||||
return formatDate(new Date(d), {
|
||||
weekday: 'short',
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
Shows stats, log timeline, edit form, and archive/delete actions.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import type { Habit, HabitLog } from '../types';
|
||||
import { habitsStore } from '../stores/habits.svelte';
|
||||
import { getCountForDate, getStreak, groupLogsByDate, todayStr, formatTime } from '../queries';
|
||||
|
|
@ -55,7 +56,7 @@
|
|||
const yesterday = new Date(Date.now() - 86400000).toISOString().split('T')[0];
|
||||
if (d === today) return 'Heute';
|
||||
if (d === yesterday) return 'Gestern';
|
||||
return new Date(d).toLocaleDateString('de-DE', {
|
||||
return formatDate(new Date(d), {
|
||||
weekday: 'short',
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
|
|
@ -63,7 +64,7 @@
|
|||
}
|
||||
|
||||
function formatDayLabel(d: string): string {
|
||||
return new Date(d).toLocaleDateString('de-DE', { weekday: 'narrow' });
|
||||
return formatDate(new Date(d), { weekday: 'narrow' });
|
||||
}
|
||||
|
||||
async function handleDelete() {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { getDateFnsLocale } from '$lib/i18n/format';
|
||||
import { format } from 'date-fns';
|
||||
import { de } from 'date-fns/locale';
|
||||
|
||||
interface FieldDef {
|
||||
id: string;
|
||||
name: string;
|
||||
|
|
@ -27,7 +26,7 @@
|
|||
function formatDate(val: unknown): string {
|
||||
if (!val) return '';
|
||||
try {
|
||||
return format(new Date(String(val)), 'dd.MM.yyyy', { locale: de });
|
||||
return format(new Date(String(val)), 'dd.MM.yyyy', { locale: getDateFnsLocale() });
|
||||
} catch {
|
||||
return String(val);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
Collection details, always editable, auto-save on blur.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { useLiveQueryWithDefault } from '@mana/local-store/svelte';
|
||||
import { db } from '$lib/data/database';
|
||||
import { useDetailEntity } from '$lib/data/detail-entity.svelte';
|
||||
|
|
@ -124,9 +125,9 @@
|
|||
</div>
|
||||
|
||||
<div class="meta">
|
||||
<span>Erstellt: {new Date(collection.createdAt ?? '').toLocaleDateString('de')}</span>
|
||||
<span>Erstellt: {formatDate(new Date(collection.createdAt ?? ''))}</span>
|
||||
{#if collection.updatedAt}
|
||||
<span>Bearbeitet: {new Date(collection.updatedAt).toLocaleDateString('de')}</span>
|
||||
<span>Bearbeitet: {formatDate(new Date(collection.updatedAt))}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/snippet}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { formatDate } from '$lib/i18n/format';
|
||||
/**
|
||||
* Reactive Queries & Pure Helpers for Journal module.
|
||||
*
|
||||
|
|
@ -81,7 +82,7 @@ export function groupByMonth(
|
|||
const groups = new Map<string, JournalEntry[]>();
|
||||
for (const e of entries) {
|
||||
const date = new Date(e.entryDate);
|
||||
const label = date.toLocaleDateString('de-DE', { month: 'long', year: 'numeric' });
|
||||
const label = formatDate(date, { month: 'long', year: 'numeric' });
|
||||
if (!groups.has(label)) groups.set(label, []);
|
||||
groups.get(label)!.push(e);
|
||||
}
|
||||
|
|
@ -96,7 +97,7 @@ export function formatEntryDate(iso: string): string {
|
|||
if (diffDays === 0) return 'Heute';
|
||||
if (diffDays === 1) return 'Gestern';
|
||||
if (diffDays < 7) return `vor ${diffDays} Tagen`;
|
||||
return date.toLocaleDateString('de-DE', { day: 'numeric', month: 'short', year: 'numeric' });
|
||||
return formatDate(date, { day: 'numeric', month: 'short', year: 'numeric' });
|
||||
}
|
||||
|
||||
/** Find "On this day" entries — same month+day from previous years. */
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
SessionCard — displays a completed meditation session.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import type { MeditateSession, MeditatePreset } from '../types';
|
||||
import { CATEGORY_LABELS } from '../types';
|
||||
import { formatDuration } from '../queries';
|
||||
|
|
@ -17,7 +18,7 @@
|
|||
const name = $derived(preset?.name ?? CATEGORY_LABELS[session.category].de);
|
||||
const duration = $derived(formatDuration(session.durationSec));
|
||||
const date = $derived(
|
||||
new Date(session.startedAt).toLocaleDateString('de-DE', {
|
||||
formatDate(new Date(session.startedAt), {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
hour: '2-digit',
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { formatDate } from '$lib/i18n/format';
|
||||
/**
|
||||
* Memoro LLM result watcher.
|
||||
*
|
||||
|
|
@ -140,12 +141,12 @@ async function applyRow(row: QueuedTask): Promise<void> {
|
|||
if (!titleToWrite) {
|
||||
const created = (memo as { createdAt?: string }).createdAt;
|
||||
const dateLabel = created
|
||||
? new Date(created).toLocaleDateString('de', {
|
||||
? formatDate(new Date(created), {
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
})
|
||||
: new Date().toLocaleDateString('de');
|
||||
: formatDate(new Date());
|
||||
titleToWrite = `Memo vom ${dateLabel}`;
|
||||
console.warn(
|
||||
`[memoro-llm-watcher] row ${row.id} returned empty title — using date fallback "${titleToWrite}"`,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
Memo details with transcript, pin toggle, auto-save on blur.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { useDetailEntity } from '$lib/data/detail-entity.svelte';
|
||||
import { useLiveQueryWithDefault } from '@mana/local-store/svelte';
|
||||
import DetailViewShell from '$lib/components/DetailViewShell.svelte';
|
||||
|
|
@ -224,9 +225,9 @@
|
|||
</div>
|
||||
|
||||
<div class="meta">
|
||||
<span>Erstellt: {new Date(memo.createdAt ?? '').toLocaleDateString('de')}</span>
|
||||
<span>Erstellt: {formatDate(new Date(memo.createdAt ?? ''))}</span>
|
||||
{#if memo.updatedAt}
|
||||
<span>Bearbeitet: {new Date(memo.updatedAt).toLocaleDateString('de')}</span>
|
||||
<span>Bearbeitet: {formatDate(new Date(memo.updatedAt))}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/snippet}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
All fields are always editable. Changes auto-save on blur.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { useDetailEntity } from '$lib/data/detail-entity.svelte';
|
||||
import DetailViewShell from '$lib/components/DetailViewShell.svelte';
|
||||
import { libraryStore } from '../stores/library.svelte';
|
||||
|
|
@ -157,12 +158,12 @@
|
|||
</div>
|
||||
|
||||
<div class="meta">
|
||||
<span>Erstellt: {new Date(song.createdAt ?? '').toLocaleDateString('de')}</span>
|
||||
<span>Erstellt: {formatDate(new Date(song.createdAt ?? ''))}</span>
|
||||
{#if song.updatedAt}
|
||||
<span>Bearbeitet: {new Date(song.updatedAt).toLocaleDateString('de')}</span>
|
||||
<span>Bearbeitet: {formatDate(new Date(song.updatedAt))}</span>
|
||||
{/if}
|
||||
{#if song.lastPlayedAt}
|
||||
<span>Zuletzt gehört: {new Date(song.lastPlayedAt).toLocaleDateString('de')}</span>
|
||||
<span>Zuletzt gehört: {formatDate(new Date(song.lastPlayedAt))}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/snippet}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { formatDate } from '$lib/i18n/format';
|
||||
/**
|
||||
* Reactive queries + type converters for News.
|
||||
*
|
||||
|
|
@ -176,5 +177,5 @@ export function formatRelativeTime(iso: string | null): string {
|
|||
if (hours < 24) return `vor ${hours}h`;
|
||||
const days = Math.floor(hours / 24);
|
||||
if (days < 7) return `vor ${days}d`;
|
||||
return new Date(iso).toLocaleDateString('de-DE', { day: 'numeric', month: 'short' });
|
||||
return formatDate(new Date(iso), { day: 'numeric', month: 'short' });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { formatDate } from '$lib/i18n/format';
|
||||
/**
|
||||
* Reactive Queries & Pure Helpers for Notes module.
|
||||
*
|
||||
|
|
@ -104,5 +105,5 @@ export function formatRelativeTime(iso: string): string {
|
|||
if (hours < 24) return `vor ${hours}h`;
|
||||
const days = Math.floor(hours / 24);
|
||||
if (days < 7) return `vor ${days}d`;
|
||||
return new Date(iso).toLocaleDateString('de-DE', { day: 'numeric', month: 'short' });
|
||||
return formatDate(new Date(iso), { day: 'numeric', month: 'short' });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
and ESC both close the modal.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { onMount, type Snippet } from 'svelte';
|
||||
import { SquaresFour } from '@mana/shared-icons';
|
||||
import type { Image } from '../types';
|
||||
|
|
@ -89,7 +90,7 @@
|
|||
</p>
|
||||
{/if}
|
||||
<p class="text-xs text-muted-foreground">
|
||||
{new Date(image.createdAt).toLocaleDateString('de-DE', {
|
||||
{formatDate(new Date(image.createdAt), {
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
Presentation deck details. All fields auto-save on blur.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { liveQuery } from 'dexie';
|
||||
import { db } from '$lib/data/database';
|
||||
import { useDetailEntity } from '$lib/data/detail-entity.svelte';
|
||||
|
|
@ -113,9 +114,9 @@
|
|||
</div>
|
||||
|
||||
<div class="meta">
|
||||
<span>Erstellt: {new Date(deck.createdAt ?? '').toLocaleDateString('de')}</span>
|
||||
<span>Erstellt: {formatDate(new Date(deck.createdAt ?? ''))}</span>
|
||||
{#if deck.updatedAt}
|
||||
<span>Bearbeitet: {new Date(deck.updatedAt).toLocaleDateString('de')}</span>
|
||||
<span>Bearbeitet: {formatDate(new Date(deck.updatedAt))}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/snippet}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
All fields are always editable. Changes auto-save on blur.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { db } from '$lib/data/database';
|
||||
import { encryptRecord } from '$lib/data/crypto';
|
||||
import { useDetailEntity } from '$lib/data/detail-entity.svelte';
|
||||
|
|
@ -172,9 +173,9 @@
|
|||
{/if}
|
||||
|
||||
<div class="meta">
|
||||
<span>Erstellt: {new Date(question.createdAt ?? '').toLocaleDateString('de')}</span>
|
||||
<span>Erstellt: {formatDate(new Date(question.createdAt ?? ''))}</span>
|
||||
{#if question.updatedAt}
|
||||
<span>Bearbeitet: {new Date(question.updatedAt).toLocaleDateString('de')}</span>
|
||||
<span>Bearbeitet: {formatDate(new Date(question.updatedAt))}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/snippet}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import type { AchievementWithStatus } from '../types';
|
||||
import { RARITY_INFO } from '../types';
|
||||
import { Trophy, Lock, Star } from '@mana/shared-icons';
|
||||
|
|
@ -88,7 +89,7 @@
|
|||
</span>
|
||||
{#if achievement.unlocked && achievement.unlockedAt}
|
||||
<span class="text-muted-foreground">
|
||||
{new Date(achievement.unlockedAt).toLocaleDateString('de-DE')}
|
||||
{formatDate(new Date(achievement.unlockedAt))}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import type { Skill, SkillBranch } from '../types';
|
||||
import { BRANCH_INFO } from '../types';
|
||||
|
|
@ -165,7 +166,7 @@
|
|||
<div>
|
||||
<div class="text-muted-foreground">Erstellt</div>
|
||||
<div class="font-semibold text-white">
|
||||
{new Date(skill.createdAt).toLocaleDateString('de-DE')}
|
||||
{formatDate(new Date(skill.createdAt))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
Skill details with XP display and quick add XP. Auto-save on blur.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { useDetailEntity } from '$lib/data/detail-entity.svelte';
|
||||
import DetailViewShell from '$lib/components/DetailViewShell.svelte';
|
||||
import { skillStore } from '../stores/skills.svelte';
|
||||
|
|
@ -177,9 +178,9 @@
|
|||
</div>
|
||||
|
||||
<div class="meta">
|
||||
<span>Erstellt: {new Date(skill.createdAt ?? '').toLocaleDateString('de')}</span>
|
||||
<span>Erstellt: {formatDate(new Date(skill.createdAt ?? ''))}</span>
|
||||
{#if skill.updatedAt}
|
||||
<span>Bearbeitet: {new Date(skill.updatedAt).toLocaleDateString('de')}</span>
|
||||
<span>Bearbeitet: {formatDate(new Date(skill.updatedAt))}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/snippet}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
Renders equally well inside the workbench or standalone at /spaces.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { onMount } from 'svelte';
|
||||
import { getActiveSpace, authFetch } from '$lib/data/scope';
|
||||
import { SPACE_TYPE_LABELS } from '@mana/shared-branding';
|
||||
|
|
@ -135,7 +136,7 @@
|
|||
if (sec < 60) return 'gerade eben';
|
||||
if (sec < 3600) return `vor ${Math.floor(sec / 60)} min`;
|
||||
if (sec < 86400) return `vor ${Math.floor(sec / 3600)} h`;
|
||||
return d.toLocaleDateString('de-DE');
|
||||
return formatDate(d);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
a spiral pattern. Extracted from the former /spiral standalone route.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatTime } from '$lib/i18n/format';
|
||||
import { onMount } from 'svelte';
|
||||
import { COLORS } from '@mana/spiral-db';
|
||||
import type { ColorDefinition } from '@mana/spiral-db';
|
||||
|
|
@ -142,7 +143,7 @@
|
|||
|
||||
{#if manaSpiralStore.lastCollectedAt}
|
||||
<p class="collected-at">
|
||||
Zuletzt gesammelt: {manaSpiralStore.lastCollectedAt.toLocaleTimeString('de-DE')}
|
||||
Zuletzt gesammelt: {formatTime(manaSpiralStore.lastCollectedAt)}
|
||||
</p>
|
||||
{/if}
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
File details with editable name, favorite toggle. Auto-save on blur.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { useDetailEntity } from '$lib/data/detail-entity.svelte';
|
||||
import DetailViewShell from '$lib/components/DetailViewShell.svelte';
|
||||
import { filesStore } from '../stores/files.svelte';
|
||||
|
|
@ -97,9 +98,9 @@
|
|||
</div>
|
||||
|
||||
<div class="meta">
|
||||
<span>Erstellt: {new Date(file.createdAt ?? '').toLocaleDateString('de')}</span>
|
||||
<span>Erstellt: {formatDate(new Date(file.createdAt ?? ''))}</span>
|
||||
{#if file.updatedAt}
|
||||
<span>Bearbeitet: {new Date(file.updatedAt).toLocaleDateString('de')}</span>
|
||||
<span>Bearbeitet: {formatDate(new Date(file.updatedAt))}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/snippet}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
Streak, quick-start routines, assessment recommendation, recent sessions.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import {
|
||||
useAllStretchExercises,
|
||||
useAllStretchRoutines,
|
||||
|
|
@ -135,7 +136,7 @@
|
|||
<div class="heatmap-day" title="{day.date}: {day.minutes} Min">
|
||||
<div class="heatmap-dot" class:active={day.count > 0} class:multi={day.count > 1}></div>
|
||||
<span class="heatmap-label"
|
||||
>{new Date(day.date + 'T00:00').toLocaleDateString('de', { weekday: 'narrow' })}</span
|
||||
>{formatDate(new Date(day.date + 'T00:00'), { weekday: 'narrow' })}</span
|
||||
>
|
||||
</div>
|
||||
{/each}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatTime } from '$lib/i18n/format';
|
||||
import { getContext } from 'svelte';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { timeEntryTable } from '$lib/modules/times/collections';
|
||||
|
|
@ -93,16 +94,14 @@
|
|||
|
||||
let startTimeStr = $derived(
|
||||
entry.startTime
|
||||
? new Date(entry.startTime).toLocaleTimeString('de-DE', {
|
||||
? formatTime(new Date(entry.startTime), {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
})
|
||||
: ''
|
||||
);
|
||||
let endTimeStr = $derived(
|
||||
entry.endTime
|
||||
? new Date(entry.endTime).toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' })
|
||||
: ''
|
||||
entry.endTime ? formatTime(new Date(entry.endTime), { hour: '2-digit', minute: '2-digit' }) : ''
|
||||
);
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import EntryItem from './EntryItem.svelte';
|
||||
import {
|
||||
|
|
@ -26,7 +27,7 @@
|
|||
if (dateStr === today) return $_('entry.today');
|
||||
if (dateStr === yesterday) return 'Gestern';
|
||||
|
||||
return new Date(dateStr + 'T00:00:00').toLocaleDateString('de-DE', {
|
||||
return formatDate(new Date(dateStr + 'T00:00:00'), {
|
||||
weekday: 'long',
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { formatTime } from '$lib/i18n/format';
|
||||
/**
|
||||
* CSV Export utility for time entries
|
||||
*/
|
||||
|
|
@ -40,12 +41,8 @@ export function exportEntriesToCSV(
|
|||
(hours * 60 + minutes).toString(),
|
||||
e.isBillable ? 'Ja' : 'Nein',
|
||||
`"${e.tags.join(', ')}"`,
|
||||
e.startTime
|
||||
? new Date(e.startTime).toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' })
|
||||
: '',
|
||||
e.endTime
|
||||
? new Date(e.endTime).toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' })
|
||||
: '',
|
||||
e.startTime ? formatTime(new Date(e.startTime), { hour: '2-digit', minute: '2-digit' }) : '',
|
||||
e.endTime ? formatTime(new Date(e.endTime), { hour: '2-digit', minute: '2-digit' }) : '',
|
||||
];
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
All fields are always editable. Changes auto-save on blur.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatDate, formatTime } from '$lib/i18n/format';
|
||||
import { db } from '$lib/data/database';
|
||||
import { useDetailEntity } from '$lib/data/detail-entity.svelte';
|
||||
import { useLiveQueryWithDefault } from '@mana/local-store/svelte';
|
||||
|
|
@ -73,7 +74,7 @@
|
|||
function fmtTime(iso?: string | null): string {
|
||||
if (!iso) return '';
|
||||
const d = new Date(iso);
|
||||
return d.toLocaleTimeString('de', { hour: '2-digit', minute: '2-digit' });
|
||||
return formatTime(d, { hour: '2-digit', minute: '2-digit' });
|
||||
}
|
||||
|
||||
async function saveField() {
|
||||
|
|
@ -230,7 +231,7 @@
|
|||
|
||||
<div class="meta">
|
||||
{#if entry.createdAt}
|
||||
<span>Erstellt: {new Date(entry.createdAt).toLocaleDateString('de')}</span>
|
||||
<span>Erstellt: {formatDate(new Date(entry.createdAt))}</span>
|
||||
{/if}
|
||||
{#if entry.source?.app}
|
||||
<span>Quelle: {entry.source.app}</span>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
Minimal task list. Inline due-date badges. Floating input at bottom.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatDate, formatTime } from '$lib/i18n/format';
|
||||
import { useAllTasks, filterIncomplete, filterOverdue, filterToday, sortTasks } from './queries';
|
||||
import { tasksStore } from './stores/tasks.svelte';
|
||||
import { toastStore } from '@mana/shared-ui/toast';
|
||||
|
|
@ -55,7 +56,7 @@
|
|||
if (due < todayStart) return { label: 'Überfällig', variant: 'overdue' };
|
||||
if (due < tomorrowStart) return { label: 'Heute', variant: 'today' };
|
||||
return {
|
||||
label: due.toLocaleDateString('de', { day: 'numeric', month: 'short' }),
|
||||
label: formatDate(due, { day: 'numeric', month: 'short' }),
|
||||
variant: 'upcoming',
|
||||
};
|
||||
}
|
||||
|
|
@ -185,10 +186,10 @@
|
|||
{/if}
|
||||
{#if task.isCompleted && task.completedAt}
|
||||
<span class="completed-at"
|
||||
>{new Date(task.completedAt).toLocaleTimeString('de', {
|
||||
>{formatTime(new Date(task.completedAt), {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
})} Uhr, {new Date(task.completedAt).toLocaleDateString('de', {
|
||||
})} Uhr, {formatDate(new Date(task.completedAt), {
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
})}</span
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { tasksStore } from '../stores/tasks.svelte';
|
||||
import {
|
||||
|
|
@ -141,7 +142,7 @@
|
|||
{#if task.dueDate}
|
||||
<span class="inline-flex items-center gap-0.5 text-amber-500">
|
||||
<CalendarBlank size={10} />
|
||||
{task.dueDate.toLocaleDateString('de-DE', { day: 'numeric', month: 'short' })}
|
||||
{formatDate(task.dueDate, { day: 'numeric', month: 'short' })}
|
||||
{#if task.dueTime}
|
||||
{task.dueTime}
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
<script lang="ts">
|
||||
import { getDateFnsLocale } from '$lib/i18n/format';
|
||||
import type { Task, TaskPriority } from '../types';
|
||||
import { getPriorityLabel, getPriorityColor } from '../queries';
|
||||
import { Check, Circle, CalendarBlank, CheckSquare } from '@mana/shared-icons';
|
||||
import { TagChip } from '@mana/shared-ui';
|
||||
import { isToday, isPast, format } from 'date-fns';
|
||||
import { de } from 'date-fns/locale';
|
||||
|
||||
interface Props {
|
||||
task: Task;
|
||||
tags?: { id: string; name: string; color: string }[];
|
||||
|
|
@ -38,7 +37,7 @@
|
|||
const overdue = isPast(d) && !isToday(d) && !task.isCompleted;
|
||||
const today = isToday(d);
|
||||
return {
|
||||
text: today ? 'Heute' : format(d, 'd. MMM', { locale: de }),
|
||||
text: today ? 'Heute' : format(d, 'd. MMM', { locale: getDateFnsLocale() }),
|
||||
overdue,
|
||||
today,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { getDateFnsLocale } from '$lib/i18n/format';
|
||||
import type { Task } from '../../types';
|
||||
import { isToday, isPast, format } from 'date-fns';
|
||||
import { de } from 'date-fns/locale';
|
||||
import { Check, Circle, CalendarBlank, CheckSquare, Flag, Trash } from '@mana/shared-icons';
|
||||
import { TagChip } from '@mana/shared-ui';
|
||||
|
||||
|
|
@ -31,7 +31,7 @@
|
|||
const overdue = isPast(d) && !isToday(d) && !task.isCompleted;
|
||||
const today = isToday(d);
|
||||
return {
|
||||
text: today ? 'Heute' : format(d, 'd. MMM', { locale: de }),
|
||||
text: today ? 'Heute' : format(d, 'd. MMM', { locale: getDateFnsLocale() }),
|
||||
overdue,
|
||||
today,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { formatDate } from '$lib/i18n/format';
|
||||
/**
|
||||
* Todo QuickInputBar Adapter
|
||||
*/
|
||||
|
|
@ -33,7 +34,7 @@ export function createAdapter(): InputBarAdapter {
|
|||
.map((t) => ({
|
||||
id: t.id,
|
||||
title: t.title || '',
|
||||
subtitle: t.dueDate ? new Date(t.dueDate).toLocaleDateString('de-DE') : 'Keine Frist',
|
||||
subtitle: t.dueDate ? formatDate(new Date(t.dueDate)) : 'Keine Frist',
|
||||
}));
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
All fields are always editable. Changes auto-save on blur.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { db } from '$lib/data/database';
|
||||
import { decryptRecord } from '$lib/data/crypto';
|
||||
import { useDetailEntity } from '$lib/data/detail-entity.svelte';
|
||||
|
|
@ -325,9 +326,9 @@
|
|||
{/if}
|
||||
|
||||
<div class="meta">
|
||||
<span>Erstellt: {new Date(task.createdAt ?? '').toLocaleDateString('de')}</span>
|
||||
<span>Erstellt: {formatDate(new Date(task.createdAt ?? ''))}</span>
|
||||
{#if task.updatedAt}
|
||||
<span>Bearbeitet: {new Date(task.updatedAt).toLocaleDateString('de')}</span>
|
||||
<span>Bearbeitet: {formatDate(new Date(task.updatedAt))}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/snippet}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
All fields are always editable. Changes auto-save on blur.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { db } from '$lib/data/database';
|
||||
import { encryptRecord } from '$lib/data/crypto';
|
||||
import { useDetailEntity } from '$lib/data/detail-entity.svelte';
|
||||
|
|
@ -162,9 +163,9 @@
|
|||
</div>
|
||||
|
||||
<div class="meta">
|
||||
<span>Erstellt: {new Date(link.createdAt ?? '').toLocaleDateString('de')}</span>
|
||||
<span>Erstellt: {formatDate(new Date(link.createdAt ?? ''))}</span>
|
||||
{#if link.updatedAt}
|
||||
<span>Bearbeitet: {new Date(link.updatedAt).toLocaleDateString('de')}</span>
|
||||
<span>Bearbeitet: {formatDate(new Date(link.updatedAt))}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/snippet}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { useAllSites } from './queries';
|
||||
|
||||
const sites = useAllSites();
|
||||
|
|
@ -13,7 +14,7 @@
|
|||
if (diffH < 24) return `vor ${diffH} Std`;
|
||||
const diffD = Math.floor(diffH / 24);
|
||||
if (diffD < 30) return `vor ${diffD} Tg`;
|
||||
return new Date(iso).toLocaleDateString('de-DE');
|
||||
return formatDate(new Date(iso));
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { formatDateTime } from '$lib/i18n/format';
|
||||
/**
|
||||
* Module-embed resolvers — client-side functions that walk Dexie to
|
||||
* pre-fetch data for `moduleEmbed` blocks at publish time.
|
||||
|
|
@ -260,11 +261,11 @@ function formatEventSubtitle(
|
|||
location: string | null | undefined
|
||||
): string {
|
||||
const start = new Date(startIso);
|
||||
const dateParts = new Intl.DateTimeFormat('de-DE', {
|
||||
const dateParts = formatDateTime(start, {
|
||||
day: '2-digit',
|
||||
month: 'short',
|
||||
year: 'numeric',
|
||||
}).format(start);
|
||||
});
|
||||
|
||||
let timePart = '';
|
||||
if (!allDay) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
conditions, wind, humidity, pressure, UV index, and last-updated time.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatTime } from '$lib/i18n/format';
|
||||
import type { CurrentWeather } from '../types';
|
||||
import { getWeatherIcon, getWeatherLabel, windDirectionLabel } from '../weather-icons';
|
||||
|
||||
|
|
@ -23,7 +24,7 @@
|
|||
|
||||
let lastUpdated = $derived(() => {
|
||||
if (!fetchedAt) return '';
|
||||
return new Date(fetchedAt).toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' });
|
||||
return formatTime(new Date(fetchedAt), { hour: '2-digit', minute: '2-digit' });
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
7-day daily forecast — shows min/max temp, weather icon, precipitation.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import type { DailyForecast } from '../types';
|
||||
import { getWeatherIcon, getWeatherLabel } from '../weather-icons';
|
||||
|
||||
|
|
@ -16,7 +17,7 @@
|
|||
function dayLabel(dateStr: string, idx: number): string {
|
||||
if (idx === 0) return 'Heute';
|
||||
if (idx === 1) return 'Morgen';
|
||||
return new Date(dateStr).toLocaleDateString('de-DE', { weekday: 'short' });
|
||||
return formatDate(new Date(dateStr), { weekday: 'short' });
|
||||
}
|
||||
|
||||
function tempBarStyle(min: number, max: number): string {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
Horizontal scrolling hourly forecast — next 24 hours.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatTime } from '$lib/i18n/format';
|
||||
import type { HourlyForecast } from '../types';
|
||||
import { getWeatherIcon } from '../weather-icons';
|
||||
|
||||
|
|
@ -18,7 +19,7 @@
|
|||
<span class="section-label">Stundenvorhersage</span>
|
||||
<div class="hourly-scroll">
|
||||
{#each visibleHours as hour (hour.time)}
|
||||
{@const time = new Date(hour.time).toLocaleTimeString('de-DE', {
|
||||
{@const time = formatTime(new Date(hour.time), {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
})}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
Rain nowcast — bar chart showing minute-level precipitation forecast.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatTime } from '$lib/i18n/format';
|
||||
import type { RainNowcast } from '../types';
|
||||
|
||||
interface Props {
|
||||
|
|
@ -20,7 +21,7 @@
|
|||
<div class="nowcast-chart">
|
||||
{#each nowcast.minutely as point (point.time)}
|
||||
{@const height = Math.max(2, (point.precipitation / maxPrecip) * 100)}
|
||||
{@const time = new Date(point.time).toLocaleTimeString('de-DE', {
|
||||
{@const time = formatTime(new Date(point.time), {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
})}
|
||||
|
|
@ -31,13 +32,13 @@
|
|||
</div>
|
||||
<div class="nowcast-time-labels">
|
||||
<span
|
||||
>{new Date(nowcast.minutely[0].time).toLocaleTimeString('de-DE', {
|
||||
>{formatTime(new Date(nowcast.minutely[0].time), {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
})}</span
|
||||
>
|
||||
<span
|
||||
>{new Date(nowcast.minutely[nowcast.minutely.length - 1].time).toLocaleTimeString('de-DE', {
|
||||
>{formatTime(new Date(nowcast.minutely[nowcast.minutely.length - 1].time), {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
})}</span
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
Open-Meteo Best Match) stacked for easy comparison.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatDate, formatTime } from '$lib/i18n/format';
|
||||
import { getComparison, type CompareResponse, type ModelComparison } from '../api';
|
||||
import { getWeatherIcon, getWeatherLabel } from '../weather-icons';
|
||||
|
||||
|
|
@ -122,7 +123,7 @@
|
|||
? 'Heute'
|
||||
: dayIdx === 1
|
||||
? 'Morgen'
|
||||
: new Date(dateStr).toLocaleDateString('de-DE', { weekday: 'short', day: 'numeric' })}
|
||||
: formatDate(new Date(dateStr), { weekday: 'short', day: 'numeric' })}
|
||||
<div class="day-compare">
|
||||
<div class="day-compare-header">{dayLabel}</div>
|
||||
<div class="day-models">
|
||||
|
|
@ -176,7 +177,7 @@
|
|||
{/if}
|
||||
|
||||
<div class="fetched-at">
|
||||
Abgerufen: {new Date(data.fetchedAt).toLocaleTimeString('de-DE')}
|
||||
Abgerufen: {formatTime(new Date(data.fetchedAt))}
|
||||
<button class="refresh-btn" onclick={() => loadComparison(lat, lon)} disabled={loading}>
|
||||
Aktualisieren
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { formatDate, formatTime } from '$lib/i18n/format';
|
||||
/**
|
||||
* Wetter AI tools — expose weather data to the AI companion.
|
||||
* Both tools are read-only (auto policy) and run during the reasoning loop.
|
||||
|
|
@ -75,7 +76,7 @@ export const wetterTools: ModuleTool[] = [
|
|||
|
||||
lines.push('## 7-Tage-Vorhersage');
|
||||
for (const d of forecast.daily.slice(0, 7)) {
|
||||
const day = new Date(d.date).toLocaleDateString('de-DE', {
|
||||
const day = formatDate(new Date(d.date), {
|
||||
weekday: 'short',
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
|
|
@ -145,7 +146,7 @@ export const wetterTools: ModuleTool[] = [
|
|||
lines.push('Kein Niederschlag erwartet.');
|
||||
} else {
|
||||
for (const m of withRain.slice(0, 12)) {
|
||||
const t = new Date(m.time).toLocaleTimeString('de-DE', {
|
||||
const t = formatTime(new Date(m.time), {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate, formatDateTime } from '$lib/i18n/format';
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
import { ArrowLeft, Check, Trash, Plus, Link as LinkIcon, Star, X } from '@mana/shared-icons';
|
||||
|
|
@ -296,7 +297,7 @@
|
|||
{#each priceChecks.value.slice(0, 10) as check (check.id)}
|
||||
<div class="flex items-center justify-between text-sm">
|
||||
<span class="truncate text-[hsl(var(--color-muted-foreground))]">
|
||||
{new Date(check.checkedAt).toLocaleDateString('de-DE')}
|
||||
{formatDate(new Date(check.checkedAt))}
|
||||
</span>
|
||||
<span
|
||||
class="font-medium {check.available
|
||||
|
|
@ -328,7 +329,7 @@
|
|||
>
|
||||
<p>{note.content}</p>
|
||||
<p class="mt-1 text-[10px] text-[hsl(var(--color-muted-foreground))]">
|
||||
{new Date(note.createdAt).toLocaleString('de-DE')}
|
||||
{formatDateTime(new Date(note.createdAt))}
|
||||
</p>
|
||||
</li>
|
||||
{/each}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import StatusBadge from './StatusBadge.svelte';
|
||||
import { KIND_LABELS } from '../constants';
|
||||
import type { Draft, DraftVersion } from '../types';
|
||||
|
|
@ -32,7 +33,7 @@
|
|||
if (hrs < 24) return `vor ${hrs} h`;
|
||||
const days = Math.round(hrs / 24);
|
||||
if (days < 30) return `vor ${days} d`;
|
||||
return d.toLocaleDateString('de-DE');
|
||||
return formatDate(d);
|
||||
}
|
||||
|
||||
function open() {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
place).
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatDateTime } from '$lib/i18n/format';
|
||||
import { goto } from '$app/navigation';
|
||||
import { VisibilityPicker, type VisibilityLevel } from '@mana/shared-privacy';
|
||||
import BriefingForm from '../components/BriefingForm.svelte';
|
||||
|
|
@ -338,10 +339,7 @@
|
|||
<div class="published-row">
|
||||
<span class="published-label">📤 Veröffentlicht:</span>
|
||||
{#each draft.publishedTo as target (`${target.module}:${target.targetId}`)}
|
||||
<span
|
||||
class="published-chip"
|
||||
title={new Date(target.publishedAt).toLocaleString('de-DE')}
|
||||
>
|
||||
<span class="published-chip" title={formatDateTime(new Date(target.publishedAt))}>
|
||||
{#if target.module === 'articles'}
|
||||
📚 <a href={`/articles/${target.targetId}`}>Artikel</a>
|
||||
{:else if target.module === 'website'}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { getDateFnsLocale } from '$lib/i18n/format';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { page } from '$app/stores';
|
||||
import { goto } from '$app/navigation';
|
||||
|
|
@ -7,7 +8,6 @@
|
|||
import { getEventById, getCalendarById, getCalendarColor } from '$lib/modules/calendar/queries';
|
||||
import type { Calendar, CalendarEvent } from '$lib/modules/calendar/types';
|
||||
import { format } from 'date-fns';
|
||||
import { de } from 'date-fns/locale';
|
||||
import { CaretLeft, Trash, PencilSimple, MapPin, Clock } from '@mana/shared-icons';
|
||||
import { RoutePage } from '$lib/components/shell';
|
||||
|
||||
|
|
@ -239,7 +239,11 @@
|
|||
<div class="flex items-center gap-2 text-foreground">
|
||||
<Clock size={18} class="text-muted-foreground" />
|
||||
<div>
|
||||
<div>{format(new Date(event.startTime), 'EEEE, d. MMMM yyyy', { locale: de })}</div>
|
||||
<div>
|
||||
{format(new Date(event.startTime), 'EEEE, d. MMMM yyyy', {
|
||||
locale: getDateFnsLocale(),
|
||||
})}
|
||||
</div>
|
||||
{#if event.isAllDay}
|
||||
<div class="text-sm text-muted-foreground">Ganztägig</div>
|
||||
{:else}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { getContext } from 'svelte';
|
||||
import { ChartBar } from '@mana/shared-icons';
|
||||
import type { Deck } from '$lib/modules/cards/types';
|
||||
|
|
@ -67,7 +68,7 @@
|
|||
</div>
|
||||
<div class="text-right">
|
||||
<div class="text-sm text-muted-foreground">
|
||||
{new Date(deck.updatedAt).toLocaleDateString('de-DE', {
|
||||
{formatDate(new Date(deck.updatedAt), {
|
||||
day: '2-digit',
|
||||
month: 'short',
|
||||
})}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatTime } from '$lib/i18n/format';
|
||||
import { page } from '$app/stores';
|
||||
import { goto } from '$app/navigation';
|
||||
import { getContext } from 'svelte';
|
||||
|
|
@ -200,7 +201,7 @@
|
|||
>
|
||||
<p class="whitespace-pre-wrap">{msg.messageText}</p>
|
||||
<p class="mt-1 text-[10px] opacity-60">
|
||||
{new Date(msg.createdAt).toLocaleTimeString('de-DE', {
|
||||
{formatTime(new Date(msg.createdAt), {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
})}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { page } from '$app/stores';
|
||||
import { goto } from '$app/navigation';
|
||||
|
|
@ -570,7 +571,7 @@
|
|||
<div class="flex items-center gap-3">
|
||||
<Cake size={16} class="flex-shrink-0 text-muted-foreground" />
|
||||
<span class="text-sm text-foreground">
|
||||
{new Date(contact.birthday).toLocaleDateString('de-DE', {
|
||||
{formatDate(new Date(contact.birthday), {
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
|
|
@ -675,7 +676,7 @@
|
|||
<div class="grid grid-cols-2 gap-y-2 text-xs text-muted-foreground">
|
||||
<span>Erstellt</span>
|
||||
<span
|
||||
>{new Date(contact.createdAt).toLocaleDateString('de-DE', {
|
||||
>{formatDate(new Date(contact.createdAt), {
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
year: 'numeric',
|
||||
|
|
@ -683,7 +684,7 @@
|
|||
>
|
||||
<span>Aktualisiert</span>
|
||||
<span
|
||||
>{new Date(contact.updatedAt).toLocaleDateString('de-DE', {
|
||||
>{formatDate(new Date(contact.updatedAt), {
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
year: 'numeric',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { Folder, FileText, Plus } from '@mana/shared-icons';
|
||||
import {
|
||||
useAllSpaces,
|
||||
|
|
@ -174,7 +175,7 @@
|
|||
{/each}
|
||||
{/if}
|
||||
<span class="ml-auto">
|
||||
{new Date(doc.updated_at).toLocaleDateString('de')}
|
||||
{formatDate(new Date(doc.updated_at))}
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { goto } from '$app/navigation';
|
||||
import { Plus, MagnifyingGlass, FileText } from '@mana/shared-icons';
|
||||
import {
|
||||
|
|
@ -209,7 +210,7 @@
|
|||
{/each}
|
||||
{/if}
|
||||
<span class="ml-auto">
|
||||
{new Date(doc.updated_at).toLocaleDateString('de')}
|
||||
{formatDate(new Date(doc.updated_at))}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { page } from '$app/stores';
|
||||
import { goto } from '$app/navigation';
|
||||
import { ArrowLeft, Trash } from '@mana/shared-icons';
|
||||
|
|
@ -200,10 +201,10 @@
|
|||
<span>{doc.metadata.word_count} Woerter</span>
|
||||
{/if}
|
||||
<span>
|
||||
Erstellt: {new Date(doc.created_at).toLocaleDateString('de')}
|
||||
Erstellt: {formatDate(new Date(doc.created_at))}
|
||||
</span>
|
||||
<span>
|
||||
Aktualisiert: {new Date(doc.updated_at).toLocaleDateString('de')}
|
||||
Aktualisiert: {formatDate(new Date(doc.updated_at))}
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { Plus, MagnifyingGlass } from '@mana/shared-icons';
|
||||
import { useAllSpaces } from '$lib/modules/context/queries';
|
||||
import { contextSpaceTable } from '$lib/modules/context/collections';
|
||||
|
|
@ -192,7 +193,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="mt-3 text-xs opacity-40">
|
||||
Erstellt: {new Date(space.created_at).toLocaleDateString('de')}
|
||||
Erstellt: {formatDate(new Date(space.created_at))}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { page } from '$app/stores';
|
||||
import { goto } from '$app/navigation';
|
||||
|
|
@ -260,7 +261,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="mt-2 text-xs opacity-40">
|
||||
{new Date(doc.updated_at).toLocaleDateString('de')}
|
||||
{formatDate(new Date(doc.updated_at))}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { getContext } from 'svelte';
|
||||
import type { Observable } from 'dexie';
|
||||
import type { Transaction, FinanceCategory, TransactionType } from '$lib/modules/finance/types';
|
||||
|
|
@ -83,7 +84,7 @@
|
|||
}
|
||||
|
||||
let monthLabel = $derived(
|
||||
new Date(month + '-01').toLocaleDateString('de-DE', { month: 'long', year: 'numeric' })
|
||||
formatDate(new Date(month + '-01'), { month: 'long', year: 'numeric' })
|
||||
);
|
||||
|
||||
let maxSpend = $derived(Math.max(1, ...spending.values()));
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { page } from '$app/stores';
|
||||
import { goto } from '$app/navigation';
|
||||
|
|
@ -323,7 +324,7 @@
|
|||
<div>
|
||||
<p class="text-sm text-[hsl(var(--color-foreground))]">{note.content}</p>
|
||||
<p class="mt-1 text-xs text-[hsl(var(--color-muted-foreground))]">
|
||||
{new Date(note.createdAt).toLocaleDateString('de-DE')}
|
||||
{formatDate(new Date(note.createdAt))}
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { page } from '$app/stores';
|
||||
import { goto } from '$app/navigation';
|
||||
import { getContext } from 'svelte';
|
||||
|
|
@ -143,7 +144,7 @@
|
|||
</button>
|
||||
{/if}
|
||||
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">
|
||||
{new Date(memo.createdAt).toLocaleDateString('de-DE', {
|
||||
{formatDate(new Date(memo.createdAt), {
|
||||
day: '2-digit',
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { goto } from '$app/navigation';
|
||||
import { getContext } from 'svelte';
|
||||
|
|
@ -129,7 +130,7 @@
|
|||
|
||||
<div class="mt-2 flex items-center justify-between text-xs text-muted-foreground">
|
||||
<span>{board.itemCount} {board.itemCount === 1 ? 'Element' : 'Elemente'}</span>
|
||||
<span>{new Date(board.updatedAt).toLocaleDateString('de-DE')}</span>
|
||||
<span>{formatDate(new Date(board.updatedAt))}</span>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { skillStore } from '$lib/modules/skilltree/stores/skills.svelte';
|
||||
import {
|
||||
achievementStore,
|
||||
|
|
@ -336,7 +337,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<span class="text-sm text-muted-foreground">
|
||||
{new Date(activity.timestamp).toLocaleDateString('de-DE')}
|
||||
{formatDate(new Date(activity.timestamp))}
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
"What did I do today?" as a standalone page.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { getDateFnsLocale } from '$lib/i18n/format';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { useLiveQueryWithDefault } from '@mana/local-store/svelte';
|
||||
import { db } from '$lib/data/database';
|
||||
|
|
@ -22,7 +23,6 @@
|
|||
Funnel,
|
||||
} from '@mana/shared-icons';
|
||||
import { format, addDays, subDays, isToday, isTomorrow, isYesterday } from 'date-fns';
|
||||
import { de } from 'date-fns/locale';
|
||||
import { RoutePage } from '$lib/components/shell';
|
||||
|
||||
let currentDate = $state(new Date());
|
||||
|
|
@ -78,7 +78,7 @@
|
|||
if (isToday(date)) return 'Heute';
|
||||
if (isTomorrow(date)) return 'Morgen';
|
||||
if (isYesterday(date)) return 'Gestern';
|
||||
return format(date, 'EEEE, d. MMMM yyyy', { locale: de });
|
||||
return format(date, 'EEEE, d. MMMM yyyy', { locale: getDateFnsLocale() });
|
||||
}
|
||||
|
||||
function formatBlockTime(block: TimeBlock): string {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
Shows time breakdown, daily trends, habit heatmap, and plan adherence.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { getDateFnsLocale } from '$lib/i18n/format';
|
||||
import { useLiveQueryWithDefault } from '@mana/local-store/svelte';
|
||||
import { db } from '$lib/data/database';
|
||||
import type { LocalTimeBlock } from '$lib/data/time-blocks/types';
|
||||
|
|
@ -20,7 +21,6 @@
|
|||
} from '$lib/data/time-blocks/analytics';
|
||||
import { Clock, TrendUp, Fire, Target } from '@mana/shared-icons';
|
||||
import { format, subDays } from 'date-fns';
|
||||
import { de } from 'date-fns/locale';
|
||||
import { RoutePage } from '$lib/components/shell';
|
||||
|
||||
let periodDays = $state(7);
|
||||
|
|
@ -156,7 +156,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<span class="daily-label">
|
||||
{format(new Date(day.date), 'EEE', { locale: de })}
|
||||
{format(new Date(day.date), 'EEE', { locale: getDateFnsLocale() })}
|
||||
</span>
|
||||
</div>
|
||||
{/each}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate, formatTime } from '$lib/i18n/format';
|
||||
import { Clock, Bell, Timer, Hourglass, Globe } from '@mana/shared-icons';
|
||||
import { RoutePage } from '$lib/components/shell';
|
||||
|
||||
|
|
@ -53,14 +54,14 @@
|
|||
</div>
|
||||
<div>
|
||||
<div class="text-4xl font-bold tabular-nums text-foreground">
|
||||
{new Date().toLocaleTimeString('de-DE', {
|
||||
{formatTime(new Date(), {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
})}
|
||||
</div>
|
||||
<div class="text-muted-foreground">
|
||||
{new Date().toLocaleDateString('de-DE', {
|
||||
{formatDate(new Date(), {
|
||||
weekday: 'long',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { getContext } from 'svelte';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import type { TimeEntry, Project, Client } from '$lib/modules/times/types';
|
||||
|
|
@ -69,7 +70,7 @@
|
|||
const dayEntries = entries().filter((e) => e.date === dateStr);
|
||||
days.push({
|
||||
date: dateStr,
|
||||
label: d.toLocaleDateString('de-DE', { weekday: 'short', day: 'numeric' }),
|
||||
label: formatDate(d, { weekday: 'short', day: 'numeric' }),
|
||||
duration: getTotalDuration(dayEntries),
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import {
|
||||
useAllLinks,
|
||||
|
|
@ -527,8 +528,7 @@
|
|||
{#if link.expiresAt}
|
||||
<span
|
||||
class="shrink-0 rounded bg-orange-100 px-1.5 py-0.5 text-xs text-orange-700 dark:bg-orange-900 dark:text-orange-300"
|
||||
title="Laeuft ab: {new Date(link.expiresAt).toLocaleDateString('de')}"
|
||||
>Ablauf</span
|
||||
title="Laeuft ab: {formatDate(new Date(link.expiresAt))}">Ablauf</span
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { formatDate } from '$lib/i18n/format';
|
||||
import { page } from '$app/stores';
|
||||
import { onMount } from 'svelte';
|
||||
import { CaretLeft } from '@mana/shared-icons';
|
||||
|
|
@ -129,7 +130,7 @@
|
|||
<div class="rounded-xl border border-border/10 bg-muted/5 p-5">
|
||||
<p class="text-xs font-medium uppercase tracking-wider text-muted-foreground">Erstellt</p>
|
||||
<p class="mt-1 text-lg font-bold text-white">
|
||||
{new Date(link.createdAt).toLocaleDateString('de')}
|
||||
{formatDate(new Date(link.createdAt))}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -185,7 +186,7 @@
|
|||
{#if link.expiresAt}
|
||||
<div class="flex items-center justify-between text-sm">
|
||||
<span class="text-muted-foreground">Laeuft ab</span>
|
||||
<span class="text-white">{new Date(link.expiresAt).toLocaleDateString('de')}</span>
|
||||
<span class="text-white">{formatDate(new Date(link.expiresAt))}</span>
|
||||
</div>
|
||||
{/if}
|
||||
{#if link.maxClicks}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue