diff --git a/web/src/lib/components/admin-page/settings/ffmpeg/ffmpeg-settings.svelte b/web/src/lib/components/admin-page/settings/ffmpeg/ffmpeg-settings.svelte
index 496d579cae..f4bead5b39 100644
--- a/web/src/lib/components/admin-page/settings/ffmpeg/ffmpeg-settings.svelte
+++ b/web/src/lib/components/admin-page/settings/ffmpeg/ffmpeg-settings.svelte
@@ -14,12 +14,14 @@
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition';
import type { SettingsEventType } from '../admin-settings';
- import SettingAccordion from '../setting-accordion.svelte';
- import SettingButtonsRow from '../setting-buttons-row.svelte';
- import SettingCheckboxes from '../setting-checkboxes.svelte';
- import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte';
- import SettingSelect from '../setting-select.svelte';
- import SettingSwitch from '../setting-switch.svelte';
+ import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte';
+ import SettingInputField, {
+ SettingInputFieldType,
+ } from '$lib/components/shared-components/settings/setting-input-field.svelte';
+ import SettingSelect from '$lib/components/shared-components/settings/setting-select.svelte';
+ import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
+ import SettingCheckboxes from '$lib/components/shared-components/settings/setting-checkboxes.svelte';
+ import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
export let savedConfig: SystemConfigDto;
export let defaultConfig: SystemConfigDto;
diff --git a/web/src/lib/components/admin-page/settings/job-settings/job-settings.svelte b/web/src/lib/components/admin-page/settings/job-settings/job-settings.svelte
index d3315f18a8..3db53f749b 100644
--- a/web/src/lib/components/admin-page/settings/job-settings/job-settings.svelte
+++ b/web/src/lib/components/admin-page/settings/job-settings/job-settings.svelte
@@ -5,8 +5,10 @@
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition';
import type { SettingsEventType } from '../admin-settings';
- import SettingButtonsRow from '../setting-buttons-row.svelte';
- import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte';
+ import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
+ import SettingInputField, {
+ SettingInputFieldType,
+ } from '$lib/components/shared-components/settings/setting-input-field.svelte';
export let savedConfig: SystemConfigDto;
export let defaultConfig: SystemConfigDto;
diff --git a/web/src/lib/components/admin-page/settings/library-settings/library-settings.svelte b/web/src/lib/components/admin-page/settings/library-settings/library-settings.svelte
index 078f522833..746db2c198 100644
--- a/web/src/lib/components/admin-page/settings/library-settings/library-settings.svelte
+++ b/web/src/lib/components/admin-page/settings/library-settings/library-settings.svelte
@@ -4,10 +4,12 @@
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition';
import type { SettingsEventType } from '../admin-settings';
- import SettingAccordion from '../setting-accordion.svelte';
- import SettingButtonsRow from '../setting-buttons-row.svelte';
- import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte';
- import SettingSwitch from '../setting-switch.svelte';
+ import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte';
+ import SettingInputField, {
+ SettingInputFieldType,
+ } from '$lib/components/shared-components/settings/setting-input-field.svelte';
+ import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
+ import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
export let savedConfig: SystemConfigDto;
export let defaultConfig: SystemConfigDto;
diff --git a/web/src/lib/components/admin-page/settings/logging-settings/logging-settings.svelte b/web/src/lib/components/admin-page/settings/logging-settings/logging-settings.svelte
index f29e6f9eaf..97ae006816 100644
--- a/web/src/lib/components/admin-page/settings/logging-settings/logging-settings.svelte
+++ b/web/src/lib/components/admin-page/settings/logging-settings/logging-settings.svelte
@@ -4,9 +4,9 @@
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition';
import type { SettingsEventType } from '../admin-settings';
- import SettingButtonsRow from '../setting-buttons-row.svelte';
- import SettingSelect from '../setting-select.svelte';
- import SettingSwitch from '../setting-switch.svelte';
+ import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
+ import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
+ import SettingSelect from '$lib/components/shared-components/settings/setting-select.svelte';
export let savedConfig: SystemConfigDto;
export let defaultConfig: SystemConfigDto;
diff --git a/web/src/lib/components/admin-page/settings/machine-learning-settings/machine-learning-settings.svelte b/web/src/lib/components/admin-page/settings/machine-learning-settings/machine-learning-settings.svelte
index 13ee941991..6a542d81d4 100644
--- a/web/src/lib/components/admin-page/settings/machine-learning-settings/machine-learning-settings.svelte
+++ b/web/src/lib/components/admin-page/settings/machine-learning-settings/machine-learning-settings.svelte
@@ -4,11 +4,13 @@
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition';
import type { SettingsEventType } from '../admin-settings';
- import SettingAccordion from '../setting-accordion.svelte';
- import SettingButtonsRow from '../setting-buttons-row.svelte';
- import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte';
- import SettingSelect from '../setting-select.svelte';
- import SettingSwitch from '../setting-switch.svelte';
+ import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte';
+ import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
+ import SettingInputField, {
+ SettingInputFieldType,
+ } from '$lib/components/shared-components/settings/setting-input-field.svelte';
+ import SettingSelect from '$lib/components/shared-components/settings/setting-select.svelte';
+ import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
export let savedConfig: SystemConfigDto;
export let defaultConfig: SystemConfigDto;
diff --git a/web/src/lib/components/admin-page/settings/map-settings/map-settings.svelte b/web/src/lib/components/admin-page/settings/map-settings/map-settings.svelte
index f7c5a9db61..46dc7a6351 100644
--- a/web/src/lib/components/admin-page/settings/map-settings/map-settings.svelte
+++ b/web/src/lib/components/admin-page/settings/map-settings/map-settings.svelte
@@ -4,10 +4,12 @@
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition';
import type { SettingsEventType } from '../admin-settings';
- import SettingAccordion from '../setting-accordion.svelte';
- import SettingButtonsRow from '../setting-buttons-row.svelte';
- import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte';
- import SettingSwitch from '../setting-switch.svelte';
+ import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte';
+ import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
+ import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
+ import SettingInputField, {
+ SettingInputFieldType,
+ } from '$lib/components/shared-components/settings/setting-input-field.svelte';
export let savedConfig: SystemConfigDto;
export let defaultConfig: SystemConfigDto;
diff --git a/web/src/lib/components/admin-page/settings/new-version-check-settings/new-version-check-settings.svelte b/web/src/lib/components/admin-page/settings/new-version-check-settings/new-version-check-settings.svelte
index f62684ebf2..fbda787bcd 100644
--- a/web/src/lib/components/admin-page/settings/new-version-check-settings/new-version-check-settings.svelte
+++ b/web/src/lib/components/admin-page/settings/new-version-check-settings/new-version-check-settings.svelte
@@ -4,8 +4,8 @@
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition';
import type { SettingsEventType } from '../admin-settings';
- import SettingButtonsRow from '../setting-buttons-row.svelte';
- import SettingSwitch from '../setting-switch.svelte';
+ import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
+ import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
export let savedConfig: SystemConfigDto;
export let defaultConfig: SystemConfigDto;
diff --git a/web/src/lib/components/admin-page/settings/oauth/oauth-settings.svelte b/web/src/lib/components/admin-page/settings/oauth/oauth-settings.svelte
index cca08b574e..10716728c5 100644
--- a/web/src/lib/components/admin-page/settings/oauth/oauth-settings.svelte
+++ b/web/src/lib/components/admin-page/settings/oauth/oauth-settings.svelte
@@ -5,9 +5,11 @@
import { fade } from 'svelte/transition';
import type { SettingsEventType } from '../admin-settings';
import ConfirmDisableLogin from '../confirm-disable-login.svelte';
- import SettingButtonsRow from '../setting-buttons-row.svelte';
- import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte';
- import SettingSwitch from '../setting-switch.svelte';
+ import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
+ import SettingInputField, {
+ SettingInputFieldType,
+ } from '$lib/components/shared-components/settings/setting-input-field.svelte';
+ import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
export let savedConfig: SystemConfigDto;
export let defaultConfig: SystemConfigDto;
diff --git a/web/src/lib/components/admin-page/settings/password-login/password-login-settings.svelte b/web/src/lib/components/admin-page/settings/password-login/password-login-settings.svelte
index f67bc04182..8dc7323cf9 100644
--- a/web/src/lib/components/admin-page/settings/password-login/password-login-settings.svelte
+++ b/web/src/lib/components/admin-page/settings/password-login/password-login-settings.svelte
@@ -5,8 +5,8 @@
import { fade } from 'svelte/transition';
import type { SettingsEventType } from '../admin-settings';
import ConfirmDisableLogin from '../confirm-disable-login.svelte';
- import SettingButtonsRow from '../setting-buttons-row.svelte';
- import SettingSwitch from '../setting-switch.svelte';
+ import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
+ import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
export let savedConfig: SystemConfigDto;
export let defaultConfig: SystemConfigDto;
diff --git a/web/src/lib/components/admin-page/settings/server/server-settings.svelte b/web/src/lib/components/admin-page/settings/server/server-settings.svelte
index 3c70d44205..4f235af1f6 100644
--- a/web/src/lib/components/admin-page/settings/server/server-settings.svelte
+++ b/web/src/lib/components/admin-page/settings/server/server-settings.svelte
@@ -1,11 +1,13 @@
diff --git a/web/src/lib/components/admin-page/settings/theme/theme-settings.svelte b/web/src/lib/components/admin-page/settings/theme/theme-settings.svelte
index bb1f6351be..10c52c1361 100644
--- a/web/src/lib/components/admin-page/settings/theme/theme-settings.svelte
+++ b/web/src/lib/components/admin-page/settings/theme/theme-settings.svelte
@@ -4,8 +4,8 @@
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition';
import type { SettingsEventType } from '../admin-settings';
- import SettingButtonsRow from '../setting-buttons-row.svelte';
- import SettingTextarea from '../setting-textarea.svelte';
+ import SettingTextarea from '$lib/components/shared-components/settings/setting-textarea.svelte';
+ import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
export let savedConfig: SystemConfigDto;
export let defaultConfig: SystemConfigDto;
diff --git a/web/src/lib/components/admin-page/settings/thumbnail/thumbnail-settings.svelte b/web/src/lib/components/admin-page/settings/thumbnail/thumbnail-settings.svelte
index 4c63380fda..8e2936b556 100644
--- a/web/src/lib/components/admin-page/settings/thumbnail/thumbnail-settings.svelte
+++ b/web/src/lib/components/admin-page/settings/thumbnail/thumbnail-settings.svelte
@@ -1,13 +1,16 @@
-
+
{#if isOpen}
diff --git a/web/src/lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte b/web/src/lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte
index 28a11999ab..b641260cb3 100644
--- a/web/src/lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte
+++ b/web/src/lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte
@@ -1,8 +1,4 @@
+
+
+
+
+
+ {title}
+
+ {#if isEdited}
+
+ Unsaved change
+
+ {/if}
+
+
+
{subtitle}
+
+
+ onSelect(detail)}
+ />
+
+
+
diff --git a/web/src/lib/components/admin-page/settings/setting-input-field.svelte b/web/src/lib/components/shared-components/settings/setting-input-field.svelte
similarity index 100%
rename from web/src/lib/components/admin-page/settings/setting-input-field.svelte
rename to web/src/lib/components/shared-components/settings/setting-input-field.svelte
diff --git a/web/src/lib/components/admin-page/settings/setting-select.svelte b/web/src/lib/components/shared-components/settings/setting-select.svelte
similarity index 100%
rename from web/src/lib/components/admin-page/settings/setting-select.svelte
rename to web/src/lib/components/shared-components/settings/setting-select.svelte
diff --git a/web/src/lib/components/admin-page/settings/setting-switch.svelte b/web/src/lib/components/shared-components/settings/setting-switch.svelte
similarity index 99%
rename from web/src/lib/components/admin-page/settings/setting-switch.svelte
rename to web/src/lib/components/shared-components/settings/setting-switch.svelte
index 6797423a55..2a06b272ce 100644
--- a/web/src/lib/components/admin-page/settings/setting-switch.svelte
+++ b/web/src/lib/components/shared-components/settings/setting-switch.svelte
@@ -30,6 +30,7 @@
{subtitle}
+
diff --git a/web/src/lib/components/admin-page/settings/setting-textarea.svelte b/web/src/lib/components/shared-components/settings/setting-textarea.svelte
similarity index 100%
rename from web/src/lib/components/admin-page/settings/setting-textarea.svelte
rename to web/src/lib/components/shared-components/settings/setting-textarea.svelte
diff --git a/web/src/lib/components/sharedlinks-page/shared-link-card.svelte b/web/src/lib/components/sharedlinks-page/shared-link-card.svelte
index 719b4791a7..51f1d440d3 100644
--- a/web/src/lib/components/sharedlinks-page/shared-link-card.svelte
+++ b/web/src/lib/components/sharedlinks-page/shared-link-card.svelte
@@ -16,6 +16,7 @@
import { createEventDispatcher } from 'svelte';
import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
import LoadingSpinner from '../shared-components/loading-spinner.svelte';
+ import { locale } from '$lib/stores/preferences.store';
export let link: SharedLinkResponseDto;
@@ -43,7 +44,7 @@
return;
}
- const expiresAtDate = luxon.DateTime.fromISO(new Date(link.expiresAt).toISOString());
+ const expiresAtDate = luxon.DateTime.fromISO(new Date(link.expiresAt).toISOString(), { locale: $locale });
const now = luxon.DateTime.now();
expirationCountdown = expiresAtDate.diff(now, ['days', 'hours', 'minutes', 'seconds']).toObject();
diff --git a/web/src/lib/components/user-settings-page/appearance-settings.svelte b/web/src/lib/components/user-settings-page/appearance-settings.svelte
index 99f00a4b0f..51e605ff54 100644
--- a/web/src/lib/components/user-settings-page/appearance-settings.svelte
+++ b/web/src/lib/components/user-settings-page/appearance-settings.svelte
@@ -1,11 +1,62 @@
@@ -16,9 +67,31 @@
title="Theme selection"
subtitle="Automatically set the theme to light or dark based on your browser's system preference"
bind:checked={$colorTheme.system}
- on:toggle={handleToggle}
+ on:toggle={handleToggleColorTheme}
/>
+
+ {#if $locale !== undefined}
+
+ handleLocaleChange(combobox?.value)}
+ />
+
+ {/if}
diff --git a/web/src/lib/components/user-settings-page/change-password-settings.svelte b/web/src/lib/components/user-settings-page/change-password-settings.svelte
index 6e17f2e7db..9745b15fa7 100644
--- a/web/src/lib/components/user-settings-page/change-password-settings.svelte
+++ b/web/src/lib/components/user-settings-page/change-password-settings.svelte
@@ -5,9 +5,12 @@
} from '$lib/components/shared-components/notification/notification';
import { changePassword } from '@immich/sdk';
import { fade } from 'svelte/transition';
- import SettingInputField, { SettingInputFieldType } from '../admin-page/settings/setting-input-field.svelte';
- import Button from '../elements/buttons/button.svelte';
+
+ import Button from '$lib/components/elements/buttons/button.svelte';
import type { HttpError } from '@sveltejs/kit';
+ import SettingInputField, {
+ SettingInputFieldType,
+ } from '$lib/components/shared-components/settings/setting-input-field.svelte';
let password = '';
let newPassword = '';
diff --git a/web/src/lib/components/user-settings-page/device-card.svelte b/web/src/lib/components/user-settings-page/device-card.svelte
index 29067aaca1..5395ae43f4 100644
--- a/web/src/lib/components/user-settings-page/device-card.svelte
+++ b/web/src/lib/components/user-settings-page/device-card.svelte
@@ -57,7 +57,7 @@
Last seen
- {DateTime.fromISO(device.updatedAt).toRelativeCalendar(options)}
+ {DateTime.fromISO(device.updatedAt, { locale: $locale }).toRelativeCalendar(options)}
{#if !device.current}
diff --git a/web/src/lib/components/user-settings-page/memories-settings.svelte b/web/src/lib/components/user-settings-page/memories-settings.svelte
index 8dc34f5374..938936b6c5 100644
--- a/web/src/lib/components/user-settings-page/memories-settings.svelte
+++ b/web/src/lib/components/user-settings-page/memories-settings.svelte
@@ -6,8 +6,9 @@
import { updateUser, type UserResponseDto } from '@immich/sdk';
import { fade } from 'svelte/transition';
import { handleError } from '../../utils/handle-error';
- import SettingSwitch from '../admin-page/settings/setting-switch.svelte';
+
import Button from '../elements/buttons/button.svelte';
+ import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
export let user: UserResponseDto;
diff --git a/web/src/lib/components/user-settings-page/partner-settings.svelte b/web/src/lib/components/user-settings-page/partner-settings.svelte
index a485564191..f29cf42961 100644
--- a/web/src/lib/components/user-settings-page/partner-settings.svelte
+++ b/web/src/lib/components/user-settings-page/partner-settings.svelte
@@ -10,13 +10,13 @@
import { mdiCheck, mdiClose } from '@mdi/js';
import { onMount } from 'svelte';
import { handleError } from '../../utils/handle-error';
- import SettingSwitch from '../admin-page/settings/setting-switch.svelte';
import Button from '../elements/buttons/button.svelte';
import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
import Icon from '../elements/icon.svelte';
- import ConfirmDialogue from '../shared-components/confirm-dialogue.svelte';
- import UserAvatar from '../shared-components/user-avatar.svelte';
+ import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte';
+ import UserAvatar from '$lib/components/shared-components/user-avatar.svelte';
import PartnerSelectionModal from './partner-selection-modal.svelte';
+ import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
interface PartnerSharing {
user: UserResponseDto;
diff --git a/web/src/lib/components/user-settings-page/quality-settings.svelte b/web/src/lib/components/user-settings-page/quality-settings.svelte
index 9adf77c5f4..6d2b4283ed 100644
--- a/web/src/lib/components/user-settings-page/quality-settings.svelte
+++ b/web/src/lib/components/user-settings-page/quality-settings.svelte
@@ -1,7 +1,7 @@
diff --git a/web/src/lib/components/user-settings-page/trash-settings.svelte b/web/src/lib/components/user-settings-page/trash-settings.svelte
index 21f00178d4..989f39af4e 100644
--- a/web/src/lib/components/user-settings-page/trash-settings.svelte
+++ b/web/src/lib/components/user-settings-page/trash-settings.svelte
@@ -1,7 +1,7 @@
diff --git a/web/src/lib/components/user-settings-page/user-profile-settings.svelte b/web/src/lib/components/user-settings-page/user-profile-settings.svelte
index 404202dace..f24f0ac7f6 100644
--- a/web/src/lib/components/user-settings-page/user-profile-settings.svelte
+++ b/web/src/lib/components/user-settings-page/user-profile-settings.svelte
@@ -5,11 +5,13 @@
} from '$lib/components/shared-components/notification/notification';
import { fade } from 'svelte/transition';
import { handleError } from '../../utils/handle-error';
- import SettingInputField, { SettingInputFieldType } from '../admin-page/settings/setting-input-field.svelte';
import Button from '../elements/buttons/button.svelte';
import { user } from '$lib/stores/user.store';
import { cloneDeep } from 'lodash-es';
import { updateUser } from '@immich/sdk';
+ import SettingInputField, {
+ SettingInputFieldType,
+ } from '$lib/components/shared-components/settings/setting-input-field.svelte';
let editedUser = cloneDeep($user);
diff --git a/web/src/lib/components/user-settings-page/user-settings-list.svelte b/web/src/lib/components/user-settings-page/user-settings-list.svelte
index 56177cfb0b..40322a5efb 100644
--- a/web/src/lib/components/user-settings-page/user-settings-list.svelte
+++ b/web/src/lib/components/user-settings-page/user-settings-list.svelte
@@ -6,7 +6,7 @@
import { user } from '$lib/stores/user.store';
import { oauth } from '$lib/utils';
import { type ApiKeyResponseDto, type AuthDeviceResponseDto } from '@immich/sdk';
- import SettingAccordion from '../admin-page/settings/setting-accordion.svelte';
+ import SettingAccordion from '../shared-components/settings/setting-accordion.svelte';
import AppearanceSettings from './appearance-settings.svelte';
import ChangePasswordSettings from './change-password-settings.svelte';
import DeviceList from './device-list.svelte';
diff --git a/web/src/lib/constants.ts b/web/src/lib/constants.ts
index 6608550200..dbb60df3e9 100644
--- a/web/src/lib/constants.ts
+++ b/web/src/lib/constants.ts
@@ -95,3 +95,147 @@ export enum Theme {
LIGHT = 'light',
DARK = 'dark',
}
+
+export const fallbackLocale = {
+ code: 'en-US',
+ name: 'English (US)',
+};
+
+export const locales = [
+ { code: 'af-ZA', name: 'Afrikaans (South Africa)' },
+ { code: 'sq-AL', name: 'Albanian (Albania)' },
+ { code: 'ar-DZ', name: 'Arabic (Algeria)' },
+ { code: 'ar-BH', name: 'Arabic (Bahrain)' },
+ { code: 'ar-EG', name: 'Arabic (Egypt)' },
+ { code: 'ar-IQ', name: 'Arabic (Iraq)' },
+ { code: 'ar-JO', name: 'Arabic (Jordan)' },
+ { code: 'ar-KW', name: 'Arabic (Kuwait)' },
+ { code: 'ar-LB', name: 'Arabic (Lebanon)' },
+ { code: 'ar-LY', name: 'Arabic (Libya)' },
+ { code: 'ar-MA', name: 'Arabic (Morocco)' },
+ { code: 'ar-OM', name: 'Arabic (Oman)' },
+ { code: 'ar-QA', name: 'Arabic (Qatar)' },
+ { code: 'ar-SA', name: 'Arabic (Saudi Arabia)' },
+ { code: 'ar-SY', name: 'Arabic (Syria)' },
+ { code: 'ar-TN', name: 'Arabic (Tunisia)' },
+ { code: 'ar-AE', name: 'Arabic (United Arab Emirates)' },
+ { code: 'ar-YE', name: 'Arabic (Yemen)' },
+ { code: 'hy-AM', name: 'Armenian (Armenia)' },
+ { code: 'az-AZ', name: 'Azerbaijani (Azerbaijan)' },
+ { code: 'eu-ES', name: 'Basque (Spain)' },
+ { code: 'be-BY', name: 'Belarusian (Belarus)' },
+ { code: 'bn-IN', name: 'Bengali (India)' },
+ { code: 'bs-BA', name: 'Bosnian (Bosnia and Herzegovina)' },
+ { code: 'bg-BG', name: 'Bulgarian (Bulgaria)' },
+ { code: 'ca-ES', name: 'Catalan (Spain)' },
+ { code: 'zh-CN', name: 'Chinese (China)' },
+ { code: 'zh-HK', name: 'Chinese (Hong Kong SAR China)' },
+ { code: 'zh-MO', name: 'Chinese (Macao SAR China)' },
+ { code: 'zh-SG', name: 'Chinese (Singapore)' },
+ { code: 'zh-TW', name: 'Chinese (Taiwan)' },
+ { code: 'hr-HR', name: 'Croatian (Croatia)' },
+ { code: 'cs-CZ', name: 'Czech (Czech Republic)' },
+ { code: 'da-DK', name: 'Danish (Denmark)' },
+ { code: 'nl-BE', name: 'Dutch (Belgium)' },
+ { code: 'nl-NL', name: 'Dutch (Netherlands)' },
+ { code: 'en-AU', name: 'English (Australia)' },
+ { code: 'en-BZ', name: 'English (Belize)' },
+ { code: 'en-CA', name: 'English (Canada)' },
+ { code: 'en-IE', name: 'English (Ireland)' },
+ { code: 'en-JM', name: 'English (Jamaica)' },
+ { code: 'en-NZ', name: 'English (New Zealand)' },
+ { code: 'en-PH', name: 'English (Philippines)' },
+ { code: 'en-ZA', name: 'English (South Africa)' },
+ { code: 'en-TT', name: 'English (Trinidad and Tobago)' },
+ { code: 'en-VI', name: 'English (U.S. Virgin Islands)' },
+ { code: 'en-GB', name: 'English (United Kingdom)' },
+ { code: 'en-US', name: 'English (United States)' },
+ { code: 'en-ZW', name: 'English (Zimbabwe)' },
+ { code: 'et-EE', name: 'Estonian (Estonia)' },
+ { code: 'fo-FO', name: 'Faroese (Faroe Islands)' },
+ { code: 'fi-FI', name: 'Finnish (Finland)' },
+ { code: 'fr-BE', name: 'French (Belgium)' },
+ { code: 'fr-CA', name: 'French (Canada)' },
+ { code: 'fr-FR', name: 'French (France)' },
+ { code: 'fr-LU', name: 'French (Luxembourg)' },
+ { code: 'fr-MC', name: 'French (Monaco)' },
+ { code: 'fr-CH', name: 'French (Switzerland)' },
+ { code: 'gl-ES', name: 'Galician (Spain)' },
+ { code: 'ka-GE', name: 'Georgian (Georgia)' },
+ { code: 'de-AT', name: 'German (Austria)' },
+ { code: 'de-DE', name: 'German (Germany)' },
+ { code: 'de-LI', name: 'German (Liechtenstein)' },
+ { code: 'de-LU', name: 'German (Luxembourg)' },
+ { code: 'de-CH', name: 'German (Switzerland)' },
+ { code: 'el-GR', name: 'Greek (Greece)' },
+ { code: 'gu-IN', name: 'Gujarati (India)' },
+ { code: 'he-IL', name: 'Hebrew (Israel)' },
+ { code: 'hi-IN', name: 'Hindi (India)' },
+ { code: 'hu-HU', name: 'Hungarian (Hungary)' },
+ { code: 'is-IS', name: 'Icelandic (Iceland)' },
+ { code: 'id-ID', name: 'Indonesian (Indonesia)' },
+ { code: 'it-IT', name: 'Italian (Italy)' },
+ { code: 'it-CH', name: 'Italian (Switzerland)' },
+ { code: 'ja-JP', name: 'Japanese (Japan)' },
+ { code: 'kn-IN', name: 'Kannada (India)' },
+ { code: 'kk-KZ', name: 'Kazakh (Kazakhstan)' },
+ { code: 'kok-IN', name: 'Konkani (India)' },
+ { code: 'ko-KR', name: 'Korean (South Korea)' },
+ { code: 'lv-LV', name: 'Latvian (Latvia)' },
+ { code: 'lt-LT', name: 'Lithuanian (Lithuania)' },
+ { code: 'mk-MK', name: 'Macedonian (Macedonia)' },
+ { code: 'ms-BN', name: 'Malay (Brunei)' },
+ { code: 'ms-MY', name: 'Malay (Malaysia)' },
+ { code: 'ml-IN', name: 'Malayalam (India)' },
+ { code: 'mt-MT', name: 'Maltese (Malta)' },
+ { code: 'mr-IN', name: 'Marathi (India)' },
+ { code: 'mn-MN', name: 'Mongolian (Mongolia)' },
+ { code: 'se-NO', name: 'Northern Sami (Norway)' },
+ { code: 'nb-NO', name: 'Norwegian Bokmål (Norway)' },
+ { code: 'nn-NO', name: 'Norwegian Nynorsk (Norway)' },
+ { code: 'fa-IR', name: 'Persian (Iran)' },
+ { code: 'pl-PL', name: 'Polish (Poland)' },
+ { code: 'pt-BR', name: 'Portuguese (Brazil)' },
+ { code: 'pt-PT', name: 'Portuguese (Portugal)' },
+ { code: 'pa-IN', name: 'Punjabi (India)' },
+ { code: 'ro-RO', name: 'Romanian (Romania)' },
+ { code: 'ru-RU', name: 'Russian (Russia)' },
+ { code: 'sr-BA', name: 'Serbian (Bosnia and Herzegovina)' },
+ { code: 'sr-CS', name: 'Serbian (Serbia And Montenegro)' },
+ { code: 'sk-SK', name: 'Slovak (Slovakia)' },
+ { code: 'sl-SI', name: 'Slovenian (Slovenia)' },
+ { code: 'es-AR', name: 'Spanish (Argentina)' },
+ { code: 'es-BO', name: 'Spanish (Bolivia)' },
+ { code: 'es-CL', name: 'Spanish (Chile)' },
+ { code: 'es-CO', name: 'Spanish (Colombia)' },
+ { code: 'es-CR', name: 'Spanish (Costa Rica)' },
+ { code: 'es-DO', name: 'Spanish (Dominican Republic)' },
+ { code: 'es-EC', name: 'Spanish (Ecuador)' },
+ { code: 'es-SV', name: 'Spanish (El Salvador)' },
+ { code: 'es-GT', name: 'Spanish (Guatemala)' },
+ { code: 'es-HN', name: 'Spanish (Honduras)' },
+ { code: 'es-MX', name: 'Spanish (Mexico)' },
+ { code: 'es-NI', name: 'Spanish (Nicaragua)' },
+ { code: 'es-PA', name: 'Spanish (Panama)' },
+ { code: 'es-PY', name: 'Spanish (Paraguay)' },
+ { code: 'es-PE', name: 'Spanish (Peru)' },
+ { code: 'es-PR', name: 'Spanish (Puerto Rico)' },
+ { code: 'es-ES', name: 'Spanish (Spain)' },
+ { code: 'es-UY', name: 'Spanish (Uruguay)' },
+ { code: 'es-VE', name: 'Spanish (Venezuela)' },
+ { code: 'sw-KE', name: 'Swahili (Kenya)' },
+ { code: 'sv-FI', name: 'Swedish (Finland)' },
+ { code: 'sv-SE', name: 'Swedish (Sweden)' },
+ { code: 'syr-SY', name: 'Syriac (Syria)' },
+ { code: 'ta-IN', name: 'Tamil (India)' },
+ { code: 'te-IN', name: 'Telugu (India)' },
+ { code: 'th-TH', name: 'Thai (Thailand)' },
+ { code: 'tn-ZA', name: 'Tswana (South Africa)' },
+ { code: 'tr-TR', name: 'Turkish (Turkey)' },
+ { code: 'uk-UA', name: 'Ukrainian (Ukraine)' },
+ { code: 'uz-UZ', name: 'Uzbek (Uzbekistan)' },
+ { code: 'vi-VN', name: 'Vietnamese (Vietnam)' },
+ { code: 'cy-GB', name: 'Welsh (United Kingdom)' },
+ { code: 'xh-ZA', name: 'Xhosa (South Africa)' },
+ { code: 'zu-ZA', name: 'Zulu (South Africa)' },
+];
diff --git a/web/src/lib/utils.ts b/web/src/lib/utils.ts
index 88b0f0650f..644dd7f638 100644
--- a/web/src/lib/utils.ts
+++ b/web/src/lib/utils.ts
@@ -1,6 +1,7 @@
import { goto } from '$app/navigation';
import { page } from '$app/stores';
import { NotificationType, notificationController } from '$lib/components/shared-components/notification/notification';
+import { locales } from '$lib/constants';
import { handleError } from '$lib/utils/handle-error';
import {
AssetJobName,
@@ -185,3 +186,11 @@ export const oauth = {
return unlinkOAuthAccount();
},
};
+
+export const findLocale = (code: string | undefined) => {
+ const language = locales.find((lang) => lang.code === code);
+ return {
+ code: language?.code,
+ name: language?.name,
+ };
+};
diff --git a/web/src/lib/utils/timeline-util.ts b/web/src/lib/utils/timeline-util.ts
index fa1ceb88cb..83756a4064 100644
--- a/web/src/lib/utils/timeline-util.ts
+++ b/web/src/lib/utils/timeline-util.ts
@@ -1,8 +1,11 @@
+import { locale } from '$lib/stores/preferences.store';
import type { AssetResponseDto } from '@immich/sdk';
import { groupBy, sortBy } from 'lodash-es';
import { DateTime, Interval } from 'luxon';
+import { get } from 'svelte/store';
-export const fromLocalDateTime = (localDateTime: string) => DateTime.fromISO(localDateTime, { zone: 'UTC' });
+export const fromLocalDateTime = (localDateTime: string) =>
+ DateTime.fromISO(localDateTime, { zone: 'UTC', locale: get(locale) });
export const groupDateFormat: Intl.DateTimeFormatOptions = {
weekday: 'short',
diff --git a/web/src/routes/admin/system-settings/+page.svelte b/web/src/routes/admin/system-settings/+page.svelte
index f8062e6211..df1160dd87 100644
--- a/web/src/routes/admin/system-settings/+page.svelte
+++ b/web/src/routes/admin/system-settings/+page.svelte
@@ -10,7 +10,7 @@
import OAuthSettings from '$lib/components/admin-page/settings/oauth/oauth-settings.svelte';
import PasswordLoginSettings from '$lib/components/admin-page/settings/password-login/password-login-settings.svelte';
import ServerSettings from '$lib/components/admin-page/settings/server/server-settings.svelte';
- import SettingAccordion from '$lib/components/admin-page/settings/setting-accordion.svelte';
+ import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte';
import StorageTemplateSettings from '$lib/components/admin-page/settings/storage-template/storage-template-settings.svelte';
import ThemeSettings from '$lib/components/admin-page/settings/theme/theme-settings.svelte';
import ThumbnailSettings from '$lib/components/admin-page/settings/thumbnail/thumbnail-settings.svelte';