mirror of
https://github.com/immich-app/immich.git
synced 2025-12-15 06:24:23 +03:00
chore: i18n pass, update progress bar
This commit is contained in:
31
i18n/en.json
31
i18n/en.json
@@ -177,10 +177,17 @@
|
||||
"machine_learning_smart_search_enabled": "Enable smart search",
|
||||
"machine_learning_smart_search_enabled_description": "If disabled, images will not be encoded for smart search.",
|
||||
"machine_learning_url_description": "The URL of the machine learning server. If more than one URL is provided, each server will be attempted one-at-a-time until one responds successfully, in order from first to last. Servers that don't respond will be temporarily ignored until they come back online.",
|
||||
"maintenance_restore_backup": "Restore Backup",
|
||||
"maintenance_delete_backup": "Delete Backup",
|
||||
"maintenance_delete_backup_description": "This file will be irrevocably deleted.",
|
||||
"maintenance_restore_backup_description": "Immich will be wiped and restored from the chosen backup. A backup will be created before continuing.",
|
||||
"maintenance_settings": "Maintenance",
|
||||
"maintenance_settings_description": "Put Immich into maintenance mode.",
|
||||
"maintenance_start": "Start maintenance mode",
|
||||
"maintenance_upload_backup": "Upload database backup file",
|
||||
"maintenance_start_error": "Failed to start maintenance mode.",
|
||||
"maintenance_delete_error": "Failed to delete backup.",
|
||||
"maintenance_upload_backup_error": "Could not upload backup, is it an .sql/.sql.gz file?",
|
||||
"manage_concurrency": "Manage Concurrency",
|
||||
"manage_log_settings": "Manage log settings",
|
||||
"map_dark_style": "Dark style",
|
||||
@@ -1324,12 +1331,26 @@
|
||||
"loop_videos_description": "Enable to automatically loop a video in the detail viewer.",
|
||||
"main_branch_warning": "You're using a development version; we strongly recommend using a release version!",
|
||||
"main_menu": "Main menu",
|
||||
"maintenance_action_restore": "Restoring Database",
|
||||
"maintenance_description": "Immich has been put into <link>maintenance mode</link>.",
|
||||
"maintenance_end": "End maintenance mode",
|
||||
"maintenance_end_error": "Failed to end maintenance mode.",
|
||||
"maintenance_logged_in_as": "Currently logged in as {user}",
|
||||
"maintenance_task_backup": "Creating a backup of the existing database...",
|
||||
"maintenance_task_restore": "Restoring the chosen backup...",
|
||||
"maintenance_restore_from_backup": "Restore From Backup",
|
||||
"maintenance_restore_library": "Restore Your Library",
|
||||
"maintenance_restore_library_confirm": "If this looks correct, continue to restoring a backup!",
|
||||
"maintenance_restore_library_description": "Restoring Database",
|
||||
"maintenance_restore_library_folder_has_files": "{folder} has {count} folder(s)",
|
||||
"maintenance_restore_library_folder_no_files": "{folder} is missing files!",
|
||||
"maintenance_restore_library_folder_pass": "readable and writable",
|
||||
"maintenance_restore_library_folder_read_fail": "not readable",
|
||||
"maintenance_restore_library_folder_write_fail": "not writable",
|
||||
"maintenance_restore_library_hint_missing_files": "You may be missing important files",
|
||||
"maintenance_restore_library_hint_regenerate_later": "You can regenerate these later in settings",
|
||||
"maintenance_restore_library_hint_storage_template_missing_files": "Using storage template? You may be missing files",
|
||||
"maintenance_restore_library_loading": "Loading integrity checks and heuristics…",
|
||||
"maintenance_task_backup": "Creating a backup of the existing database…",
|
||||
"maintenance_task_restore": "Restoring the chosen backup…",
|
||||
"maintenance_title": "Temporarily Unavailable",
|
||||
"make": "Make",
|
||||
"manage_geolocation": "Manage location",
|
||||
@@ -1916,6 +1937,12 @@
|
||||
"shared_link_edit_expire_after_option_hours": "{count} hours",
|
||||
"shared_link_edit_expire_after_option_minute": "1 minute",
|
||||
"shared_link_edit_expire_after_option_minutes": "{count} minutes",
|
||||
"created_day_ago": "Created 1 day ago",
|
||||
"created_days_ago": "Created {count} days ago",
|
||||
"created_hour_ago": "Created 1 hour ago",
|
||||
"created_hours_ago": "Created {count} hours ago",
|
||||
"created_minute_ago": "Created 1 minute ago",
|
||||
"created_minutes_ago": "Created {count} minutes ago",
|
||||
"shared_link_edit_expire_after_option_months": "{count} months",
|
||||
"shared_link_edit_expire_after_option_year": "{count} year",
|
||||
"shared_link_edit_password_hint": "Enter the share password",
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
IconButton,
|
||||
menuManager,
|
||||
modalManager,
|
||||
ProgressBar,
|
||||
Stack,
|
||||
Text,
|
||||
type ContextMenuBaseProps,
|
||||
@@ -36,15 +37,15 @@
|
||||
function mapBackups(filenames: string[]) {
|
||||
return filenames.map((filename) => {
|
||||
const date = /(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})/.exec(filename);
|
||||
const hoursAgo = date
|
||||
const minutesAgo = date
|
||||
? Math.floor(
|
||||
(+Date.now() - +new Date(`${date[1]}-${date[2]}-${date[3]}T${date[4]}:${date[5]}:${date[6]}`)) / 36e5,
|
||||
(+Date.now() - +new Date(`${date[1]}-${date[2]}-${date[3]}T${date[4]}:${date[5]}:${date[6]}`)) / 60_000,
|
||||
)
|
||||
: null;
|
||||
|
||||
return {
|
||||
filename,
|
||||
hoursAgo,
|
||||
minutesAgo,
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -61,9 +62,9 @@
|
||||
|
||||
async function restore(filename: string) {
|
||||
const confirm = await modalManager.showDialog({
|
||||
confirmText: 'Restore',
|
||||
title: 'Restore Backup',
|
||||
prompt: 'Immich will be wiped and restored from the chosen backup. A backup will be created before continuing.',
|
||||
confirmText: $t('restore'),
|
||||
title: $t('admin.maintenance_restore_backup'),
|
||||
prompt: $t('admin.maintenance_restore_backup_description'),
|
||||
});
|
||||
|
||||
if (confirm) {
|
||||
@@ -82,9 +83,9 @@
|
||||
|
||||
async function remove(filename: string) {
|
||||
const confirm = await modalManager.showDialog({
|
||||
confirmText: 'Delete',
|
||||
title: 'Delete Backup',
|
||||
prompt: 'This file will be irrevocably deleted.',
|
||||
confirmText: $t('delete'),
|
||||
title: $t('admin.maintenance_delete_backup'),
|
||||
prompt: $t('admin.maintenance_delete_backup_description'),
|
||||
});
|
||||
|
||||
if (confirm) {
|
||||
@@ -97,7 +98,7 @@
|
||||
|
||||
backups = backups.filter((backup) => backup.filename !== filename);
|
||||
} catch (error) {
|
||||
handleError(error, 'failed to delete backup i18n');
|
||||
handleError(error, $t('admin.maintenance_delete_error'));
|
||||
} finally {
|
||||
deleting.delete(filename);
|
||||
}
|
||||
@@ -114,14 +115,14 @@
|
||||
target: event.currentTarget as HTMLElement,
|
||||
items: [
|
||||
{
|
||||
title: 'Download',
|
||||
title: $t('download'),
|
||||
icon: mdiDownload,
|
||||
onSelect() {
|
||||
void download(filename);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Delete',
|
||||
title: $t('delete'),
|
||||
icon: mdiTrashCanOutline,
|
||||
color: 'danger',
|
||||
onSelect() {
|
||||
@@ -153,7 +154,7 @@
|
||||
const { backups: newList } = await listBackups();
|
||||
backups = mapBackups(newList);
|
||||
} catch (error) {
|
||||
handleError(error, 'Could not upload backup, is it an .sql/.sql.gz file?');
|
||||
handleError(error, $t('admin.maintenance_upload_backup_error'));
|
||||
} finally {
|
||||
uploadProgress = -1;
|
||||
}
|
||||
@@ -165,15 +166,13 @@
|
||||
<CardBody>
|
||||
{#if uploadProgress === -1}
|
||||
<HStack>
|
||||
<Text class="grow">Upload database backup file</Text>
|
||||
<Button size="small" onclick={upload}>Select file</Button>
|
||||
<Text class="grow">{$t('admin.maintenance_upload_backup')}</Text>
|
||||
<Button size="tiny" onclick={upload}>{$t('select_from_computer')}</Button>
|
||||
</HStack>
|
||||
{:else}
|
||||
<HStack>
|
||||
<Text class="grow">Uploading...</Text>
|
||||
<div class="grow h-2.5 bg-gray-300 rounded-full overflow-hidden">
|
||||
<div class="h-full bg-blue-600 transition-all duration-700" style="width: {uploadProgress * 100}%"></div>
|
||||
</div>
|
||||
<HStack gap={8}>
|
||||
<Text class="grow">{$t('asset_uploading')}</Text>
|
||||
<ProgressBar progress={uploadProgress} size="tiny" />
|
||||
</HStack>
|
||||
{/if}
|
||||
</CardBody>
|
||||
@@ -185,20 +184,48 @@
|
||||
<HStack>
|
||||
<Stack class="grow">
|
||||
<Text>{backup.filename}</Text>
|
||||
{#if typeof backup.hoursAgo === 'number'}
|
||||
{#if backup.hoursAgo <= 24}
|
||||
{#if typeof backup.minutesAgo === 'number'}
|
||||
<Text color="info" size="small">
|
||||
{#if backup.minutesAgo <= 1}
|
||||
{$t('created_minute_ago')}
|
||||
{:else if backup.minutesAgo < 60}
|
||||
{$t('created_minutes_ago', {
|
||||
values: {
|
||||
count: backup.minutesAgo,
|
||||
},
|
||||
})}
|
||||
{:else if backup.minutesAgo < 60 * 2}
|
||||
{$t('created_hour_ago')}
|
||||
{:else if backup.minutesAgo < 60 * 24}
|
||||
{$t('created_hours_ago', {
|
||||
values: {
|
||||
count: Math.floor(backup.minutesAgo / 60),
|
||||
},
|
||||
})}
|
||||
{:else if backup.minutesAgo < 60 * 24 * 2}
|
||||
{$t('created_day_ago')}
|
||||
{:else}
|
||||
{$t('created_days_ago', {
|
||||
values: {
|
||||
count: Math.floor(backup.minutesAgo / (60 * 24)),
|
||||
},
|
||||
})}
|
||||
{/if}
|
||||
</Text>
|
||||
<!-- {#if backup.hoursAgo <= 24}
|
||||
<Text color="info" size="small">Created {backup.hoursAgo} hours ago</Text>
|
||||
{:else if backup.hoursAgo <= 48}
|
||||
<Text color="info" size="small">Created 1 day ago</Text>
|
||||
{:else}
|
||||
<Text color="info" size="small">Created {Math.floor(backup.hoursAgo / 24)} days ago</Text>
|
||||
{/if}
|
||||
{/if} -->
|
||||
{/if}
|
||||
</Stack>
|
||||
|
||||
<Button size="small" disabled={deleting.has(backup.filename)} onclick={() => restore(backup.filename)}
|
||||
>Restore</Button
|
||||
>{$t('restore')}</Button
|
||||
>
|
||||
|
||||
<IconButton
|
||||
shape="round"
|
||||
variant="ghost"
|
||||
@@ -207,7 +234,7 @@
|
||||
class="shrink-0"
|
||||
disabled={deleting.has(backup.filename)}
|
||||
onclick={(event: Event) => handleOpen(event, { position: 'top-right' }, backup.filename)}
|
||||
aria-label="Open menu"
|
||||
aria-label={$t('open')}
|
||||
/>
|
||||
</HStack>
|
||||
</CardBody>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import { Button, Card, CardBody, Heading, HStack, Icon, Scrollable, Stack, Text } from '@immich/ui';
|
||||
import { mdiAlert, mdiArrowLeft, mdiArrowRight, mdiCheck, mdiClose, mdiRefresh } from '@mdi/js';
|
||||
import { onMount } from 'svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
interface Props {
|
||||
end?: () => void;
|
||||
@@ -19,22 +20,11 @@
|
||||
}
|
||||
|
||||
onMount(reload);
|
||||
|
||||
const i18nMap = {
|
||||
'encoded-video': 'Encoded Video',
|
||||
library: 'Library',
|
||||
upload: 'Upload',
|
||||
profile: 'Profile',
|
||||
thumbs: 'Thumbs',
|
||||
backups: 'Backups',
|
||||
};
|
||||
</script>
|
||||
|
||||
{#if stage === 0}
|
||||
<Heading size="large" color="primary" tag="h1">Restore Your Library</Heading>
|
||||
<Text
|
||||
>Before restoring a database backup, you must ensure your library has been restored or is otherwise already present.</Text
|
||||
>
|
||||
<Heading size="large" color="primary" tag="h1">{$t('maintenance_restore_library')}</Heading>
|
||||
<Text>{$t('maintenance_restore_library_description')}</Text>
|
||||
<Card>
|
||||
<CardBody>
|
||||
<Stack>
|
||||
@@ -46,12 +36,10 @@
|
||||
color={`rgb(var(--immich-ui-${writable ? 'success' : 'danger'}))`}
|
||||
/>
|
||||
<Text
|
||||
>{i18nMap[folder as keyof typeof i18nMap]} ({writable
|
||||
? 'readable and writable'
|
||||
: readable
|
||||
? 'not writable'
|
||||
: 'not readable'})</Text
|
||||
>
|
||||
>{folder} ({$t(
|
||||
`maintenance_restore_library_folder_${writable ? 'pass' : readable ? 'write_fail' : 'read_fail'}`,
|
||||
)})
|
||||
</Text>
|
||||
</HStack>
|
||||
{/each}
|
||||
{#each integrity.storage as { folder, files } (folder)}
|
||||
@@ -65,47 +53,57 @@
|
||||
<Stack gap={0} class="items-start">
|
||||
<Text>
|
||||
{#if files}
|
||||
{i18nMap[folder as keyof typeof i18nMap]} has {files} folder(s)
|
||||
{$t('maintenance_restore_library_folder_has_files', {
|
||||
values: {
|
||||
folder,
|
||||
count: files,
|
||||
},
|
||||
})}
|
||||
{:else}
|
||||
{i18nMap[folder as keyof typeof i18nMap]} is missing files!
|
||||
{$t('maintenance_restore_library_folder_no_files', {
|
||||
values: {
|
||||
folder,
|
||||
},
|
||||
})}
|
||||
{/if}
|
||||
</Text>
|
||||
{#if !files && (folder === 'profile' || folder === 'upload')}
|
||||
<Text variant="italic">You may be missing files</Text>
|
||||
<Text variant="italic">{$t('maintenance_restore_library_hint_missing_files')}</Text>
|
||||
{/if}
|
||||
{#if !files && (folder === 'encoded-video' || folder === 'thumbs')}
|
||||
<Text variant="italic">You can regenerate these later in settings</Text>
|
||||
<Text variant="italic">{$t('maintenance_restore_library_hint_regenerate_later')}</Text>
|
||||
{/if}
|
||||
{#if !files && folder === 'library'}
|
||||
<Text variant="italic">Using storage template? You may be missing files</Text>
|
||||
<Text variant="italic">{$t('maintenance_restore_library_hint_storage_template_missing_files')}</Text
|
||||
>
|
||||
{/if}
|
||||
</Stack>
|
||||
</HStack>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
<Button leadingIcon={mdiRefresh} variant="ghost" onclick={reload}>Refresh</Button>
|
||||
<Button leadingIcon={mdiRefresh} variant="ghost" onclick={reload}>{$t('refresh')}</Button>
|
||||
{:else}
|
||||
<HStack>
|
||||
<Icon icon={mdiRefresh} color="rgb(var(--immich-ui-primary))" />
|
||||
<Text>Loading integrity checks and heuristics...</Text>
|
||||
<Text>{$t('maintenance_restore_library_loading')}</Text>
|
||||
</HStack>
|
||||
{/if}
|
||||
</Stack>
|
||||
</CardBody>
|
||||
</Card>
|
||||
<Text>If this looks correct, continue to restoring a backup!</Text>
|
||||
<Text>{$t('maintenance_restore_library_confirm')}</Text>
|
||||
<HStack>
|
||||
<Button onclick={props.end} variant="ghost">Cancel</Button>
|
||||
<Button onclick={() => stage++} trailingIcon={mdiArrowRight}>Next</Button>
|
||||
<Button onclick={props.end} variant="ghost">{$t('cancel')}</Button>
|
||||
<Button onclick={() => stage++} trailingIcon={mdiArrowRight}>{$t('next')}</Button>
|
||||
</HStack>
|
||||
{:else}
|
||||
<Heading size="large" color="primary" tag="h1">Restore From Backup</Heading>
|
||||
<Heading size="large" color="primary" tag="h1">{$t('maintenance_restore_from_backup')}</Heading>
|
||||
<Scrollable class="max-h-80">
|
||||
<MaintenanceBackupsList />
|
||||
</Scrollable>
|
||||
<HStack>
|
||||
<Button onclick={props.end} variant="ghost">Cancel</Button>
|
||||
<Button onclick={() => stage--} variant="ghost" leadingIcon={mdiArrowLeft}>Back</Button>
|
||||
<Button onclick={props.end} variant="ghost">{$t('cancel')}</Button>
|
||||
<Button onclick={() => stage--} variant="ghost" leadingIcon={mdiArrowLeft}>{$t('back')}</Button>
|
||||
</HStack>
|
||||
{/if}
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<span class="px-2 font-semibold">{$t('getting_started')}</span>
|
||||
</Button>
|
||||
<Button size="medium" shape="round" variant="ghost" onclick={switchToMaintenance}>
|
||||
<span class="px-2 font-semibold">Restore from backup</span>
|
||||
<span class="px-2 font-semibold">{$t('maintenance_restore_library')}</span>
|
||||
</Button>
|
||||
</Stack>
|
||||
</div>
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
>
|
||||
<div class="flex flex-col place-items-center text-center gap-4">
|
||||
{#if $status?.action === MaintenanceAction.RestoreDatabase && $status.task}
|
||||
<Heading size="large" color="primary" tag="h1">Restoring Database</Heading>
|
||||
<Heading size="large" color="primary" tag="h1">{$t('maintenance_action_restore')}</Heading>
|
||||
{#if $status.error}
|
||||
<Scrollable class="max-h-80">
|
||||
<pre class="text-left"><code>{error}</code></pre>
|
||||
|
||||
Reference in New Issue
Block a user