diff --git a/web/src/lib/components/album-page/album-card.svelte b/web/src/lib/components/album-page/album-card.svelte
index 6bd5f85485..6d64783e38 100644
--- a/web/src/lib/components/album-page/album-card.svelte
+++ b/web/src/lib/components/album-page/album-card.svelte
@@ -7,6 +7,7 @@
import { getShortDateRange } from '$lib/utils/date-time';
import AlbumCover from '$lib/components/album-page/album-cover.svelte';
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
+ import { s } from '$lib/utils';
export let album: AlbumResponseDto;
export let showOwner = false;
@@ -65,7 +66,7 @@
{#if showItemCount}
{album.assetCount.toLocaleString($locale)}
- {album.assetCount === 1 ? `item` : `items`}
+ item{s(album.assetCount)}
{/if}
diff --git a/web/src/lib/components/faces-page/unmerge-face-selector.svelte b/web/src/lib/components/faces-page/unmerge-face-selector.svelte
index 2343df68c9..1e480f85db 100644
--- a/web/src/lib/components/faces-page/unmerge-face-selector.svelte
+++ b/web/src/lib/components/faces-page/unmerge-face-selector.svelte
@@ -19,6 +19,7 @@
import { NotificationType, notificationController } from '../shared-components/notification/notification';
import FaceThumbnail from './face-thumbnail.svelte';
import PeopleList from './people-list.svelte';
+ import { s } from '$lib/utils';
export let assetIds: string[];
export let personAssets: PersonResponseDto;
@@ -76,7 +77,7 @@
await reassignFaces({ id: data.id, assetFaceUpdateDto: { data: selectedPeople } });
notificationController.show({
- message: `Re-assigned ${assetIds.length} asset${assetIds.length > 1 ? 's' : ''} to a new person`,
+ message: `Re-assigned ${assetIds.length} asset${s(assetIds.length)} to a new person`,
type: NotificationType.Info,
});
} catch (error) {
@@ -96,7 +97,7 @@
if (selectedPerson) {
await reassignFaces({ id: selectedPerson.id, assetFaceUpdateDto: { data: selectedPeople } });
notificationController.show({
- message: `Re-assigned ${assetIds.length} asset${assetIds.length > 1 ? 's' : ''} to ${
+ message: `Re-assigned ${assetIds.length} asset${s(assetIds.length)} to ${
selectedPerson.name || 'an existing person'
}`,
type: NotificationType.Info,
diff --git a/web/src/lib/components/forms/library-import-paths-form.svelte b/web/src/lib/components/forms/library-import-paths-form.svelte
index dca565ce31..be832a63a3 100644
--- a/web/src/lib/components/forms/library-import-paths-form.svelte
+++ b/web/src/lib/components/forms/library-import-paths-form.svelte
@@ -9,6 +9,7 @@
import type { ValidateLibraryImportPathResponseDto } from '@immich/sdk';
import { NotificationType, notificationController } from '../shared-components/notification/notification';
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
+ import { s } from '$lib/utils';
export let library: LibraryResponseDto;
@@ -56,14 +57,9 @@
type: NotificationType.Info,
});
}
- } else if (failedPaths === 1) {
- notificationController.show({
- message: `${failedPaths} path failed validation`,
- type: NotificationType.Warning,
- });
} else {
notificationController.show({
- message: `${failedPaths} paths failed validation`,
+ message: `${failedPaths} path${s(failedPaths)} failed validation`,
type: NotificationType.Warning,
});
}
diff --git a/web/src/lib/components/photos-page/actions/remove-from-album.svelte b/web/src/lib/components/photos-page/actions/remove-from-album.svelte
index c6db3e077a..89385327a5 100644
--- a/web/src/lib/components/photos-page/actions/remove-from-album.svelte
+++ b/web/src/lib/components/photos-page/actions/remove-from-album.svelte
@@ -9,6 +9,7 @@
import { mdiDeleteOutline, mdiImageRemoveOutline } from '@mdi/js';
import MenuOption from '../../shared-components/context-menu/menu-option.svelte';
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
+ import { s } from '$lib/utils';
export let album: AlbumResponseDto;
export let onRemove: ((assetIds: string[]) => void) | undefined;
@@ -33,7 +34,7 @@
const count = results.filter(({ success }) => success).length;
notificationController.show({
type: NotificationType.Info,
- message: `Removed ${count} asset${count === 1 ? '' : 's'}`,
+ message: `Removed ${count} asset${s(count)}`,
});
clearSelect();
diff --git a/web/src/lib/components/photos-page/delete-asset-dialog.svelte b/web/src/lib/components/photos-page/delete-asset-dialog.svelte
index 6338ff875f..22a1855ab4 100644
--- a/web/src/lib/components/photos-page/delete-asset-dialog.svelte
+++ b/web/src/lib/components/photos-page/delete-asset-dialog.svelte
@@ -3,6 +3,7 @@
import ConfirmDialogue from '../shared-components/confirm-dialogue.svelte';
import { showDeleteModal } from '$lib/stores/preferences.store';
import Checkbox from '$lib/components/elements/checkbox.svelte';
+ import { s } from '$lib/utils';
export let size: number;
@@ -23,7 +24,7 @@
dispatch('cancel')}
diff --git a/web/src/lib/components/shared-components/upload-panel.svelte b/web/src/lib/components/shared-components/upload-panel.svelte
index bbcba662b3..7793fd5a1c 100644
--- a/web/src/lib/components/shared-components/upload-panel.svelte
+++ b/web/src/lib/components/shared-components/upload-panel.svelte
@@ -8,6 +8,7 @@
import { uploadExecutionQueue } from '$lib/utils/file-uploader';
import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
import { mdiCog, mdiWindowMinimize, mdiCancel, mdiCloudUploadOutline } from '@mdi/js';
+ import { s } from '$lib/utils';
let showDetail = false;
let showOptions = false;
@@ -36,7 +37,7 @@
on:outroend={() => {
if ($errorCounter > 0) {
notificationController.show({
- message: `Upload completed with ${$errorCounter} error${$errorCounter > 1 ? 's' : ''}, refresh the page to see new upload assets.`,
+ message: `Upload completed with ${$errorCounter} error${s($errorCounter)}, refresh the page to see new upload assets.`,
type: NotificationType.Warning,
});
} else if ($successCounter > 0) {
@@ -47,7 +48,7 @@
}
if ($duplicateCounter > 0) {
notificationController.show({
- message: `Skipped ${$duplicateCounter} duplicate asset${$duplicateCounter > 1 ? 's' : ''}`,
+ message: `Skipped ${$duplicateCounter} duplicate asset${s($duplicateCounter)}`,
type: NotificationType.Warning,
});
}
diff --git a/web/src/lib/utils.ts b/web/src/lib/utils.ts
index 87cef15737..2baabd0a44 100644
--- a/web/src/lib/utils.ts
+++ b/web/src/lib/utils.ts
@@ -279,4 +279,6 @@ export const handlePromiseError = (promise: Promise): void => {
promise.catch((error) => console.error(`[utils.ts]:handlePromiseError ${error}`, error));
};
-export const memoryLaneTitle = (yearsAgo: number) => `${yearsAgo} ${yearsAgo ? 'years' : 'year'} ago`;
+export const s = (count: number) => (count === 1 ? '' : 's');
+
+export const memoryLaneTitle = (yearsAgo: number) => `year${s(yearsAgo)} ago`;
diff --git a/web/src/lib/utils/asset-utils.ts b/web/src/lib/utils/asset-utils.ts
index 7a8bff68b2..dc29375ddd 100644
--- a/web/src/lib/utils/asset-utils.ts
+++ b/web/src/lib/utils/asset-utils.ts
@@ -5,7 +5,7 @@ import type { AssetInteractionStore } from '$lib/stores/asset-interaction.store'
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
import { BucketPosition, isSelectingAllAssets, type AssetStore } from '$lib/stores/assets.store';
import { downloadManager } from '$lib/stores/download';
-import { downloadRequest, getKey } from '$lib/utils';
+import { downloadRequest, getKey, s } from '$lib/utils';
import { createAlbum } from '$lib/utils/album-utils';
import { encodeHTMLSpecialChars } from '$lib/utils/string-utils';
import {
@@ -38,7 +38,7 @@ export const addAssetsToAlbum = async (albumId: string, assetIds: string[]) => {
timeout: 5000,
message:
count > 0
- ? `Added ${count} asset${count === 1 ? '' : 's'} to the album`
+ ? `Added ${count} asset${s(count)} to the album`
: `Asset${assetIds.length === 1 ? ' was' : 's were'} already part of the album`,
button: {
text: 'View Album',
@@ -58,7 +58,7 @@ export const addAssetsToNewAlbum = async (albumName: string, assetIds: string[])
notificationController.show({
type: NotificationType.Info,
timeout: 5000,
- message: `Added ${assetIds.length} asset${assetIds.length === 1 ? '' : 's'} to ${displayName}`,
+ message: `Added ${assetIds.length} asset${s(assetIds.length)} to ${displayName}`,
html: true,
button: {
text: 'View Album',
@@ -267,7 +267,7 @@ export const getSelectedAssets = (assets: Set, user: UserRespo
const numberOfIssues = [...assets].filter((a) => user && a.ownerId !== user.id).length;
if (numberOfIssues > 0) {
notificationController.show({
- message: `Can't change metadata of ${numberOfIssues} asset${numberOfIssues > 1 ? 's' : ''}`,
+ message: `Can't change metadata of ${numberOfIssues} asset${s(numberOfIssues)}`,
type: NotificationType.Warning,
});
}
diff --git a/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte
index 4c57a9b967..c4e859fc3d 100644
--- a/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte
+++ b/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte
@@ -42,7 +42,7 @@
import { locale } from '$lib/stores/preferences.store';
import { SlideshowNavigation, SlideshowState, slideshowStore } from '$lib/stores/slideshow.store';
import { user } from '$lib/stores/user.store';
- import { handlePromiseError } from '$lib/utils';
+ import { handlePromiseError, s } from '$lib/utils';
import { downloadAlbum } from '$lib/utils/asset-utils';
import { clickOutside } from '$lib/utils/click-outside';
import { getContextMenuPosition } from '$lib/utils/context-menu';
@@ -291,7 +291,7 @@
const count = results.filter(({ success }) => success).length;
notificationController.show({
type: NotificationType.Info,
- message: `Added ${count} asset${count === 1 ? '' : 's'}`,
+ message: `Added ${count} asset${s(count)}`,
});
await refreshAlbum();
diff --git a/web/src/routes/(user)/people/[personId]/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/people/[personId]/[[photos=photos]]/[[assetId=id]]/+page.svelte
index fcf31584f4..b87af4d37f 100644
--- a/web/src/routes/(user)/people/[personId]/[[photos=photos]]/[[assetId=id]]/+page.svelte
+++ b/web/src/routes/(user)/people/[personId]/[[photos=photos]]/[[assetId=id]]/+page.svelte
@@ -31,7 +31,7 @@
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
import { AssetStore } from '$lib/stores/assets.store';
import { websocketEvents } from '$lib/stores/websocket';
- import { getPeopleThumbnailUrl, handlePromiseError } from '$lib/utils';
+ import { getPeopleThumbnailUrl, handlePromiseError, s } from '$lib/utils';
import { clickOutside } from '$lib/utils/click-outside';
import { handleError } from '$lib/utils/handle-error';
import { isExternalUrl } from '$lib/utils/navigation';
@@ -482,7 +482,7 @@
{#if data.person.name}
{data.person.name}
- {`${numberOfAssets} asset${numberOfAssets > 1 ? 's' : ''}`}
+ {`${numberOfAssets} asset${s(numberOfAssets)}`}
{:else}
Add a name