mirror of
https://github.com/immich-app/immich
synced 2025-11-14 17:36:12 +00:00
chore(web): another missing translations (#10274)
* chore(web): another missing translations * unused removed * more keys * lint fix * test fixed * dynamic translation fix * fixes * people search translation * params fixed * keep filter setting fix * lint fix * $t fixes * Update web/src/lib/i18n/en.json Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com> * another missing * activity translation * link sharing translations * expiration dropdown fix - didn't work localized * notification title * device logout * search results * reset to default * unsaved change * select from computer * selected * select-2 * select-3 * unmerge * pluralize, force icu message * Update web/src/lib/components/asset-viewer/asset-viewer.svelte Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com> * review fixes * remove user * plural fixes * ffmpeg settings * fixes * error title * plural fixes * onboarding * change password * more more * console log fix * another * api key desc * map marker * format fix * key fix * asset-utils * utils * misc --------- Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com>
This commit is contained in:
parent
df9e074304
commit
dd2c7400a6
90 changed files with 635 additions and 322 deletions
|
|
@ -1,5 +1,7 @@
|
|||
import { notificationController, NotificationType } from '$lib/components/shared-components/notification/notification';
|
||||
import { deleteAssets as deleteBulk, type AssetResponseDto } from '@immich/sdk';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { get } from 'svelte/store';
|
||||
import { handleError } from './handle-error';
|
||||
|
||||
export type OnDelete = (assetIds: string[]) => void;
|
||||
|
|
@ -10,15 +12,18 @@ export type OnStack = (ids: string[]) => void;
|
|||
export type OnUnstack = (assets: AssetResponseDto[]) => void;
|
||||
|
||||
export const deleteAssets = async (force: boolean, onAssetDelete: OnDelete, ids: string[]) => {
|
||||
const $t = get(t);
|
||||
try {
|
||||
await deleteBulk({ assetBulkDeleteDto: { ids, force } });
|
||||
onAssetDelete(ids);
|
||||
|
||||
notificationController.show({
|
||||
message: `${force ? 'Permanently deleted' : 'Trashed'} ${ids.length} assets`,
|
||||
message: force
|
||||
? $t('assets_permanently_deleted_count', { values: { count: ids.length } })
|
||||
: $t('assets_trashed_count', { values: { count: ids.length } }),
|
||||
type: NotificationType.Info,
|
||||
});
|
||||
} catch (error) {
|
||||
handleError(error, 'Error deleting assets');
|
||||
handleError(error, $t('errors.unable_to_delete_assets'));
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { goto } from '$app/navigation';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import {
|
||||
AlbumFilter,
|
||||
AlbumGroupBy,
|
||||
AlbumSortBy,
|
||||
SortOrder,
|
||||
|
|
@ -10,6 +11,7 @@ import {
|
|||
import { handleError } from '$lib/utils/handle-error';
|
||||
import type { AlbumResponseDto } from '@immich/sdk';
|
||||
import * as sdk from '@immich/sdk';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { get } from 'svelte/store';
|
||||
|
||||
/**
|
||||
|
|
@ -27,7 +29,8 @@ export const createAlbum = async (name?: string, assetIds?: string[]) => {
|
|||
});
|
||||
return newAlbum;
|
||||
} catch (error) {
|
||||
handleError(error, 'Failed to create album');
|
||||
const $t = get(t);
|
||||
handleError(error, $t('errors.failed_to_create_album'));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -45,7 +48,6 @@ export const createAlbumAndRedirect = async (name?: string, assetIds?: string[])
|
|||
*/
|
||||
export interface AlbumSortOptionMetadata {
|
||||
id: AlbumSortBy;
|
||||
text: string;
|
||||
defaultOrder: SortOrder;
|
||||
columnStyle: string;
|
||||
}
|
||||
|
|
@ -53,37 +55,31 @@ export interface AlbumSortOptionMetadata {
|
|||
export const sortOptionsMetadata: AlbumSortOptionMetadata[] = [
|
||||
{
|
||||
id: AlbumSortBy.Title,
|
||||
text: 'Title',
|
||||
defaultOrder: SortOrder.Asc,
|
||||
columnStyle: 'text-left w-8/12 sm:w-4/12 md:w-4/12 md:w-4/12 xl:w-[30%] 2xl:w-[40%]',
|
||||
},
|
||||
{
|
||||
id: AlbumSortBy.ItemCount,
|
||||
text: 'Number of items',
|
||||
defaultOrder: SortOrder.Desc,
|
||||
columnStyle: 'text-center w-4/12 m:w-2/12 md:w-2/12 xl:w-[15%] 2xl:w-[12%]',
|
||||
},
|
||||
{
|
||||
id: AlbumSortBy.DateModified,
|
||||
text: 'Date modified',
|
||||
defaultOrder: SortOrder.Desc,
|
||||
columnStyle: 'text-center hidden sm:block w-3/12 xl:w-[15%] 2xl:w-[12%]',
|
||||
},
|
||||
{
|
||||
id: AlbumSortBy.DateCreated,
|
||||
text: 'Date created',
|
||||
defaultOrder: SortOrder.Desc,
|
||||
columnStyle: 'text-center hidden sm:block w-3/12 xl:w-[15%] 2xl:w-[12%]',
|
||||
},
|
||||
{
|
||||
id: AlbumSortBy.MostRecentPhoto,
|
||||
text: 'Most recent photo',
|
||||
defaultOrder: SortOrder.Desc,
|
||||
columnStyle: 'text-center hidden xl:block xl:w-[15%] 2xl:w-[12%]',
|
||||
},
|
||||
{
|
||||
id: AlbumSortBy.OldestPhoto,
|
||||
text: 'Oldest photo',
|
||||
defaultOrder: SortOrder.Desc,
|
||||
columnStyle: 'text-center hidden xl:block xl:w-[15%] 2xl:w-[12%]',
|
||||
},
|
||||
|
|
@ -95,6 +91,12 @@ export const findSortOptionMetadata = (sortBy: string) => {
|
|||
return sortOptionsMetadata.find(({ id }) => sortBy === id) ?? defaultSortOption;
|
||||
};
|
||||
|
||||
export const findFilterOption = (filter: string) => {
|
||||
// Default is All filter
|
||||
const defaultFilterOption = AlbumFilter.All;
|
||||
return Object.values(AlbumFilter).find((key) => filter === AlbumFilter[key]) ?? defaultFilterOption;
|
||||
};
|
||||
|
||||
/**
|
||||
* --------------
|
||||
* Album Grouping
|
||||
|
|
@ -108,7 +110,6 @@ export interface AlbumGroup {
|
|||
|
||||
export interface AlbumGroupOptionMetadata {
|
||||
id: AlbumGroupBy;
|
||||
text: string;
|
||||
defaultOrder: SortOrder;
|
||||
isDisabled: () => boolean;
|
||||
}
|
||||
|
|
@ -116,13 +117,11 @@ export interface AlbumGroupOptionMetadata {
|
|||
export const groupOptionsMetadata: AlbumGroupOptionMetadata[] = [
|
||||
{
|
||||
id: AlbumGroupBy.None,
|
||||
text: 'No grouping',
|
||||
defaultOrder: SortOrder.Asc,
|
||||
isDisabled: () => false,
|
||||
},
|
||||
{
|
||||
id: AlbumGroupBy.Year,
|
||||
text: 'Group by year',
|
||||
defaultOrder: SortOrder.Desc,
|
||||
isDisabled() {
|
||||
const disabledWithSortOptions: string[] = [AlbumSortBy.DateCreated, AlbumSortBy.DateModified];
|
||||
|
|
@ -131,7 +130,6 @@ export const groupOptionsMetadata: AlbumGroupOptionMetadata[] = [
|
|||
},
|
||||
{
|
||||
id: AlbumGroupBy.Owner,
|
||||
text: 'Group by owner',
|
||||
defaultOrder: SortOrder.Asc,
|
||||
isDisabled: () => false,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ 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 { preferences } from '$lib/stores/user.store';
|
||||
import { downloadRequest, getKey, s, withError } from '$lib/utils';
|
||||
import { downloadRequest, getKey, withError } from '$lib/utils';
|
||||
import { createAlbum } from '$lib/utils/album-utils';
|
||||
import { getByteUnitString } from '$lib/utils/byte-units';
|
||||
import { encodeHTMLSpecialChars } from '$lib/utils/string-utils';
|
||||
|
|
@ -24,7 +24,7 @@ import {
|
|||
type UserResponseDto,
|
||||
} from '@immich/sdk';
|
||||
import { DateTime } from 'luxon';
|
||||
import { t as translate } from 'svelte-i18n';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { get } from 'svelte/store';
|
||||
import { handleError } from './handle-error';
|
||||
|
||||
|
|
@ -37,15 +37,16 @@ export const addAssetsToAlbum = async (albumId: string, assetIds: string[]) => {
|
|||
key: getKey(),
|
||||
});
|
||||
const count = result.filter(({ success }) => success).length;
|
||||
const $t = get(t);
|
||||
notificationController.show({
|
||||
type: NotificationType.Info,
|
||||
timeout: 5000,
|
||||
message:
|
||||
count > 0
|
||||
? `Added ${count} asset${s(count)} to the album`
|
||||
: `Asset${assetIds.length === 1 ? ' was' : 's were'} already part of the album`,
|
||||
? $t('assets_added_to_album_count', { values: { count: count } })
|
||||
: $t('assets_were_part_of_album_count', { values: { count: assetIds.length } }),
|
||||
button: {
|
||||
text: 'View Album',
|
||||
text: $t('view_album'),
|
||||
onClick() {
|
||||
return goto(`${AppRoute.ALBUMS}/${albumId}`);
|
||||
},
|
||||
|
|
@ -59,13 +60,14 @@ export const addAssetsToNewAlbum = async (albumName: string, assetIds: string[])
|
|||
return;
|
||||
}
|
||||
const displayName = albumName ? `<b>${encodeHTMLSpecialChars(albumName)}</b>` : 'new album';
|
||||
const $t = get(t);
|
||||
notificationController.show({
|
||||
type: NotificationType.Info,
|
||||
timeout: 5000,
|
||||
message: `Added ${assetIds.length} asset${s(assetIds.length)} to ${displayName}`,
|
||||
message: $t('assets_added_to_name_count', { values: { count: assetIds.length, name: displayName } }),
|
||||
html: true,
|
||||
button: {
|
||||
text: 'View Album',
|
||||
text: $t('view_album'),
|
||||
onClick() {
|
||||
return goto(`${AppRoute.ALBUMS}/${album.id}`);
|
||||
},
|
||||
|
|
@ -100,7 +102,8 @@ export const downloadArchive = async (fileName: string, options: Omit<DownloadIn
|
|||
|
||||
const [error, downloadInfo] = await withError(() => getDownloadInfo({ downloadInfoDto: dto, key: getKey() }));
|
||||
if (error) {
|
||||
handleError(error, 'Unable to download files');
|
||||
const $t = get(t);
|
||||
handleError(error, $t('errors.unable_to_download_files'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -134,7 +137,8 @@ export const downloadArchive = async (fileName: string, options: Omit<DownloadIn
|
|||
|
||||
downloadBlob(data, archiveName);
|
||||
} catch (error) {
|
||||
handleError(error, 'Unable to download files');
|
||||
const $t = get(t);
|
||||
handleError(error, $t('errors.unable_to_download_files'));
|
||||
downloadManager.clear(downloadKey);
|
||||
return;
|
||||
} finally {
|
||||
|
|
@ -144,10 +148,11 @@ export const downloadArchive = async (fileName: string, options: Omit<DownloadIn
|
|||
};
|
||||
|
||||
export const downloadFile = async (asset: AssetResponseDto) => {
|
||||
const $t = get(t);
|
||||
if (asset.isOffline) {
|
||||
notificationController.show({
|
||||
type: NotificationType.Info,
|
||||
message: `Asset ${asset.originalFileName} is offline`,
|
||||
message: $t('asset_filename_is_offline', { values: { filename: asset.originalFileName } }),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
|
@ -178,7 +183,7 @@ export const downloadFile = async (asset: AssetResponseDto) => {
|
|||
|
||||
notificationController.show({
|
||||
type: NotificationType.Info,
|
||||
message: `Downloading asset ${asset.originalFileName}`,
|
||||
message: $t('downloading_asset_filename', { values: { filename: asset.originalFileName } }),
|
||||
});
|
||||
|
||||
// TODO use sdk once it supports progress events
|
||||
|
|
@ -191,7 +196,7 @@ export const downloadFile = async (asset: AssetResponseDto) => {
|
|||
|
||||
downloadBlob(data, filename);
|
||||
} catch (error) {
|
||||
handleError(error, `Error downloading ${filename}`);
|
||||
handleError(error, $t('errors.error_downloading', { values: { filename: filename } }));
|
||||
downloadManager.clear(downloadKey);
|
||||
} finally {
|
||||
setTimeout(() => downloadManager.clear(downloadKey), 5000);
|
||||
|
|
@ -302,8 +307,9 @@ export const getSelectedAssets = (assets: Set<AssetResponseDto>, user: UserRespo
|
|||
|
||||
const numberOfIssues = [...assets].filter((a) => user && a.ownerId !== user.id).length;
|
||||
if (numberOfIssues > 0) {
|
||||
const $t = get(t);
|
||||
notificationController.show({
|
||||
message: `Can't change metadata of ${numberOfIssues} asset${s(numberOfIssues)}`,
|
||||
message: $t('errors.cant_change_metadata_assets_count', { values: { count: numberOfIssues } }),
|
||||
type: NotificationType.Warning,
|
||||
});
|
||||
}
|
||||
|
|
@ -318,6 +324,7 @@ export const stackAssets = async (assets: AssetResponseDto[]) => {
|
|||
const parent = assets[0];
|
||||
const children = assets.slice(1);
|
||||
const ids = children.map(({ id }) => id);
|
||||
const $t = get(t);
|
||||
|
||||
try {
|
||||
await updateAssets({
|
||||
|
|
@ -327,7 +334,7 @@ export const stackAssets = async (assets: AssetResponseDto[]) => {
|
|||
},
|
||||
});
|
||||
} catch (error) {
|
||||
handleError(error, 'Failed to stack assets');
|
||||
handleError(error, $t('errors.failed_to_stack_assets'));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -348,10 +355,10 @@ export const stackAssets = async (assets: AssetResponseDto[]) => {
|
|||
parent.stackCount = parent.stack.length + 1;
|
||||
|
||||
notificationController.show({
|
||||
message: `Stacked ${parent.stackCount} assets`,
|
||||
message: $t('stacked_assets_count', { values: { count: parent.stackCount } }),
|
||||
type: NotificationType.Info,
|
||||
button: {
|
||||
text: 'View Stack',
|
||||
text: $t('view_stack'),
|
||||
onClick() {
|
||||
return assetViewingStore.setAssetId(parent.id);
|
||||
},
|
||||
|
|
@ -363,6 +370,7 @@ export const stackAssets = async (assets: AssetResponseDto[]) => {
|
|||
|
||||
export const unstackAssets = async (assets: AssetResponseDto[]) => {
|
||||
const ids = assets.map(({ id }) => id);
|
||||
const $t = get(t);
|
||||
try {
|
||||
await updateAssets({
|
||||
assetBulkUpdateDto: {
|
||||
|
|
@ -371,7 +379,7 @@ export const unstackAssets = async (assets: AssetResponseDto[]) => {
|
|||
},
|
||||
});
|
||||
} catch (error) {
|
||||
handleError(error, 'Failed to un-stack assets');
|
||||
handleError(error, $t('errors.failed_to_unstack_assets'));
|
||||
return;
|
||||
}
|
||||
for (const asset of assets) {
|
||||
|
|
@ -381,7 +389,7 @@ export const unstackAssets = async (assets: AssetResponseDto[]) => {
|
|||
}
|
||||
notificationController.show({
|
||||
type: NotificationType.Info,
|
||||
message: `Un-stacked ${assets.length} assets`,
|
||||
message: $t('unstacked_assets_count', { values: { count: assets.length } }),
|
||||
});
|
||||
return assets;
|
||||
};
|
||||
|
|
@ -409,12 +417,14 @@ export const selectAllAssets = async (assetStore: AssetStore, assetInteractionSt
|
|||
await delay(0);
|
||||
}
|
||||
} catch (error) {
|
||||
handleError(error, 'Error selecting all assets');
|
||||
const $t = get(t);
|
||||
handleError(error, $t('errors.error_selecting_all_assets'));
|
||||
isSelectingAllAssets.set(false);
|
||||
}
|
||||
};
|
||||
|
||||
export const toggleArchive = async (asset: AssetResponseDto) => {
|
||||
const $t = get(t);
|
||||
try {
|
||||
const data = await updateAsset({
|
||||
id: asset.id,
|
||||
|
|
@ -427,10 +437,10 @@ export const toggleArchive = async (asset: AssetResponseDto) => {
|
|||
|
||||
notificationController.show({
|
||||
type: NotificationType.Info,
|
||||
message: asset.isArchived ? `Added to archive` : `Removed from archive`,
|
||||
message: asset.isArchived ? $t(`added_to_archive`) : $t(`removed_from_archive`),
|
||||
});
|
||||
} catch (error) {
|
||||
handleError(error, `Unable to ${asset.isArchived ? `remove asset from` : `add asset to`} archive`);
|
||||
handleError(error, $t('errors.unable_to_add_remove_archive', { values: { archived: asset.isArchived } }));
|
||||
}
|
||||
|
||||
return asset;
|
||||
|
|
@ -439,6 +449,7 @@ export const toggleArchive = async (asset: AssetResponseDto) => {
|
|||
export const archiveAssets = async (assets: AssetResponseDto[], archive: boolean) => {
|
||||
const isArchived = archive;
|
||||
const ids = assets.map(({ id }) => id);
|
||||
const $t = get(t);
|
||||
|
||||
try {
|
||||
if (ids.length > 0) {
|
||||
|
|
@ -449,13 +460,14 @@ export const archiveAssets = async (assets: AssetResponseDto[], archive: boolean
|
|||
asset.isArchived = isArchived;
|
||||
}
|
||||
|
||||
const t = get(translate);
|
||||
notificationController.show({
|
||||
message: `${isArchived ? t('archived') : t('unarchived')} ${ids.length}`,
|
||||
message: isArchived
|
||||
? $t('archived_count', { values: { count: ids.length } })
|
||||
: $t('unarchived_count', { values: { count: ids.length } }),
|
||||
type: NotificationType.Info,
|
||||
});
|
||||
} catch (error) {
|
||||
handleError(error, `Unable to ${isArchived ? 'archive' : 'unarchive'}`);
|
||||
handleError(error, $t('errors.unable_to_archive_unarchive', { values: { archived: isArchived } }));
|
||||
}
|
||||
|
||||
return ids;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import type { PersonResponseDto } from '@immich/sdk';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { get } from 'svelte/store';
|
||||
|
||||
export const searchNameLocal = (
|
||||
name: string,
|
||||
|
|
@ -26,5 +28,6 @@ export const searchNameLocal = (
|
|||
};
|
||||
|
||||
export const getPersonNameWithHiddenValue = (name: string, isHidden: boolean) => {
|
||||
return `${name ? name + (isHidden ? ' ' : '') : ''}${isHidden ? '(hidden)' : ''}`;
|
||||
const $t = get(t);
|
||||
return $t('person_hidden', { values: { name: name, hidden: isHidden } });
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue