chore(web): more translations for user settings and admin pages (#10161)
* chore(web): more translations for user settings and admin pages * JobSettings translations * feedback * missed one * feedback
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
import { getAllJobsStatus, type AllJobStatusResponseDto } from '@immich/sdk';
|
||||
import { mdiCog } from '@mdi/js';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
export let data: PageData;
|
||||
@@ -34,7 +35,7 @@
|
||||
<LinkButton>
|
||||
<div class="flex place-items-center gap-2 text-sm">
|
||||
<Icon path={mdiCog} size="18" />
|
||||
Manage Concurrency
|
||||
{$t('admin.manage_concurrency')}
|
||||
</div>
|
||||
</LinkButton>
|
||||
</a>
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import { authenticate } from '$lib/utils/auth';
|
||||
import { getAllJobsStatus } from '@immich/sdk';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { get } from 'svelte/store';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load = (async () => {
|
||||
await authenticate({ admin: true });
|
||||
|
||||
const jobs = await getAllJobsStatus();
|
||||
const $t = get(t);
|
||||
|
||||
return {
|
||||
jobs,
|
||||
meta: {
|
||||
title: 'Job Status',
|
||||
title: $t('admin.job_status'),
|
||||
},
|
||||
};
|
||||
}) satisfies PageLoad;
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
try {
|
||||
const createdLibrary = await createLibrary({ createLibraryDto: { ownerId } });
|
||||
notificationController.show({
|
||||
message: `Created library: ${createdLibrary.name}`,
|
||||
message: $t('admin.library_created', { values: { library: createdLibrary.name } }),
|
||||
type: NotificationType.Info,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -159,7 +159,7 @@
|
||||
try {
|
||||
await deleteLibrary({ id: deletedLibrary.id });
|
||||
notificationController.show({
|
||||
message: `Library deleted`,
|
||||
message: $t('admin.library_deleted'),
|
||||
type: NotificationType.Info,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -177,7 +177,7 @@
|
||||
await scanLibrary({ id: library.id, scanLibraryDto: {} });
|
||||
}
|
||||
notificationController.show({
|
||||
message: `Refreshing all libraries`,
|
||||
message: $t('admin.refreshing_all_libraries'),
|
||||
type: NotificationType.Info,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -189,7 +189,7 @@
|
||||
try {
|
||||
await scanLibrary({ id: libraryId, scanLibraryDto: {} });
|
||||
notificationController.show({
|
||||
message: `Scanning library for new files`,
|
||||
message: $t('admin.scanning_library_for_new_files'),
|
||||
type: NotificationType.Info,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -201,7 +201,7 @@
|
||||
try {
|
||||
await scanLibrary({ id: libraryId, scanLibraryDto: { refreshModifiedFiles: true } });
|
||||
notificationController.show({
|
||||
message: `Scanning library for changed files`,
|
||||
message: $t('admin.scanning_library_for_changed_files'),
|
||||
type: NotificationType.Info,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -213,7 +213,7 @@
|
||||
try {
|
||||
await scanLibrary({ id: libraryId, scanLibraryDto: { refreshAllFiles: true } });
|
||||
notificationController.show({
|
||||
message: `Forcing refresh of all library files`,
|
||||
message: $t('admin.forcing_refresh_library_files'),
|
||||
type: NotificationType.Info,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -225,11 +225,11 @@
|
||||
try {
|
||||
await removeOfflineFiles({ id: libraryId });
|
||||
notificationController.show({
|
||||
message: `Removing Offline Files`,
|
||||
message: $t('admin.removing_offline_files'),
|
||||
type: NotificationType.Info,
|
||||
});
|
||||
} catch (error) {
|
||||
handleError(error, 'Unable to remove offline files');
|
||||
handleError(error, $t('errors.unable_to_remove_offline_files'));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -289,7 +289,7 @@
|
||||
|
||||
const isConfirmedLibrary = await dialogController.show({
|
||||
id: 'delete-library',
|
||||
prompt: `Are you sure you want to delete ${selectedLibrary.name} library?`,
|
||||
prompt: $t('admin.confirm_delete_library', { values: { library: selectedLibrary.name } }),
|
||||
});
|
||||
|
||||
if (!isConfirmedLibrary) {
|
||||
@@ -302,7 +302,7 @@
|
||||
|
||||
const isConfirmedLibraryAssetCount = await dialogController.show({
|
||||
id: 'delete-library-assets',
|
||||
prompt: `Are you sure you want to delete this library? This will delete all ${deleteAssetCount} contained assets from Immich and cannot be undone. Files will remain on disk.`,
|
||||
prompt: $t('admin.confirm_delete_library_assets', { values: { count: deleteAssetCount } }),
|
||||
});
|
||||
|
||||
if (!isConfirmedLibraryAssetCount) {
|
||||
@@ -366,7 +366,11 @@
|
||||
}`}
|
||||
>
|
||||
<td class=" px-10 text-sm">
|
||||
<Icon path={mdiDatabase} size="40" title="External library (created on {library.createdAt})" />
|
||||
<Icon
|
||||
path={mdiDatabase}
|
||||
size="40"
|
||||
title={$t('admin.external_library_created_at', { values: { date: library.createdAt } })}
|
||||
/>
|
||||
</td>
|
||||
|
||||
<td class=" text-ellipsis px-4 text-sm">{library.name}</td>
|
||||
@@ -399,7 +403,7 @@
|
||||
{#if showContextMenu}
|
||||
<Portal target="body">
|
||||
<ContextMenu {...contextMenuPosition} onClose={() => onMenuExit()}>
|
||||
<MenuOption on:click={() => onRenameClicked()} text={`Rename`} />
|
||||
<MenuOption on:click={() => onRenameClicked()} text={$t('rename')} />
|
||||
|
||||
{#if selectedLibrary}
|
||||
<MenuOption on:click={() => onEditImportPathClicked()} text={$t('edit_import_paths')} />
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import { authenticate, requestServerInfo } from '$lib/utils/auth';
|
||||
import { searchUsersAdmin } from '@immich/sdk';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { get } from 'svelte/store';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load = (async () => {
|
||||
await authenticate({ admin: true });
|
||||
await requestServerInfo();
|
||||
const allUsers = await searchUsersAdmin({ withDeleted: false });
|
||||
const $t = get(t);
|
||||
|
||||
return {
|
||||
allUsers,
|
||||
meta: {
|
||||
title: 'External Library Management',
|
||||
title: $t('admin.external_library_management'),
|
||||
},
|
||||
};
|
||||
}) satisfies PageLoad;
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
|
||||
notificationController.show({
|
||||
type: NotificationType.Info,
|
||||
message: `Repaired ${matches.length} items`,
|
||||
message: $t('admin.repaired_items', { values: { count: matches.length } }),
|
||||
});
|
||||
|
||||
matches = [];
|
||||
@@ -118,10 +118,13 @@
|
||||
try {
|
||||
const matched = await loadAndMatch([filename]);
|
||||
if (matched) {
|
||||
notificationController.show({ message: `Matched 1 item`, type: NotificationType.Info });
|
||||
notificationController.show({
|
||||
message: $t('admin.repair_matched_items', { values: { count: 1 } }),
|
||||
type: NotificationType.Info,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
handleError(error, $t('errors.unable_to_check_item'));
|
||||
handleError(error, $t('errors.repair_unable_to_check_items', { values: { count: 'one' } }));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -137,12 +140,15 @@
|
||||
count += await loadAndMatch(filenames.slice(index, index + chunkSize));
|
||||
}
|
||||
} catch (error) {
|
||||
handleError(error, $t('errors.unable_to_check_items'));
|
||||
handleError(error, $t('errors.repair_unable_to_check_items', { values: { count: 'other' } }));
|
||||
} finally {
|
||||
checking = false;
|
||||
}
|
||||
|
||||
notificationController.show({ message: `Matched ${count} items`, type: NotificationType.Info });
|
||||
notificationController.show({
|
||||
message: $t('admin.repair_matched_items', { values: { count } }),
|
||||
type: NotificationType.Info,
|
||||
});
|
||||
};
|
||||
|
||||
const loadAndMatch = async (filenames: string[]) => {
|
||||
@@ -178,25 +184,25 @@
|
||||
<LinkButton on:click={() => handleRepair()} disabled={matches.length === 0 || repairing}>
|
||||
<div class="flex place-items-center gap-2 text-sm">
|
||||
<Icon path={mdiWrench} size="18" />
|
||||
Repair All
|
||||
{$t('admin.repair_all')}
|
||||
</div>
|
||||
</LinkButton>
|
||||
<LinkButton on:click={() => handleCheckAll()} disabled={extras.length === 0 || checking}>
|
||||
<div class="flex place-items-center gap-2 text-sm">
|
||||
<Icon path={mdiCheckAll} size="18" />
|
||||
Check All
|
||||
{$t('admin.check_all')}
|
||||
</div>
|
||||
</LinkButton>
|
||||
<LinkButton on:click={() => handleDownload()} disabled={extras.length + orphans.length === 0}>
|
||||
<div class="flex place-items-center gap-2 text-sm">
|
||||
<Icon path={mdiDownload} size="18" />
|
||||
Export
|
||||
{$t('export')}
|
||||
</div>
|
||||
</LinkButton>
|
||||
<LinkButton on:click={() => handleRefresh()}>
|
||||
<div class="flex place-items-center gap-2 text-sm">
|
||||
<Icon path={mdiRefresh} size="18" />
|
||||
Refresh
|
||||
{$t('refresh')}
|
||||
</div>
|
||||
</LinkButton>
|
||||
</div>
|
||||
@@ -215,8 +221,8 @@
|
||||
<tr class="flex w-full place-items-center p-2 md:p-5">
|
||||
<th class="w-full text-sm place-items-center font-medium flex justify-between" colspan="2">
|
||||
<div class="px-3">
|
||||
<p>MATCHES {matches.length > 0 ? `(${matches.length})` : ''}</p>
|
||||
<p class="text-gray-600 dark:text-gray-300 mt-1">These files are matched by their checksums</p>
|
||||
<p>{$t('matches').toUpperCase()} {matches.length > 0 ? `(${matches.length})` : ''}</p>
|
||||
<p class="text-gray-600 dark:text-gray-300 mt-1">{$t('admin.these_files_matched_by_checksum')}</p>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
@@ -249,9 +255,9 @@
|
||||
<tr class="flex w-full place-items-center p-1 md:p-5">
|
||||
<th class="w-full text-sm font-medium justify-between place-items-center flex" colspan="2">
|
||||
<div class="px-3">
|
||||
<p>OFFLINE PATHS {orphans.length > 0 ? `(${orphans.length})` : ''}</p>
|
||||
<p>{$t('admin.offline_paths').toUpperCase()} {orphans.length > 0 ? `(${orphans.length})` : ''}</p>
|
||||
<p class="text-gray-600 dark:text-gray-300 mt-1">
|
||||
These results may be due to manual deletion of files that are not part of an external library.
|
||||
{$t('admin.offline_paths_description')}
|
||||
</p>
|
||||
</div>
|
||||
</th>
|
||||
@@ -287,10 +293,9 @@
|
||||
<tr class="flex w-full place-items-center p-2 md:p-5">
|
||||
<th class="w-full text-sm font-medium place-items-center flex justify-between" colspan="2">
|
||||
<div class="px-3">
|
||||
<p>UNTRACKED FILES {extras.length > 0 ? `(${extras.length})` : ''}</p>
|
||||
<p>{$t('admin.untracked_files').toUpperCase()} {extras.length > 0 ? `(${extras.length})` : ''}</p>
|
||||
<p class="text-gray-600 dark:text-gray-300 mt-1">
|
||||
These files are not tracked by the application. They can be the results of failed moves,
|
||||
interrupted uploads, or left behind due to a bug
|
||||
{$t('admin.untracked_files_description')}
|
||||
</p>
|
||||
</div>
|
||||
</th>
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import { authenticate } from '$lib/utils/auth';
|
||||
import { getAuditFiles } from '@immich/sdk';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { get } from 'svelte/store';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load = (async () => {
|
||||
await authenticate({ admin: true });
|
||||
const { orphans, extras } = await getAuditFiles();
|
||||
const $t = get(t);
|
||||
|
||||
return {
|
||||
orphans,
|
||||
extras,
|
||||
meta: {
|
||||
title: 'Repair',
|
||||
title: $t('repair'),
|
||||
},
|
||||
};
|
||||
}) satisfies PageLoad;
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import { authenticate } from '$lib/utils/auth';
|
||||
import { getServerStatistics } from '@immich/sdk';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { get } from 'svelte/store';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load = (async () => {
|
||||
await authenticate({ admin: true });
|
||||
const stats = await getServerStatistics();
|
||||
const $t = get(t);
|
||||
|
||||
return {
|
||||
stats,
|
||||
meta: {
|
||||
title: 'Server Stats',
|
||||
title: $t('server_stats'),
|
||||
},
|
||||
};
|
||||
}) satisfies PageLoad;
|
||||
|
||||
@@ -181,7 +181,7 @@
|
||||
<div class="flex flex-row items-center gap-2 bg-gray-100 p-3 dark:bg-gray-800">
|
||||
<Icon path={mdiAlert} class="text-yellow-400" size={18} />
|
||||
<h2 class="text-md text-immich-primary dark:text-immich-dark-primary">
|
||||
Config is currently set by a config file
|
||||
{$t('admin.config_set_by_file')}
|
||||
</h2>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -191,19 +191,19 @@
|
||||
<LinkButton on:click={() => copyToClipboard(JSON.stringify(config, null, 2))}>
|
||||
<div class="flex place-items-center gap-2 text-sm">
|
||||
<Icon path={mdiContentCopy} size="18" />
|
||||
Copy to Clipboard
|
||||
{$t('copy_to_clipboard')}
|
||||
</div>
|
||||
</LinkButton>
|
||||
<LinkButton on:click={() => downloadConfig()}>
|
||||
<div class="flex place-items-center gap-2 text-sm">
|
||||
<Icon path={mdiDownload} size="18" />
|
||||
Export as JSON
|
||||
{$t('export_as_json')}
|
||||
</div>
|
||||
</LinkButton>
|
||||
<LinkButton on:click={() => inputElement?.click()}>
|
||||
<div class="flex place-items-center gap-2 text-sm">
|
||||
<Icon path={mdiUpload} size="18" />
|
||||
Import from JSON
|
||||
{$t('import_from_json')}
|
||||
</div>
|
||||
</LinkButton>
|
||||
</div>
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import { authenticate } from '$lib/utils/auth';
|
||||
import { getConfig } from '@immich/sdk';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { get } from 'svelte/store';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load = (async () => {
|
||||
await authenticate({ admin: true });
|
||||
const configs = await getConfig();
|
||||
const $t = get(t);
|
||||
|
||||
return {
|
||||
configs,
|
||||
meta: {
|
||||
title: 'System Settings',
|
||||
title: $t('admin.system_settings'),
|
||||
},
|
||||
};
|
||||
}) satisfies PageLoad;
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
allUsers = allUsers.filter((user) => user.id !== userId);
|
||||
notificationController.show({
|
||||
type: NotificationType.Info,
|
||||
message: `User ${user.email} has been successfully removed.`,
|
||||
message: $t('admin.user_successfully_removed', { values: { email: user.email } }),
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -164,7 +164,7 @@
|
||||
>
|
||||
<svelte:fragment slot="prompt">
|
||||
<div class="flex flex-col gap-4">
|
||||
<p>The user's password has been reset:</p>
|
||||
<p>{$t('admin.user_password_has_been_reset')}</p>
|
||||
|
||||
<div class="flex justify-center gap-2">
|
||||
<code
|
||||
@@ -179,10 +179,7 @@
|
||||
</LinkButton>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Please provide the temporary password to the user and inform them they will need to change the password
|
||||
at their next login.
|
||||
</p>
|
||||
<p>{$t('admin.user_password_reset_description')}</p>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</ConfirmDialog>
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import { authenticate, requestServerInfo } from '$lib/utils/auth';
|
||||
import { searchUsersAdmin } from '@immich/sdk';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { get } from 'svelte/store';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load = (async () => {
|
||||
await authenticate({ admin: true });
|
||||
await requestServerInfo();
|
||||
const allUsers = await searchUsersAdmin({ withDeleted: true });
|
||||
const $t = get(t);
|
||||
|
||||
return {
|
||||
allUsers,
|
||||
meta: {
|
||||
title: 'User Management',
|
||||
title: $t('admin.user_management'),
|
||||
},
|
||||
};
|
||||
}) satisfies PageLoad;
|
||||
|
||||
Reference in New Issue
Block a user