Merge pull request #2930 from Yokayo/staging Work on translation

a09a60ef314cc79e9f9c8046e407189814d428f6

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
13 files changed, +296 -146Ignore whitespace
public/locales/ru-ru.json+152 -6
@@ -56,7 +56,7 @@
56 "Rep. Pen. Slope": "Rep. Pen. Slope",56 "Rep. Pen. Slope": "Rep. Pen. Slope",
57 "Top K": "Top K",57 "Top K": "Top K",
58 "Top P": "Top P",58 "Top P": "Top P",
59 "Do Sample": "Сделать образец",59 "Do Sample": "Включить сэмплинг",
60 "Add BOS Token": "Добавлять BOS-токен",60 "Add BOS Token": "Добавлять BOS-токен",
61 "Add the bos_token to the beginning of prompts. Disabling this can make the replies more creative": "Добавлять BOS-токен в начале промпта. Если выключить, ответы могут стать более креативными.",61 "Add the bos_token to the beginning of prompts. Disabling this can make the replies more creative": "Добавлять BOS-токен в начале промпта. Если выключить, ответы могут стать более креативными.",
62 "Ban EOS Token": "Запретить EOS-токен",62 "Ban EOS Token": "Запретить EOS-токен",
@@ -451,8 +451,8 @@
451 "Creator's Notes": "Заметки создателя",451 "Creator's Notes": "Заметки создателя",
452 "A-Z": "A-Z",452 "A-Z": "A-Z",
453 "Z-A": "Z-A",453 "Z-A": "Z-A",
454 "Newest": "Новейшие",454 "Newest": "Сначала новые",
455 "Oldest": "Старейшие",455 "Oldest": "Сначала старые",
456 "Favorites": "Избранные",456 "Favorites": "Избранные",
457 "Recent": "Последние",457 "Recent": "Последние",
458 "Most chats": "Больше всего чатов",458 "Most chats": "Больше всего чатов",
@@ -564,7 +564,7 @@
564 "Advanced Definition": "Расширенное описание",564 "Advanced Definition": "Расширенное описание",
565 "Character Lore": "Лор персонажа",565 "Character Lore": "Лор персонажа",
566 "Export and Download": "Экспортировать и скачать",566 "Export and Download": "Экспортировать и скачать",
567 "Duplicate Character": "Дублировать персонажа",567 "Duplicate Character": "Клонировать персонажа",
568 "Create Character": "Создать персонажа",568 "Create Character": "Создать персонажа",
569 "Delete Character": "Удалить персонажа",569 "Delete Character": "Удалить персонажа",
570 "View all tags": "Показать все тэги",570 "View all tags": "Показать все тэги",
@@ -1690,7 +1690,7 @@
1690 "Delete Message": "Удалить сообщение",1690 "Delete Message": "Удалить сообщение",
1691 "Delete Swipe": "Удалить свайп",1691 "Delete Swipe": "Удалить свайп",
1692 "Could not get a reply from API. Check your connection settings / API key and try again.": "Не удалось получить ответ от API. Проверьте настройки соединения и API-ключ и повторите попытку.",1692 "Could not get a reply from API. Check your connection settings / API key and try again.": "Не удалось получить ответ от API. Проверьте настройки соединения и API-ключ и повторите попытку.",
1693 "Connecting To Proxy": "Подключение к прокси",1693 "Connecting To Proxy": "Подключиться к прокси",
1694 "Are you sure you want to connect to the following proxy URL?": "Вы точно хотите соединиться с прокси по этому адресу?",1694 "Are you sure you want to connect to the following proxy URL?": "Вы точно хотите соединиться с прокси по этому адресу?",
1695 "API connection successful!": "Соединение с API установлено!",1695 "API connection successful!": "Соединение с API установлено!",
1696 "Proxy Saved": "Прокси сохранена",1696 "Proxy Saved": "Прокси сохранена",
@@ -1747,5 +1747,151 @@
1747 "Markdown Hotkeys": "Горячие клавиши для разметки",1747 "Markdown Hotkeys": "Горячие клавиши для разметки",
1748 "markdown_hotkeys_desc": "Включить горячие клавиши для вставки символов разметки в некоторых полях ввода. См. '/help hotkeys'.",1748 "markdown_hotkeys_desc": "Включить горячие клавиши для вставки символов разметки в некоторых полях ввода. См. '/help hotkeys'.",
1749 "Save and Update": "Сохранить и обновить",1749 "Save and Update": "Сохранить и обновить",
1750 "Profile name:": "Название профиля:"1750 "Profile name:": "Название профиля:",
1751 "API returned an error": "API вернуло ошибку",
1752 "Failed to save preset": "Не удалось сохранить пресет",
1753 "Preset name should be unique.": "Название пресета должно быть уникальным.",
1754 "Invalid file": "Невалидный файл",
1755 "No preset selected": "Пресет не выбран",
1756 "Invalid logit bias preset file.": "Файл пресета невалиден.",
1757 "Preset was not deleted from server": "Пресет не удалён с сервера",
1758 "Preset deleted": "Пресет удалён",
1759 "Delete the preset?": "Удалить пресет?",
1760 "Preset updated": "Пресет обновлён",
1761 "Entered reverse proxy address is not a valid URL": "Введённый адрес прокси невалиден",
1762 "An error occurred while counting tokens: Token budget exceeded.": "Ошибка при подсчёте токенов: Превышен бюджет токенов",
1763 "An error occurred while counting tokens: Invalid character name": "Ошибка при подсчёте токенов: Невалидное имя персонажа",
1764 "Not enough free tokens for mandatory prompts. Raise your token Limit or disable custom prompts.": "Недостаточно токенов для всех выбранных промптов. Повысьте лимит токенов или отключите часть промптов.",
1765 "The name of at least one character contained whitespaces or special characters. Please check your user and character name.": "В имени одного из персонажей содержится пробел или иной спецсимвол. Проверьте имена пользователя и персонажа.",
1766 "An unknown error occurred while counting tokens. Further information may be available in console.": "Неизвестная ошибка при подсчёте токенов. Проверьте консоль, возможно, подробная информация есть там.",
1767 "Encountered an error while processing your request.": "При обработке вашего запроса возникла ошибка.",
1768 "Check you have credits available on your": "Убедитесь, что на вашем",
1769 "OpenAI account quora_error": "аккаунте OpenAI",
1770 "dot quota_error": "имеется достаточно кредитов.",
1771 "If you have sufficient credits, please try again later.": "Если кредитов достаточно, то повторите попытку позднее.",
1772 "Proxy preset '${0}' not found": "Пресет '${0}' не найден",
1773 "Window.ai returned an error": "Window.ai вернул ошибку",
1774 "Get it here:": "Загрузите здесь:",
1775 "Extension is not installed": "Расширение не установлено",
1776 "Update or remove your reverse proxy settings.": "Измените или удалите ваши настройки прокси.",
1777 "An error occurred while importing prompts. More info available in console.": "В процессе импорта произошла ошибка. Подробную информацию см. в консоли.",
1778 "Could not import prompts. Export failed validation.": "Не удалось импортировать промпты. Не пройдена валидация при экспорте.",
1779 "Prompt import complete.": "Импорт завершён.",
1780 "Are you sure you want to delete this prompt?": "Вы точно хотите удалить этот промпт?",
1781 "Existing prompts with the same ID will be overridden. Do you want to proceed?": "Имеющиеся промпты с совпадающими идентификаторами будут перезаписаны. Продолжить?",
1782 "This will reset the prompt order for this character. You will not lose any prompts.": "Будет сброшен порядок промптов для этого персонажа. Сами промпты вы не потеряете.",
1783 "Note:": "Примечание:",
1784 "this chat is temporary and will be deleted as soon as you leave it.": "это временный чат, он будет удалён, как только вы из него выйдете.",
1785 "help_hotkeys_20": "Горячие клавиши для разметки",
1786 "help_hotkeys_21": "Работают в окне ввода чата, а также в полях, отмеченных этим значком:",
1787 "help_hotkeys_22": "**полужирный**",
1788 "help_hotkeys_23": "*курсив*",
1789 "help_hotkeys_24": "__подчёркивание__",
1790 "help_hotkeys_25": "`inline-код`",
1791 "help_hotkeys_26": "~~зачёркнутый~~",
1792 "ext_regex_only_format_visual_desc": "Содержимое файла с историей чата останется нетронутым, изменения будут лишь визуальными (в UI).",
1793 "Could not convert file": "Не удалось сконвертировать файл",
1794 "Could not upload file": "Не удалось загрузить файл",
1795 "Could not download file": "Не удалось скачать файл",
1796 "File is too big. Maximum size is ${0}.": "Слишком большой файл. Максимальный размер: ${0}.",
1797 "Binary files are not supported. Select a text file or image.": "Бинарные файлы не поддерживаются. Выберите текстовый файл или изображение.",
1798 "No character or group selected": "Не выбрано ни одного персонажа или группы",
1799 "Could not delete file": "Не удалось удалить файл",
1800 "No attachments selected.": "Вложение не выбрано.",
1801 "Data Bank": "Банк данных",
1802 "No files were scraped.": "Скрапинг не выполнен.",
1803 "Scraped ${0} files from ${1} to ${2}.": "Соскраплено из ${0} файлов из ${1} в ${2}.",
1804 "Check browser console for details.": "Подробности см. в консоли браузера.",
1805 "Scraping failed": "Ошибка скрапинга",
1806 "External media has been blocked": "Внешние медиа отключены",
1807 "Use the 'Ext. Media' button to allow it. Click on this message to dismiss.": "Разрешить можно с помощью кнопки 'Внешн. медиа'. Нажмите на это сообщение, чтобы его скрыть.",
1808 "Couldn't get CSRF token. Please refresh the page.": "Не удалось получить CSRF токен. Попробуйте перезагрузить страницу.",
1809 "Error": "Ошибка",
1810 "API Error": "Ошибка API",
1811 "Please wait until the chat is saved before switching characters.": "Пожалуйста, дождитесь сохранения чата, прежде чем переключать персонажа.",
1812 "Your chat is still saving...": "Чат всё ещё сохраняется...",
1813 "Character ${0} not found in the list": "Персонаж ${0} не найден в списке",
1814 "Streaming is enabled, but the version of Kobold used does not support token streaming.": "Включён стриминг текста, но ваша версия Kobold не поддерживает стриминг токенов.",
1815 "Streaming is not supported for the Legacy API. Update Ooba and use new API to enable streaming.": "Для устаревшего API стриминг недоступен. Обновите oobaboga и используйте новый API, чтобы включить стриминг.",
1816 "Verify that the server is running and accessible.": "Убедитесь, что сервер запущен и доступен по сети.",
1817 "ST Server cannot be reached": "Не удалось соединиться с сервером ST",
1818 "You must first select a character to duplicate!": "Вы не выбрали персонажа, которого хотите клонировать!",
1819 "Character Duplicated": "Персонаж склонирован",
1820 "No character name provided.": "Вы не ввели имя персонажа.",
1821 "Rename Character": "Переименование",
1822 "No character selected.": "Вы не выбрали персонажа.",
1823 "New name:": "Новое имя:",
1824 "Same character name provided, so name did not change.": "Введено то же самое имя, ничего не изменилось.",
1825 "Character renamed and past chats updated!": "Персонаж переименован, а чаты обновлены!",
1826 "Character renamed!": "Персонаж переименован!",
1827 "Something went wrong. The page will be reloaded.": "Что-то пошло не так. Страница будет перезагружена.",
1828 "Past chat could not be updated: ${0}": "Не удалось обновить чат ${0}",
1829 "Trying to save group chat with regular saveChat function. Aborting to prevent corruption.": "Произошла попытка сохранения группового чата функцией saveChat. Откатываем изменения, чтобы предотвратить потерю данных.",
1830 "Check the server connection and reload the page to prevent data loss.": "Проверьте связь с сервером и перезагрузите страницу, чтобы избежать потери данных.",
1831 "Chat could not be saved": "Не удалось сохранить чат",
1832 "Settings could not be loaded after multiple attempts. Please try again later.": "Не удалось загрузить настройки за несколько попыток. Попробуйте позднее.",
1833 "Settings could not be saved": "Не удалось сохранить настройки",
1834 "Could not load chat data. Try reloading the page.": "Не удалось загрузить чат. Попробуйте обновить страницу.",
1835 "Invalid process (no 'type')": "Невалидный процесс (нет параметра 'type')",
1836 "Character Deleted: ${0}": "Персонаж удалён: ${0}",
1837 "Character Created: ${0}": "Персонаж создан: ${0}",
1838 "Group Created": "Группа создана",
1839 "Group Deleted": "Группа удалена",
1840 "Character Imported: ${0}": "Персонаж импортирован: ${0}",
1841 "Invalid swipe ID: ${0}": "Некорректный идентификатор свайпа: ${0}",
1842 "No messages to delete swipes from.": "Сообщение, из которого требуется удалить свайп, не найдено.",
1843 "Can't delete the last swipe.": "Невозможно удалить единственный свайп.",
1844 "GUI Settings preset is not supported for Horde. Please select another preset.": "Для Horde не поддерживаются пресеты настроек GUI. Пожалуйста, выберите другой пресет.",
1845 "Embedded lorebook will be removed from this character.": "Встроенный лорбук будет удалён из персонажа.",
1846 "Name is required": "Введите имя",
1847 "Cannot create characters while generating. Stop the request and try again.": "Во время генерации ответа создать персонажа невозможно. Остановите запрос и повторите попытку.",
1848 "Creation aborted": "Процесс создания прерван",
1849 "Failed to create character": "Не удалось создать персонажа",
1850 "Something went wrong while saving the character, or the image file provided was in an invalid format. Double check that the image is not a webp.": "Что-то пошло не так в процессе сохранения персонажа, либо вы загрузили файл некорректного формата. Проверьте, что изображение точно не в формате webp.",
1851 "Context template '${0}' not found": "Шаблон контекста '${0}' не найден",
1852 "Instruct template '${0}' not found": "Шаблон Instruct-режима '${0}' не найден",
1853 "Error: ${0} is not a valid API": "Ошибка: ${0} не является валидным API",
1854 "API set to ${0}, trying to connect..": "Установлено API ${0}, пробуем подключиться...",
1855 "Unsupported file type: ": "Неподдерживаемый тип файла: ",
1856 "Cannot import characters while generating. Stop the request and try again.": "Во время генерации ответа импорт персонажа невозможен. Остановите запрос и повторите попытку.",
1857 "Import aborted": "Процесс импорта прерван",
1858 "The file is likely invalid or corrupted.": "Вероятно, файл невалиден или повреждён.",
1859 "Could not import character": "Не удалось импортировать персонажа",
1860 "Cannot run /impersonate command while the reply is being generated.": "Во время генерации ответа выполнить /impersonate невозможно.",
1861 "Name must be provided as an argument to rename this chat.": "Для переименования чата необходимо предоставить новое имя в качестве аргумента.",
1862 "No chat selected that can be renamed.": "Чат, который требуется переименовать, не найден.",
1863 "Successfully renamed chat to: ${0}": "Чат успешно переименован в: ${0}",
1864 "Character ${0} not found. Skipping deletion.": "Персонаж ${0} не найден. Удаление пропускается.",
1865 "Failed to delete character": "При удалении персонажа произошло ошибка",
1866 "Are you sure you want to duplicate this character?": "Вы точно хотите клонировать этого персонажа?",
1867 "If you just want to start a new chat with the same character...": "Если вы хотите просто создать новый чат, воспользуйтесь кнопкой \"Начать новый чат\" в меню слева внизу.",
1868 "THIS IS PERMANENT!": "ОТМЕНИТЬ БУДЕТ НЕВОЗМОЖНО!",
1869 "Also delete the chat files": "Также удалить файлы чатов",
1870 "Delete the character?": "Удалить персонажа?",
1871 "Not a valid number": "Некорректное число",
1872 "Author's Note depth updated": "Глубина заметок автора обновлена",
1873 "Author's Note frequency updated": "Частота заметок автора обновлена",
1874 "Not a valid position": "Некорректная позиция",
1875 "Author's Note position updated": "Позиция заметок автора обновлена",
1876 "Something went wrong. Could not save character's author's note.": "Что-то пошло не так. Не удалось сохранить заметки автора для этого персонажа.",
1877 "Select a character before trying to use Author's Note": "Сначала необходимо выбрать персонажа",
1878 "Author's Note text updated": "Текст заметок автора обновлён",
1879 "Group Validation": "Валидация группы",
1880 "Warning: Listed member ${0} does not exist as a character. It will be removed from the group.": "Предупреждение: персонаж ${0} не существует в виде карточки. Он будет удалён из группы.",
1881 "Group Chat could not be saved": "Не удалось сохранить групповой чат",
1882 "Deleted group member swiped. To get a reply, add them back to the group.": "Вы пытаетесь свайпнуть удалённого члена группы. Чтобы получить ответ, добавьте этого персонажа обратно в группу.",
1883 "Currently no group selected.": "В данный момент не выбрано ни одной группы.",
1884 "Not so fast! Wait for the characters to stop typing before deleting the group.": "Чуть помедленнее! Перед удалением группы дождитесь, пока персонаж закончит печатать.",
1885 "Delete the group?": "Удалить группу?",
1886 "This will also delete all your chats with that group. If you want to delete a single conversation, select a \"View past chats\" option in the lower left menu.": "Вместе с ней будут удалены и все её чаты. Если требуется удалить только один чат, воспользуйтесь кнопкой \"Все чаты\" в меню в левом нижнем углу.",
1887 "Can't peek a character while group reply is being generated": "Невозможно открыть карточку персонажа во время генерации ответа",
1888 "Threshold": "Порог",
1889 "DRY Repetition Penalty": "DRY Штраф за повтор",
1890 "Multiplier": "Множитель",
1891 "DRY_Multiplier_desc": "Поставьте в положение > 0, чтобы включить DRY. Определяет величину штрафа для кратчайшей \"штрафуемой\" строки.",
1892 "DRY_Repetition_Penalty_desc": "DRY налагает штраф на токены, генерация которых приведёт к появлению строки, которая уже была в тексте раньше. Установите множитель = 0, чтобы отключить.",
1893 "Base": "Основание",
1894 "DRY_Base_desc": "Определяет, насколько быстро возрастает штраф с увеличением длины строки.",
1895 "Allowed Length": "Допустимая длина",
1896 "DRY_Allowed_Length_desc": "Длина повторяющейся строки, при превышении которой DRY начинает налагать штраф."
1751}1897}
public/script.js+59 -64
@@ -391,8 +391,8 @@ DOMPurify.addHook('uponSanitizeElement', (node, _, config) => {
391391
392 if (localStorage.getItem(warningShownKey) === null) {392 if (localStorage.getItem(warningShownKey) === null) {
393 const warningToast = toastr.warning(393 const warningToast = toastr.warning(
394 'Use the "Ext. Media" button to allow it. Click on this message to dismiss.',394 t`Use the 'Ext. Media' button to allow it. Click on this message to dismiss.`,
395 'External media has been blocked',395 t`External media has been blocked`,
396 {396 {
397 timeOut: 0,397 timeOut: 0,
398 preventDuplicates: true,398 preventDuplicates: true,
@@ -933,7 +933,7 @@ async function firstLoadInit() {
933 token = tokenData.token;933 token = tokenData.token;
934 } catch {934 } catch {
935 hideLoader();935 hideLoader();
936 toastr.error('Couldn\'t get CSRF token. Please refresh the page.', 'Error', { timeOut: 0, extendedTimeOut: 0, preventDuplicates: true });936 toastr.error(t`Couldn't get CSRF token. Please refresh the page.`, t`Error`, { timeOut: 0, extendedTimeOut: 0, preventDuplicates: true });
937 throw new Error('Initialization failed');937 throw new Error('Initialization failed');
938 }938 }
939939
@@ -1149,7 +1149,7 @@ async function getStatusKobold() {
11491149
1150 // We didn't get a 200 status code, but the endpoint has an explanation. Which means it DID connect, but I digress.1150 // We didn't get a 200 status code, but the endpoint has an explanation. Which means it DID connect, but I digress.
1151 if (online_status === 'no_connection' && data.response) {1151 if (online_status === 'no_connection' && data.response) {
1152 toastr.error(data.response, 'API Error', { timeOut: 5000, preventDuplicates: true });1152 toastr.error(data.response, t`API Error`, { timeOut: 5000, preventDuplicates: true });
1153 }1153 }
1154 } catch (err) {1154 } catch (err) {
1155 console.error('Error getting status', err);1155 console.error('Error getting status', err);
@@ -1235,7 +1235,7 @@ async function getStatusTextgen() {
12351235
1236 // We didn't get a 200 status code, but the endpoint has an explanation. Which means it DID connect, but I digress.1236 // We didn't get a 200 status code, but the endpoint has an explanation. Which means it DID connect, but I digress.
1237 if (online_status === 'no_connection' && data.response) {1237 if (online_status === 'no_connection' && data.response) {
1238 toastr.error(data.response, 'API Error', { timeOut: 5000, preventDuplicates: true });1238 toastr.error(data.response, t`API Error`, { timeOut: 5000, preventDuplicates: true });
1239 }1239 }
1240 } catch (err) {1240 } catch (err) {
1241 if (err instanceof AbortReason) {1241 if (err instanceof AbortReason) {
@@ -1287,7 +1287,7 @@ export async function selectCharacterById(id) {
1287 }1287 }
12881288
1289 if (isChatSaving) {1289 if (isChatSaving) {
1290 toastr.info('Please wait until the chat is saved before switching characters.', 'Your chat is still saving...');1290 toastr.info(t`Please wait until the chat is saved before switching characters.`, t`Your chat is still saving...`);
1291 return;1291 return;
1292 }1292 }
12931293
@@ -1641,7 +1641,7 @@ export async function getOneCharacter(avatarUrl) {
1641 if (indexOf !== -1) {1641 if (indexOf !== -1) {
1642 characters[indexOf] = getData;1642 characters[indexOf] = getData;
1643 } else {1643 } else {
1644 toastr.error(`Character ${avatarUrl} not found in the list`, 'Error', { timeOut: 5000, preventDuplicates: true });1644 toastr.error(t`Character ${avatarUrl} not found in the list`, t`Error`, { timeOut: 5000, preventDuplicates: true });
1645 }1645 }
1646 }1646 }
1647}1647}
@@ -3408,7 +3408,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
3408 await eventSource.emit(event_types.GENERATION_AFTER_COMMANDS, type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage }, dryRun);3408 await eventSource.emit(event_types.GENERATION_AFTER_COMMANDS, type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage }, dryRun);
34093409
3410 if (main_api == 'kobold' && kai_settings.streaming_kobold && !kai_flags.can_use_streaming) {3410 if (main_api == 'kobold' && kai_settings.streaming_kobold && !kai_flags.can_use_streaming) {
3411 toastr.error('Streaming is enabled, but the version of Kobold used does not support token streaming.', undefined, { timeOut: 10000, preventDuplicates: true });3411 toastr.error(t`Streaming is enabled, but the version of Kobold used does not support token streaming.`, undefined, { timeOut: 10000, preventDuplicates: true });
3412 unblockGeneration(type);3412 unblockGeneration(type);
3413 return Promise.resolve();3413 return Promise.resolve();
3414 }3414 }
@@ -3417,7 +3417,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
3417 textgen_settings.streaming &&3417 textgen_settings.streaming &&
3418 textgen_settings.legacy_api &&3418 textgen_settings.legacy_api &&
3419 textgen_settings.type === OOBA) {3419 textgen_settings.type === OOBA) {
3420 toastr.error('Streaming is not supported for the Legacy API. Update Ooba and use new API to enable streaming.', undefined, { timeOut: 10000, preventDuplicates: true });3420 toastr.error(t`Streaming is not supported for the Legacy API. Update Ooba and use new API to enable streaming.`, undefined, { timeOut: 10000, preventDuplicates: true });
3421 unblockGeneration(type);3421 unblockGeneration(type);
3422 return Promise.resolve();3422 return Promise.resolve();
3423 }3423 }
@@ -3433,7 +3433,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
34333433
3434 if (!pingResult) {3434 if (!pingResult) {
3435 unblockGeneration(type);3435 unblockGeneration(type);
3436 toastr.error('Verify that the server is running and accessible.', 'ST Server cannot be reached');3436 toastr.error(t`Verify that the server is running and accessible.`, t`ST Server cannot be reached`);
3437 throw new Error('Server unreachable');3437 throw new Error('Server unreachable');
3438 }3438 }
34393439
@@ -4461,7 +4461,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
4461 generatedPromptCache = '';4461 generatedPromptCache = '';
44624462
4463 if (data?.response) {4463 if (data?.response) {
4464 toastr.error(data.response, 'API Error', { preventDuplicates: true });4464 toastr.error(data.response, t`API Error`, { preventDuplicates: true });
4465 }4465 }
4466 throw new Error(data?.response);4466 throw new Error(data?.response);
4467 }4467 }
@@ -4552,7 +4552,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
45524552
4553 function onError(exception) {4553 function onError(exception) {
4554 if (typeof exception?.error?.message === 'string') {4554 if (typeof exception?.error?.message === 'string') {
4555 toastr.error(exception.error.message, 'Error', { timeOut: 10000, extendedTimeOut: 20000 });4555 toastr.error(exception.error.message, t`Error`, { timeOut: 10000, extendedTimeOut: 20000 });
4556 }4556 }
45574557
4558 generatedPromptCache = '';4558 generatedPromptCache = '';
@@ -4956,7 +4956,7 @@ function addChatsSeparator(mesSendString) {
49564956
4957async function duplicateCharacter() {4957async function duplicateCharacter() {
4958 if (!this_chid) {4958 if (!this_chid) {
4959 toastr.warning('You must first select a character to duplicate!');4959 toastr.warning(t`You must first select a character to duplicate!`);
4960 return '';4960 return '';
4961 }4961 }
49624962
@@ -4975,7 +4975,7 @@ async function duplicateCharacter() {
4975 body: JSON.stringify(body),4975 body: JSON.stringify(body),
4976 });4976 });
4977 if (response.ok) {4977 if (response.ok) {
4978 toastr.success('Character Duplicated');4978 toastr.success(t`Character Duplicated`);
4979 const data = await response.json();4979 const data = await response.json();
4980 await eventSource.emit(event_types.CHARACTER_DUPLICATED, { oldAvatar: body.avatar_url, newAvatar: data.path });4980 await eventSource.emit(event_types.CHARACTER_DUPLICATED, { oldAvatar: body.avatar_url, newAvatar: data.path });
4981 await getCharacters();4981 await getCharacters();
@@ -5822,23 +5822,23 @@ export function setSendButtonState(value) {
58225822
5823export async function renameCharacter(name = null, { silent = false, renameChats = null } = {}) {5823export async function renameCharacter(name = null, { silent = false, renameChats = null } = {}) {
5824 if (!name && silent) {5824 if (!name && silent) {
5825 toastr.warning('No character name provided.', 'Rename Character');5825 toastr.warning(t`No character name provided.`, t`Rename Character`);
5826 return false;5826 return false;
5827 }5827 }
5828 if (this_chid === undefined) {5828 if (this_chid === undefined) {
5829 toastr.warning('No character selected.', 'Rename Character');5829 toastr.warning(t`No character selected.`, t`Rename Character`);
5830 return false;5830 return false;
5831 }5831 }
58325832
5833 const oldAvatar = characters[this_chid].avatar;5833 const oldAvatar = characters[this_chid].avatar;
5834 const newValue = name || await callGenericPopup('<h3>New name:</h3>', POPUP_TYPE.INPUT, characters[this_chid].name);5834 const newValue = name || await callGenericPopup('<h3>' + t`New name:` + '</h3>', POPUP_TYPE.INPUT, characters[this_chid].name);
58355835
5836 if (!newValue) {5836 if (!newValue) {
5837 toastr.warning('No character name provided.', 'Rename Character');5837 toastr.warning(t`No character name provided.`, t`Rename Character`);
5838 return false;5838 return false;
5839 }5839 }
5840 if (newValue === characters[this_chid].name) {5840 if (newValue === characters[this_chid].name) {
5841 toastr.info('Same character name provided, so name did not change.', 'Rename Character');5841 toastr.info(t`Same character name provided, so name did not change.`, t`Rename Character`);
5842 return false;5842 return false;
5843 }5843 }
58445844
@@ -5885,9 +5885,9 @@ export async function renameCharacter(name = null, { silent = false, renameChats
5885 if (renamePastChatsConfirm) {5885 if (renamePastChatsConfirm) {
5886 await renamePastChats(newAvatar, newValue);5886 await renamePastChats(newAvatar, newValue);
5887 await reloadCurrentChat();5887 await reloadCurrentChat();
5888 toastr.success('Character renamed and past chats updated!', 'Rename Character');5888 toastr.success(t`Character renamed and past chats updated!`, t`Rename Character`);
5889 } else {5889 } else {
5890 toastr.success('Character renamed!', 'Rename Character');5890 toastr.success(t`Character renamed!`, t`Rename Character`);
5891 }5891 }
5892 }5892 }
5893 else {5893 else {
@@ -5900,8 +5900,8 @@ export async function renameCharacter(name = null, { silent = false, renameChats
5900 }5900 }
5901 catch (error) {5901 catch (error) {
5902 // Reloading to prevent data corruption5902 // Reloading to prevent data corruption
5903 if (!silent) await callPopup('Something went wrong. The page will be reloaded.', 'text');5903 if (!silent) await callPopup(t`Something went wrong. The page will be reloaded.`, 'text');
5904 else toastr.error('Something went wrong. The page will be reloaded.', 'Rename Character');5904 else toastr.error(t`Something went wrong. The page will be reloaded.`, t`Rename Character`);
59055905
5906 console.log('Renaming character error:', error);5906 console.log('Renaming character error:', error);
5907 location.reload();5907 location.reload();
@@ -5958,7 +5958,7 @@ async function renamePastChats(newAvatar, newValue) {
5958 }5958 }
5959 }5959 }
5960 } catch (error) {5960 } catch (error) {
5961 toastr.error(`Past chat could not be updated: ${file_name}`);5961 toastr.error(t`Past chat could not be updated: ${file_name}`);
5962 console.error(error);5962 console.error(error);
5963 }5963 }
5964 }5964 }
@@ -6007,7 +6007,7 @@ export async function saveChat(chatName, withMetadata, mesId) {
6007 characters[this_chid]['date_last_chat'] = Date.now();6007 characters[this_chid]['date_last_chat'] = Date.now();
6008 chat.forEach(function (item, i) {6008 chat.forEach(function (item, i) {
6009 if (item['is_group']) {6009 if (item['is_group']) {
6010 toastr.error('Trying to save group chat with regular saveChat function. Aborting to prevent corruption.');6010 toastr.error(t`Trying to save group chat with regular saveChat function. Aborting to prevent corruption.`);
6011 throw new Error('Group chat saved from saveChat');6011 throw new Error('Group chat saved from saveChat');
6012 }6012 }
6013 /*6013 /*
@@ -6052,7 +6052,7 @@ export async function saveChat(chatName, withMetadata, mesId) {
6052 contentType: 'application/json',6052 contentType: 'application/json',
6053 success: function (data) { },6053 success: function (data) { },
6054 error: function (jqXHR, exception) {6054 error: function (jqXHR, exception) {
6055 toastr.error('Check the server connection and reload the page to prevent data loss.', 'Chat could not be saved');6055 toastr.error(t`Check the server connection and reload the page to prevent data loss.`, t`Chat could not be saved`);
6056 console.log(exception);6056 console.log(exception);
6057 console.log(jqXHR);6057 console.log(jqXHR);
6058 },6058 },
@@ -6449,7 +6449,7 @@ export async function getSettings() {
64496449
6450 if (!response.ok) {6450 if (!response.ok) {
6451 reloadLoop();6451 reloadLoop();
6452 toastr.error('Settings could not be loaded after multiple attempts. Please try again later.');6452 toastr.error(t`Settings could not be loaded after multiple attempts. Please try again later.`);
6453 throw new Error('Error getting settings');6453 throw new Error('Error getting settings');
6454 }6454 }
64556455
@@ -6676,7 +6676,7 @@ export async function saveSettings(type) {
6676 eventSource.emit(event_types.SETTINGS_UPDATED);6676 eventSource.emit(event_types.SETTINGS_UPDATED);
6677 },6677 },
6678 error: function (jqXHR, exception) {6678 error: function (jqXHR, exception) {
6679 toastr.error('Check the server connection and reload the page to prevent data loss.', 'Settings could not be saved');6679 toastr.error(t`Check the server connection and reload the page to prevent data loss.t`, t`Settings could not be saved`);
6680 console.log(exception);6680 console.log(exception);
6681 console.log(jqXHR);6681 console.log(jqXHR);
6682 },6682 },
@@ -6942,7 +6942,7 @@ export async function displayPastChats() {
6942 const data = await (selected_group ? getGroupPastChats(selected_group) : getPastCharacterChats());6942 const data = await (selected_group ? getGroupPastChats(selected_group) : getPastCharacterChats());
69436943
6944 if (!data) {6944 if (!data) {
6945 toastr.error('Could not load chat data. Try reloading the page.');6945 toastr.error(t`Could not load chat data. Try reloading the page.`);
6946 return;6946 return;
6947 }6947 }
69486948
@@ -7075,7 +7075,7 @@ export function selectRightMenuWithAnimation(selectedMenuId) {
70757075
7076export function select_rm_info(type, charId, previousCharId = null) {7076export function select_rm_info(type, charId, previousCharId = null) {
7077 if (!type) {7077 if (!type) {
7078 toastr.error('Invalid process (no \'type\')');7078 toastr.error(t`Invalid process (no 'type')`);
7079 return;7079 return;
7080 }7080 }
7081 if (type !== 'group_create') {7081 if (type !== 'group_create') {
@@ -7083,20 +7083,20 @@ export function select_rm_info(type, charId, previousCharId = null) {
7083 }7083 }
70847084
7085 if (type === 'char_delete') {7085 if (type === 'char_delete') {
7086 toastr.warning(`Character Deleted: ${displayName}`);7086 toastr.warning(t`Character Deleted: ${displayName}`);
7087 }7087 }
7088 if (type === 'char_create') {7088 if (type === 'char_create') {
7089 toastr.success(`Character Created: ${displayName}`);7089 toastr.success(t`Character Created: ${displayName}`);
7090 }7090 }
7091 if (type === 'group_create') {7091 if (type === 'group_create') {
7092 toastr.success('Group Created');7092 toastr.success(t`Group Created`);
7093 }7093 }
7094 if (type === 'group_delete') {7094 if (type === 'group_delete') {
7095 toastr.warning('Group Deleted');7095 toastr.warning(t`Group Deleted`);
7096 }7096 }
70977097
7098 if (type === 'char_import') {7098 if (type === 'char_import') {
7099 toastr.success(`Character Imported: ${displayName}`);7099 toastr.success(t`Character Imported: ${displayName}`);
7100 }7100 }
71017101
7102 selectRightMenuWithAnimation('rm_characters_block');7102 selectRightMenuWithAnimation('rm_characters_block');
@@ -7554,25 +7554,25 @@ export function hideSwipeButtons() {
7554 */7554 */
7555export async function deleteSwipe(swipeId = null) {7555export async function deleteSwipe(swipeId = null) {
7556 if (swipeId && (isNaN(swipeId) || swipeId < 0)) {7556 if (swipeId && (isNaN(swipeId) || swipeId < 0)) {
7557 toastr.warning(`Invalid swipe ID: ${swipeId + 1}`);7557 toastr.warning(t`Invalid swipe ID: ${swipeId + 1}`);
7558 return;7558 return;
7559 }7559 }
75607560
7561 const lastMessage = chat[chat.length - 1];7561 const lastMessage = chat[chat.length - 1];
7562 if (!lastMessage || !Array.isArray(lastMessage.swipes) || !lastMessage.swipes.length) {7562 if (!lastMessage || !Array.isArray(lastMessage.swipes) || !lastMessage.swipes.length) {
7563 toastr.warning('No messages to delete swipes from.');7563 toastr.warning(t`No messages to delete swipes from.`);
7564 return;7564 return;
7565 }7565 }
75667566
7567 if (lastMessage.swipes.length <= 1) {7567 if (lastMessage.swipes.length <= 1) {
7568 toastr.warning('Can\'t delete the last swipe.');7568 toastr.warning(t`Can't delete the last swipe.`);
7569 return;7569 return;
7570 }7570 }
75717571
7572 swipeId = swipeId ?? lastMessage.swipe_id;7572 swipeId = swipeId ?? lastMessage.swipe_id;
75737573
7574 if (swipeId < 0 || swipeId >= lastMessage.swipes.length) {7574 if (swipeId < 0 || swipeId >= lastMessage.swipes.length) {
7575 toastr.warning(`Invalid swipe ID: ${swipeId + 1}`);7575 toastr.warning(t`Invalid swipe ID: ${swipeId + 1}`);
7576 return;7576 return;
7577 }7577 }
75787578
@@ -7711,7 +7711,7 @@ export function setGenerationProgress(progress) {
77117711
7712function isHordeGenerationNotAllowed() {7712function isHordeGenerationNotAllowed() {
7713 if (main_api == 'koboldhorde' && preset_settings == 'gui') {7713 if (main_api == 'koboldhorde' && preset_settings == 'gui') {
7714 toastr.error('GUI Settings preset is not supported for Horde. Please select another preset.');7714 toastr.error(t`GUI Settings preset is not supported for Horde. Please select another preset.`);
7715 return true;7715 return true;
7716 }7716 }
77177717
@@ -7760,7 +7760,7 @@ function openCharacterWorldPopup() {
7760 }7760 }
77617761
7762 $('#character_json_data').val(JSON.stringify(data));7762 $('#character_json_data').val(JSON.stringify(data));
7763 toastr.info('Embedded lorebook will be removed from this character.');7763 toastr.info(t`Embedded lorebook will be removed from this character.`);
7764 } catch {7764 } catch {
7765 console.error('Failed to parse character JSON data.');7765 console.error('Failed to parse character JSON data.');
7766 }7766 }
@@ -7951,11 +7951,11 @@ async function createOrEditCharacter(e) {
79517951
7952 if ($('#form_create').attr('actiontype') == 'createcharacter') {7952 if ($('#form_create').attr('actiontype') == 'createcharacter') {
7953 if (String($('#character_name_pole').val()).length === 0) {7953 if (String($('#character_name_pole').val()).length === 0) {
7954 toastr.error('Name is required');7954 toastr.error(t`Name is required`);
7955 return;7955 return;
7956 }7956 }
7957 if (is_group_generating || is_send_press) {7957 if (is_group_generating || is_send_press) {
7958 toastr.error('Cannot create characters while generating. Stop the request and try again.', 'Creation aborted');7958 toastr.error(t`Cannot create characters while generating. Stop the request and try again.`, t`Creation aborted`);
7959 return;7959 return;
7960 }7960 }
7961 try {7961 try {
@@ -8039,7 +8039,7 @@ async function createOrEditCharacter(e) {
80398039
8040 } catch (error) {8040 } catch (error) {
8041 console.error('Error creating character', error);8041 console.error('Error creating character', error);
8042 toastr.error('Failed to create character');8042 toastr.error(t`Failed to create character`);
8043 }8043 }
8044 } else {8044 } else {
8045 try {8045 try {
@@ -8098,7 +8098,7 @@ async function createOrEditCharacter(e) {
8098 }8098 }
8099 } catch (error) {8099 } catch (error) {
8100 console.log(error);8100 console.log(error);
8101 toastr.error('Something went wrong while saving the character, or the image file provided was in an invalid format. Double check that the image is not a webp.');8101 toastr.error(t`Something went wrong while saving the character, or the image file provided was in an invalid format. Double check that the image is not a webp.`);
8102 }8102 }
8103 }8103 }
8104}8104}
@@ -8604,7 +8604,7 @@ async function selectContextCallback(args, name) {
8604 const result = fuse.search(name);8604 const result = fuse.search(name);
86058605
8606 if (result.length === 0) {8606 if (result.length === 0) {
8607 !quiet && toastr.warning(`Context template "${name}" not found`);8607 !quiet && toastr.warning(t`Context template '${name}' not found`);
8608 return '';8608 return '';
8609 }8609 }
86108610
@@ -8624,7 +8624,7 @@ async function selectInstructCallback(args, name) {
8624 const result = fuse.search(name);8624 const result = fuse.search(name);
86258625
8626 if (result.length === 0) {8626 if (result.length === 0) {
8627 !quiet && toastr.warning(`Instruct template "${name}" not found`);8627 !quiet && toastr.warning(t`Instruct template '${name}' not found`);
8628 return '';8628 return '';
8629 }8629 }
86308630
@@ -8676,7 +8676,7 @@ async function connectAPISlash(args, text) {
86768676
8677 const apiConfig = CONNECT_API_MAP[text.toLowerCase()];8677 const apiConfig = CONNECT_API_MAP[text.toLowerCase()];
8678 if (!apiConfig) {8678 if (!apiConfig) {
8679 toastr.error(`Error: ${text} is not a valid API`);8679 toastr.error(t`Error: ${text} is not a valid API`);
8680 return '';8680 return '';
8681 }8681 }
86828682
@@ -8705,7 +8705,7 @@ async function connectAPISlash(args, text) {
8705 }8705 }
87068706
8707 const quiet = isTrueBoolean(args?.quiet);8707 const quiet = isTrueBoolean(args?.quiet);
8708 const toast = quiet ? jQuery() : toastr.info(`API set to ${text}, trying to connect..`);8708 const toast = quiet ? jQuery() : toastr.info(t`API set to ${text}, trying to connect..`);
87098709
8710 try {8710 try {
8711 await waitUntilCondition(() => online_status !== 'no_connection', 10000, 100);8711 await waitUntilCondition(() => online_status !== 'no_connection', 10000, 100);
@@ -8744,7 +8744,7 @@ export async function processDroppedFiles(files, data = new Map()) {
8744 const preservedName = data instanceof Map && data.get(file);8744 const preservedName = data instanceof Map && data.get(file);
8745 await importCharacter(file, preservedName);8745 await importCharacter(file, preservedName);
8746 } else {8746 } else {
8747 toastr.warning('Unsupported file type: ' + file.name);8747 toastr.warning(t`Unsupported file type: ` + file.name);
8748 }8748 }
8749 }8749 }
8750}8750}
@@ -8757,7 +8757,7 @@ export async function processDroppedFiles(files, data = new Map()) {
8757 */8757 */
8758async function importCharacter(file, preserveFileName = '') {8758async function importCharacter(file, preserveFileName = '') {
8759 if (is_group_generating || is_send_press) {8759 if (is_group_generating || is_send_press) {
8760 toastr.error('Cannot import characters while generating. Stop the request and try again.', 'Import aborted');8760 toastr.error(t`Cannot import characters while generating. Stop the request and try again.`, t`Import aborted`);
8761 throw new Error('Cannot import character while generating');8761 throw new Error('Cannot import character while generating');
8762 }8762 }
87638763
@@ -8784,7 +8784,7 @@ async function importCharacter(file, preserveFileName = '') {
8784 });8784 });
87858785
8786 if (data.error) {8786 if (data.error) {
8787 toastr.error('The file is likely invalid or corrupted.', 'Could not import character');8787 toastr.error(t`The file is likely invalid or corrupted.`, t`Could not import character`);
8788 return;8788 return;
8789 }8789 }
87908790
@@ -8837,7 +8837,7 @@ async function doImpersonate(args, prompt) {
8837 await waitUntilCondition(() => !is_send_press && !is_group_generating, 10000, 100);8837 await waitUntilCondition(() => !is_send_press && !is_group_generating, 10000, 100);
8838 } catch {8838 } catch {
8839 console.warn('Timeout waiting for generation unlock');8839 console.warn('Timeout waiting for generation unlock');
8840 toastr.warning('Cannot run /impersonate command while the reply is being generated.');8840 toastr.warning(t`Cannot run /impersonate command while the reply is being generated.`);
8841 return '';8841 return '';
8842 }8842 }
88438843
@@ -8899,19 +8899,19 @@ async function doDeleteChat() {
88998899
8900async function doRenameChat(_, chatName) {8900async function doRenameChat(_, chatName) {
8901 if (!chatName) {8901 if (!chatName) {
8902 toastr.warning('Name must be provided as an argument to rename this chat.');8902 toastr.warning(t`Name must be provided as an argument to rename this chat.`);
8903 return '';8903 return '';
8904 }8904 }
89058905
8906 const currentChatName = getCurrentChatId();8906 const currentChatName = getCurrentChatId();
8907 if (!currentChatName) {8907 if (!currentChatName) {
8908 toastr.warning('No chat selected that can be renamed.');8908 toastr.warning(t`No chat selected that can be renamed.`);
8909 return '';8909 return '';
8910 }8910 }
89118911
8912 await renameChat(currentChatName, chatName);8912 await renameChat(currentChatName, chatName);
89138913
8914 toastr.success(`Successfully renamed chat to: ${chatName}`);8914 toastr.success(t`Successfully renamed chat to: ${chatName}`);
8915 return '';8915 return '';
8916}8916}
89178917
@@ -9026,7 +9026,7 @@ export async function deleteCharacter(characterKey, { deleteChats = true } = {})
9026 for (const key of characterKey) {9026 for (const key of characterKey) {
9027 const character = characters.find(x => x.avatar == key);9027 const character = characters.find(x => x.avatar == key);
9028 if (!character) {9028 if (!character) {
9029 toastr.warning(`Character ${key} not found. Skipping deletion.`);9029 toastr.warning(t`Character ${key} not found. Skipping deletion.`);
9030 continue;9030 continue;
9031 }9031 }
90329032
@@ -9043,7 +9043,7 @@ export async function deleteCharacter(characterKey, { deleteChats = true } = {})
9043 });9043 });
90449044
9045 if (!response.ok) {9045 if (!response.ok) {
9046 toastr.error(`${response.status} ${response.statusText}`, 'Failed to delete character');9046 toastr.error(`${response.status} ${response.statusText}`, t`Failed to delete character`);
9047 continue;9047 continue;
9048 }9048 }
90499049
@@ -9802,12 +9802,7 @@ jQuery(async function () {
98029802
9803 let deleteChats = false;9803 let deleteChats = false;
98049804
9805 const confirm = await Popup.show.confirm('Delete the character?', `9805 const confirm = await Popup.show.confirm(t`Delete the character?`, await renderTemplateAsync('deleteConfirm'), {
9806 <b>THIS IS PERMANENT!<br><br>
9807 <label for="del_char_checkbox" class="checkbox_label justifyCenter">
9808 <input type="checkbox" id="del_char_checkbox" />
9809 <small>Also delete the chat files</small>
9810 </label></b>`, {
9811 onClose: () => deleteChats = !!$('#del_char_checkbox').prop('checked'),9806 onClose: () => deleteChats = !!$('#del_char_checkbox').prop('checked'),
9812 });9807 });
9813 if (!confirm) {9808 if (!confirm) {
public/scripts/PromptManager.js+7 -6
@@ -8,6 +8,7 @@ import { debounce, waitUntilCondition, escapeHtml } from './utils.js';
8import { debounce_timeout } from './constants.js';8import { debounce_timeout } from './constants.js';
9import { renderTemplateAsync } from './templates.js';9import { renderTemplateAsync } from './templates.js';
10import { Popup } from './popup.js';10import { Popup } from './popup.js';
11import { t } from './i18n.js';
1112
12function debouncePromise(func, delay) {13function debouncePromise(func, delay) {
13 let timeoutId;14 let timeoutId;
@@ -455,7 +456,7 @@ class PromptManager {
455456
456 // Delete selected prompt from list form and close edit form457 // Delete selected prompt from list form and close edit form
457 this.handleDeletePrompt = async (event) => {458 this.handleDeletePrompt = async (event) => {
458 Popup.show.confirm('Are you sure you want to delete this prompt?', null).then((userChoice) => {459 Popup.show.confirm(t`Are you sure you want to delete this prompt?`, null).then((userChoice) => {
459 if (!userChoice) return;460 if (!userChoice) return;
460 const promptID = document.getElementById(this.configuration.prefix + 'prompt_manager_footer_append_prompt').value;461 const promptID = document.getElementById(this.configuration.prefix + 'prompt_manager_footer_append_prompt').value;
461 const prompt = this.getPromptById(promptID);462 const prompt = this.getPromptById(promptID);
@@ -531,7 +532,7 @@ class PromptManager {
531532
532 // Import prompts for the selected character533 // Import prompts for the selected character
533 this.handleImport = () => {534 this.handleImport = () => {
534 Popup.show.confirm('Existing prompts with the same ID will be overridden. Do you want to proceed?', null)535 Popup.show.confirm(t`Existing prompts with the same ID will be overridden. Do you want to proceed?`, null)
535 .then(userChoice => {536 .then(userChoice => {
536 if (!userChoice) return;537 if (!userChoice) return;
537538
@@ -552,7 +553,7 @@ class PromptManager {
552 const data = JSON.parse(fileContent);553 const data = JSON.parse(fileContent);
553 this.import(data);554 this.import(data);
554 } catch (err) {555 } catch (err) {
555 toastr.error('An error occurred while importing prompts. More info available in console.');556 toastr.error(t`An error occurred while importing prompts. More info available in console.`);
556 console.log('An error occurred while importing prompts');557 console.log('An error occurred while importing prompts');
557 console.log(err.toString());558 console.log(err.toString());
558 }559 }
@@ -567,7 +568,7 @@ class PromptManager {
567568
568 // Restore default state of a characters prompt order569 // Restore default state of a characters prompt order
569 this.handleCharacterReset = () => {570 this.handleCharacterReset = () => {
570 Popup.show.confirm('This will reset the prompt order for this character. You will not lose any prompts.', null)571 Popup.show.confirm(t`This will reset the prompt order for this character. You will not lose any prompts.`, null)
571 .then(userChoice => {572 .then(userChoice => {
572 if (!userChoice) return;573 if (!userChoice) return;
573574
@@ -1649,7 +1650,7 @@ class PromptManager {
1649 };1650 };
16501651
1651 if (false === this.validateObject(controlObj, importData)) {1652 if (false === this.validateObject(controlObj, importData)) {
1652 toastr.warning('Could not import prompts. Export failed validation.');1653 toastr.warning(t`Could not import prompts. Export failed validation.`);
1653 return;1654 return;
1654 }1655 }
16551656
@@ -1672,7 +1673,7 @@ class PromptManager {
1672 throw new Error('Prompt order strategy not supported.');1673 throw new Error('Prompt order strategy not supported.');
1673 }1674 }
16741675
1675 toastr.success('Prompt import complete.');1676 toastr.success(t`Prompt import complete.`);
1676 this.saveServiceSettings().then(() => this.render());1677 this.saveServiceSettings().then(() => this.render());
1677 }1678 }
16781679
public/scripts/authors-note.js+10 -9
@@ -16,6 +16,7 @@ import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
16import { SlashCommand } from './slash-commands/SlashCommand.js';16import { SlashCommand } from './slash-commands/SlashCommand.js';
17import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js';17import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js';
18export { MODULE_NAME as NOTE_MODULE_NAME };18export { MODULE_NAME as NOTE_MODULE_NAME };
19import { t } from './i18n.js';
1920
20const MODULE_NAME = '2_floating_prompt'; // <= Deliberate, for sorting lower than memory21const MODULE_NAME = '2_floating_prompt'; // <= Deliberate, for sorting lower than memory
2122
@@ -37,7 +38,7 @@ const chara_note_position = {
3738
38function setNoteTextCommand(_, text) {39function setNoteTextCommand(_, text) {
39 $('#extension_floating_prompt').val(text).trigger('input');40 $('#extension_floating_prompt').val(text).trigger('input');
40 toastr.success('Author\'s Note text updated');41 toastr.success(t`Author's Note text updated`);
41 return '';42 return '';
42}43}
4344
@@ -45,12 +46,12 @@ function setNoteDepthCommand(_, text) {
45 const value = Number(text);46 const value = Number(text);
4647
47 if (Number.isNaN(value)) {48 if (Number.isNaN(value)) {
48 toastr.error('Not a valid number');49 toastr.error(t`Not a valid number`);
49 return;50 return;
50 }51 }
5152
52 $('#extension_floating_depth').val(Math.abs(value)).trigger('input');53 $('#extension_floating_depth').val(Math.abs(value)).trigger('input');
53 toastr.success('Author\'s Note depth updated');54 toastr.success(t`Author's Note depth updated`);
54 return '';55 return '';
55}56}
5657
@@ -58,12 +59,12 @@ function setNoteIntervalCommand(_, text) {
58 const value = Number(text);59 const value = Number(text);
5960
60 if (Number.isNaN(value)) {61 if (Number.isNaN(value)) {
61 toastr.error('Not a valid number');62 toastr.error(t`Not a valid number`);
62 return;63 return;
63 }64 }
6465
65 $('#extension_floating_interval').val(Math.abs(value)).trigger('input');66 $('#extension_floating_interval').val(Math.abs(value)).trigger('input');
66 toastr.success('Author\'s Note frequency updated');67 toastr.success(t`Author's Note frequency updated`);
67 return '';68 return '';
68}69}
6970
@@ -76,12 +77,12 @@ function setNotePositionCommand(_, text) {
76 const position = validPositions[text?.trim()];77 const position = validPositions[text?.trim()];
7778
78 if (Number.isNaN(position)) {79 if (Number.isNaN(position)) {
79 toastr.error('Not a valid position');80 toastr.error(t`Not a valid position`);
80 return;81 return;
81 }82 }
8283
83 $(`input[name="extension_floating_position"][value="${position}"]`).prop('checked', true).trigger('input');84 $(`input[name="extension_floating_position"][value="${position}"]`).prop('checked', true).trigger('input');
84 toastr.info('Author\'s Note position updated');85 toastr.info(t`Author's Note position updated`);
85 return '';86 return '';
86}87}
8788
@@ -206,7 +207,7 @@ function onExtensionFloatingCharaPromptInput() {
206 extension_settings.note.chara.push(tempCharaNote);207 extension_settings.note.chara.push(tempCharaNote);
207 } else {208 } else {
208 console.log('Character author\'s note error: No avatar name key could be found.');209 console.log('Character author\'s note error: No avatar name key could be found.');
209 toastr.error('Something went wrong. Could not save character\'s author\'s note.');210 toastr.error(t`Something went wrong. Could not save character's author's note.`);
210211
211 // Don't save settings if something went wrong212 // Don't save settings if something went wrong
212 return;213 return;
@@ -397,7 +398,7 @@ function onANMenuItemClick() {
397 //because this listener takes priority398 //because this listener takes priority
398 $('#options').stop().fadeOut(animation_duration);399 $('#options').stop().fadeOut(animation_duration);
399 } else {400 } else {
400 toastr.warning('Select a character before trying to use Author\'s Note', '', { timeOut: 2000 });401 toastr.warning(t`Select a character before trying to use Author's Note`, '', { timeOut: 2000 });
401 }402 }
402}403}
403404
public/scripts/chats.js+13 -12
@@ -40,6 +40,7 @@ import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js';
40import { ScraperManager } from './scrapers.js';40import { ScraperManager } from './scrapers.js';
41import { DragAndDropHandler } from './dragdrop.js';41import { DragAndDropHandler } from './dragdrop.js';
42import { renderTemplateAsync } from './templates.js';42import { renderTemplateAsync } from './templates.js';
43import { t } from './i18n.js';
4344
44/**45/**
45 * @typedef {Object} FileAttachment46 * @typedef {Object} FileAttachment
@@ -206,7 +207,7 @@ export async function populateFileAttachment(message, inputId = 'file_form_input
206 const fileText = await converter(file);207 const fileText = await converter(file);
207 base64Data = window.btoa(unescape(encodeURIComponent(fileText)));208 base64Data = window.btoa(unescape(encodeURIComponent(fileText)));
208 } catch (error) {209 } catch (error) {
209 toastr.error(String(error), 'Could not convert file');210 toastr.error(String(error), t`Could not convert file`);
210 console.error('Could not convert file', error);211 console.error('Could not convert file', error);
211 }212 }
212 }213 }
@@ -257,7 +258,7 @@ export async function uploadFileAttachment(fileName, base64Data) {
257 const responseData = await result.json();258 const responseData = await result.json();
258 return responseData.path;259 return responseData.path;
259 } catch (error) {260 } catch (error) {
260 toastr.error(String(error), 'Could not upload file');261 toastr.error(String(error), t`Could not upload file`);
261 console.error('Could not upload file', error);262 console.error('Could not upload file', error);
262 }263 }
263}264}
@@ -283,7 +284,7 @@ export async function getFileAttachment(url) {
283 const text = await result.text();284 const text = await result.text();
284 return text;285 return text;
285 } catch (error) {286 } catch (error) {
286 toastr.error(error, 'Could not download file');287 toastr.error(error, t`Could not download file`);
287 console.error('Could not download file', error);288 console.error('Could not download file', error);
288 }289 }
289}290}
@@ -299,13 +300,13 @@ async function validateFile(file) {
299 const isBinary = /^[\x00-\x08\x0E-\x1F\x7F-\xFF]*$/.test(fileText);300 const isBinary = /^[\x00-\x08\x0E-\x1F\x7F-\xFF]*$/.test(fileText);
300301
301 if (!isImage && file.size > fileSizeLimit) {302 if (!isImage && file.size > fileSizeLimit) {
302 toastr.error(`File is too big. Maximum size is ${humanFileSize(fileSizeLimit)}.`);303 toastr.error(t`File is too big. Maximum size is ${humanFileSize(fileSizeLimit)}.`);
303 return false;304 return false;
304 }305 }
305306
306 // If file is binary307 // If file is binary
307 if (isBinary && !isImage && !isConvertible(file.type)) {308 if (isBinary && !isImage && !isConvertible(file.type)) {
308 toastr.error('Binary files are not supported. Select a text file or image.');309 toastr.error(t`Binary files are not supported. Select a text file or image.`);
309 return false;310 return false;
310 }311 }
311312
@@ -521,7 +522,7 @@ async function openExternalMediaOverridesDialog() {
521 const entityId = getCurrentEntityId();522 const entityId = getCurrentEntityId();
522523
523 if (!entityId) {524 if (!entityId) {
524 toastr.info('No character or group selected');525 toastr.info(t`No character or group selected`);
525 return;526 return;
526 }527 }
527528
@@ -646,7 +647,7 @@ async function deleteFileFromServer(url, silent = false) {
646 await eventSource.emit(event_types.FILE_ATTACHMENT_DELETED, url);647 await eventSource.emit(event_types.FILE_ATTACHMENT_DELETED, url);
647 return true;648 return true;
648 } catch (error) {649 } catch (error) {
649 toastr.error(String(error), 'Could not delete file');650 toastr.error(String(error), t`Could not delete file`);
650 console.error('Could not delete file', error);651 console.error('Could not delete file', error);
651 return false;652 return false;
652 }653 }
@@ -1054,7 +1055,7 @@ async function openAttachmentManager() {
1054 const selectedAttachments = document.querySelectorAll('.attachmentListItemCheckboxContainer .attachmentListItemCheckbox:checked');1055 const selectedAttachments = document.querySelectorAll('.attachmentListItemCheckboxContainer .attachmentListItemCheckbox:checked');
10551056
1056 if (selectedAttachments.length === 0) {1057 if (selectedAttachments.length === 0) {
1057 toastr.info('No attachments selected.', 'Data Bank');1058 toastr.info(t`No attachments selected.`, t`Data Bank`);
1058 return;1059 return;
1059 }1060 }
10601061
@@ -1168,7 +1169,7 @@ async function runScraper(scraperId, target, callback) {
11681169
1169 if (files.length === 0) {1170 if (files.length === 0) {
1170 console.warn('Scraping returned no files');1171 console.warn('Scraping returned no files');
1171 toastr.info('No files were scraped.', 'Data Bank');1172 toastr.info(t`No files were scraped.`, t`Data Bank`);
1172 return;1173 return;
1173 }1174 }
11741175
@@ -1176,12 +1177,12 @@ async function runScraper(scraperId, target, callback) {
1176 await uploadFileAttachmentToServer(file, target);1177 await uploadFileAttachmentToServer(file, target);
1177 }1178 }
11781179
1179 toastr.success(`Scraped ${files.length} files from ${scraperId} to ${target}.`, 'Data Bank');1180 toastr.success(t`Scraped ${files.length} files from ${scraperId} to ${target}.`, t`Data Bank`);
1180 callback();1181 callback();
1181 }1182 }
1182 catch (error) {1183 catch (error) {
1183 console.error('Scraping failed', error);1184 console.error('Scraping failed', error);
1184 toastr.error('Check browser console for details.', 'Scraping failed');1185 toastr.error(t`Check browser console for details.`, t`Scraping failed`);
1185 }1186 }
1186}1187}
11871188
@@ -1208,7 +1209,7 @@ export async function uploadFileAttachmentToServer(file, target) {
1208 const fileText = await converter(file);1209 const fileText = await converter(file);
1209 base64Data = window.btoa(unescape(encodeURIComponent(fileText)));1210 base64Data = window.btoa(unescape(encodeURIComponent(fileText)));
1210 } catch (error) {1211 } catch (error) {
1211 toastr.error(String(error), 'Could not convert file');1212 toastr.error(String(error), t`Could not convert file`);
1212 console.error('Could not convert file', error);1213 console.error('Could not convert file', error);
1213 }1214 }
1214 } else {1215 } else {
public/scripts/extensions/regex/editor.html+1 -1
@@ -132,7 +132,7 @@
132 <small data-i18n="ext_regex_other_options" data-i18n="Ephemerality">Ephemerality</small>132 <small data-i18n="ext_regex_other_options" data-i18n="Ephemerality">Ephemerality</small>
133 <span class="fa-solid fa-circle-question note-link-span" title="By default, regex scripts alter the chat file directly and irreversibly.&#13;Enabling either (or both) of the options below will prevent chat file alteration, while still altering the specified item(s)."></span>133 <span class="fa-solid fa-circle-question note-link-span" title="By default, regex scripts alter the chat file directly and irreversibly.&#13;Enabling either (or both) of the options below will prevent chat file alteration, while still altering the specified item(s)."></span>
134 </span>134 </span>
135 <label class="checkbox flex-container" title="Chat history file contents won't change, but regex will be applied to the messages displayed in the Chat UI.">135 <label class="checkbox flex-container" data-i18n="[title]ext_regex_only_format_visual_desc" title="Chat history file contents won't change, but regex will be applied to the messages displayed in the Chat UI.">
136 <input type="checkbox" name="only_format_display" />136 <input type="checkbox" name="only_format_display" />
137 <span data-i18n="Only Format Display">Alter Chat Display</span>137 <span data-i18n="Only Format Display">Alter Chat Display</span>
138 </label>138 </label>
public/scripts/group-chats.js+10 -10
@@ -189,8 +189,8 @@ async function validateGroup(group) {
189 group.members = group.members.filter(member => {189 group.members = group.members.filter(member => {
190 const character = characters.find(x => x.avatar === member || x.name === member);190 const character = characters.find(x => x.avatar === member || x.name === member);
191 if (!character) {191 if (!character) {
192 const msg = `Warning: Listed member ${member} does not exist as a character. It will be removed from the group.`;192 const msg = t`Warning: Listed member ${member} does not exist as a character. It will be removed from the group.`;
193 toastr.warning(msg, 'Group Validation');193 toastr.warning(msg, t`Group Validation`);
194 console.warn(msg);194 console.warn(msg);
195 dirty = true;195 dirty = true;
196 }196 }
@@ -522,7 +522,7 @@ async function saveGroupChat(groupId, shouldSaveGroup) {
522 });522 });
523523
524 if (!response.ok) {524 if (!response.ok) {
525 toastr.error('Check the server connection and reload the page to prevent data loss.', 'Group Chat could not be saved');525 toastr.error(t`Check the server connection and reload the page to prevent data loss.`, t`Group Chat could not be saved`);
526 console.error('Group chat could not be saved', response);526 console.error('Group chat could not be saved', response);
527 return;527 return;
528 }528 }
@@ -837,7 +837,7 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
837 activatedMembers = activateSwipe(group.members);837 activatedMembers = activateSwipe(group.members);
838838
839 if (activatedMembers.length === 0) {839 if (activatedMembers.length === 0) {
840 toastr.warning('Deleted group member swiped. To get a reply, add them back to the group.');840 toastr.warning(t`Deleted group member swiped. To get a reply, add them back to the group.`);
841 throw new Error('Deleted group member swiped');841 throw new Error('Deleted group member swiped');
842 }842 }
843 }843 }
@@ -1368,15 +1368,15 @@ function isGroupMemberDisabled(avatarId) {
13681368
1369async function onDeleteGroupClick() {1369async function onDeleteGroupClick() {
1370 if (!openGroupId) {1370 if (!openGroupId) {
1371 toastr.warning('Currently no group selected.');1371 toastr.warning(t`Currently no group selected.`);
1372 return;1372 return;
1373 }1373 }
1374 if (is_group_generating) {1374 if (is_group_generating) {
1375 toastr.warning('Not so fast! Wait for the characters to stop typing before deleting the group.');1375 toastr.warning(t`Not so fast! Wait for the characters to stop typing before deleting the group.`);
1376 return;1376 return;
1377 }1377 }
13781378
1379 const confirm = await Popup.show.confirm('Delete the group?', '<p>This will also delete all your chats with that group. If you want to delete a single conversation, select a "View past chats" option in the lower left menu.</p>');1379 const confirm = await Popup.show.confirm(t`Delete the group?`, '<p>' + t`This will also delete all your chats with that group. If you want to delete a single conversation, select a "View past chats" option in the lower left menu.` + '</p>');
1380 if (confirm) {1380 if (confirm) {
1381 deleteGroup(openGroupId);1381 deleteGroup(openGroupId);
1382 }1382 }
@@ -1630,7 +1630,7 @@ function updateFavButtonState(state) {
16301630
1631export async function openGroupById(groupId) {1631export async function openGroupById(groupId) {
1632 if (isChatSaving) {1632 if (isChatSaving) {
1633 toastr.info('Please wait until the chat is saved before switching characters.', 'Your chat is still saving...');1633 toastr.info(t`Please wait until the chat is saved before switching characters.`, t`Your chat is still saving...`);
1634 return;1634 return;
1635 }1635 }
16361636
@@ -1659,7 +1659,7 @@ export async function openGroupById(groupId) {
16591659
1660function openCharacterDefinition(characterSelect) {1660function openCharacterDefinition(characterSelect) {
1661 if (is_group_generating) {1661 if (is_group_generating) {
1662 toastr.warning('Can\'t peek a character while group reply is being generated');1662 toastr.warning(t`Can't peek a character while group reply is being generated`);
1663 console.warn('Can\'t peek a character def while group reply is being generated');1663 console.warn('Can\'t peek a character def while group reply is being generated');
1664 return;1664 return;
1665 }1665 }
@@ -1908,7 +1908,7 @@ export async function saveGroupBookmarkChat(groupId, name, metadata, mesId) {
1908 });1908 });
19091909
1910 if (!response.ok) {1910 if (!response.ok) {
1911 toastr.error('Check the server connection and reload the page to prevent data loss.', 'Group chat could not be saved');1911 toastr.error(t`Check the server connection and reload the page to prevent data loss.`, t`Group chat could not be saved`);
1912 console.error('Group chat could not be saved', response);1912 console.error('Group chat could not be saved', response);
1913 }1913 }
1914}1914}
public/scripts/openai.js+25 -28
@@ -388,7 +388,7 @@ async function validateReverseProxy() {
388 new URL(oai_settings.reverse_proxy);388 new URL(oai_settings.reverse_proxy);
389 }389 }
390 catch (err) {390 catch (err) {
391 toastr.error('Entered reverse proxy address is not a valid URL');391 toastr.error(t`Entered reverse proxy address is not a valid URL`);
392 setOnlineStatus('no_connection');392 setOnlineStatus('no_connection');
393 resultCheckStatus();393 resultCheckStatus();
394 throw err;394 throw err;
@@ -399,7 +399,7 @@ async function validateReverseProxy() {
399 const confirmation = skipConfirm || await Popup.show.confirm(t`Connecting To Proxy`, await renderTemplateAsync('proxyConnectionWarning', { proxyURL: DOMPurify.sanitize(oai_settings.reverse_proxy) }));399 const confirmation = skipConfirm || await Popup.show.confirm(t`Connecting To Proxy`, await renderTemplateAsync('proxyConnectionWarning', { proxyURL: DOMPurify.sanitize(oai_settings.reverse_proxy) }));
400400
401 if (!confirmation) {401 if (!confirmation) {
402 toastr.error('Update or remove your reverse proxy settings.');402 toastr.error(t`Update or remove your reverse proxy settings.`);
403 setOnlineStatus('no_connection');403 setOnlineStatus('no_connection');
404 resultCheckStatus();404 resultCheckStatus();
405 throw new Error('Proxy connection denied.');405 throw new Error('Proxy connection denied.');
@@ -1231,15 +1231,15 @@ export async function prepareOpenAIMessages({
1231 await populateChatCompletion(prompts, chatCompletion, { bias, quietPrompt, quietImage, type, cyclePrompt, messages, messageExamples });1231 await populateChatCompletion(prompts, chatCompletion, { bias, quietPrompt, quietImage, type, cyclePrompt, messages, messageExamples });
1232 } catch (error) {1232 } catch (error) {
1233 if (error instanceof TokenBudgetExceededError) {1233 if (error instanceof TokenBudgetExceededError) {
1234 toastr.error('An error occurred while counting tokens: Token budget exceeded.');1234 toastr.error(t`An error occurred while counting tokens: Token budget exceeded.`);
1235 chatCompletion.log('Token budget exceeded.');1235 chatCompletion.log('Token budget exceeded.');
1236 promptManager.error = 'Not enough free tokens for mandatory prompts. Raise your token Limit or disable custom prompts.';1236 promptManager.error = t`Not enough free tokens for mandatory prompts. Raise your token Limit or disable custom prompts.`;
1237 } else if (error instanceof InvalidCharacterNameError) {1237 } else if (error instanceof InvalidCharacterNameError) {
1238 toastr.warning('An error occurred while counting tokens: Invalid character name');1238 toastr.warning(t`An error occurred while counting tokens: Invalid character name`);
1239 chatCompletion.log('Invalid character name');1239 chatCompletion.log('Invalid character name');
1240 promptManager.error = 'The name of at least one character contained whitespaces or special characters. Please check your user and character name.';1240 promptManager.error = t`The name of at least one character contained whitespaces or special characters. Please check your user and character name.`;
1241 } else {1241 } else {
1242 toastr.error('An unknown error occurred while counting tokens. Further information may be available in console.');1242 toastr.error(t`An unknown error occurred while counting tokens. Further information may be available in console.`);
1243 chatCompletion.log('----- Unexpected error while preparing prompts -----');1243 chatCompletion.log('----- Unexpected error while preparing prompts -----');
1244 chatCompletion.log(error);1244 chatCompletion.log(error);
1245 chatCompletion.log(error.stack);1245 chatCompletion.log(error.stack);
@@ -1293,11 +1293,8 @@ function tryParseStreamingError(response, decoded) {
1293 }1293 }
1294}1294}
12951295
1296function checkQuotaError(data) {1296async function checkQuotaError(data) {
1297 const errorText = `<h3>Encountered an error while processing your request.<br>1297 const errorText = await renderTemplateAsync('quotaError');
1298 Check you have credits available on your
1299 <a href="https://platform.openai.com/account/usage" target="_blank">OpenAI account</a>.<br>
1300 If you have sufficient credits, please try again later.</h3>`;
13011298
1302 if (!data) {1299 if (!data) {
1303 return;1300 return;
@@ -1933,11 +1930,11 @@ async function sendOpenAIRequest(type, messages, signal) {
1933 else {1930 else {
1934 const data = await response.json();1931 const data = await response.json();
19351932
1936 checkQuotaError(data);1933 await checkQuotaError(data);
1937 checkModerationError(data);1934 checkModerationError(data);
19381935
1939 if (data.error) {1936 if (data.error) {
1940 toastr.error(data.error.message || response.statusText, 'API returned an error');1937 toastr.error(data.error.message || response.statusText, t`API returned an error`);
1941 throw new Error(data);1938 throw new Error(data);
1942 }1939 }
19431940
@@ -2184,7 +2181,7 @@ function parseOpenAITextLogprobs(logprobs) {
21842181
2185function handleWindowError(err) {2182function handleWindowError(err) {
2186 const text = parseWindowError(err);2183 const text = parseWindowError(err);
2187 toastr.error(text, 'Window.ai returned an error');2184 toastr.error(text, t`Window.ai returned an error`);
2188 throw err;2185 throw err;
2189}2186}
21902187
@@ -3257,7 +3254,7 @@ async function getStatusOpen() {
3257}3254}
32583255
3259function showWindowExtensionError() {3256function showWindowExtensionError() {
3260 toastr.error('Get it here: <a href="https://windowai.io/" target="_blank">windowai.io</a>', 'Extension is not installed', {3257 toastr.error(t`Get it here:` + ' <a href="https://windowai.io/" target="_blank">windowai.io</a>', t`Extension is not installed`, {
3261 escapeHtml: false,3258 escapeHtml: false,
3262 timeOut: 0,3259 timeOut: 0,
3263 extendedTimeOut: 0,3260 extendedTimeOut: 0,
@@ -3376,7 +3373,7 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
3376 if (triggerUi) $('#settings_preset_openai').append(option).trigger('change');3373 if (triggerUi) $('#settings_preset_openai').append(option).trigger('change');
3377 }3374 }
3378 } else {3375 } else {
3379 toastr.error('Failed to save preset');3376 toastr.error(t`Failed to save preset`);
3380 throw new Error('Failed to save preset');3377 throw new Error('Failed to save preset');
3381 }3378 }
3382}3379}
@@ -3455,7 +3452,7 @@ async function createNewLogitBiasPreset() {
3455 }3452 }
34563453
3457 if (name in oai_settings.bias_presets) {3454 if (name in oai_settings.bias_presets) {
3458 toastr.error('Preset name should be unique.');3455 toastr.error(t`Preset name should be unique.`);
3459 return;3456 return;
3460 }3457 }
34613458
@@ -3499,7 +3496,7 @@ async function onPresetImportFileChange(e) {
3499 try {3496 try {
3500 presetBody = JSON.parse(importedFile);3497 presetBody = JSON.parse(importedFile);
3501 } catch (err) {3498 } catch (err) {
3502 toastr.error('Invalid file');3499 toastr.error(t`Invalid file`);
3503 return;3500 return;
3504 }3501 }
35053502
@@ -3540,7 +3537,7 @@ async function onPresetImportFileChange(e) {
3540 });3537 });
35413538
3542 if (!savePresetSettings.ok) {3539 if (!savePresetSettings.ok) {
3543 toastr.error('Failed to save preset');3540 toastr.error(t`Failed to save preset`);
3544 return;3541 return;
3545 }3542 }
35463543
@@ -3565,7 +3562,7 @@ async function onPresetImportFileChange(e) {
35653562
3566async function onExportPresetClick() {3563async function onExportPresetClick() {
3567 if (!oai_settings.preset_settings_openai) {3564 if (!oai_settings.preset_settings_openai) {
3568 toastr.error('No preset selected');3565 toastr.error(t`No preset selected`);
3569 return;3566 return;
3570 }3567 }
35713568
@@ -3606,12 +3603,12 @@ async function onLogitBiasPresetImportFileChange(e) {
3606 e.target.value = '';3603 e.target.value = '';
36073604
3608 if (name in oai_settings.bias_presets) {3605 if (name in oai_settings.bias_presets) {
3609 toastr.error('Preset name should be unique.');3606 toastr.error(t`Preset name should be unique.`);
3610 return;3607 return;
3611 }3608 }
36123609
3613 if (!Array.isArray(importedFile)) {3610 if (!Array.isArray(importedFile)) {
3614 toastr.error('Invalid logit bias preset file.');3611 toastr.error(t`Invalid logit bias preset file.`);
3615 return;3612 return;
3616 }3613 }
36173614
@@ -3670,16 +3667,16 @@ async function onDeletePresetClick() {
3670 });3667 });
36713668
3672 if (!response.ok) {3669 if (!response.ok) {
3673 toastr.warning('Preset was not deleted from server');3670 toastr.warning(t`Preset was not deleted from server`);
3674 } else {3671 } else {
3675 toastr.success('Preset deleted');3672 toastr.success(t`Preset deleted`);
3676 }3673 }
36773674
3678 saveSettingsDebounced();3675 saveSettingsDebounced();
3679}3676}
36803677
3681async function onLogitBiasPresetDeleteClick() {3678async function onLogitBiasPresetDeleteClick() {
3682 const value = await callPopup('Delete the preset?', 'confirm');3679 const value = await callPopup(t`Delete the preset?`, 'confirm');
36833680
3684 if (!value) {3681 if (!value) {
3685 return;3682 return;
@@ -4816,7 +4813,7 @@ function runProxyCallback(_, value) {
4816 const result = fuse.search(value);4813 const result = fuse.search(value);
48174814
4818 if (result.length === 0) {4815 if (result.length === 0) {
4819 toastr.warning(`Proxy preset "${value}" not found`);4816 toastr.warning(t`Proxy preset '${value}' not found`);
4820 return '';4817 return '';
4821 }4818 }
48224819
@@ -4990,7 +4987,7 @@ export function initOpenAI() {
4990 $('#update_oai_preset').on('click', async function () {4987 $('#update_oai_preset').on('click', async function () {
4991 const name = oai_settings.preset_settings_openai;4988 const name = oai_settings.preset_settings_openai;
4992 await saveOpenAIPreset(name, oai_settings);4989 await saveOpenAIPreset(name, oai_settings);
4993 toastr.success('Preset updated');4990 toastr.success(t`Preset updated`);
4994 });4991 });
49954992
4996 $('#impersonation_prompt_restore').on('click', function () {4993 $('#impersonation_prompt_restore').on('click', function () {
public/scripts/templates/assistantNote.html+1 -1
@@ -1,3 +1,3 @@
1<div>1<div>
2 <b>Note:</b> this chat is temporary and will be deleted as soon as you leave it.2 <b data-i18n="Note:">Note:</b> <span data-i18n="this chat is temporary and will be deleted as soon as you leave it.">this chat is temporary and will be deleted as soon as you leave it.</span>
3</div>3</div>
public/scripts/templates/deleteConfirm.html+5 -0
@@ -0,0 +1,5 @@
1<b><span data-i18n="THIS IS PERMANENT!">THIS IS PERMANENT!</span><br><br>
2<label for="del_char_checkbox" class="checkbox_label justifyCenter">
3 <input type="checkbox" id="del_char_checkbox" />
4 <small data-i18n="Also delete the chat files">Also delete the chat files</small>
5</label></b>
\ No newline at end of file5 \ No newline at end of file
public/scripts/templates/duplicateConfirm.html+2 -2
@@ -1,5 +1,5 @@
1<div>1<div>
2 <h3>Are you sure you want to duplicate this character?</h3>2 <h3 data-i18n="Are you sure you want to duplicate this character?">Are you sure you want to duplicate this character?</h3>
3 <span>If you just want to start a new chat with the same character, use "Start new chat" option in the bottom-left options menu.</span>3 <span data-i18n="If you just want to start a new chat with the same character...">If you just want to start a new chat with the same character, use "Start new chat" option in the bottom-left options menu.</span>
4 <br>4 <br>
5</div>5</div>
public/scripts/templates/hotkeys.html+7 -7
@@ -14,18 +14,18 @@
14 <li><kbd data-i18n="help_hotkeys_19">Ctrl+Shift+Down</kbd> = <span data-i18n="help_hotkeys_20">Scroll chat to bottom</span></li>14 <li><kbd data-i18n="help_hotkeys_19">Ctrl+Shift+Down</kbd> = <span data-i18n="help_hotkeys_20">Scroll chat to bottom</span></li>
15</ul>15</ul>
16<div>16<div>
17 <strong>Markdown Hotkeys</strong>17 <strong data-i18n="help_hotkeys_20">Markdown Hotkeys</strong>
18</div>18</div>
19<div>19<div>
20 <small>20 <small>
21 <span>Works in the chatbar and textareas marked with this icon:</span>21 <span data-i18n="help_hotkeys_21">Works in the chatbar and textareas marked with this icon:</span>
22 <code><i class="fa-brands fa-markdown"></i></code>22 <code><i class="fa-brands fa-markdown"></i></code>
23 </small>23 </small>
24</div>24</div>
25<ul>25<ul>
26 <li><kbd>Ctrl+B</kbd> = <span>**bold**</span></li>26 <li><kbd>Ctrl+B</kbd> = <span data-i18n="help_hotkeys_22">**bold**</span></li>
27 <li><kbd>Ctrl+I</kbd> = <span>*italic*</span></li>27 <li><kbd>Ctrl+I</kbd> = <span data-i18n="help_hotkeys_23">*italic*</span></li>
28 <li><kbd>Ctrl+U</kbd> = <span>__underline__</span></li>28 <li><kbd>Ctrl+U</kbd> = <span data-i18n="help_hotkeys_24">__underline__</span></li>
29 <li><kbd>Ctrl+K</kbd> = <span>`inline code`</span></li>29 <li><kbd>Ctrl+K</kbd> = <span data-i18n="help_hotkeys_25">`inline code`</span></li>
30 <li><kbd>Ctrl+Shift+~</kbd> = <span>~~strikethrough~~</span></li>30 <li><kbd>Ctrl+Shift+~</kbd> = <span data-i18n="help_hotkeys_26">~~strikethrough~~</span></li>
31</ul>31</ul>
public/scripts/templates/quotaError.html+4 -0
@@ -0,0 +1,4 @@
1<h3><span data-i18n="Encountered an error while processing your request.">Encountered an error while processing your request.</span><br>
2 <span data-i18n="Check you have credits available on your">Check you have credits available on your</span>
3 <a href="https://platform.openai.com/account/usage" target="_blank" data-i18n="OpenAI account quora_error">OpenAI account</a><span data-i18n="dot quota_error">.</span><br>
4 <span data-i18n="If you have sufficient credits, please try again later.">If you have sufficient credits, please try again later.</span></h3>
\ No newline at end of file4 \ No newline at end of file