diff --git a/src/lib/components/admin/Analytics/AnalyticsModelModal.svelte b/src/lib/components/admin/Analytics/AnalyticsModelModal.svelte index 616a6e37b6..ca929870e4 100644 --- a/src/lib/components/admin/Analytics/AnalyticsModelModal.svelte +++ b/src/lib/components/admin/Analytics/AnalyticsModelModal.svelte @@ -25,7 +25,7 @@ const TIME_RANGES: { key: TimeRange; label: string; days: number }[] = [ { key: '30d', label: '30D', days: 30 }, { key: '1y', label: '1Y', days: 365 }, - { key: 'all', label: $i18n.t('All'), days: 0 } + { key: 'all', label: 'All', days: 0 } ]; let selectedRange: TimeRange = '30d'; let history: Array<{ date: string; won: number; lost: number }> = []; @@ -239,7 +239,7 @@ : 'text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200'}" on:click={() => selectRange(range.key)} > - {range.label} + {range.key === 'all' ? $i18n.t('All') : range.label} {/each} diff --git a/src/lib/components/admin/Analytics/ChartLine.svelte b/src/lib/components/admin/Analytics/ChartLine.svelte index 12de0eb36f..c78f6dccca 100644 --- a/src/lib/components/admin/Analytics/ChartLine.svelte +++ b/src/lib/components/admin/Analytics/ChartLine.svelte @@ -1,4 +1,6 @@
@@ -15,9 +15,9 @@ diff --git a/src/lib/components/chat/MessageInput/InputVariablesModal.svelte b/src/lib/components/chat/MessageInput/InputVariablesModal.svelte index c89f8c8d77..9d9c679e74 100644 --- a/src/lib/components/chat/MessageInput/InputVariablesModal.svelte +++ b/src/lib/components/chat/MessageInput/InputVariablesModal.svelte @@ -10,11 +10,11 @@ import Spinner from '$lib/components/common/Spinner.svelte'; import MapSelector from '$lib/components/common/Valves/MapSelector.svelte'; - const i18n = getContext('i18n'); + const i18n = getContext('i18n'); export let show = false; export let variables = {}; - export let title = $i18n.t('Input Variables'); + export let title: string | undefined = undefined; export let onSave = (e) => {}; @@ -81,7 +81,7 @@
- {title} + {title ?? $i18n.t('Input Variables')}
{$i18n.t('Note')}
diff --git a/src/lib/components/chat/Messages/MultiResponseMessages.svelte b/src/lib/components/chat/Messages/MultiResponseMessages.svelte index e9f6b83e29..0c46a3d4bb 100644 --- a/src/lib/components/chat/Messages/MultiResponseMessages.svelte +++ b/src/lib/components/chat/Messages/MultiResponseMessages.svelte @@ -24,7 +24,7 @@ formatMessageTimestampFull, getDeepestChildId } from '$lib/utils'; - const i18n = getContext('i18n'); + const i18n = getContext('i18n'); export let chatId; export let history; @@ -421,14 +421,14 @@ >
diff --git a/src/lib/components/chat/Messages/ResponseMessage.svelte b/src/lib/components/chat/Messages/ResponseMessage.svelte index 103753f9c4..8ad461e075 100644 --- a/src/lib/components/chat/Messages/ResponseMessage.svelte +++ b/src/lib/components/chat/Messages/ResponseMessage.svelte @@ -933,14 +933,14 @@
@@ -1606,14 +1606,14 @@ {#if message.timestamp} {/if} diff --git a/src/lib/components/chat/Messages/UserMessage.svelte b/src/lib/components/chat/Messages/UserMessage.svelte index 90c2f2eecd..9b4dd7cdee 100644 --- a/src/lib/components/chat/Messages/UserMessage.svelte +++ b/src/lib/components/chat/Messages/UserMessage.svelte @@ -22,7 +22,7 @@ import DeleteConfirmDialog from '$lib/components/common/ConfirmDialog.svelte'; import SubagentResultRow from './SubagentResultRow.svelte'; - const i18n = getContext('i18n'); + const i18n = getContext('i18n'); export let user; export let chatId; @@ -448,7 +448,7 @@ className="flex self-center {($settings?.chatBubble ?? true) && isOwn ? 'mr-1' : 'order-last'}" - content={formatMessageTimestampFull(message.timestamp * 1000)} + content={formatMessageTimestampFull(message.timestamp * 1000, $i18n.language)} placement="bottom" > {/if} diff --git a/src/lib/components/chat/ModelSelector/ModelItem.svelte b/src/lib/components/chat/ModelSelector/ModelItem.svelte index dad2ab33e5..f6a40325f3 100644 --- a/src/lib/components/chat/ModelSelector/ModelItem.svelte +++ b/src/lib/components/chat/ModelSelector/ModelItem.svelte @@ -18,7 +18,7 @@ import Tag from '$lib/components/icons/Tag.svelte'; import Label from '$lib/components/icons/Label.svelte'; - const i18n = getContext('i18n'); + const i18n = getContext('i18n'); export let selectedModelIdx: number = -1; export let item: any = {}; @@ -171,7 +171,9 @@ content={item.model.ollama?.expires_at && new Date(item.model.ollama?.expires_at * 1000) > new Date() ? `${$i18n.t('Unloads {{FROM_NOW}}', { - FROM_NOW: dayjs(item.model.ollama?.expires_at * 1000).fromNow() + FROM_NOW: dayjs(item.model.ollama?.expires_at * 1000) + .locale($i18n.language) + .fromNow() })}` : `${$i18n.t('Loaded')}`} className="self-end" diff --git a/src/lib/components/chat/ModelSelector/Selector.svelte b/src/lib/components/chat/ModelSelector/Selector.svelte index 5241dd6be5..683acd5540 100644 --- a/src/lib/components/chat/ModelSelector/Selector.svelte +++ b/src/lib/components/chat/ModelSelector/Selector.svelte @@ -62,9 +62,9 @@ export let compareEnabled = false; export let multipleEnabled = false; export let disabled = false; - export let placeholder = $i18n.t('Select a model'); + export let placeholder: string | undefined = undefined; export let searchEnabled = true; - export let searchPlaceholder = $i18n.t('Search a model'); + export let searchPlaceholder: string | undefined = undefined; export let selectionOnly = false; export let includeHidden = false; @@ -195,7 +195,7 @@ ? compareEnabled && selectedCount > 1 ? `${selectedModel.label} +${selectedCount - 1}` : selectedModel.label - : placeholder; + : (placeholder ?? $i18n.t('Select a model')); let searchValue = ''; @@ -226,7 +226,7 @@ const getProviderPoolKey = (connection, model: string) => `${connection.provider}:${connection.idx}:${model}`; - const fuse = new Fuse( + $: fuse = new Fuse( items.map((item) => { const _item = { ...item, @@ -242,26 +242,6 @@ } ); - const updateFuse = () => { - if (fuse) { - fuse.setCollection( - items.map((item) => { - const _item = { - ...item, - modelName: resolveLocalizedModelName(item.model, $i18n.language), - tags: (item.model?.tags ?? []).map((tag) => tag.name).join(' '), - desc: resolveLocalizedModelDescription(item.model, $i18n.language) - }; - return _item; - }) - ); - } - }; - - $: if (items) { - updateFuse(); - } - $: filteredItems = ( searchValue ? fuse @@ -913,7 +893,7 @@ : 'outline-hidden focus:outline-hidden'}" aria-label={selectedModel ? $i18n.t('Selected model: {{modelName}}', { modelName: triggerLabel }) - : placeholder} + : (placeholder ?? $i18n.t('Select a model'))} aria-haspopup="listbox" aria-expanded={show} id="model-selector-{id}-button" @@ -960,7 +940,7 @@ id="model-search-input" bind:value={searchValue} class="w-full bg-transparent text-[0.8125rem] font-normal outline-hidden placeholder:text-gray-400 dark:placeholder:text-gray-500" - placeholder={searchPlaceholder} + placeholder={searchPlaceholder ?? $i18n.t('Search a model')} autocomplete="off" aria-label={$i18n.t('Search In Models')} on:keydown={(e) => { diff --git a/src/lib/components/chat/Placeholder/ChatList.svelte b/src/lib/components/chat/Placeholder/ChatList.svelte index 2558c9d282..084cf8ad6f 100644 --- a/src/lib/components/chat/Placeholder/ChatList.svelte +++ b/src/lib/components/chat/Placeholder/ChatList.svelte @@ -3,6 +3,7 @@ import type { Writable } from 'svelte/store'; type ChatListI18n = { + language: string; t: (key: string, options?: Record) => string; }; @@ -250,7 +251,9 @@ diff --git a/src/lib/components/chat/Settings/ArchivedChats.svelte b/src/lib/components/chat/Settings/ArchivedChats.svelte index 6a3373e551..097db901d6 100644 --- a/src/lib/components/chat/Settings/ArchivedChats.svelte +++ b/src/lib/components/chat/Settings/ArchivedChats.svelte @@ -330,14 +330,16 @@ class="hidden w-24 shrink-0 self-center justify-end text-gray-400 dark:text-gray-600 sm:flex" > {$i18n.t( - dayjs(chat?.updated_at * 1000).calendar(null, { - sameDay: '[Today]', - nextDay: '[Tomorrow]', - nextWeek: 'dddd', - lastDay: '[Yesterday]', - lastWeek: '[Last] dddd', - sameElse: 'L' - }) + dayjs(chat?.updated_at * 1000) + .locale($i18n.language) + .calendar(null, { + sameDay: '[Today]', + nextDay: '[Tomorrow]', + nextWeek: 'dddd', + lastDay: '[Yesterday]', + lastWeek: '[Last] dddd', + sameElse: 'L' + }) )}
diff --git a/src/lib/components/chat/Settings/Notifications.svelte b/src/lib/components/chat/Settings/Notifications.svelte index c5171b75c9..a00e42d393 100644 --- a/src/lib/components/chat/Settings/Notifications.svelte +++ b/src/lib/components/chat/Settings/Notifications.svelte @@ -29,13 +29,13 @@ let events: { event: string; label: string; description?: string }[] = [ { event: 'chat.finished', - label: $i18n.t('Chat finished'), - description: $i18n.t('A chat run finished successfully.') + label: 'Chat finished', + description: 'A chat run finished successfully.' }, { event: 'chat.failed', - label: $i18n.t('Chat failed'), - description: $i18n.t('A chat run failed.') + label: 'Chat failed', + description: 'A chat run failed.' } ]; let loadingTargets = false; @@ -249,7 +249,7 @@ {#each targets as target, index} {@const alertLabels = events .filter((event) => target.events.includes(event.event)) - .map((event) => event.label) + .map((event) => $i18n.t(event.label)) .join(', ')}
@@ -388,7 +388,7 @@ type="button" on:click={() => toggleFormEvent(event.event)} > - {event.label} + {$i18n.t(event.label)} {/each}
diff --git a/src/lib/components/chat/Settings/Usage.svelte b/src/lib/components/chat/Settings/Usage.svelte index 831ba565da..2a1bb97597 100644 --- a/src/lib/components/chat/Settings/Usage.svelte +++ b/src/lib/components/chat/Settings/Usage.svelte @@ -123,9 +123,9 @@ ]; const heatmapModes: Array<{ value: HeatmapMode; label: string }> = [ - { value: 'daily', label: $i18n.t('Daily') }, - { value: 'weekly', label: $i18n.t('Weekly') }, - { value: 'cumulative', label: $i18n.t('Cumulative') } + { value: 'daily', label: 'Daily' }, + { value: 'weekly', label: 'Weekly' }, + { value: 'cumulative', label: 'Cumulative' } ]; const loadUsage = async () => { @@ -197,7 +197,9 @@ } labels.push({ - label: new Date(`${entry.date}T00:00:00`).toLocaleString(undefined, { month: 'short' }), + label: new Date(`${entry.date}T00:00:00`).toLocaleString($i18n.language, { + month: 'short' + }), column, span: Math.min(MIN_MONTH_LABEL_GAP, columns - column + 1) }); diff --git a/src/lib/components/common/ChatList.svelte b/src/lib/components/common/ChatList.svelte index 35df2e0c3e..52759f63eb 100644 --- a/src/lib/components/common/ChatList.svelte +++ b/src/lib/components/common/ChatList.svelte @@ -10,7 +10,7 @@ dayjs.extend(calendar); - const i18n = getContext('i18n'); + const i18n = getContext('i18n'); type SortKey = 'title' | 'updated_at' | 'user_name'; export let chatList: Array<{ @@ -161,12 +161,14 @@
diff --git a/src/lib/components/common/CodeEditor.svelte b/src/lib/components/common/CodeEditor.svelte index a9078d8b21..81ee22ef2d 100644 --- a/src/lib/components/common/CodeEditor.svelte +++ b/src/lib/components/common/CodeEditor.svelte @@ -22,7 +22,7 @@ import { user } from '$lib/stores'; const dispatch = createEventDispatcher(); - const i18n = getContext('i18n'); + const i18n = getContext('i18n'); export let boilerplate = ''; export let value = ''; @@ -88,6 +88,12 @@ let isDarkMode = false; let editorTheme = new Compartment(); let editorLanguage = new Compartment(); + const editorPlaceholder = new Compartment(); + $: if (codeEditor) { + codeEditor.dispatch({ + effects: editorPlaceholder.reconfigure(placeholder($i18n.t('Enter your code here...'))) + }); + } const getLang = async () => { const language = languages.find((l) => l.alias.includes(lang)); @@ -210,7 +216,7 @@ print("${endTag}") basicSetup, keymap.of([{ key: 'Tab', run: acceptCompletion }, indentWithTab]), indentUnit.of(' '), - placeholder($i18n.t('Enter your code here...')), + editorPlaceholder.of([]), EditorView.updateListener.of((e) => { if (e.docChanged) { _value = e.state.doc.toString(); diff --git a/src/lib/components/common/Collapsible.svelte b/src/lib/components/common/Collapsible.svelte index 8bfb343697..bfbacd3175 100644 --- a/src/lib/components/common/Collapsible.svelte +++ b/src/lib/components/common/Collapsible.svelte @@ -3,7 +3,7 @@ import { v4 as uuidv4 } from 'uuid'; import { getContext } from 'svelte'; - const i18n = getContext('i18n'); + const i18n = getContext('i18n'); import dayjs from '$lib/dayjs'; import duration from 'dayjs/plugin/duration'; @@ -12,23 +12,6 @@ dayjs.extend(duration); dayjs.extend(relativeTime); - async function loadLocale(locales) { - if (!locales || !Array.isArray(locales)) { - return; - } - for (const locale of locales) { - try { - dayjs.locale(locale); - break; // Stop after successfully loading the first available locale - } catch (error) { - console.error(`Could not load locale '${locale}':`, error); - } - } - } - - // Assuming $i18n.languages is an array of language codes - $: loadLocale($i18n.languages); - import { slide } from 'svelte/transition'; import { quintOut } from 'svelte/easing'; @@ -103,7 +86,10 @@ })} {:else} {$i18n.t('Thought for {{DURATION}}', { - DURATION: dayjs.duration(attributes.duration, 'seconds').humanize() + DURATION: dayjs + .duration(attributes.duration, 'seconds') + .locale($i18n.language) + .humanize() })} {/if} {:else if attributes?.done === 'true' || messageDone} diff --git a/src/lib/components/common/ConfirmDialog.svelte b/src/lib/components/common/ConfirmDialog.svelte index c1d2a5e0da..f6012dbc36 100644 --- a/src/lib/components/common/ConfirmDialog.svelte +++ b/src/lib/components/common/ConfirmDialog.svelte @@ -6,7 +6,7 @@ import { settings } from '$lib/stores'; import { matchKeybinding, Shortcut } from '$lib/shortcuts'; - const i18n = getContext('i18n'); + const i18n = getContext('i18n'); const dispatch = createEventDispatcher(); import { fade } from 'svelte/transition'; @@ -18,8 +18,8 @@ export let title = ''; export let message = ''; - export let cancelLabel = $i18n.t('Cancel'); - export let confirmLabel = $i18n.t('Confirm'); + export let cancelLabel: string | undefined = undefined; + export let confirmLabel: string | undefined = undefined; export let onConfirm = () => {}; @@ -197,7 +197,7 @@ }} type="button" > - {cancelLabel} + {cancelLabel ?? $i18n.t('Cancel')}
diff --git a/src/lib/components/common/DocxPreview.svelte b/src/lib/components/common/DocxPreview.svelte index eb49021be8..eb95852f72 100644 --- a/src/lib/components/common/DocxPreview.svelte +++ b/src/lib/components/common/DocxPreview.svelte @@ -20,7 +20,7 @@ let containerEl: HTMLDivElement; let styleEl: HTMLDivElement; let fallbackHtml = ''; - let error = ''; + let error = false; let loading = false; let renderId = 0; let mounted = false; @@ -36,7 +36,7 @@ if (containerEl) containerEl.innerHTML = ''; if (styleEl) styleEl.innerHTML = ''; fallbackHtml = ''; - error = ''; + error = false; }; const updateFitScale = () => { @@ -157,7 +157,7 @@ fallbackHtml = DOMPurify.sanitize(await docxToHtml(arrayBuffer.slice(0))); } catch (fallbackError) { console.error('Error rendering DOCX fallback:', fallbackError); - error = $i18n.t('Failed to load DOCX file. Please try downloading it instead.'); + error = true; } } finally { if (currentRender === renderId) loading = false; @@ -200,7 +200,9 @@ {/if} {#if error} -
{error}
+
+ {$i18n.t('Failed to load DOCX file. Please try downloading it instead.')} +
{:else if fallbackHtml}
('i18n'); export let align = 'center'; export let className = ''; export let value = ''; - export let placeholder = 'Select an option'; - export let items = [ - { value: 'new', label: $i18n.t('New') }, - { value: 'top', label: $i18n.t('Top') } - ]; - + export let placeholder: string | undefined = undefined; + export let items: { value: string; label: string }[] | undefined = undefined; export let onChange: (value: string) => void = () => {}; let selectComponent; @@ -25,8 +21,11 @@ bind:this={selectComponent} bind:value bind:open - {items} - {placeholder} + items={items ?? [ + { value: 'new', label: $i18n.t('New') }, + { value: 'top', label: $i18n.t('Top') } + ]} + placeholder={placeholder ?? $i18n.t('Select an option')} {align} triggerClass={className ? className diff --git a/src/lib/components/common/FileItemModal.svelte b/src/lib/components/common/FileItemModal.svelte index a19e5246d5..a4f4606e4c 100644 --- a/src/lib/components/common/FileItemModal.svelte +++ b/src/lib/components/common/FileItemModal.svelte @@ -49,17 +49,17 @@ let excelSheetNames: string[] = []; let selectedSheet = ''; let excelHtml = ''; - let excelError = ''; + let excelError = false; let rowCount = 0; // DOCX state let docxData: ArrayBuffer | null = null; - let docxError = ''; + let docxError = false; // PPTX state let pptxSlides: string[] = []; let pptxCurrentSlide = 0; - let pptxError = ''; + let pptxError = false; let panzoomRef: PanzoomContainer; const resetImageView = () => { @@ -140,7 +140,7 @@ const loadExcelContent = async () => { try { - excelError = ''; + excelError = false; const [arrayBuffer, { read }] = await Promise.all([ getFileContentById(item.id), import('xlsx') @@ -154,7 +154,7 @@ } } catch (error) { console.error('Error loading Excel/CSV file:', error); - excelError = $i18n.t('Failed to load Excel/CSV file. Please try downloading it instead.'); + excelError = true; } }; @@ -173,17 +173,17 @@ const loadDocxContent = async () => { try { - docxError = ''; + docxError = false; docxData = await getFileContentById(item.id); } catch (error) { console.error('Error loading DOCX file:', error); - docxError = $i18n.t('Failed to load DOCX file. Please try downloading it instead.'); + docxError = true; } }; const loadPptxContent = async () => { try { - pptxError = ''; + pptxError = false; const [arrayBuffer, { pptxToImages }] = await Promise.all([ getFileContentById(item.id), import('$lib/utils/pptxToHtml') @@ -193,7 +193,7 @@ pptxCurrentSlide = 0; } catch (error) { console.error('Error loading PPTX file:', error); - pptxError = $i18n.t('Failed to load PPTX file. Please try downloading it instead.'); + pptxError = true; } }; @@ -309,7 +309,9 @@ {#if item?.created_at}
- {dayjs(item.created_at * 1000).format('LL')} + {dayjs(item.created_at * 1000) + .locale($i18n.language) + .format('LL')}
{/if} {/if} @@ -519,7 +521,7 @@ {:else if isExcel} {#if excelError}
- {excelError} + {$i18n.t('Failed to load Excel/CSV file. Please try downloading it instead.')}
{:else} {#if excelSheetNames.length > 1} @@ -567,7 +569,9 @@
{:else if isDocx} {#if docxError} -
{docxError}
+
+ {$i18n.t('Failed to load DOCX file. Please try downloading it instead.')} +
{:else if docxData} {:else} @@ -575,7 +579,9 @@ {/if} {:else if isPptx} {#if pptxError} -
{pptxError}
+
+ {$i18n.t('Failed to load PPTX file. Please try downloading it instead.')} +
{:else if pptxSlides.length > 0} ('i18n'); const eventDispatch = createEventDispatcher(); import { Fragment, DOMParser } from 'prosemirror-model'; @@ -226,15 +226,15 @@ export let documentId = ''; export let className = 'input-prose min-h-fit h-full'; - export let placeholder = $i18n.t('Type here...'); - let _placeholder = placeholder; + export let placeholder: string | undefined = undefined; + let _placeholder = ''; - $: if (placeholder !== _placeholder) { - setPlaceholder(); + $: if ((placeholder ?? $i18n.t('Type here...')) !== _placeholder) { + setPlaceholder(placeholder ?? $i18n.t('Type here...')); } - const setPlaceholder = () => { - _placeholder = placeholder; + const setPlaceholder = (value: string) => { + _placeholder = value; if (editor) { editor?.view.dispatch(editor.state.tr); } diff --git a/src/lib/components/common/Selector.svelte b/src/lib/components/common/Selector.svelte index 80a82fb12c..90503a7fc5 100644 --- a/src/lib/components/common/Selector.svelte +++ b/src/lib/components/common/Selector.svelte @@ -1,13 +1,15 @@