Skip to content

Commit 47cf74e

Browse files
author
Giulio Savini
committed
fix: use app locale for datetime formatting instead of browser default
date displays across event details, docker info, job cards, and vuln scans were calling toLocaleString() with no locale argument, which ignores whatever language the user picked in the app and falls back to the OS setting. route everything through formatDateTime/formatDateTimeShort utilities backed by date-fns, which already picks up the paraglide locale set via setDefaultOptions. fixes #2349
1 parent 70d7a69 commit 47cf74e

File tree

6 files changed

+34
-14
lines changed

6 files changed

+34
-14
lines changed

frontend/src/lib/components/dialogs/docker-info-dialog.svelte

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import type { DockerInfo } from '$lib/types/docker-info.type';
88
import { m } from '$lib/paraglide/messages';
99
import bytes from '$lib/utils/bytes';
10+
import { formatDateTimeShort } from '$lib/utils/locale.util';
1011
1112
interface Props {
1213
open: boolean;
@@ -23,11 +24,7 @@
2324
2425
function formatTime(timeStr: string | undefined) {
2526
if (!timeStr) return '-';
26-
try {
27-
return new Date(timeStr).toLocaleString();
28-
} catch {
29-
return timeStr;
30-
}
27+
return formatDateTimeShort(timeStr) || timeStr;
3128
}
3229
</script>
3330

frontend/src/lib/components/dialogs/event-details-dialog.svelte

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import { m } from '$lib/paraglide/messages';
88
import { environmentStore, LOCAL_DOCKER_ENVIRONMENT_ID } from '$lib/stores/environment.store.svelte';
99
import { AlertIcon, InfoIcon, EnvironmentsIcon, UserIcon, ClockIcon } from '$lib/icons';
10+
import { formatDateTime } from '$lib/utils/locale.util';
1011
1112
type Severity = 'success' | 'warning' | 'error' | 'info';
1213
@@ -61,11 +62,7 @@
6162
});
6263
6364
function formatDate(timestamp: string): string {
64-
try {
65-
return new Date(timestamp).toLocaleString();
66-
} catch {
67-
return timestamp;
68-
}
65+
return formatDateTime(timestamp) || timestamp;
6966
}
7067
7168
function stringifyForDisplay(value: unknown): string {

frontend/src/lib/components/job-card/job-card.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import { Spinner } from '$lib/components/ui/spinner';
88
import { jobScheduleService } from '$lib/services/job-schedule-service';
99
import { formatDistanceToNow } from 'date-fns';
10+
import { formatDateTimeShort } from '$lib/utils/locale.util';
1011
import type { Snippet } from 'svelte';
1112
import type { JobStatus } from '$lib/types/job-schedule.type';
1213
import JobScheduleDialog from './job-schedule-dialog.svelte';
@@ -40,7 +41,7 @@
4041
if (!job.nextRun) return null;
4142
const nextRunDate = new Date(job.nextRun);
4243
const relative = formatDistanceToNow(nextRunDate, { addSuffix: true });
43-
const absolute = nextRunDate.toLocaleString();
44+
const absolute = formatDateTimeShort(nextRunDate);
4445
return `${relative} (${absolute})`;
4546
});
4647

frontend/src/lib/components/vulnerability/vulnerability-scan-item.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import { onDestroy } from 'svelte';
77
import type { VulnerabilityScanSummary } from '$lib/types/vulnerability.type';
88
import { m } from '$lib/paraglide/messages';
9+
import { formatDateTime } from '$lib/utils/locale.util';
910
import { queryKeys } from '$lib/query/query-keys';
1011
import { vulnerabilityService } from '$lib/services/vulnerability-service';
1112
import {
@@ -83,7 +84,7 @@
8384
if (!ts) return m.common_na();
8485
const parsed = new Date(ts);
8586
if (Number.isNaN(parsed.getTime())) return m.common_na();
86-
return parsed.toLocaleTimeString();
87+
return formatDateTime(parsed);
8788
});
8889
8990
const statusLabel = $derived.by(() => {

frontend/src/lib/components/vulnerability/vulnerability-scan-panel.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import type { VulnerabilityScanResult, Vulnerability as VulnType, SeveritySummary } from '$lib/types/vulnerability.type';
1313
import type { Paginated, SearchPaginationSortRequest } from '$lib/types/pagination.type';
1414
import { ShieldCheckIcon, ShieldAlertIcon, ShieldXIcon, ScanIcon, CodeIcon, CheckIcon } from '$lib/icons';
15+
import { formatDateTime } from '$lib/utils/locale.util';
1516
import { useQueryClient } from '@tanstack/svelte-query';
1617
1718
interface Props {
@@ -388,7 +389,7 @@
388389
<Card.Title>{m.vuln_title()}</Card.Title>
389390
<Card.Description>
390391
{#if scan?.status === 'completed'}
391-
{m.vuln_scan_time()}: {scan ? new Date(scan.scanTime).toLocaleString() : m.common_na()}
392+
{m.vuln_scan_time()}: {scan ? formatDateTime(scan.scanTime) : m.common_na()}
392393
{:else if isWorking}
393394
{m.vuln_scanning()}
394395
{:else}

frontend/src/lib/utils/locale.util.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,30 @@
11
import { setLocale as setParaglideLocale, type Locale } from '$lib/paraglide/runtime';
2-
import { setDefaultOptions } from 'date-fns';
2+
import { format, setDefaultOptions } from 'date-fns';
33
import { z } from 'zod/v4';
44

5+
/**
6+
* Format a date/timestamp as a locale-aware datetime string.
7+
* Uses date-fns which picks up the locale set by setLocale(), so it
8+
* reflects the language the user has chosen in the app rather than
9+
* defaulting to the browser's system locale.
10+
*/
11+
export function formatDateTime(date: Date | string | null | undefined): string {
12+
if (!date) return '';
13+
const d = typeof date === 'string' ? new Date(date) : date;
14+
if (isNaN(d.getTime())) return '';
15+
return format(d, 'PPpp');
16+
}
17+
18+
/**
19+
* Same as formatDateTime but omits seconds (PPp).
20+
*/
21+
export function formatDateTimeShort(date: Date | string | null | undefined): string {
22+
if (!date) return '';
23+
const d = typeof date === 'string' ? new Date(date) : date;
24+
if (isNaN(d.getTime())) return '';
25+
return format(d, 'PPp');
26+
}
27+
528
export async function setLocale(locale: Locale, reload = true) {
629
let dateFnsLocale: string = locale;
730
if (dateFnsLocale === 'en') {

0 commit comments

Comments
 (0)