Unverified Commit db0712ae authored by Danny Liu's avatar Danny Liu Committed by GitHub
Browse files

Merge branch 'dev' into feat/system-wide-theme

parents f1716f45 e414b9ea
<script lang="ts">
export let className = 'w-4 h-4';
export let strokeWidth = '1.5';
</script>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width={strokeWidth}
stroke="currentColor"
class={className}
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
/>
</svg>
<script lang="ts"> <script lang="ts">
import { getContext } from 'svelte';
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
import fileSaver from 'file-saver'; import fileSaver from 'file-saver';
const { saveAs } = fileSaver; const { saveAs } = fileSaver;
...@@ -9,6 +10,8 @@ ...@@ -9,6 +10,8 @@
import TagInput from '../common/Tags/TagInput.svelte'; import TagInput from '../common/Tags/TagInput.svelte';
import Tags from '../common/Tags.svelte'; import Tags from '../common/Tags.svelte';
const i18n = getContext('i18n');
export let initNewChat: Function; export let initNewChat: Function;
export let title: string = $WEBUI_NAME; export let title: string = $WEBUI_NAME;
export let shareEnabled: boolean = false; export let shareEnabled: boolean = false;
...@@ -26,7 +29,7 @@ ...@@ -26,7 +29,7 @@
const chat = (await getChatById(localStorage.token, $chatId)).chat; const chat = (await getChatById(localStorage.token, $chatId)).chat;
console.log('share', chat); console.log('share', chat);
toast.success('Redirecting you to OpenWebUI Community'); toast.success($i18n.t('Redirecting you to OpenWebUI Community'));
const url = 'https://openwebui.com'; const url = 'https://openwebui.com';
// const url = 'http://localhost:5173'; // const url = 'http://localhost:5173';
......
This diff is collapsed.
<script lang="ts">
import { DropdownMenu } from 'bits-ui';
import Dropdown from '$lib/components/common/Dropdown.svelte';
import GarbageBin from '$lib/components/icons/GarbageBin.svelte';
import Pencil from '$lib/components/icons/Pencil.svelte';
import Tooltip from '$lib/components/common/Tooltip.svelte';
export let renameHandler: Function;
export let deleteHandler: Function;
export let onClose: Function;
</script>
<Dropdown
on:change={(e) => {
if (e.detail === false) {
onClose();
}
}}
>
<Tooltip content="More">
<slot />
</Tooltip>
<div slot="content">
<DropdownMenu.Content
class="w-full max-w-[130px] rounded-lg px-1 py-1.5 border border-gray-700/50 z-50 bg-gray-850 text-white"
sideOffset={8}
side="bottom"
align="start"
>
<DropdownMenu.Item
class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer"
on:click={() => {
renameHandler();
}}
>
<Pencil strokeWidth="2" />
<div class="flex items-center">Rename</div>
</DropdownMenu.Item>
<DropdownMenu.Item
class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer"
on:click={() => {
deleteHandler();
}}
>
<GarbageBin strokeWidth="2" />
<div class="flex items-center">Delete</div>
</DropdownMenu.Item>
</DropdownMenu.Content>
</div>
</Dropdown>
<script lang="ts"> <script lang="ts">
import { onMount } from 'svelte'; import { onMount, getContext } from 'svelte';
const i18n = getContext('i18n');
export let messages = []; export let messages = [];
let textAreaElement: HTMLTextAreaElement; let textAreaElement: HTMLTextAreaElement;
...@@ -19,16 +21,20 @@ ...@@ -19,16 +21,20 @@
class="px-2 py-1 text-sm font-semibold uppercase min-w-[6rem] text-left dark:group-hover:bg-gray-800 rounded-lg transition" class="px-2 py-1 text-sm font-semibold uppercase min-w-[6rem] text-left dark:group-hover:bg-gray-800 rounded-lg transition"
on:click={() => { on:click={() => {
message.role = message.role === 'user' ? 'assistant' : 'user'; message.role = message.role === 'user' ? 'assistant' : 'user';
}}>{message.role}</button }}>{$i18n.t(message.role)}</button
> >
</div> </div>
<div class="flex-1"> <div class="flex-1">
<!-- $i18n.t('a user') -->
<!-- $i18n.t('an assistant') -->
<textarea <textarea
id="{message.role}-{idx}-textarea" id="{message.role}-{idx}-textarea"
bind:this={textAreaElement} bind:this={textAreaElement}
class="w-full bg-transparent outline-none rounded-lg p-2 text-sm resize-none overflow-hidden" class="w-full bg-transparent outline-none rounded-lg p-2 text-sm resize-none overflow-hidden"
placeholder="Enter {message.role === 'user' ? 'a user' : 'an assistant'} message here" placeholder={$i18n.t(`Enter {{role}} message here`, {
role: message.role === 'user' ? $i18n.t('a user') : $i18n.t('an assistant')
})}
rows="1" rows="1"
on:input={(e) => { on:input={(e) => {
textAreaElement.style.height = ''; textAreaElement.style.height = '';
...@@ -100,6 +106,6 @@ ...@@ -100,6 +106,6 @@
</svg> </svg>
</div> </div>
<div class=" text-sm font-medium">Add message</div> <div class=" text-sm font-medium">{$i18n.t('Add message')}</div>
</button> </button>
</div> </div>
import i18next from 'i18next';
import resourcesToBackend from 'i18next-resources-to-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
import type { i18n as i18nType } from 'i18next';
import { writable } from 'svelte/store';
const createI18nStore = (i18n: i18nType) => {
const i18nWritable = writable(i18n);
i18n.on('initialized', () => {
i18nWritable.set(i18n);
});
i18n.on('loaded', () => {
i18nWritable.set(i18n);
});
i18n.on('added', () => i18nWritable.set(i18n));
i18n.on('languageChanged', () => {
i18nWritable.set(i18n);
});
return i18nWritable;
};
const createIsLoadingStore = (i18n: i18nType) => {
const isLoading = writable(false);
// if loaded resources are empty || {}, set loading to true
i18n.on('loaded', (resources) => {
// console.log('loaded:', resources);
Object.keys(resources).length !== 0 && isLoading.set(false);
});
// if resources failed loading, set loading to true
i18n.on('failedLoading', () => {
isLoading.set(true);
});
return isLoading;
};
i18next
.use(
resourcesToBackend(
(language: string, namespace: string) => import(`./locales/${language}/${namespace}.json`)
)
)
.use(LanguageDetector)
.init({
debug: false,
detection: {
order: ['querystring', 'localStorage', 'navigator'],
caches: ['localStorage'],
lookupQuerystring: 'lang',
lookupLocalStorage: 'locale'
},
fallbackLng: {
default: ['en-US']
},
ns: 'translation',
returnEmptyString: false,
interpolation: {
escapeValue: false // not needed for svelte as it escapes by default
}
});
const i18n = createI18nStore(i18next);
const isLoadingStore = createIsLoadingStore(i18next);
export const getLanguages = async () => {
const languages = (await import(`./locales/languages.json`)).default;
return languages;
};
export default i18n;
export const isLoading = isLoadingStore;
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
[
{
"code": "en-US",
"title": "English (US)"
},
{
"code": "fa-IR",
"title": "فارسی (Farsi)"
},
{
"code": "de-DE",
"title": "Deutsch"
},
{
"code": "fr-FR",
"title": "French (France)"
},
{
"code": "uk-UA",
"title": "Ukrainian"
},
{
"code": "zh-TW",
"title": "Chinese (Traditional)"
},
{
"code": "zh-CN",
"title": "Chinese (Simplified)"
}
]
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
<script lang="ts"> <script lang="ts">
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
import { onMount, tick, getContext } from 'svelte';
import { openDB, deleteDB } from 'idb'; import { openDB, deleteDB } from 'idb';
import fileSaver from 'file-saver'; import fileSaver from 'file-saver';
const { saveAs } = fileSaver; const { saveAs } = fileSaver;
import { onMount, tick } from 'svelte';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { getOllamaModels, getOllamaVersion } from '$lib/apis/ollama'; import { getOllamaModels, getOllamaVersion } from '$lib/apis/ollama';
...@@ -36,6 +36,8 @@ ...@@ -36,6 +36,8 @@
import ChangelogModal from '$lib/components/ChangelogModal.svelte'; import ChangelogModal from '$lib/components/ChangelogModal.svelte';
import Tooltip from '$lib/components/common/Tooltip.svelte'; import Tooltip from '$lib/components/common/Tooltip.svelte';
const i18n = getContext('i18n');
let ollamaVersion = ''; let ollamaVersion = '';
let loaded = false; let loaded = false;
let showShortcutsButtonElement: HTMLButtonElement; let showShortcutsButtonElement: HTMLButtonElement;
...@@ -199,7 +201,7 @@ ...@@ -199,7 +201,7 @@
{#if loaded} {#if loaded}
<div class=" hidden lg:flex fixed bottom-0 right-0 px-3 py-3 z-10"> <div class=" hidden lg:flex fixed bottom-0 right-0 px-3 py-3 z-10">
<Tooltip content="help" placement="left"> <Tooltip content="Help" placement="left">
<button <button
id="show-shortcuts-button" id="show-shortcuts-button"
bind:this={showShortcutsButtonElement} bind:this={showShortcutsButtonElement}
...@@ -240,7 +242,7 @@ ...@@ -240,7 +242,7 @@
location.href = '/'; location.href = '/';
}} }}
> >
Check Again {$i18n.t('Check Again')}
</button> </button>
<button <button
...@@ -248,7 +250,7 @@ ...@@ -248,7 +250,7 @@
on:click={async () => { on:click={async () => {
localStorage.removeItem('token'); localStorage.removeItem('token');
location.href = '/auth'; location.href = '/auth';
}}>Sign Out</button }}>{$i18n.t('Sign Out')}</button
> >
</div> </div>
</div> </div>
...@@ -267,12 +269,14 @@ ...@@ -267,12 +269,14 @@
</div> </div>
<div class=" mt-4 text-center text-sm dark:text-gray-200 w-full"> <div class=" mt-4 text-center text-sm dark:text-gray-200 w-full">
Saving chat logs directly to your browser's storage is no longer supported. Please {$i18n.t(
take a moment to download and delete your chat logs by clicking the button below. "Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through"
Don't worry, you can easily re-import your chat logs to the backend through <span )}
class="font-semibold dark:text-white">Settings > Chats > Import Chats</span <span class="font-semibold dark:text-white"
>. This ensures that your valuable conversations are securely saved to your backend >{$i18n.t('Settings')} > {$i18n.t('Chats')} > {$i18n.t('Import Chats')}</span
database. Thank you! >. {$i18n.t(
'This ensures that your valuable conversations are securely saved to your backend database. Thank you!'
)}
</div> </div>
<div class=" mt-6 mx-auto relative group w-fit"> <div class=" mt-6 mx-auto relative group w-fit">
...@@ -298,7 +302,7 @@ ...@@ -298,7 +302,7 @@
class="text-xs text-center w-full mt-2 text-gray-400 underline" class="text-xs text-center w-full mt-2 text-gray-400 underline"
on:click={async () => { on:click={async () => {
localDBChats = []; localDBChats = [];
}}>Close</button }}>{$i18n.t('Close')}</button
> >
</div> </div>
</div> </div>
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment