diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index a95d2e78d4..15e5971b5c 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -2163,6 +2163,7 @@ else: class BannerModel(BaseModel): + i18n: dict[str, dict[str, str]] | None = None id: str type: str title: str | None = None diff --git a/backend/open_webui/models/skills.py b/backend/open_webui/models/skills.py index cff4778394..9aabdb5bf1 100644 --- a/backend/open_webui/models/skills.py +++ b/backend/open_webui/models/skills.py @@ -33,6 +33,7 @@ class Skill(Base): class SkillMeta(BaseModel): + i18n: dict[str, dict[str, str]] | None = None tags: Optional[list[str]] = [] diff --git a/backend/open_webui/models/tools.py b/backend/open_webui/models/tools.py index ddb4d78581..60c4ab9f6d 100644 --- a/backend/open_webui/models/tools.py +++ b/backend/open_webui/models/tools.py @@ -34,6 +34,7 @@ class Tool(Base): # database table definition class ToolMeta(BaseModel): + i18n: dict[str, dict[str, str]] | None = None description: str | None = None manifest: dict | None = {} has_user_valves: bool = False diff --git a/src/lib/components/admin/Functions.svelte b/src/lib/components/admin/Functions.svelte index 82df84b0d8..d7f6547042 100644 --- a/src/lib/components/admin/Functions.svelte +++ b/src/lib/components/admin/Functions.svelte @@ -1,4 +1,5 @@ - { - addModelHandler(e.detail); - }} -/> +
{ - editModelHandler(e.detail, index); - }} + onEdit={(model) => editModelHandler(model, index)} on:delete={(e) => { deleteModelHandler(index); }} diff --git a/src/lib/components/admin/Settings/Evaluations/ArenaModelModal.svelte b/src/lib/components/admin/Settings/Evaluations/ArenaModelModal.svelte index 7f2667e35d..f27a0c69d0 100644 --- a/src/lib/components/admin/Settings/Evaluations/ArenaModelModal.svelte +++ b/src/lib/components/admin/Settings/Evaluations/ArenaModelModal.svelte @@ -14,13 +14,19 @@ import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte'; import XMark from '$lib/components/icons/XMark.svelte'; import { WEBUI_BASE_URL } from '$lib/constants'; + import LanguageModeSelect from '$lib/components/common/LanguageModeSelect.svelte'; + import LocalizedField from '$lib/components/common/LocalizedField.svelte'; export let show = false; export let edit = false; export let model = null; + export let onSubmit = async (_model) => true; let name = ''; + let locale = ''; + let translations = {}; + let originalMeta = {}; let id = ''; $: if (name) { @@ -60,7 +66,7 @@ selectedModelId = ''; }; - const submitHandler = () => { + const submitHandler = async () => { loading = true; if (!name || !id) { @@ -72,7 +78,6 @@ if (!edit) { if ($models.find((model) => model.name === name)) { loading = false; - name = ''; toast.error($i18n.t('Model name already exists, please choose a different one')); return; } @@ -82,6 +87,8 @@ id: id, name: name, meta: { + ...originalMeta, + i18n: translations, profile_image_url: profileImageUrl, description: description || null, model_ids: modelIds.length > 0 ? modelIds : null, @@ -90,8 +97,14 @@ } }; - dispatch('submit', model); - loading = false; + try { + if ((await onSubmit(model)) === false) return; + } catch (error) { + toast.error(String(error)); + return; + } finally { + loading = false; + } show = false; name = ''; @@ -106,6 +119,16 @@ }; const initModel = () => { + locale = ''; + originalMeta = structuredClone(model?.meta ?? {}); + translations = structuredClone(model?.meta?.i18n ?? {}); + if (!model) { + name = ''; + id = ''; + description = ''; + modelIds = []; + accessGrants = []; + } if (model) { name = model.name; id = model.id; @@ -163,6 +186,12 @@ }} >
+
+ +
{$i18n.t('Name')}
-
@@ -286,12 +315,12 @@
{$i18n.t('Description')}
-
diff --git a/src/lib/components/admin/Settings/Evaluations/Model.svelte b/src/lib/components/admin/Settings/Evaluations/Model.svelte index 3eaaa7e4fc..8d3ad3722d 100644 --- a/src/lib/components/admin/Settings/Evaluations/Model.svelte +++ b/src/lib/components/admin/Settings/Evaluations/Model.svelte @@ -1,4 +1,5 @@ @@ -15,9 +17,7 @@ bind:show={showModel} edit={true} {model} - on:submit={async (e) => { - dispatch('edit', e.detail); - }} + onSubmit={onEdit} on:delete={async () => { dispatch('delete'); }} @@ -29,14 +29,14 @@
{model.name}
- {model.name} + {resolveLocalizedResource(model, $i18n.language)}
diff --git a/src/lib/components/admin/Settings/General.svelte b/src/lib/components/admin/Settings/General.svelte index b54863a1a4..92a7ddc605 100644 --- a/src/lib/components/admin/Settings/General.svelte +++ b/src/lib/components/admin/Settings/General.svelte @@ -7,6 +7,7 @@ import { getBanners, setBanners } from '$lib/apis/configs'; import InterfaceSettings from '$lib/components/common/InterfaceSettings.svelte'; import SettingsSelect from '$lib/components/common/SettingsSelect.svelte'; + import LanguageModeSelect from '$lib/components/common/LanguageModeSelect.svelte'; import Switch from '$lib/components/common/Switch.svelte'; import Tooltip from '$lib/components/common/Tooltip.svelte'; import { WEBUI_BUILD_HASH, WEBUI_VERSION } from '$lib/constants'; @@ -38,6 +39,7 @@ let showUserUiDefaults = false; let banners: Banner[] = []; + let bannerLocale = ''; const inputClass = 'w-full h-7 rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 text-xs text-gray-700 outline-hidden transition-colors placeholder:text-gray-300 focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:placeholder:text-gray-700 dark:focus:border-blue-500'; const textareaClass = @@ -454,39 +456,40 @@
-
-
-
{$i18n.t('Banners')}
-
- {$i18n.t('Create announcements shown to users in the app.')} -
+
+
{$i18n.t('Banners')}
+
+ {#if banners.length > 0} + + {/if} +
- -
- - +
+ {$i18n.t('Create announcements shown to users in the app.')} +
+
{/if} diff --git a/src/lib/components/admin/Settings/Interface/Banners.svelte b/src/lib/components/admin/Settings/Interface/Banners.svelte index dd73555cb5..6d70798142 100644 --- a/src/lib/components/admin/Settings/Interface/Banners.svelte +++ b/src/lib/components/admin/Settings/Interface/Banners.svelte @@ -1,6 +1,6 @@ -
+
{#each banners as banner, bannerIdx (banner.id)} -