mirror of
https://github.com/immich-app/immich.git
synced 2025-05-24 01:12:58 -04:00
fix(web): translations (#10021)
This commit is contained in:
parent
f2148ddf03
commit
c8f2d994c6
@ -61,12 +61,12 @@
|
|||||||
[JobName.ThumbnailGeneration]: {
|
[JobName.ThumbnailGeneration]: {
|
||||||
icon: mdiFileJpgBox,
|
icon: mdiFileJpgBox,
|
||||||
title: getJobName(JobName.ThumbnailGeneration),
|
title: getJobName(JobName.ThumbnailGeneration),
|
||||||
subtitle: $t('thumbnail_generation_job_description'),
|
subtitle: $t('admin.thumbnail_generation_job_description'),
|
||||||
},
|
},
|
||||||
[JobName.MetadataExtraction]: {
|
[JobName.MetadataExtraction]: {
|
||||||
icon: mdiTable,
|
icon: mdiTable,
|
||||||
title: getJobName(JobName.MetadataExtraction),
|
title: getJobName(JobName.MetadataExtraction),
|
||||||
subtitle: $t('metadata_extraction_job_description'),
|
subtitle: $t('admin.metadata_extraction_job_description'),
|
||||||
},
|
},
|
||||||
[JobName.Library]: {
|
[JobName.Library]: {
|
||||||
icon: mdiLibraryShelves,
|
icon: mdiLibraryShelves,
|
||||||
@ -78,7 +78,7 @@
|
|||||||
[JobName.Sidecar]: {
|
[JobName.Sidecar]: {
|
||||||
title: getJobName(JobName.Sidecar),
|
title: getJobName(JobName.Sidecar),
|
||||||
icon: mdiFileXmlBox,
|
icon: mdiFileXmlBox,
|
||||||
subtitle: $t('sidecar_job_description'),
|
subtitle: $t('admin.sidecar_job_description'),
|
||||||
allText: $t('sync').toUpperCase(),
|
allText: $t('sync').toUpperCase(),
|
||||||
missingText: $t('discover').toUpperCase(),
|
missingText: $t('discover').toUpperCase(),
|
||||||
disabled: !$featureFlags.sidecar,
|
disabled: !$featureFlags.sidecar,
|
||||||
@ -86,13 +86,13 @@
|
|||||||
[JobName.SmartSearch]: {
|
[JobName.SmartSearch]: {
|
||||||
icon: mdiImageSearch,
|
icon: mdiImageSearch,
|
||||||
title: getJobName(JobName.SmartSearch),
|
title: getJobName(JobName.SmartSearch),
|
||||||
subtitle: $t('smart_search_job_description'),
|
subtitle: $t('admin.smart_search_job_description'),
|
||||||
disabled: !$featureFlags.smartSearch,
|
disabled: !$featureFlags.smartSearch,
|
||||||
},
|
},
|
||||||
[JobName.DuplicateDetection]: {
|
[JobName.DuplicateDetection]: {
|
||||||
icon: mdiContentDuplicate,
|
icon: mdiContentDuplicate,
|
||||||
title: getJobName(JobName.DuplicateDetection),
|
title: getJobName(JobName.DuplicateDetection),
|
||||||
subtitle: $t('duplicate_detection_job_description'),
|
subtitle: $t('admin.duplicate_detection_job_description'),
|
||||||
disabled: !$featureFlags.duplicateDetection,
|
disabled: !$featureFlags.duplicateDetection,
|
||||||
},
|
},
|
||||||
[JobName.FaceDetection]: {
|
[JobName.FaceDetection]: {
|
||||||
@ -114,7 +114,7 @@
|
|||||||
[JobName.VideoConversion]: {
|
[JobName.VideoConversion]: {
|
||||||
icon: mdiVideo,
|
icon: mdiVideo,
|
||||||
title: getJobName(JobName.VideoConversion),
|
title: getJobName(JobName.VideoConversion),
|
||||||
subtitle: $t('video_conversion_job_description'),
|
subtitle: $t('admin.video_conversion_job_description'),
|
||||||
},
|
},
|
||||||
[JobName.StorageTemplateMigration]: {
|
[JobName.StorageTemplateMigration]: {
|
||||||
icon: mdiFolderMove,
|
icon: mdiFolderMove,
|
||||||
@ -125,7 +125,7 @@
|
|||||||
[JobName.Migration]: {
|
[JobName.Migration]: {
|
||||||
icon: mdiFolderMove,
|
icon: mdiFolderMove,
|
||||||
title: getJobName(JobName.Migration),
|
title: getJobName(JobName.Migration),
|
||||||
subtitle: $t('migration_job_description'),
|
subtitle: $t('admin.migration_job_description'),
|
||||||
allowForceCommand: false,
|
allowForceCommand: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -5,6 +5,6 @@
|
|||||||
|
|
||||||
Apply the current
|
Apply the current
|
||||||
<a href="{AppRoute.ADMIN_SETTINGS}?open=storageTemplate" class="text-immich-primary dark:text-immich-dark-primary"
|
<a href="{AppRoute.ADMIN_SETTINGS}?open=storageTemplate" class="text-immich-primary dark:text-immich-dark-primary"
|
||||||
>{$t('storage_template_settings')}</a
|
>{$t('admin.storage_template_settings')}</a
|
||||||
>
|
>
|
||||||
to previously uploaded assets
|
to previously uploaded assets
|
||||||
|
@ -270,7 +270,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: TranscodeHWAccel.Disabled,
|
value: TranscodeHWAccel.Disabled,
|
||||||
text: $t('disabled'),
|
text: $t('admin.disabled'),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
isEdited={config.ffmpeg.accel !== savedConfig.ffmpeg.accel}
|
isEdited={config.ffmpeg.accel !== savedConfig.ffmpeg.accel}
|
||||||
|
@ -100,7 +100,7 @@
|
|||||||
href="https://crontab.guru"
|
href="https://crontab.guru"
|
||||||
class="underline"
|
class="underline"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer">{$t('crontab_guru')}</a
|
rel="noreferrer">{$t('admin.crontab_guru')}</a
|
||||||
>
|
>
|
||||||
</p>
|
</p>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
|
@ -315,7 +315,7 @@
|
|||||||
await handleDeleteAlbum(albumToDelete);
|
await handleDeleteAlbum(albumToDelete);
|
||||||
} catch {
|
} catch {
|
||||||
notificationController.show({
|
notificationController.show({
|
||||||
message: $t('errors.errors.unable_to_delete_album'),
|
message: $t('errors.unable_to_delete_album'),
|
||||||
type: NotificationType.Error,
|
type: NotificationType.Error,
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
<OnboardingCard>
|
<OnboardingCard>
|
||||||
<p class="text-xl text-immich-primary dark:text-immich-dark-primary">
|
<p class="text-xl text-immich-primary dark:text-immich-dark-primary">
|
||||||
{$t('storage_template_settings').toUpperCase()}
|
{$t('admin.storage_template_settings').toUpperCase()}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if unstack}
|
{#if unstack}
|
||||||
<MenuOption text={$t('un-stack')} icon={mdiImageMinusOutline} on:click={handleUnstack} />
|
<MenuOption text={$t('unstack')} icon={mdiImageMinusOutline} on:click={handleUnstack} />
|
||||||
{:else}
|
{:else}
|
||||||
<MenuOption text={$t('stack')} icon={mdiImageMultipleOutline} on:click={handleStack} />
|
<MenuOption text={$t('stack')} icon={mdiImageMultipleOutline} on:click={handleStack} />
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -68,7 +68,7 @@
|
|||||||
<SettingInputField
|
<SettingInputField
|
||||||
inputType={SettingInputFieldType.NUMBER}
|
inputType={SettingInputFieldType.NUMBER}
|
||||||
label={$t('duration')}
|
label={$t('duration')}
|
||||||
desc={$t('slideshow_duration_description')}
|
desc={$t('admin.slideshow_duration_description')}
|
||||||
min={1}
|
min={1}
|
||||||
bind:value={$slideshowDelay}
|
bind:value={$slideshowDelay}
|
||||||
/>
|
/>
|
||||||
|
36
web/src/lib/i18n.spec.ts
Normal file
36
web/src/lib/i18n.spec.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import messages from '$lib/i18n/en.json';
|
||||||
|
import { exec as execCallback } from 'node:child_process';
|
||||||
|
import { promisify } from 'node:util';
|
||||||
|
import { init } from 'svelte-i18n';
|
||||||
|
|
||||||
|
type Messages = { [key: string]: string | Messages };
|
||||||
|
|
||||||
|
const exec = promisify(execCallback);
|
||||||
|
|
||||||
|
function setEmptyMessages(messages: Messages) {
|
||||||
|
const copy = { ...messages };
|
||||||
|
|
||||||
|
for (const key in copy) {
|
||||||
|
const message = copy[key];
|
||||||
|
if (typeof message === 'string') {
|
||||||
|
copy[key] = '';
|
||||||
|
} else if (typeof message === 'object') {
|
||||||
|
setEmptyMessages(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('i18n', () => {
|
||||||
|
beforeEach(() => init({ fallbackLocale: 'dev' }));
|
||||||
|
|
||||||
|
test('no missing messages', async () => {
|
||||||
|
const { stdout } = await exec('npx svelte-i18n extract -c svelte.config.js "src/**/*"');
|
||||||
|
const extractedMessages: Messages = JSON.parse(stdout);
|
||||||
|
const existingMessages = setEmptyMessages(messages);
|
||||||
|
|
||||||
|
// Only translations directly using the store seem to get extracted
|
||||||
|
expect({ ...extractedMessages, ...existingMessages }).toEqual(existingMessages);
|
||||||
|
});
|
||||||
|
});
|
@ -473,7 +473,7 @@
|
|||||||
"info": "Info",
|
"info": "Info",
|
||||||
"interval": {
|
"interval": {
|
||||||
"day_at_onepm": "Every day at 1pm",
|
"day_at_onepm": "Every day at 1pm",
|
||||||
"hours": "Every {hours, plural, one {hour} other {{hours, number} hours}",
|
"hours": "Every {hours, plural, one {hour} other {{hours, number} hours}}",
|
||||||
"night_at_midnight": "Every night at midnight",
|
"night_at_midnight": "Every night at midnight",
|
||||||
"night_at_twoam": "Every night at 2am"
|
"night_at_twoam": "Every night at 2am"
|
||||||
},
|
},
|
||||||
@ -552,6 +552,7 @@
|
|||||||
"no_shared_albums_message": "Create an album to share photos and videos with people in your network",
|
"no_shared_albums_message": "Create an album to share photos and videos with people in your network",
|
||||||
"not_in_any_album": "Not in any album",
|
"not_in_any_album": "Not in any album",
|
||||||
"notes": "Notes",
|
"notes": "Notes",
|
||||||
|
"oauth": "OAuth",
|
||||||
"offline": "Offline",
|
"offline": "Offline",
|
||||||
"ok": "Ok",
|
"ok": "Ok",
|
||||||
"oldest_first": "Oldest first",
|
"oldest_first": "Oldest first",
|
||||||
|
@ -364,7 +364,7 @@
|
|||||||
await deleteAlbum({ id: album.id });
|
await deleteAlbum({ id: album.id });
|
||||||
await goto(backUrl);
|
await goto(backUrl);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('unable_to_delete_album'));
|
handleError(error, $t('errors.unable_to_delete_album'));
|
||||||
} finally {
|
} finally {
|
||||||
viewMode = ViewMode.VIEW;
|
viewMode = ViewMode.VIEW;
|
||||||
}
|
}
|
||||||
|
@ -209,7 +209,7 @@
|
|||||||
type: NotificationType.Info,
|
type: NotificationType.Info,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('unable_to_save_name'));
|
handleError(error, $t('errors.unable_to_save_name'));
|
||||||
}
|
}
|
||||||
if (personToBeMergedIn.name !== personName && edittingPerson.id === personToBeMergedIn.id) {
|
if (personToBeMergedIn.name !== personName && edittingPerson.id === personToBeMergedIn.id) {
|
||||||
/*
|
/*
|
||||||
@ -235,7 +235,7 @@
|
|||||||
// trigger reactivity
|
// trigger reactivity
|
||||||
people = people;
|
people = people;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('unable_to_save_name'));
|
handleError(error, $t('errors.unable_to_save_name'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -279,7 +279,7 @@
|
|||||||
type: NotificationType.Info,
|
type: NotificationType.Info,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('unable_to_hide_person'));
|
handleError(error, $t('errors.unable_to_hide_person'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -350,7 +350,7 @@
|
|||||||
type: NotificationType.Info,
|
type: NotificationType.Info,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('unable_to_save_name'));
|
handleError(error, $t('errors.unable_to_save_name'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -377,7 +377,7 @@
|
|||||||
type: NotificationType.Info,
|
type: NotificationType.Info,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('unable_to_save_name'));
|
handleError(error, $t('errors.unable_to_save_name'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -189,7 +189,7 @@
|
|||||||
|
|
||||||
await goto(previousRoute, { replaceState: true });
|
await goto(previousRoute, { replaceState: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('unable_to_hide_person'));
|
handleError(error, $t('errors.unable_to_hide_person'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -236,7 +236,7 @@
|
|||||||
}
|
}
|
||||||
await goto(`${AppRoute.PEOPLE}/${personToBeMergedIn.id}`, { replaceState: true });
|
await goto(`${AppRoute.PEOPLE}/${personToBeMergedIn.id}`, { replaceState: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('unable_to_save_name'));
|
handleError(error, $t('errors.unable_to_save_name'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -262,7 +262,7 @@
|
|||||||
type: NotificationType.Info,
|
type: NotificationType.Info,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('unable_to_save_name'));
|
handleError(error, $t('errors.unable_to_save_name'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
type: NotificationType.Info,
|
type: NotificationType.Info,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('unable_to_resolve_duplicate'));
|
handleError(error, $t('errors.unable_to_resolve_duplicate'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -125,7 +125,7 @@
|
|||||||
type: NotificationType.Info,
|
type: NotificationType.Info,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('unable_to_create_library'));
|
handleError(error, $t('errors.unable_to_create_library'));
|
||||||
} finally {
|
} finally {
|
||||||
toCreateLibrary = false;
|
toCreateLibrary = false;
|
||||||
await readLibraryList();
|
await readLibraryList();
|
||||||
@ -143,7 +143,7 @@
|
|||||||
closeAll();
|
closeAll();
|
||||||
await readLibraryList();
|
await readLibraryList();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('unable_to_update_library'));
|
handleError(error, $t('errors.unable_to_update_library'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -163,7 +163,7 @@
|
|||||||
type: NotificationType.Info,
|
type: NotificationType.Info,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('unable_to_remove_library'));
|
handleError(error, $t('errors.unable_to_remove_library'));
|
||||||
} finally {
|
} finally {
|
||||||
confirmDeleteLibrary = null;
|
confirmDeleteLibrary = null;
|
||||||
deletedLibrary = null;
|
deletedLibrary = null;
|
||||||
@ -181,7 +181,7 @@
|
|||||||
type: NotificationType.Info,
|
type: NotificationType.Info,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('unable_to_scan_libraries'));
|
handleError(error, $t('errors.unable_to_scan_libraries'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -193,7 +193,7 @@
|
|||||||
type: NotificationType.Info,
|
type: NotificationType.Info,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('unable_to_scan_library'));
|
handleError(error, $t('errors.unable_to_scan_library'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -205,7 +205,7 @@
|
|||||||
type: NotificationType.Info,
|
type: NotificationType.Info,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('unable_to_scan_library'));
|
handleError(error, $t('errors.unable_to_scan_library'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -217,7 +217,7 @@
|
|||||||
type: NotificationType.Info,
|
type: NotificationType.Info,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('unable_to_scan_library'));
|
handleError(error, $t('errors.unable_to_scan_library'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@
|
|||||||
|
|
||||||
matches = [];
|
matches = [];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('unable_to_repair_items'));
|
handleError(error, $t('errors.unable_to_repair_items'));
|
||||||
} finally {
|
} finally {
|
||||||
repairing = false;
|
repairing = false;
|
||||||
}
|
}
|
||||||
@ -110,7 +110,7 @@
|
|||||||
|
|
||||||
notificationController.show({ message: $t('refreshed'), type: NotificationType.Info });
|
notificationController.show({ message: $t('refreshed'), type: NotificationType.Info });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('unable_to_load_items'));
|
handleError(error, $t('errors.unable_to_load_items'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -121,7 +121,7 @@
|
|||||||
notificationController.show({ message: `Matched 1 item`, type: NotificationType.Info });
|
notificationController.show({ message: `Matched 1 item`, type: NotificationType.Info });
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('unable_to_check_item'));
|
handleError(error, $t('errors.unable_to_check_item'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -137,7 +137,7 @@
|
|||||||
count += await loadAndMatch(filenames.slice(index, index + chunkSize));
|
count += await loadAndMatch(filenames.slice(index, index + chunkSize));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('unable_to_check_items'));
|
handleError(error, $t('errors.unable_to_check_items'));
|
||||||
} finally {
|
} finally {
|
||||||
checking = false;
|
checking = false;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user