mirror of
https://github.com/immich-app/immich.git
synced 2024-12-28 22:51:59 +00:00
feat(web): add more translations (#10700)
* feat(web): add more translations * formatting
This commit is contained in:
parent
e54c18367b
commit
c58148af35
20 changed files with 90 additions and 53 deletions
|
@ -74,7 +74,7 @@
|
|||
<div class="flex justify-center m-4 gap-2">
|
||||
<Checkbox
|
||||
id="queue-user-deletion-checkbox"
|
||||
label="Queue user and assets for immediate deletion"
|
||||
label={$t('admin.user_delete_immediately_checkbox')}
|
||||
labelClass="text-sm dark:text-immich-dark-fg"
|
||||
bind:checked={forceDelete}
|
||||
on:change={() => {
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
>
|
||||
<svelte:fragment slot="prompt">
|
||||
<div class="flex flex-col gap-4">
|
||||
<p>Are you sure you want to disable all login methods? Login will be completely disabled.</p>
|
||||
<p>{$t('admin.authentication_settings_disable_all')}</p>
|
||||
<p>
|
||||
<FormatMessage key="admin.authentication_settings_reenable" let:message>
|
||||
<a
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
export let config: SystemConfigDto; // this is the config that is being edited
|
||||
export let disabled = false;
|
||||
|
||||
const cronExpressionOptions = [
|
||||
$: cronExpressionOptions = [
|
||||
{ title: $t('interval.night_at_midnight'), expression: '0 0 * * *' },
|
||||
{ title: $t('interval.night_at_twoam'), expression: '0 2 * * *' },
|
||||
{ title: $t('interval.day_at_onepm'), expression: '0 13 * * *' },
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
SettingInputFieldType,
|
||||
} from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
import FormatMessage from '$lib/components/i18n/format-message.svelte';
|
||||
|
||||
export let savedConfig: SystemConfigDto;
|
||||
export let defaultConfig: SystemConfigDto;
|
||||
|
@ -52,12 +53,16 @@
|
|||
<SettingAccordion key="reverse-geocoding" title={$t('admin.map_reverse_geocoding_settings')}>
|
||||
<svelte:fragment slot="subtitle">
|
||||
<p class="text-sm dark:text-immich-dark-fg">
|
||||
Manage <a
|
||||
href="https://immich.app/docs/features/reverse-geocoding"
|
||||
class="underline"
|
||||
target="_blank"
|
||||
rel="noreferrer">{$t('admin.map_reverse_geocoding')}</a
|
||||
> settings
|
||||
<FormatMessage key="admin.map_manage_reverse_geocoding_settings" let:message>
|
||||
<a
|
||||
href="https://immich.app/docs/features/reverse-geocoding"
|
||||
class="underline"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{message}
|
||||
</a>
|
||||
</FormatMessage>
|
||||
</p>
|
||||
</svelte:fragment>
|
||||
<div class="ml-4 mt-4 flex flex-col gap-4">
|
||||
|
|
|
@ -12,13 +12,13 @@
|
|||
</script>
|
||||
|
||||
<div class="mt-2 text-sm">
|
||||
<h4>DATE & TIME</h4>
|
||||
<h4>{$t('date_and_time').toUpperCase()}</h4>
|
||||
</div>
|
||||
|
||||
<div class="mt-2 rounded-lg bg-gray-200 p-4 text-xs dark:bg-gray-700 dark:text-immich-dark-fg">
|
||||
<div class="mb-2 text-gray-600 dark:text-immich-dark-fg">
|
||||
<p>Asset's creation timestamp is used for the datetime information</p>
|
||||
<p>Sample time 2022-02-03T04:56:05.250</p>
|
||||
<p>{$t('admin.storage_template_date_time_description')}</p>
|
||||
<p>{$t('admin.storage_template_date_time_sample', { values: { date: '2022-02-03T04:56:05.250' } })}</p>
|
||||
</div>
|
||||
<div class="flex gap-[40px]">
|
||||
<div>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
await updateAsset({ id: asset.id, updateAssetDto: { description: newDescription } });
|
||||
notificationController.show({
|
||||
type: NotificationType.Info,
|
||||
message: 'Asset description has been updated',
|
||||
message: $t('asset_description_updated'),
|
||||
});
|
||||
} catch (error) {
|
||||
handleError(error, $t('cannot_update_the_description'));
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
import type { AdapterConstructor, PluginConstructor } from '@photo-sphere-viewer/core';
|
||||
import { fade } from 'svelte/transition';
|
||||
import LoadingSpinner from '../shared-components/loading-spinner.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
export let asset: Pick<AssetResponseDto, 'id' | 'type'>;
|
||||
|
||||
const photoSphereConfigs =
|
||||
|
@ -35,6 +36,6 @@
|
|||
{:then [data, module, adapter, plugins, navbar]}
|
||||
<svelte:component this={module.default} panorama={data} plugins={plugins ?? undefined} {navbar} {adapter} />
|
||||
{:catch}
|
||||
Failed to load asset
|
||||
{$t('errors.failed_to_load_asset')}
|
||||
{/await}
|
||||
</div>
|
||||
|
|
|
@ -209,7 +209,7 @@
|
|||
</div>
|
||||
{:else}
|
||||
{#each peopleWithFaces as face, index}
|
||||
{@const personName = face.person ? face.person?.name : 'Unassigned'}
|
||||
{@const personName = face.person ? face.person?.name : $t('face_unassigned')}
|
||||
<div class="relative z-[20001] h-[115px] w-[95px]">
|
||||
<div
|
||||
role="button"
|
||||
|
@ -261,8 +261,8 @@
|
|||
curve
|
||||
shadow
|
||||
url="/src/lib/assets/no-thumbnail.png"
|
||||
altText="Unassigned"
|
||||
title="Unassigned"
|
||||
altText={$t('face_unassigned')}
|
||||
title={$t('face_unassigned')}
|
||||
widthStyle="90px"
|
||||
heightStyle="90px"
|
||||
thumbhash={null}
|
||||
|
@ -273,8 +273,8 @@
|
|||
curve
|
||||
shadow
|
||||
url={data === null ? '/src/lib/assets/no-thumbnail.png' : data}
|
||||
altText="Unassigned"
|
||||
title="Unassigned"
|
||||
altText={$t('face_unassigned')}
|
||||
title={$t('face_unassigned')}
|
||||
widthStyle="90px"
|
||||
heightStyle="90px"
|
||||
thumbhash={null}
|
||||
|
@ -289,7 +289,7 @@
|
|||
{#if selectedPersonToReassign[face.id]?.id}
|
||||
{selectedPersonToReassign[face.id]?.name}
|
||||
{:else}
|
||||
<span class={personName == 'Unassigned' ? 'dark:text-gray-500' : ''}>{personName}</span>
|
||||
<span class={personName === $t('face_unassigned') ? 'dark:text-gray-500' : ''}>{personName}</span>
|
||||
{/if}
|
||||
</p>
|
||||
{/if}
|
||||
|
@ -322,7 +322,7 @@
|
|||
<div
|
||||
class="flex place-content-center place-items-center rounded-full bg-[#d3d3d3] p-1 transition-all absolute left-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform"
|
||||
>
|
||||
<Icon color="primary" path={mdiAccountOff} ariaLabel="Just a face" size="18" />
|
||||
<Icon color="primary" path={mdiAccountOff} ariaHidden size="18" />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
@ -57,7 +57,12 @@
|
|||
<div class="dark:text-immich-dark-fg">
|
||||
<div
|
||||
class="storage-status grid grid-cols-[64px_auto]"
|
||||
title="Used {getByteUnitString(usedBytes, $locale, 3)} of {getByteUnitString(availableBytes, $locale, 3)}"
|
||||
title={$t('storage_usage', {
|
||||
values: {
|
||||
used: getByteUnitString(usedBytes, $locale, 3),
|
||||
available: getByteUnitString(availableBytes, $locale, 3),
|
||||
},
|
||||
})}
|
||||
>
|
||||
<div class="pb-[2.15rem] pl-5 pr-6 text-immich-primary dark:text-immich-dark-primary group-hover:sm:pb-0 md:pb-0">
|
||||
<Icon path={mdiChartPie} size="24" />
|
||||
|
|
|
@ -67,7 +67,7 @@
|
|||
{:else if uploadAsset.state === UploadState.DUPLICATED}
|
||||
<div class="h-[15px] rounded-md bg-immich-warning transition-all" style="width: 100%" />
|
||||
<p class="absolute top-0 h-full w-full text-center text-[10px]">
|
||||
Skipped
|
||||
{$t('asset_skipped')}
|
||||
{#if uploadAsset.message}
|
||||
({uploadAsset.message})
|
||||
{/if}
|
||||
|
@ -75,7 +75,7 @@
|
|||
{:else if uploadAsset.state === UploadState.DONE}
|
||||
<div class="h-[15px] rounded-md bg-immich-success transition-all" style="width: 100%" />
|
||||
<p class="absolute top-0 h-full w-full text-center text-[10px]">
|
||||
Uploaded
|
||||
{$t('asset_uploaded')}
|
||||
{#if uploadAsset.message}
|
||||
({uploadAsset.message})
|
||||
{/if}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
<script lang="ts">
|
||||
import { getProfileImageUrl } from '$lib/utils';
|
||||
import { type UserAvatarColor } from '@immich/sdk';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
interface User {
|
||||
id: string;
|
||||
|
@ -77,7 +78,7 @@
|
|||
<img
|
||||
bind:this={img}
|
||||
src={getProfileImageUrl(user.id)}
|
||||
alt="Profile image of {title}"
|
||||
alt={$t('profile_image_of_user', { values: { user: title } })}
|
||||
class="h-full w-full object-cover"
|
||||
class:hidden={showFallback}
|
||||
draggable="false"
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
</FormatMessage>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 font-medium">Your friend, Alex</div>
|
||||
<div class="mt-4 font-medium">{$t('version_announcement_closing')}</div>
|
||||
|
||||
<div class="font-sm mt-8">
|
||||
<code>{$t('server_version')}: {serverVersion}</code>
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
<div class="flex flex-col justify-center gap-1 dark:text-white">
|
||||
<span class="text-sm">
|
||||
{#if device.deviceType || device.deviceOS}
|
||||
<span>{device.deviceOS || 'Unknown'} • {device.deviceType || 'Unknown'}</span>
|
||||
<span>{device.deviceOS || $t('unknown')} • {device.deviceType || $t('unknown')}</span>
|
||||
{:else}
|
||||
<span>{$t('unknown')}</span>
|
||||
{/if}
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
"add_exclusion_pattern_description": "Add exclusion patterns. Globbing using *, **, and ? is supported. To ignore all files in any directory named \"Raw\", use \"**/Raw/**\". To ignore all files ending in \".tif\", use \"**/*.tif\". To ignore an absolute path, use \"/path/to/ignore/**\".",
|
||||
"authentication_settings": "Authentication Settings",
|
||||
"authentication_settings_description": "Manage password, OAuth, and other authentication settings",
|
||||
"authentication_settings_disable_all": "Are you sure you want to disable all login methods? Login will be completely disabled.",
|
||||
"authentication_settings_reenable": "To re-enable, use a <link>Server Command</link>.",
|
||||
"background_task_job": "Background Tasks",
|
||||
"check_all": "Check All",
|
||||
|
@ -125,6 +126,7 @@
|
|||
"map_dark_style": "Dark style",
|
||||
"map_enable_description": "Enable map features",
|
||||
"map_light_style": "Light style",
|
||||
"map_manage_reverse_geocoding_settings": "Manage <link>Reverse Geocoding</link> settings",
|
||||
"map_reverse_geocoding": "Reverse Geocoding",
|
||||
"map_reverse_geocoding_enable_description": "Enable reverse geocoding",
|
||||
"map_reverse_geocoding_settings": "Reverse Geocoding Settings",
|
||||
|
@ -209,6 +211,8 @@
|
|||
"sidecar_job_description": "Discover or synchronize sidecar metadata from the filesystem",
|
||||
"slideshow_duration_description": "Number of seconds to display each image",
|
||||
"smart_search_job_description": "Run machine learning on assets to support smart search",
|
||||
"storage_template_date_time_description": "Asset's creation timestamp is used for the datetime information",
|
||||
"storage_template_date_time_sample": "Sample time {date}",
|
||||
"storage_template_enable_description": "Enable storage template engine",
|
||||
"storage_template_hash_verification_enabled": "Hash verification enabled",
|
||||
"storage_template_hash_verification_enabled_description": "Enables hash verification, don't disable this unless you're certain of the implications",
|
||||
|
@ -298,10 +302,12 @@
|
|||
"user_delete_delay_settings": "Delete delay",
|
||||
"user_delete_delay_settings_description": "Number of days after removal to permanently delete a user's account and assets. The user deletion job runs at midnight to check for users that are ready for deletion. Changes to this setting will be evaluated at the next execution.",
|
||||
"user_delete_immediately": "<b>{user}</b>'s account and assets will be queued for permanent deletion <b>immediately</b>.",
|
||||
"user_delete_immediately_checkbox": "Queue user and assets for immediate deletion",
|
||||
"user_management": "User Management",
|
||||
"user_password_has_been_reset": "The user's password has been reset:",
|
||||
"user_password_reset_description": "Please provide the temporary password to the user and inform them they will need to change the password at their next login.",
|
||||
"user_restore_description": "<b>{user}</b>'s account will be restored.",
|
||||
"user_restore_scheduled_removal": "Restore user - scheduled removal on {date, date, long}",
|
||||
"user_settings": "User Settings",
|
||||
"user_settings_description": "Manage user settings",
|
||||
"user_successfully_removed": "User {email} has been successfully removed.",
|
||||
|
@ -358,10 +364,17 @@
|
|||
"archived_count": "{count, plural, other {Archived #}}",
|
||||
"are_these_the_same_person": "Are these the same person?",
|
||||
"are_you_sure_to_do_this": "Are you sure you want to do this?",
|
||||
"asset_added_to_album": "Added to album",
|
||||
"asset_adding_to_album": "Adding to album...",
|
||||
"asset_description_updated": "Asset description has been updated",
|
||||
"asset_filename_is_offline": "Asset {filename} is offline",
|
||||
"asset_has_unassigned_faces": "Asset has unassigned faces",
|
||||
"asset_hashing": "Hashing...",
|
||||
"asset_offline": "Asset offline",
|
||||
"asset_offline_description": "This asset is offline. Immich can not access its file location. Please ensure the asset is available and then rescan the library.",
|
||||
"asset_skipped": "Skipped",
|
||||
"asset_uploaded": "Uploaded",
|
||||
"asset_uploading": "Uploading...",
|
||||
"assets": "Assets",
|
||||
"assets_added_count": "Added {count, plural, one {# asset} other {# assets}}",
|
||||
"assets_added_to_album_count": "Added {count, plural, one {# asset} other {# assets}} to the album",
|
||||
|
@ -456,6 +469,7 @@
|
|||
"date_after": "Date after",
|
||||
"date_and_time": "Date and Time",
|
||||
"date_before": "Date before",
|
||||
"date_of_birth_saved": "Date of birth saved successfully",
|
||||
"date_range": "Date range",
|
||||
"day": "Day",
|
||||
"deduplicate_all": "Deduplicate All",
|
||||
|
@ -544,6 +558,8 @@
|
|||
"failed_to_create_shared_link": "Failed to create shared link",
|
||||
"failed_to_edit_shared_link": "Failed to edit shared link",
|
||||
"failed_to_get_people": "Failed to get people",
|
||||
"failed_to_load_asset": "Failed to load asset",
|
||||
"failed_to_load_assets": "Failed to load assets",
|
||||
"failed_to_stack_assets": "Failed to stack assets",
|
||||
"failed_to_unstack_assets": "Failed to un-stack assets",
|
||||
"import_path_already_exists": "This import path already exists.",
|
||||
|
@ -569,6 +585,7 @@
|
|||
"unable_to_change_visibility": "Unable to change the visibility for {count, plural, one {# person} other {# people}}",
|
||||
"unable_to_complete_oauth_login": "Unable to complete OAuth login",
|
||||
"unable_to_connect": "Unable to connect",
|
||||
"unable_to_connect_to_server": "Unable to connect to server",
|
||||
"unable_to_copy_to_clipboard": "Cannot copy to clipboard, make sure you are accessing the page through https",
|
||||
"unable_to_create_admin_account": "Unable to create admin account",
|
||||
"unable_to_create_api_key": "Unable to create a new API Key",
|
||||
|
@ -588,6 +605,7 @@
|
|||
"unable_to_enter_fullscreen": "Unable to enter fullscreen",
|
||||
"unable_to_exit_fullscreen": "Unable to exit fullscreen",
|
||||
"unable_to_get_comments_number": "Unable to get number of comments",
|
||||
"unable_to_get_shared_link": "Failed to get shared link",
|
||||
"unable_to_hide_person": "Unable to hide person",
|
||||
"unable_to_link_oauth_account": "Unable to link OAuth account",
|
||||
"unable_to_load_album": "Unable to load album",
|
||||
|
@ -616,6 +634,7 @@
|
|||
"unable_to_restore_user": "Unable to restore user",
|
||||
"unable_to_save_album": "Unable to save album",
|
||||
"unable_to_save_api_key": "Unable to save API Key",
|
||||
"unable_to_save_date_of_birth": "Unable to save date of birth",
|
||||
"unable_to_save_name": "Unable to save name",
|
||||
"unable_to_save_profile": "Unable to save profile",
|
||||
"unable_to_save_settings": "Unable to save settings",
|
||||
|
@ -632,7 +651,8 @@
|
|||
"unable_to_update_location": "Unable to update location",
|
||||
"unable_to_update_settings": "Unable to update settings",
|
||||
"unable_to_update_timeline_display_status": "Unable to update timeline display status",
|
||||
"unable_to_update_user": "Unable to update user"
|
||||
"unable_to_update_user": "Unable to update user",
|
||||
"unable_to_upload_file": "Unable to upload file"
|
||||
},
|
||||
"exif": "Exif",
|
||||
"exit_slideshow": "Exit Slideshow",
|
||||
|
@ -646,6 +666,7 @@
|
|||
"extension": "Extension",
|
||||
"external": "External",
|
||||
"external_libraries": "External Libraries",
|
||||
"face_unassigned": "Unassigned",
|
||||
"favorite": "Favorite",
|
||||
"favorite_or_unfavorite_photo": "Favorite or unfavorite photo",
|
||||
"favorites": "Favorites",
|
||||
|
@ -870,6 +891,7 @@
|
|||
"previous_memory": "Previous memory",
|
||||
"previous_or_next_photo": "Previous or next photo",
|
||||
"primary": "Primary",
|
||||
"profile_image_of_user": "Profile image of {title}",
|
||||
"profile_picture_set": "Profile picture set.",
|
||||
"public_album": "Public album",
|
||||
"public_share": "Public Share",
|
||||
|
@ -991,6 +1013,7 @@
|
|||
"shared_photos_and_videos_count": "{assetCount, plural, other {# shared photos & videos.}}",
|
||||
"shared_with_partner": "Shared with {partner}",
|
||||
"sharing": "Sharing",
|
||||
"sharing_enter_password": "Please enter the password to view this page.",
|
||||
"sharing_sidebar_description": "Display a link to Sharing in the sidebar",
|
||||
"shift_to_permanent_delete": "press ⇧ to permanently delete asset",
|
||||
"show_album_options": "Show album options",
|
||||
|
@ -1107,6 +1130,7 @@
|
|||
"validate": "Validate",
|
||||
"variables": "Variables",
|
||||
"version": "Version",
|
||||
"version_announcement_closing": "Your friend, Alex",
|
||||
"version_announcement_message": "Hi friend, there is a new version of the application please take your time to visit the <link>release notes</link> and ensure your <code>docker-compose.yml</code>, and <code>.env</code> setup is up-to-date to prevent any misconfigurations, especially if you use WatchTower or any mechanism that handles updating your application automatically.",
|
||||
"video": "Video",
|
||||
"video_hover_setting": "Play video thumbnail on hover",
|
||||
|
|
|
@ -3,7 +3,8 @@ import { fromLocalDateTime } from '$lib/utils/timeline-util';
|
|||
import { TimeBucketSize, getTimeBucket, getTimeBuckets, type AssetResponseDto } from '@immich/sdk';
|
||||
import { throttle } from 'lodash-es';
|
||||
import { DateTime } from 'luxon';
|
||||
import { writable, type Unsubscriber } from 'svelte/store';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { get, writable, type Unsubscriber } from 'svelte/store';
|
||||
import { handleError } from '../utils/handle-error';
|
||||
import { websocketEvents } from './websocket';
|
||||
|
||||
|
@ -286,7 +287,8 @@ export class AssetStore {
|
|||
|
||||
this.emit(true);
|
||||
} catch (error) {
|
||||
handleError(error, 'Failed to load assets');
|
||||
const $t = get(t);
|
||||
handleError(error, $t('errors.failed_to_load_assets'));
|
||||
} finally {
|
||||
bucket.cancelToken = null;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ import {
|
|||
type AssetMediaResponseDto,
|
||||
} from '@immich/sdk';
|
||||
import { tick } from 'svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { get } from 'svelte/store';
|
||||
import { getServerErrorMessage, handleError } from './handle-error';
|
||||
|
||||
let _extensions: string[];
|
||||
|
@ -83,6 +85,7 @@ function getDeviceAssetId(asset: File) {
|
|||
async function fileUploader(assetFile: File, albumId?: string, replaceAssetId?: string): Promise<string | undefined> {
|
||||
const fileCreatedAt = new Date(assetFile.lastModified).toISOString();
|
||||
const deviceAssetId = getDeviceAssetId(assetFile);
|
||||
const $t = get(t);
|
||||
|
||||
uploadAssetsStore.markStarted(deviceAssetId);
|
||||
|
||||
|
@ -103,7 +106,7 @@ async function fileUploader(assetFile: File, albumId?: string, replaceAssetId?:
|
|||
let responseData: AssetMediaResponseDto | undefined;
|
||||
const key = getKey();
|
||||
if (crypto?.subtle?.digest && !key) {
|
||||
uploadAssetsStore.updateAsset(deviceAssetId, { message: 'Hashing...' });
|
||||
uploadAssetsStore.updateAsset(deviceAssetId, { message: $t('asset_hashing') });
|
||||
await tick();
|
||||
try {
|
||||
const bytes = await assetFile.arrayBuffer();
|
||||
|
@ -124,7 +127,7 @@ async function fileUploader(assetFile: File, albumId?: string, replaceAssetId?:
|
|||
}
|
||||
|
||||
if (!responseData) {
|
||||
uploadAssetsStore.updateAsset(deviceAssetId, { message: 'Uploading...' });
|
||||
uploadAssetsStore.updateAsset(deviceAssetId, { message: $t('asset_uploading') });
|
||||
if (replaceAssetId) {
|
||||
const response = await uploadRequest<AssetMediaResponseDto>({
|
||||
url: getBaseUrl() + getAssetOriginalPath(replaceAssetId) + (key ? `?key=${key}` : ''),
|
||||
|
@ -141,7 +144,7 @@ async function fileUploader(assetFile: File, albumId?: string, replaceAssetId?:
|
|||
});
|
||||
|
||||
if (![200, 201].includes(response.status)) {
|
||||
throw new Error('Failed to upload file');
|
||||
throw new Error($t('errors.unable_to_upload_file'));
|
||||
}
|
||||
|
||||
responseData = response.data;
|
||||
|
@ -155,9 +158,9 @@ async function fileUploader(assetFile: File, albumId?: string, replaceAssetId?:
|
|||
}
|
||||
|
||||
if (albumId) {
|
||||
uploadAssetsStore.updateAsset(deviceAssetId, { message: 'Adding to album...' });
|
||||
uploadAssetsStore.updateAsset(deviceAssetId, { message: $t('asset_adding_to_album') });
|
||||
await addAssetsToAlbum(albumId, [responseData.id]);
|
||||
uploadAssetsStore.updateAsset(deviceAssetId, { message: 'Added to album' });
|
||||
uploadAssetsStore.updateAsset(deviceAssetId, { message: $t('asset_added_to_album') });
|
||||
}
|
||||
|
||||
uploadAssetsStore.updateAsset(deviceAssetId, {
|
||||
|
@ -170,7 +173,7 @@ async function fileUploader(assetFile: File, albumId?: string, replaceAssetId?:
|
|||
|
||||
return responseData.id;
|
||||
} catch (error) {
|
||||
handleError(error, 'Unable to upload file');
|
||||
handleError(error, $t('errors.unable_to_upload_file'));
|
||||
const reason = getServerErrorMessage(error) || error;
|
||||
uploadAssetsStore.updateAsset(deviceAssetId, { state: UploadState.ERROR, error: reason });
|
||||
return;
|
||||
|
|
|
@ -331,9 +331,9 @@
|
|||
return person;
|
||||
});
|
||||
|
||||
notificationController.show({ message: 'Date of birth saved successfully', type: NotificationType.Info });
|
||||
notificationController.show({ message: $t('date_of_birth_saved'), type: NotificationType.Info });
|
||||
} catch (error) {
|
||||
handleError(error, 'Unable to save date of birth');
|
||||
handleError(error, $t('errors.unable_to_save_date_of_birth'));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -26,9 +26,11 @@
|
|||
passwordRequired = false;
|
||||
isOwned = $user ? $user.id === sharedLink.userId : false;
|
||||
title = (sharedLink.album ? sharedLink.album.albumName : $t('public_share')) + ' - Immich';
|
||||
description = sharedLink.description || `${sharedLink.assets.length} shared photos & videos.`;
|
||||
description =
|
||||
sharedLink.description ||
|
||||
$t('shared_photos_and_videos_count', { values: { assetCount: sharedLink.assets.length } });
|
||||
} catch (error) {
|
||||
handleError(error, 'Failed to get shared link');
|
||||
handleError(error, $t('errors.unable_to_get_shared_link'));
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@ -57,7 +59,7 @@
|
|||
<div class="flex flex-col items-center justify-center mt-20">
|
||||
<div class="text-2xl font-bold text-immich-primary dark:text-immich-dark-primary">{$t('password_required')}</div>
|
||||
<div class="mt-4 text-lg text-immich-primary dark:text-immich-dark-primary">
|
||||
Please enter the password to view this page.
|
||||
{$t('sharing_enter_password')}
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<form novalidate autocomplete="off" on:submit|preventDefault={handlePasswordSubmit}>
|
||||
|
|
|
@ -78,7 +78,7 @@
|
|||
try {
|
||||
await loadConfig();
|
||||
} catch (error) {
|
||||
handleError(error, 'Unable to connect to server');
|
||||
handleError(error, $t('errors.unable_to_connect_to_server'));
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -59,16 +59,8 @@
|
|||
return websocketEvents.on('on_user_delete', onDeleteSuccess);
|
||||
});
|
||||
|
||||
const deleteDateFormat: Intl.DateTimeFormatOptions = {
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
year: 'numeric',
|
||||
};
|
||||
|
||||
const getDeleteDate = (deletedAt: string): string => {
|
||||
return DateTime.fromISO(deletedAt)
|
||||
.plus({ days: $serverConfig.userDeleteDelay })
|
||||
.toLocaleString(deleteDateFormat, { locale: $locale });
|
||||
const getDeleteDate = (deletedAt: string): Date => {
|
||||
return DateTime.fromISO(deletedAt).plus({ days: $serverConfig.userDeleteDelay }).toJSDate();
|
||||
};
|
||||
|
||||
const onUserCreated = async () => {
|
||||
|
@ -245,7 +237,9 @@
|
|||
{#if immichUser.deletedAt && immichUser.status === UserStatus.Deleted}
|
||||
<CircleIconButton
|
||||
icon={mdiDeleteRestore}
|
||||
title="Restore user - scheduled removal on {getDeleteDate(immichUser.deletedAt)}"
|
||||
title={$t('admin.user_restore_scheduled_removal', {
|
||||
values: { date: getDeleteDate(immichUser.deletedAt) },
|
||||
})}
|
||||
color="primary"
|
||||
size="16"
|
||||
on:click={() => restoreUserHandler(immichUser)}
|
||||
|
|
Loading…
Reference in a new issue