forked from Cutlery/immich
		
	feat(web): add an option to change the date formats (#7174)
* feat: add an option to change the date formats * pr feedback * fix: change title * fix: show list supported by the browser * fix: tests * fix: dates * fix: check only if locale is set * fix: better fallback value * fix: fallback * fix: fallback * feat: add default locale option * refactor: shared components * refactor: shared components * prepare for svelte 5 * don't use relative paths * refactor: fallback value Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com> * fix: parsing store * fix: lint * refactor: locales --------- Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com> Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
This commit is contained in:
		
							parent
							
								
									a224bb23d0
								
							
						
					
					
						commit
						01d6707b59
					
				@ -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;
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,13 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
  import SettingButtonsRow from '$lib/components/admin-page/settings/setting-buttons-row.svelte';
 | 
			
		||||
  import type { SystemConfigDto } from '@immich/sdk';
 | 
			
		||||
  import { isEqual } from 'lodash-es';
 | 
			
		||||
  import { createEventDispatcher } from 'svelte';
 | 
			
		||||
  import { fade } from 'svelte/transition';
 | 
			
		||||
  import type { SettingsEventType } from '../admin-settings';
 | 
			
		||||
  import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte';
 | 
			
		||||
  import SettingInputField, {
 | 
			
		||||
    SettingInputFieldType,
 | 
			
		||||
  } from '$lib/components/shared-components/settings/setting-input-field.svelte';
 | 
			
		||||
  import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
 | 
			
		||||
 | 
			
		||||
  export let savedConfig: SystemConfigDto;
 | 
			
		||||
  export let defaultConfig: SystemConfigDto;
 | 
			
		||||
 | 
			
		||||
@ -13,11 +13,13 @@
 | 
			
		||||
  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 SettingSwitch from '../setting-switch.svelte';
 | 
			
		||||
  import SupportedDatetimePanel from './supported-datetime-panel.svelte';
 | 
			
		||||
  import SupportedVariablesPanel from './supported-variables-panel.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;
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,12 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
  import { locale } from '$lib/stores/preferences.store';
 | 
			
		||||
  import type { SystemConfigTemplateStorageOptionDto } from '@immich/sdk';
 | 
			
		||||
  import * as luxon from 'luxon';
 | 
			
		||||
  import { DateTime } from 'luxon';
 | 
			
		||||
 | 
			
		||||
  export let options: SystemConfigTemplateStorageOptionDto;
 | 
			
		||||
 | 
			
		||||
  const getLuxonExample = (format: string) => {
 | 
			
		||||
    return luxon.DateTime.fromISO(new Date('2022-09-04T20:03:05.250').toISOString()).toFormat(format);
 | 
			
		||||
    return DateTime.fromISO('2022-09-04T20:03:05.250Z', { locale: $locale }).toFormat(format);
 | 
			
		||||
  };
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
@ -1,13 +1,16 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
  import SettingButtonsRow from '$lib/components/admin-page/settings/setting-buttons-row.svelte';
 | 
			
		||||
  import SettingSelect from '$lib/components/admin-page/settings/setting-select.svelte';
 | 
			
		||||
  import { Colorspace, type SystemConfigDto } from '@immich/sdk';
 | 
			
		||||
  import { isEqual } from 'lodash-es';
 | 
			
		||||
  import { createEventDispatcher } from 'svelte';
 | 
			
		||||
  import { fade } from 'svelte/transition';
 | 
			
		||||
  import type { SettingsEventType } from '../admin-settings';
 | 
			
		||||
  import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte';
 | 
			
		||||
  import SettingSwitch from '../setting-switch.svelte';
 | 
			
		||||
  import SettingSelect from '$lib/components/shared-components/settings/setting-select.svelte';
 | 
			
		||||
 | 
			
		||||
  import SettingSwitch from '$lib/components/shared-components/settings/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';
 | 
			
		||||
 | 
			
		||||
  export let savedConfig: SystemConfigDto;
 | 
			
		||||
  export let defaultConfig: SystemConfigDto;
 | 
			
		||||
 | 
			
		||||
@ -4,9 +4,11 @@
 | 
			
		||||
  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 SettingSwitch from '../setting-switch.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';
 | 
			
		||||
  import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte';
 | 
			
		||||
 | 
			
		||||
  export let savedConfig: SystemConfigDto;
 | 
			
		||||
  export let defaultConfig: SystemConfigDto;
 | 
			
		||||
 | 
			
		||||
@ -3,10 +3,11 @@
 | 
			
		||||
  import type { AlbumResponseDto, UserResponseDto } from '@immich/sdk';
 | 
			
		||||
  import { mdiClose, mdiPlus } from '@mdi/js';
 | 
			
		||||
  import { createEventDispatcher } from 'svelte';
 | 
			
		||||
  import SettingSwitch from '../admin-page/settings/setting-switch.svelte';
 | 
			
		||||
 | 
			
		||||
  import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
 | 
			
		||||
  import FullScreenModal from '../shared-components/full-screen-modal.svelte';
 | 
			
		||||
  import UserAvatar from '../shared-components/user-avatar.svelte';
 | 
			
		||||
  import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
 | 
			
		||||
  import UserAvatar from '$lib/components/shared-components/user-avatar.svelte';
 | 
			
		||||
  import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
 | 
			
		||||
 | 
			
		||||
  export let album: AlbumResponseDto;
 | 
			
		||||
  export let user: UserResponseDto;
 | 
			
		||||
 | 
			
		||||
@ -24,12 +24,13 @@
 | 
			
		||||
  import LoadingSpinner from '../shared-components/loading-spinner.svelte';
 | 
			
		||||
  import { NotificationType, notificationController } from '../shared-components/notification/notification';
 | 
			
		||||
  import UserAvatar from '../shared-components/user-avatar.svelte';
 | 
			
		||||
  import { locale } from '$lib/stores/preferences.store';
 | 
			
		||||
 | 
			
		||||
  const units: Intl.RelativeTimeFormatUnit[] = ['year', 'month', 'week', 'day', 'hour', 'minute', 'second'];
 | 
			
		||||
 | 
			
		||||
  const shouldGroup = (currentDate: string, nextDate: string): boolean => {
 | 
			
		||||
    const currentDateTime = luxon.DateTime.fromISO(currentDate);
 | 
			
		||||
    const nextDateTime = luxon.DateTime.fromISO(nextDate);
 | 
			
		||||
    const currentDateTime = luxon.DateTime.fromISO(currentDate, { locale: $locale });
 | 
			
		||||
    const nextDateTime = luxon.DateTime.fromISO(nextDate, { locale: $locale });
 | 
			
		||||
 | 
			
		||||
    return currentDateTime.hasSame(nextDateTime, 'hour') || currentDateTime.toRelative() === nextDateTime.toRelative();
 | 
			
		||||
  };
 | 
			
		||||
@ -224,7 +225,7 @@
 | 
			
		||||
                class="pt-1 px-2 text-right w-full text-sm text-gray-500 dark:text-gray-300"
 | 
			
		||||
                title={new Date(reaction.createdAt).toLocaleDateString(undefined, timeOptions)}
 | 
			
		||||
              >
 | 
			
		||||
                {timeSince(luxon.DateTime.fromISO(reaction.createdAt))}
 | 
			
		||||
                {timeSince(luxon.DateTime.fromISO(reaction.createdAt, { locale: $locale }))}
 | 
			
		||||
              </div>
 | 
			
		||||
            {/if}
 | 
			
		||||
          {:else if reaction.type === 'like'}
 | 
			
		||||
@ -269,7 +270,7 @@
 | 
			
		||||
                  class="pt-1 px-2 text-right w-full text-sm text-gray-500 dark:text-gray-300"
 | 
			
		||||
                  title={new Date(reaction.createdAt).toLocaleDateString(navigator.language, timeOptions)}
 | 
			
		||||
                >
 | 
			
		||||
                  {timeSince(luxon.DateTime.fromISO(reaction.createdAt))}
 | 
			
		||||
                  {timeSince(luxon.DateTime.fromISO(reaction.createdAt, { locale: $locale }))}
 | 
			
		||||
                </div>
 | 
			
		||||
              {/if}
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
@ -443,6 +443,7 @@
 | 
			
		||||
      {@const assetDateTimeOriginal = asset.exifInfo?.dateTimeOriginal
 | 
			
		||||
        ? DateTime.fromISO(asset.exifInfo.dateTimeOriginal, {
 | 
			
		||||
            zone: asset.exifInfo.timeZone ?? undefined,
 | 
			
		||||
            locale: $locale,
 | 
			
		||||
          })
 | 
			
		||||
        : DateTime.now()}
 | 
			
		||||
      <ChangeDate
 | 
			
		||||
 | 
			
		||||
@ -4,10 +4,10 @@
 | 
			
		||||
  import { Duration } from 'luxon';
 | 
			
		||||
  import { createEventDispatcher } from 'svelte';
 | 
			
		||||
  import { fly } from 'svelte/transition';
 | 
			
		||||
  import SettingSelect from '../admin-page/settings/setting-select.svelte';
 | 
			
		||||
  import SettingSwitch from '../admin-page/settings/setting-switch.svelte';
 | 
			
		||||
  import Button from '../elements/buttons/button.svelte';
 | 
			
		||||
  import LinkButton from '../elements/buttons/link-button.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 settings: MapSettings;
 | 
			
		||||
  let customDateRange = !!settings.dateAfter || !!settings.dateBefore;
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,7 @@
 | 
			
		||||
  import { DateTime } from 'luxon';
 | 
			
		||||
  import ConfirmDialogue from './confirm-dialogue.svelte';
 | 
			
		||||
  import Combobox from './combobox.svelte';
 | 
			
		||||
 | 
			
		||||
  export let initialDate: DateTime = DateTime.now();
 | 
			
		||||
 | 
			
		||||
  type ZoneOption = {
 | 
			
		||||
 | 
			
		||||
@ -52,7 +52,7 @@
 | 
			
		||||
  };
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div class="relative" use:clickOutside on:outclick={handleOutClick}>
 | 
			
		||||
<div class="relative w-full" use:clickOutside on:outclick={handleOutClick}>
 | 
			
		||||
  <div>
 | 
			
		||||
    {#if isOpen}
 | 
			
		||||
      <div class="absolute inset-y-0 left-0 flex items-center pl-3">
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,4 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
  import SettingInputField, {
 | 
			
		||||
    SettingInputFieldType,
 | 
			
		||||
  } from '$lib/components/admin-page/settings/setting-input-field.svelte';
 | 
			
		||||
  import SettingSwitch from '$lib/components/admin-page/settings/setting-switch.svelte';
 | 
			
		||||
  import Button from '$lib/components/elements/buttons/button.svelte';
 | 
			
		||||
  import Icon from '$lib/components/elements/icon.svelte';
 | 
			
		||||
  import { serverConfig } from '$lib/stores/server-config.store';
 | 
			
		||||
@ -15,6 +11,8 @@
 | 
			
		||||
  import type { ImmichDropDownOption } from '../dropdown-button.svelte';
 | 
			
		||||
  import DropdownButton from '../dropdown-button.svelte';
 | 
			
		||||
  import { NotificationType, notificationController } from '../notification/notification';
 | 
			
		||||
  import SettingInputField, { SettingInputFieldType } from '../settings/setting-input-field.svelte';
 | 
			
		||||
  import SettingSwitch from '../settings/setting-switch.svelte';
 | 
			
		||||
 | 
			
		||||
  export let albumId: string | undefined = undefined;
 | 
			
		||||
  export let assetIds: string[] = [];
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,42 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
  import { quintOut } from 'svelte/easing';
 | 
			
		||||
  import { fly } from 'svelte/transition';
 | 
			
		||||
  import Combobox, { type ComboBoxOption } from '$lib/components/shared-components/combobox.svelte';
 | 
			
		||||
 | 
			
		||||
  export let title: string;
 | 
			
		||||
  export let comboboxPlaceholder: string;
 | 
			
		||||
  export let subtitle = '';
 | 
			
		||||
  export let isEdited = false;
 | 
			
		||||
  export let options: ComboBoxOption[];
 | 
			
		||||
  export let selectedOption: ComboBoxOption;
 | 
			
		||||
  export let onSelect: (combobox: ComboBoxOption | undefined) => void;
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div class="grid grid-cols-2">
 | 
			
		||||
  <div>
 | 
			
		||||
    <div class="flex h-[26px] place-items-center gap-1">
 | 
			
		||||
      <label class="font-medium text-immich-primary dark:text-immich-dark-primary text-sm" for={title}>
 | 
			
		||||
        {title}
 | 
			
		||||
      </label>
 | 
			
		||||
      {#if isEdited}
 | 
			
		||||
        <div
 | 
			
		||||
          transition:fly={{ x: 10, duration: 200, easing: quintOut }}
 | 
			
		||||
          class="rounded-full bg-orange-100 px-2 text-[10px] text-orange-900"
 | 
			
		||||
        >
 | 
			
		||||
          Unsaved change
 | 
			
		||||
        </div>
 | 
			
		||||
      {/if}
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <p class="text-sm dark:text-immich-dark-fg">{subtitle}</p>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="flex items-center">
 | 
			
		||||
    <Combobox
 | 
			
		||||
      {selectedOption}
 | 
			
		||||
      {options}
 | 
			
		||||
      placeholder={comboboxPlaceholder}
 | 
			
		||||
      on:select={({ detail }) => onSelect(detail)}
 | 
			
		||||
    />
 | 
			
		||||
    <slot />
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
@ -30,6 +30,7 @@
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <p class="text-sm dark:text-immich-dark-fg">{subtitle}</p>
 | 
			
		||||
    <slot />
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <label class="relative inline-block h-[10px] w-[36px] flex-none">
 | 
			
		||||
@ -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();
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,62 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
  import type { ComboBoxOption } from '$lib/components/shared-components/combobox.svelte';
 | 
			
		||||
  import SettingCombobox from '$lib/components/shared-components/settings/setting-combobox.svelte';
 | 
			
		||||
  import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
 | 
			
		||||
  import { fallbackLocale, locales } from '$lib/constants';
 | 
			
		||||
  import { colorTheme, locale } from '$lib/stores/preferences.store';
 | 
			
		||||
  import { findLocale } from '$lib/utils';
 | 
			
		||||
  import { onMount } from 'svelte';
 | 
			
		||||
  import { fade } from 'svelte/transition';
 | 
			
		||||
  import { colorTheme } from '../../stores/preferences.store';
 | 
			
		||||
  import SettingSwitch from '../admin-page/settings/setting-switch.svelte';
 | 
			
		||||
 | 
			
		||||
  export const handleToggle = () => {
 | 
			
		||||
  let time = new Date();
 | 
			
		||||
 | 
			
		||||
  $: formattedDate = time.toLocaleString(editedLocale, {
 | 
			
		||||
    year: 'numeric',
 | 
			
		||||
    month: '2-digit',
 | 
			
		||||
    day: '2-digit',
 | 
			
		||||
  });
 | 
			
		||||
  $: timePortion = time.toLocaleString(editedLocale, {
 | 
			
		||||
    hour: '2-digit',
 | 
			
		||||
    minute: '2-digit',
 | 
			
		||||
    second: '2-digit',
 | 
			
		||||
  });
 | 
			
		||||
  $: selectedDate = `${formattedDate} ${timePortion}`;
 | 
			
		||||
  $: editedLocale = findLocale($locale).code;
 | 
			
		||||
  $: selectedOption = {
 | 
			
		||||
    value: findLocale(editedLocale).code || fallbackLocale.code,
 | 
			
		||||
    label: findLocale(editedLocale).name || fallbackLocale.name,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  onMount(() => {
 | 
			
		||||
    const interval = setInterval(() => {
 | 
			
		||||
      time = new Date();
 | 
			
		||||
    }, 1000);
 | 
			
		||||
 | 
			
		||||
    return () => {
 | 
			
		||||
      clearInterval(interval);
 | 
			
		||||
    };
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const getAllLanguages = (): ComboBoxOption[] => {
 | 
			
		||||
    return locales
 | 
			
		||||
      .filter(({ code }) => Intl.NumberFormat.supportedLocalesOf(code).length > 0)
 | 
			
		||||
      .map((locale) => ({
 | 
			
		||||
        label: locale.name,
 | 
			
		||||
        value: locale.code,
 | 
			
		||||
      }));
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const handleToggleColorTheme = () => {
 | 
			
		||||
    $colorTheme.system = !$colorTheme.system;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const handleToggleLocaleBrowser = () => {
 | 
			
		||||
    $locale = $locale ? undefined : fallbackLocale.code;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const handleLocaleChange = (newLocale: string | undefined) => {
 | 
			
		||||
    $locale = newLocale;
 | 
			
		||||
  };
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<section class="my-4">
 | 
			
		||||
@ -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}
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="ml-4">
 | 
			
		||||
        <SettingSwitch
 | 
			
		||||
          title="Default Locale"
 | 
			
		||||
          subtitle="Format dates and numbers based on your browser locale"
 | 
			
		||||
          checked={$locale == undefined}
 | 
			
		||||
          on:toggle={handleToggleLocaleBrowser}
 | 
			
		||||
        >
 | 
			
		||||
          <p class="mt-2">{selectedDate}</p>
 | 
			
		||||
        </SettingSwitch>
 | 
			
		||||
      </div>
 | 
			
		||||
      {#if $locale !== undefined}
 | 
			
		||||
        <div class="ml-4">
 | 
			
		||||
          <SettingCombobox
 | 
			
		||||
            comboboxPlaceholder="Searching locales..."
 | 
			
		||||
            {selectedOption}
 | 
			
		||||
            options={getAllLanguages()}
 | 
			
		||||
            title="Custom Locale"
 | 
			
		||||
            subtitle="Format dates and numbers based on the language and the region"
 | 
			
		||||
            onSelect={(combobox) => handleLocaleChange(combobox?.value)}
 | 
			
		||||
          />
 | 
			
		||||
        </div>
 | 
			
		||||
      {/if}
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</section>
 | 
			
		||||
 | 
			
		||||
@ -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 = '';
 | 
			
		||||
 | 
			
		||||
@ -57,7 +57,7 @@
 | 
			
		||||
      </span>
 | 
			
		||||
      <div class="text-sm">
 | 
			
		||||
        <span class="">Last seen</span>
 | 
			
		||||
        <span>{DateTime.fromISO(device.updatedAt).toRelativeCalendar(options)}</span>
 | 
			
		||||
        <span>{DateTime.fromISO(device.updatedAt, { locale: $locale }).toRelativeCalendar(options)}</span>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    {#if !device.current}
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
  import { fade } from 'svelte/transition';
 | 
			
		||||
  import { alwaysLoadOriginalFile } from '../../stores/preferences.store';
 | 
			
		||||
  import SettingSwitch from '../admin-page/settings/setting-switch.svelte';
 | 
			
		||||
  import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
 | 
			
		||||
 | 
			
		||||
  const handleToggle = () => {
 | 
			
		||||
    $alwaysLoadOriginalFile = !$alwaysLoadOriginalFile;
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
  import { fade } from 'svelte/transition';
 | 
			
		||||
  import { sidebarSettings } from '../../stores/preferences.store';
 | 
			
		||||
  import SettingSwitch from '../admin-page/settings/setting-switch.svelte';
 | 
			
		||||
  import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<section class="my-4">
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
  import SettingSwitch from '../admin-page/settings/setting-switch.svelte';
 | 
			
		||||
  import { showDeleteModal } from '$lib/stores/preferences.store';
 | 
			
		||||
  import { fade } from 'svelte/transition';
 | 
			
		||||
  import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<section class="my-4">
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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';
 | 
			
		||||
 | 
			
		||||
@ -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)' },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@ -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,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -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',
 | 
			
		||||
 | 
			
		||||
@ -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';
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user