mirror of
https://github.com/immich-app/immich.git
synced 2025-06-01 12:44:17 -04:00
fix(web): Make date-time formatting follow locale (#17899)
* fixed missing $locale parameter to .toLocaleString * Remove unused types and functions in timeline-util * remove unused export * re-enable export because it is needed for tests * format
This commit is contained in:
parent
205260d31c
commit
85ac0512a6
@ -544,7 +544,9 @@
|
|||||||
|
|
||||||
<div class="absolute left-8 top-4 text-sm font-medium text-white">
|
<div class="absolute left-8 top-4 text-sm font-medium text-white">
|
||||||
<p>
|
<p>
|
||||||
{fromLocalDateTime(current.memory.assets[0].localDateTime).toLocaleString(DateTime.DATE_FULL)}
|
{fromLocalDateTime(current.memory.assets[0].localDateTime).toLocaleString(DateTime.DATE_FULL, {
|
||||||
|
locale: $locale,
|
||||||
|
})}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{current.asset.exifInfo?.city || ''}
|
{current.asset.exifInfo?.city || ''}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import { mdiAlert } from '@mdi/js';
|
import { mdiAlert } from '@mdi/js';
|
||||||
import Icon from '$lib/components/elements/icon.svelte';
|
import Icon from '$lib/components/elements/icon.svelte';
|
||||||
|
import { locale } from '$lib/stores/preferences.store';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
@ -177,16 +178,19 @@
|
|||||||
<span
|
<span
|
||||||
class="immich-form-label pb-2 text-xs"
|
class="immich-form-label pb-2 text-xs"
|
||||||
id="version-history"
|
id="version-history"
|
||||||
title={createdAt.toLocaleString(DateTime.DATETIME_SHORT_WITH_SECONDS)}
|
title={createdAt.toLocaleString(DateTime.DATETIME_SHORT_WITH_SECONDS, { locale: $locale })}
|
||||||
>
|
>
|
||||||
{$t('version_history_item', {
|
{$t('version_history_item', {
|
||||||
values: {
|
values: {
|
||||||
version: item.version,
|
version: item.version,
|
||||||
date: createdAt.toLocaleString({
|
date: createdAt.toLocaleString(
|
||||||
month: 'short',
|
{
|
||||||
day: 'numeric',
|
month: 'short',
|
||||||
year: 'numeric',
|
day: 'numeric',
|
||||||
}),
|
year: 'numeric',
|
||||||
|
},
|
||||||
|
{ locale: $locale },
|
||||||
|
),
|
||||||
},
|
},
|
||||||
})}
|
})}
|
||||||
</span>
|
</span>
|
||||||
|
@ -64,7 +64,9 @@
|
|||||||
<span>{DateTime.fromISO(device.updatedAt, { locale: $locale }).toRelativeCalendar(options)}</span>
|
<span>{DateTime.fromISO(device.updatedAt, { locale: $locale }).toRelativeCalendar(options)}</span>
|
||||||
<span class="text-xs text-gray-500 dark:text-gray-400"> - </span>
|
<span class="text-xs text-gray-500 dark:text-gray-400"> - </span>
|
||||||
<span class="text-xs text-gray-500 dark:text-gray-400">
|
<span class="text-xs text-gray-500 dark:text-gray-400">
|
||||||
{DateTime.fromISO(device.updatedAt, { locale: $locale }).toLocaleString(DateTime.DATETIME_MED)}
|
{DateTime.fromISO(device.updatedAt, { locale: $locale }).toLocaleString(DateTime.DATETIME_MED, {
|
||||||
|
locale: $locale,
|
||||||
|
})}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -34,6 +34,7 @@ export function getBytesWithUnit(bytes: number, maxPrecision = 1): [number, Byte
|
|||||||
* * de: `1,5 KiB`
|
* * de: `1,5 KiB`
|
||||||
*
|
*
|
||||||
* @param bytes number of bytes
|
* @param bytes number of bytes
|
||||||
|
* @param locale locale to use, default is `navigator.language`
|
||||||
* @param maxPrecision maximum number of decimal places, default is `1`
|
* @param maxPrecision maximum number of decimal places, default is `1`
|
||||||
* @returns localized bytes with unit as string
|
* @returns localized bytes with unit as string
|
||||||
*/
|
*/
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
|
import { locale } from '$lib/stores/preferences.store';
|
||||||
import { AssetTypeEnum, type AssetResponseDto } from '@immich/sdk';
|
import { AssetTypeEnum, type AssetResponseDto } from '@immich/sdk';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import { derived } from 'svelte/store';
|
import { derived, get } from 'svelte/store';
|
||||||
import { fromLocalDateTime } from './timeline-util';
|
import { fromLocalDateTime } from './timeline-util';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -43,7 +44,7 @@ export const getAltText = derived(t, ($t) => {
|
|||||||
return asset.exifInfo.description;
|
return asset.exifInfo.description;
|
||||||
}
|
}
|
||||||
|
|
||||||
const date = fromLocalDateTime(asset.localDateTime).toLocaleString({ dateStyle: 'long' });
|
const date = fromLocalDateTime(asset.localDateTime).toLocaleString({ dateStyle: 'long' }, { locale: get(locale) });
|
||||||
const hasPlace = !!asset.exifInfo?.city && !!asset.exifInfo?.country;
|
const hasPlace = !!asset.exifInfo?.city && !!asset.exifInfo?.country;
|
||||||
const names = asset.people?.filter((p) => p.name).map((p) => p.name) ?? [];
|
const names = asset.people?.filter((p) => p.name).map((p) => p.name) ?? [];
|
||||||
const peopleCount = names.length;
|
const peopleCount = names.length;
|
||||||
|
@ -1,41 +1,13 @@
|
|||||||
import type { AssetBucket } from '$lib/stores/assets-store.svelte';
|
|
||||||
import { locale } from '$lib/stores/preferences.store';
|
import { locale } from '$lib/stores/preferences.store';
|
||||||
import { type CommonJustifiedLayout } from '$lib/utils/layout-utils';
|
|
||||||
|
|
||||||
import type { AssetResponseDto } from '@immich/sdk';
|
|
||||||
import { memoize } from 'lodash-es';
|
import { memoize } from 'lodash-es';
|
||||||
import { DateTime, type LocaleOptions } from 'luxon';
|
import { DateTime, type LocaleOptions } from 'luxon';
|
||||||
import { get } from 'svelte/store';
|
import { get } from 'svelte/store';
|
||||||
|
|
||||||
export type DateGroup = {
|
|
||||||
bucket: AssetBucket;
|
|
||||||
index: number;
|
|
||||||
row: number;
|
|
||||||
col: number;
|
|
||||||
date: DateTime;
|
|
||||||
groupTitle: string;
|
|
||||||
assets: AssetResponseDto[];
|
|
||||||
assetsIntersecting: boolean[];
|
|
||||||
height: number;
|
|
||||||
intersecting: boolean;
|
|
||||||
geometry: CommonJustifiedLayout;
|
|
||||||
};
|
|
||||||
export type ScrubberListener = (
|
export type ScrubberListener = (
|
||||||
bucketDate: string | undefined,
|
bucketDate: string | undefined,
|
||||||
overallScrollPercent: number,
|
overallScrollPercent: number,
|
||||||
bucketScrollPercent: number,
|
bucketScrollPercent: number,
|
||||||
) => void | Promise<void>;
|
) => void | Promise<void>;
|
||||||
export type ScrollTargetListener = ({
|
|
||||||
bucket,
|
|
||||||
dateGroup,
|
|
||||||
asset,
|
|
||||||
offset,
|
|
||||||
}: {
|
|
||||||
bucket: AssetBucket;
|
|
||||||
dateGroup: DateGroup;
|
|
||||||
asset: AssetResponseDto;
|
|
||||||
offset: number;
|
|
||||||
}) => void;
|
|
||||||
|
|
||||||
export const fromLocalDateTime = (localDateTime: string) =>
|
export const fromLocalDateTime = (localDateTime: string) =>
|
||||||
DateTime.fromISO(localDateTime, { zone: 'UTC', locale: get(locale) });
|
DateTime.fromISO(localDateTime, { zone: 'UTC', locale: get(locale) });
|
||||||
@ -43,31 +15,6 @@ export const fromLocalDateTime = (localDateTime: string) =>
|
|||||||
export const fromDateTimeOriginal = (dateTimeOriginal: string, timeZone: string) =>
|
export const fromDateTimeOriginal = (dateTimeOriginal: string, timeZone: string) =>
|
||||||
DateTime.fromISO(dateTimeOriginal, { zone: timeZone });
|
DateTime.fromISO(dateTimeOriginal, { zone: timeZone });
|
||||||
|
|
||||||
export type LayoutBox = {
|
|
||||||
aspectRatio: number;
|
|
||||||
top: number;
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
left: number;
|
|
||||||
forcedAspectRatio?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function findTotalOffset(element: HTMLElement, stop: HTMLElement) {
|
|
||||||
let offset = 0;
|
|
||||||
while (element.offsetParent && element !== stop) {
|
|
||||||
offset += element.offsetTop;
|
|
||||||
element = element.offsetParent as HTMLElement;
|
|
||||||
}
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const groupDateFormat: Intl.DateTimeFormatOptions = {
|
|
||||||
weekday: 'short',
|
|
||||||
month: 'short',
|
|
||||||
day: 'numeric',
|
|
||||||
year: 'numeric',
|
|
||||||
};
|
|
||||||
|
|
||||||
export function formatGroupTitle(_date: DateTime): string {
|
export function formatGroupTitle(_date: DateTime): string {
|
||||||
if (!_date.isValid) {
|
if (!_date.isValid) {
|
||||||
return _date.toString();
|
return _date.toString();
|
||||||
@ -87,20 +34,24 @@ export function formatGroupTitle(_date: DateTime): string {
|
|||||||
|
|
||||||
// Last week
|
// Last week
|
||||||
if (date >= today.minus({ days: 6 }) && date < today) {
|
if (date >= today.minus({ days: 6 }) && date < today) {
|
||||||
return date.toLocaleString({ weekday: 'long' });
|
return date.toLocaleString({ weekday: 'long' }, { locale: get(locale) });
|
||||||
}
|
}
|
||||||
|
|
||||||
// This year
|
// This year
|
||||||
if (today.hasSame(date, 'year')) {
|
if (today.hasSame(date, 'year')) {
|
||||||
return date.toLocaleString({
|
return date.toLocaleString(
|
||||||
weekday: 'short',
|
{
|
||||||
month: 'short',
|
weekday: 'short',
|
||||||
day: 'numeric',
|
month: 'short',
|
||||||
});
|
day: 'numeric',
|
||||||
|
},
|
||||||
|
{ locale: get(locale) },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return getDateLocaleString(date);
|
return getDateLocaleString(date, { locale: get(locale) });
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getDateLocaleString = (date: DateTime, opts?: LocaleOptions): string =>
|
export const getDateLocaleString = (date: DateTime, opts?: LocaleOptions): string =>
|
||||||
date.toLocaleString(DateTime.DATE_MED_WITH_WEEKDAY, opts);
|
date.toLocaleString(DateTime.DATE_MED_WITH_WEEKDAY, opts);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user