1
0
mirror of https://github.com/immich-app/immich.git synced 2024-11-28 09:33:27 +02:00

feat(web): more localized number formatting (#11401)

This commit is contained in:
Michel Heusschen 2024-07-29 16:38:27 +02:00 committed by GitHub
parent 2e059bfbfd
commit 0237f9baa3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 44 additions and 19 deletions

View File

@ -108,7 +108,10 @@
{/if} {/if}
<span class="text-immich-primary dark:text-immich-dark-primary"> <span class="text-immich-primary dark:text-immich-dark-primary">
{#if user.quotaSizeInBytes} {#if user.quotaSizeInBytes}
({((user.usage / user.quotaSizeInBytes) * 100).toFixed(0)}%) ({(user.usage / user.quotaSizeInBytes).toLocaleString($locale, {
style: 'percent',
maximumFractionDigits: 0,
})})
{:else} {:else}
({$t('unlimited')}) ({$t('unlimited')})
{/if} {/if}

View File

@ -1,4 +1,5 @@
<script lang="ts"> <script lang="ts">
import { locale } from '$lib/stores/preferences.store';
import type { ActivityResponseDto } from '@immich/sdk'; import type { ActivityResponseDto } from '@immich/sdk';
import { mdiCommentOutline, mdiHeart, mdiHeartOutline } from '@mdi/js'; import { mdiCommentOutline, mdiHeart, mdiHeartOutline } from '@mdi/js';
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from 'svelte';
@ -24,7 +25,7 @@
<div class="flex gap-2 items-center justify-center"> <div class="flex gap-2 items-center justify-center">
<Icon path={mdiCommentOutline} class="scale-x-[-1]" size={24} /> <Icon path={mdiCommentOutline} class="scale-x-[-1]" size={24} />
{#if numberOfComments} {#if numberOfComments}
<div class="text-xl">{numberOfComments}</div> <div class="text-xl">{numberOfComments.toLocaleString($locale)}</div>
{/if} {/if}
</div> </div>
</button> </button>

View File

@ -35,7 +35,9 @@
<div class="h-[7px] rounded-full bg-immich-primary" style={`width: ${download.percentage}%`} /> <div class="h-[7px] rounded-full bg-immich-primary" style={`width: ${download.percentage}%`} />
</div> </div>
<p class="min-w-[4em] whitespace-nowrap text-right"> <p class="min-w-[4em] whitespace-nowrap text-right">
<span class="text-immich-primary">{download.percentage}%</span> <span class="text-immich-primary">
{(download.percentage / 100).toLocaleString($locale, { style: 'percent' })}
</span>
</p> </p>
</div> </div>
</div> </div>

View File

@ -6,7 +6,7 @@
import { getAltText } from '$lib/utils/thumbnail-util'; import { getAltText } from '$lib/utils/thumbnail-util';
import { timeToSeconds } from '$lib/utils/date-time'; import { timeToSeconds } from '$lib/utils/date-time';
import { AssetMediaSize, AssetTypeEnum, type AssetResponseDto } from '@immich/sdk'; import { AssetMediaSize, AssetTypeEnum, type AssetResponseDto } from '@immich/sdk';
import { playVideoThumbnailOnHover } from '$lib/stores/preferences.store'; import { locale, playVideoThumbnailOnHover } from '$lib/stores/preferences.store';
import { getAssetPlaybackUrl } from '$lib/utils'; import { getAssetPlaybackUrl } from '$lib/utils';
import { import {
mdiArchiveArrowDownOutline, mdiArchiveArrowDownOutline,
@ -177,7 +177,7 @@
: 'top-7 right-1'} z-20 flex place-items-center gap-1 text-xs font-medium text-white" : 'top-7 right-1'} z-20 flex place-items-center gap-1 text-xs font-medium text-white"
> >
<span class="pr-2 pt-2 flex place-items-center gap-1"> <span class="pr-2 pt-2 flex place-items-center gap-1">
<p>{asset.stackCount}</p> <p>{asset.stackCount.toLocaleString($locale)}</p>
<Icon path={mdiCameraBurst} size="24" /> <Icon path={mdiCameraBurst} size="24" />
</span> </span>
</div> </div>

View File

@ -38,6 +38,7 @@
import { tweened } from 'svelte/motion'; import { tweened } from 'svelte/motion';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import { t } from 'svelte-i18n'; import { t } from 'svelte-i18n';
import { locale } from '$lib/stores/preferences.store';
const parseIndex = (s: string | null, max: number | null) => const parseIndex = (s: string | null, max: number | null) =>
Math.max(Math.min(Number.parseInt(s ?? '') || 0, max ?? 0), 0); Math.max(Math.min(Number.parseInt(s ?? '') || 0, max ?? 0), 0);
@ -201,7 +202,7 @@
<div> <div>
<p class="text-small"> <p class="text-small">
{assetIndex + 1}/{currentMemory.assets.length} {(assetIndex + 1).toLocaleString($locale)}/{currentMemory.assets.length.toLocaleString($locale)}
</p> </p>
</div> </div>
</div> </div>

View File

@ -9,6 +9,7 @@
import CircleIconButton from '../elements/buttons/circle-icon-button.svelte'; import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
import { mdiCog, mdiWindowMinimize, mdiCancel, mdiCloudUploadOutline } from '@mdi/js'; import { mdiCog, mdiWindowMinimize, mdiCancel, mdiCloudUploadOutline } from '@mdi/js';
import { t } from 'svelte-i18n'; import { t } from 'svelte-i18n';
import { locale } from '$lib/stores/preferences.store';
let showDetail = false; let showDetail = false;
let showOptions = false; let showOptions = false;
@ -73,9 +74,14 @@
})} })}
</p> </p>
<p class="immich-form-label text-xs"> <p class="immich-form-label text-xs">
{$t('upload_status_uploaded')} <span class="text-immich-success">{$successCounter}</span> - {$t('upload_status_uploaded')}
{$t('upload_status_errors')} <span class="text-immich-error">{$errorCounter}</span> - <span class="text-immich-success">{$successCounter.toLocaleString($locale)}</span>
{$t('upload_status_duplicates')} <span class="text-immich-warning">{$duplicateCounter}</span> -
{$t('upload_status_errors')}
<span class="text-immich-error">{$errorCounter.toLocaleString($locale)}</span>
-
{$t('upload_status_duplicates')}
<span class="text-immich-warning">{$duplicateCounter.toLocaleString($locale)}</span>
</p> </p>
</div> </div>
<div class="flex flex-col items-end"> <div class="flex flex-col items-end">
@ -139,7 +145,7 @@
on:click={() => (showDetail = true)} on:click={() => (showDetail = true)}
class="absolute -left-4 -top-4 flex h-10 w-10 place-content-center place-items-center rounded-full bg-immich-primary p-5 text-xs text-gray-200" class="absolute -left-4 -top-4 flex h-10 w-10 place-content-center place-items-center rounded-full bg-immich-primary p-5 text-xs text-gray-200"
> >
{$remainingUploads} {$remainingUploads.toLocaleString($locale)}
</button> </button>
{#if $hasError} {#if $hasError}
<button <button
@ -148,7 +154,7 @@
on:click={() => (showDetail = true)} on:click={() => (showDetail = true)}
class="absolute -right-4 -top-4 flex h-10 w-10 place-content-center place-items-center rounded-full bg-immich-error p-5 text-xs text-gray-200" class="absolute -right-4 -top-4 flex h-10 w-10 place-content-center place-items-center rounded-full bg-immich-error p-5 text-xs text-gray-200"
> >
{$errorCounter} {$errorCounter.toLocaleString($locale)}
</button> </button>
{/if} {/if}
<button <button

View File

@ -25,7 +25,7 @@
"add_to_shared_album": "Add to shared album", "add_to_shared_album": "Add to shared album",
"added_to_archive": "Added to archive", "added_to_archive": "Added to archive",
"added_to_favorites": "Added to favorites", "added_to_favorites": "Added to favorites",
"added_to_favorites_count": "Added {count} to favorites", "added_to_favorites_count": "Added {count, number} to favorites",
"admin": { "admin": {
"add_exclusion_pattern_description": "Add exclusion patterns. Globbing using *, **, and ? is supported. To ignore all files in any directory named \"Raw\", use \"**/Raw/**\". To ignore all files ending in \".tif\", use \"**/*.tif\". To ignore an absolute path, use \"/path/to/ignore/**\".", "add_exclusion_pattern_description": "Add exclusion patterns. Globbing using *, **, and ? is supported. To ignore all files in any directory named \"Raw\", use \"**/Raw/**\". To ignore all files ending in \".tif\", use \"**/*.tif\". To ignore an absolute path, use \"/path/to/ignore/**\".",
"authentication_settings": "Authentication Settings", "authentication_settings": "Authentication Settings",
@ -1151,7 +1151,7 @@
"total_usage": "Total usage", "total_usage": "Total usage",
"trash": "Trash", "trash": "Trash",
"trash_all": "Trash All", "trash_all": "Trash All",
"trash_count": "Trash {count}", "trash_count": "Trash {count, number}",
"trash_delete_asset": "Trash/Delete Asset", "trash_delete_asset": "Trash/Delete Asset",
"trash_no_results_message": "Trashed photos and videos will show up here.", "trash_no_results_message": "Trashed photos and videos will show up here.",
"trashed_items_will_be_permanently_deleted_after": "Trashed items will be permanently deleted after {days, plural, one {# day} other {# days}}.", "trashed_items_will_be_permanently_deleted_after": "Trashed items will be permanently deleted after {days, plural, one {# day} other {# days}}.",
@ -1179,7 +1179,7 @@
"upload": "Upload", "upload": "Upload",
"upload_concurrency": "Upload concurrency", "upload_concurrency": "Upload concurrency",
"upload_errors": "Upload completed with {count, plural, one {# error} other {# errors}}, refresh the page to see new upload assets.", "upload_errors": "Upload completed with {count, plural, one {# error} other {# errors}}, refresh the page to see new upload assets.",
"upload_progress": "Remaining {remaining} - Processed {processed}/{total}", "upload_progress": "Remaining {remaining, number} - Processed {processed, number}/{total, number}",
"upload_skipped_duplicates": "Skipped {count, plural, one {# duplicate asset} other {# duplicate assets}}", "upload_skipped_duplicates": "Skipped {count, plural, one {# duplicate asset} other {# duplicate assets}}",
"upload_status_duplicates": "Duplicates", "upload_status_duplicates": "Duplicates",
"upload_status_errors": "Errors", "upload_status_errors": "Errors",

View File

@ -18,6 +18,7 @@
import { mdiKeyboard } from '@mdi/js'; import { mdiKeyboard } from '@mdi/js';
import { mdiCheckOutline, mdiTrashCanOutline } from '@mdi/js'; import { mdiCheckOutline, mdiTrashCanOutline } from '@mdi/js';
import Icon from '$lib/components/elements/icon.svelte'; import Icon from '$lib/components/elements/icon.svelte';
import { locale } from '$lib/stores/preferences.store';
export let data: PageData; export let data: PageData;
export let isShowKeyboardShortcut = false; export let isShowKeyboardShortcut = false;
@ -142,7 +143,7 @@
}; };
</script> </script>
<UserPageLayout title={data.meta.title + ` (${data.duplicates.length})`} scrollbar={true}> <UserPageLayout title={data.meta.title + ` (${data.duplicates.length.toLocaleString($locale)})`} scrollbar={true}>
<div class="flex place-items-center gap-2" slot="buttons"> <div class="flex place-items-center gap-2" slot="buttons">
<LinkButton on:click={() => handleDeduplicateAll()} disabled={!hasDuplicates}> <LinkButton on:click={() => handleDeduplicateAll()} disabled={!hasDuplicates}>
<div class="flex place-items-center gap-2 text-sm"> <div class="flex place-items-center gap-2 text-sm">

View File

@ -35,6 +35,7 @@
import { dialogController } from '$lib/components/shared-components/dialog/dialog'; import { dialogController } from '$lib/components/shared-components/dialog/dialog';
import { t } from 'svelte-i18n'; import { t } from 'svelte-i18n';
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte'; import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
import { locale } from '$lib/stores/preferences.store';
export let data: PageData; export let data: PageData;
@ -335,7 +336,7 @@
</td> </td>
{:else} {:else}
<td class=" text-ellipsis px-4 text-sm"> <td class=" text-ellipsis px-4 text-sm">
{totalCount[index]} {totalCount[index].toLocaleString($locale)}
</td> </td>
<td class=" text-ellipsis px-4 text-sm">{diskUsage[index]} {diskUsageUnit[index]}</td> <td class=" text-ellipsis px-4 text-sm">{diskUsage[index]} {diskUsageUnit[index]}</td>
{/if} {/if}

View File

@ -17,6 +17,7 @@
import { mdiCheckAll, mdiContentCopy, mdiDownload, mdiRefresh, mdiWrench } from '@mdi/js'; import { mdiCheckAll, mdiContentCopy, mdiDownload, mdiRefresh, mdiWrench } from '@mdi/js';
import type { PageData } from './$types'; import type { PageData } from './$types';
import { t } from 'svelte-i18n'; import { t } from 'svelte-i18n';
import { locale } from '$lib/stores/preferences.store';
export let data: PageData; export let data: PageData;
@ -221,7 +222,10 @@
<tr class="flex w-full place-items-center p-2 md:p-5"> <tr class="flex w-full place-items-center p-2 md:p-5">
<th class="w-full text-sm place-items-center font-medium flex justify-between" colspan="2"> <th class="w-full text-sm place-items-center font-medium flex justify-between" colspan="2">
<div class="px-3"> <div class="px-3">
<p>{$t('matches').toUpperCase()} {matches.length > 0 ? `(${matches.length})` : ''}</p> <p>
{$t('matches').toUpperCase()}
{matches.length > 0 ? `(${matches.length.toLocaleString($locale)})` : ''}
</p>
<p class="text-gray-600 dark:text-gray-300 mt-1">{$t('admin.these_files_matched_by_checksum')}</p> <p class="text-gray-600 dark:text-gray-300 mt-1">{$t('admin.these_files_matched_by_checksum')}</p>
</div> </div>
</th> </th>
@ -255,7 +259,10 @@
<tr class="flex w-full place-items-center p-1 md:p-5"> <tr class="flex w-full place-items-center p-1 md:p-5">
<th class="w-full text-sm font-medium justify-between place-items-center flex" colspan="2"> <th class="w-full text-sm font-medium justify-between place-items-center flex" colspan="2">
<div class="px-3"> <div class="px-3">
<p>{$t('admin.offline_paths').toUpperCase()} {orphans.length > 0 ? `(${orphans.length})` : ''}</p> <p>
{$t('admin.offline_paths').toUpperCase()}
{orphans.length > 0 ? `(${orphans.length.toLocaleString($locale)})` : ''}
</p>
<p class="text-gray-600 dark:text-gray-300 mt-1"> <p class="text-gray-600 dark:text-gray-300 mt-1">
{$t('admin.offline_paths_description')} {$t('admin.offline_paths_description')}
</p> </p>
@ -293,7 +300,10 @@
<tr class="flex w-full place-items-center p-2 md:p-5"> <tr class="flex w-full place-items-center p-2 md:p-5">
<th class="w-full text-sm font-medium place-items-center flex justify-between" colspan="2"> <th class="w-full text-sm font-medium place-items-center flex justify-between" colspan="2">
<div class="px-3"> <div class="px-3">
<p>{$t('admin.untracked_files').toUpperCase()} {extras.length > 0 ? `(${extras.length})` : ''}</p> <p>
{$t('admin.untracked_files').toUpperCase()}
{extras.length > 0 ? `(${extras.length.toLocaleString($locale)})` : ''}
</p>
<p class="text-gray-600 dark:text-gray-300 mt-1"> <p class="text-gray-600 dark:text-gray-300 mt-1">
{$t('admin.untracked_files_description')} {$t('admin.untracked_files_description')}
</p> </p>