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:
Michel Heusschen
2024-06-12 12:54:40 +02:00
committed by GitHub
parent 0e1311e3d3
commit 9e5c52b7b7
34 changed files with 300 additions and 160 deletions

View File

@@ -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>

View File

@@ -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;

View File

@@ -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')} />

View File

@@ -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;

View File

@@ -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>

View File

@@ -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;

View File

@@ -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;

View File

@@ -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>

View File

@@ -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;

View File

@@ -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>

View File

@@ -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;