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

a09a60ef314cc79e9f9c8046e407189814d428f6

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

Signed
13 files changed, +296 -146Showing whitespace changes
public/locales/ru-ru.json+152 -6
@@ -56,7 +56,7 @@
5656 "Rep. Pen. Slope": "Rep. Pen. Slope",
5757 "Top K": "Top K",
5858 "Top P": "Top P",
5959 "Do Sample": "СделатьВключить образецсэмплинг",
6060 "Add BOS Token": "Добавлять BOS-токен",
6161 "Add the bos_token to the beginning of prompts. Disabling this can make the replies more creative": "Добавлять BOS-токен в начале промпта. Если выключить, ответы могут стать более креативными.",
6262 "Ban EOS Token": "Запретить EOS-токен",
@@ -451,8 +451,8 @@
451451 "Creator's Notes": "Заметки создателя",
452452 "A-Z": "A-Z",
453453 "Z-A": "Z-A",
454454 "Newest": "НовейшиеСначала новые",
455455 "Oldest": "СтарейшиеСначала старые",
456456 "Favorites": "Избранные",
457457 "Recent": "Последние",
458458 "Most chats": "Больше всего чатов",
@@ -564,7 +564,7 @@
564564 "Advanced Definition": "Расширенное описание",
565565 "Character Lore": "Лор персонажа",
566566 "Export and Download": "Экспортировать и скачать",
567567 "Duplicate Character": "ДублироватьКлонировать персонажа",
568568 "Create Character": "Создать персонажа",
569569 "Delete Character": "Удалить персонажа",
570570 "View all tags": "Показать все тэги",
@@ -1690,7 +1690,7 @@
16901690 "Delete Message": "Удалить сообщение",
16911691 "Delete Swipe": "Удалить свайп",
16921692 "Could not get a reply from API. Check your connection settings / API key and try again.": "Не удалось получить ответ от API. Проверьте настройки соединения и API-ключ и повторите попытку.",
16931693 "Connecting To Proxy": "ПодключениеПодключиться к прокси",
16941694 "Are you sure you want to connect to the following proxy URL?": "Вы точно хотите соединиться с прокси по этому адресу?",
16951695 "API connection successful!": "Соединение с API установлено!",
16961696 "Proxy Saved": "Прокси сохранена",
@@ -1747,5 +1747,151 @@
17471747 "Markdown Hotkeys": "Горячие клавиши для разметки",
17481748 "markdown_hotkeys_desc": "Включить горячие клавиши для вставки символов разметки в некоторых полях ввода. См. '/help hotkeys'.",
17491749 "Save and Update": "Сохранить и обновить",
17501750 "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 начинает налагать штраф."
17511897}
public/script.js+59 -64
@@ -391,8 +391,8 @@ DOMPurify.addHook('uponSanitizeElement', (node, _, config) => {
391391
392392 if (localStorage.getItem(warningShownKey) === null) {
393393 const warningToast = toastr.warning(
394394 't`Use the "'Ext. Media"' button to allow it. Click on this message to dismiss.'`,
395395 't`External media has been blocked'`,
396396 {
397397 timeOut: 0,
398398 preventDuplicates: true,
@@ -933,7 +933,7 @@ async function firstLoadInit() {
933933 token = tokenData.token;
934934 } catch {
935935 hideLoader();
936936 toastr.error('t`Couldn\'t get CSRF token. Please refresh the page.'`, 't`Error'`, { timeOut: 0, extendedTimeOut: 0, preventDuplicates: true });
937937 throw new Error('Initialization failed');
938938 }
939939
@@ -1149,7 +1149,7 @@ async function getStatusKobold() {
11491149
11501150 // We didn't get a 200 status code, but the endpoint has an explanation. Which means it DID connect, but I digress.
11511151 if (online_status === 'no_connection' && data.response) {
11521152 toastr.error(data.response, 't`API Error'`, { timeOut: 5000, preventDuplicates: true });
11531153 }
11541154 } catch (err) {
11551155 console.error('Error getting status', err);
@@ -1235,7 +1235,7 @@ async function getStatusTextgen() {
12351235
12361236 // We didn't get a 200 status code, but the endpoint has an explanation. Which means it DID connect, but I digress.
12371237 if (online_status === 'no_connection' && data.response) {
12381238 toastr.error(data.response, 't`API Error'`, { timeOut: 5000, preventDuplicates: true });
12391239 }
12401240 } catch (err) {
12411241 if (err instanceof AbortReason) {
@@ -1287,7 +1287,7 @@ export async function selectCharacterById(id) {
12871287 }
12881288
12891289 if (isChatSaving) {
12901290 toastr.info('t`Please wait until the chat is saved before switching characters.'`, 't`Your chat is still saving...'`);
12911291 return;
12921292 }
12931293
@@ -1641,7 +1641,7 @@ export async function getOneCharacter(avatarUrl) {
16411641 if (indexOf !== -1) {
16421642 characters[indexOf] = getData;
16431643 } else {
16441644 toastr.error(t`Character ${avatarUrl} not found in the list`, 't`Error'`, { timeOut: 5000, preventDuplicates: true });
16451645 }
16461646 }
16471647}
@@ -3408,7 +3408,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
34083408 await eventSource.emit(event_types.GENERATION_AFTER_COMMANDS, type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage }, dryRun);
34093409
34103410 if (main_api == 'kobold' && kai_settings.streaming_kobold && !kai_flags.can_use_streaming) {
34113411 toastr.error('t`Streaming is enabled, but the version of Kobold used does not support token streaming.'`, undefined, { timeOut: 10000, preventDuplicates: true });
34123412 unblockGeneration(type);
34133413 return Promise.resolve();
34143414 }
@@ -3417,7 +3417,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
34173417 textgen_settings.streaming &&
34183418 textgen_settings.legacy_api &&
34193419 textgen_settings.type === OOBA) {
34203420 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 });
34213421 unblockGeneration(type);
34223422 return Promise.resolve();
34233423 }
@@ -3433,7 +3433,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
34333433
34343434 if (!pingResult) {
34353435 unblockGeneration(type);
34363436 toastr.error('t`Verify that the server is running and accessible.'`, 't`ST Server cannot be reached'`);
34373437 throw new Error('Server unreachable');
34383438 }
34393439
@@ -4461,7 +4461,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
44614461 generatedPromptCache = '';
44624462
44634463 if (data?.response) {
44644464 toastr.error(data.response, 't`API Error'`, { preventDuplicates: true });
44654465 }
44664466 throw new Error(data?.response);
44674467 }
@@ -4552,7 +4552,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
45524552
45534553 function onError(exception) {
45544554 if (typeof exception?.error?.message === 'string') {
45554555 toastr.error(exception.error.message, 't`Error'`, { timeOut: 10000, extendedTimeOut: 20000 });
45564556 }
45574557
45584558 generatedPromptCache = '';
@@ -4956,7 +4956,7 @@ function addChatsSeparator(mesSendString) {
49564956
49574957async function duplicateCharacter() {
49584958 if (!this_chid) {
49594959 toastr.warning('t`You must first select a character to duplicate!'`);
49604960 return '';
49614961 }
49624962
@@ -4975,7 +4975,7 @@ async function duplicateCharacter() {
49754975 body: JSON.stringify(body),
49764976 });
49774977 if (response.ok) {
49784978 toastr.success('t`Character Duplicated'`);
49794979 const data = await response.json();
49804980 await eventSource.emit(event_types.CHARACTER_DUPLICATED, { oldAvatar: body.avatar_url, newAvatar: data.path });
49814981 await getCharacters();
@@ -5822,23 +5822,23 @@ export function setSendButtonState(value) {
58225822
58235823export async function renameCharacter(name = null, { silent = false, renameChats = null } = {}) {
58245824 if (!name && silent) {
58255825 toastr.warning('t`No character name provided.'`, 't`Rename Character'`);
58265826 return false;
58275827 }
58285828 if (this_chid === undefined) {
58295829 toastr.warning('t`No character selected.'`, 't`Rename Character'`);
58305830 return false;
58315831 }
58325832
58335833 const oldAvatar = characters[this_chid].avatar;
58345834 const newValue = name || await callGenericPopup('<h3>' + t`New name:` + '</h3>', POPUP_TYPE.INPUT, characters[this_chid].name);
58355835
58365836 if (!newValue) {
58375837 toastr.warning('t`No character name provided.'`, 't`Rename Character'`);
58385838 return false;
58395839 }
58405840 if (newValue === characters[this_chid].name) {
58415841 toastr.info('t`Same character name provided, so name did not change.'`, 't`Rename Character'`);
58425842 return false;
58435843 }
58445844
@@ -5885,9 +5885,9 @@ export async function renameCharacter(name = null, { silent = false, renameChats
58855885 if (renamePastChatsConfirm) {
58865886 await renamePastChats(newAvatar, newValue);
58875887 await reloadCurrentChat();
58885888 toastr.success('t`Character renamed and past chats updated!'`, 't`Rename Character'`);
58895889 } else {
58905890 toastr.success('t`Character renamed!'`, 't`Rename Character'`);
58915891 }
58925892 }
58935893 else {
@@ -5900,8 +5900,8 @@ export async function renameCharacter(name = null, { silent = false, renameChats
59005900 }
59015901 catch (error) {
59025902 // Reloading to prevent data corruption
59035903 if (!silent) await callPopup('t`Something went wrong. The page will be reloaded.'`, 'text');
59045904 else toastr.error('t`Something went wrong. The page will be reloaded.'`, 't`Rename Character'`);
59055905
59065906 console.log('Renaming character error:', error);
59075907 location.reload();
@@ -5958,7 +5958,7 @@ async function renamePastChats(newAvatar, newValue) {
59585958 }
59595959 }
59605960 } catch (error) {
59615961 toastr.error(t`Past chat could not be updated: ${file_name}`);
59625962 console.error(error);
59635963 }
59645964 }
@@ -6007,7 +6007,7 @@ export async function saveChat(chatName, withMetadata, mesId) {
60076007 characters[this_chid]['date_last_chat'] = Date.now();
60086008 chat.forEach(function (item, i) {
60096009 if (item['is_group']) {
60106010 toastr.error('t`Trying to save group chat with regular saveChat function. Aborting to prevent corruption.'`);
60116011 throw new Error('Group chat saved from saveChat');
60126012 }
60136013 /*
@@ -6052,7 +6052,7 @@ export async function saveChat(chatName, withMetadata, mesId) {
60526052 contentType: 'application/json',
60536053 success: function (data) { },
60546054 error: function (jqXHR, exception) {
60556055 toastr.error('t`Check the server connection and reload the page to prevent data loss.'`, 't`Chat could not be saved'`);
60566056 console.log(exception);
60576057 console.log(jqXHR);
60586058 },
@@ -6449,7 +6449,7 @@ export async function getSettings() {
64496449
64506450 if (!response.ok) {
64516451 reloadLoop();
64526452 toastr.error('t`Settings could not be loaded after multiple attempts. Please try again later.'`);
64536453 throw new Error('Error getting settings');
64546454 }
64556455
@@ -6676,7 +6676,7 @@ export async function saveSettings(type) {
66766676 eventSource.emit(event_types.SETTINGS_UPDATED);
66776677 },
66786678 error: function (jqXHR, exception) {
66796679 toastr.error('t`Check the server connection and reload the page to prevent data loss.'t`, 't`Settings could not be saved'`);
66806680 console.log(exception);
66816681 console.log(jqXHR);
66826682 },
@@ -6942,7 +6942,7 @@ export async function displayPastChats() {
69426942 const data = await (selected_group ? getGroupPastChats(selected_group) : getPastCharacterChats());
69436943
69446944 if (!data) {
69456945 toastr.error('t`Could not load chat data. Try reloading the page.'`);
69466946 return;
69476947 }
69486948
@@ -7075,7 +7075,7 @@ export function selectRightMenuWithAnimation(selectedMenuId) {
70757075
70767076export function select_rm_info(type, charId, previousCharId = null) {
70777077 if (!type) {
70787078 toastr.error('t`Invalid process (no \'type\')'`);
70797079 return;
70807080 }
70817081 if (type !== 'group_create') {
@@ -7083,20 +7083,20 @@ export function select_rm_info(type, charId, previousCharId = null) {
70837083 }
70847084
70857085 if (type === 'char_delete') {
70867086 toastr.warning(t`Character Deleted: ${displayName}`);
70877087 }
70887088 if (type === 'char_create') {
70897089 toastr.success(t`Character Created: ${displayName}`);
70907090 }
70917091 if (type === 'group_create') {
70927092 toastr.success('t`Group Created'`);
70937093 }
70947094 if (type === 'group_delete') {
70957095 toastr.warning('t`Group Deleted'`);
70967096 }
70977097
70987098 if (type === 'char_import') {
70997099 toastr.success(t`Character Imported: ${displayName}`);
71007100 }
71017101
71027102 selectRightMenuWithAnimation('rm_characters_block');
@@ -7554,25 +7554,25 @@ export function hideSwipeButtons() {
75547554 */
75557555export async function deleteSwipe(swipeId = null) {
75567556 if (swipeId && (isNaN(swipeId) || swipeId < 0)) {
75577557 toastr.warning(t`Invalid swipe ID: ${swipeId + 1}`);
75587558 return;
75597559 }
75607560
75617561 const lastMessage = chat[chat.length - 1];
75627562 if (!lastMessage || !Array.isArray(lastMessage.swipes) || !lastMessage.swipes.length) {
75637563 toastr.warning('t`No messages to delete swipes from.'`);
75647564 return;
75657565 }
75667566
75677567 if (lastMessage.swipes.length <= 1) {
75687568 toastr.warning('t`Can\'t delete the last swipe.'`);
75697569 return;
75707570 }
75717571
75727572 swipeId = swipeId ?? lastMessage.swipe_id;
75737573
75747574 if (swipeId < 0 || swipeId >= lastMessage.swipes.length) {
75757575 toastr.warning(t`Invalid swipe ID: ${swipeId + 1}`);
75767576 return;
75777577 }
75787578
@@ -7711,7 +7711,7 @@ export function setGenerationProgress(progress) {
77117711
77127712function isHordeGenerationNotAllowed() {
77137713 if (main_api == 'koboldhorde' && preset_settings == 'gui') {
77147714 toastr.error('t`GUI Settings preset is not supported for Horde. Please select another preset.'`);
77157715 return true;
77167716 }
77177717
@@ -7760,7 +7760,7 @@ function openCharacterWorldPopup() {
77607760 }
77617761
77627762 $('#character_json_data').val(JSON.stringify(data));
77637763 toastr.info('t`Embedded lorebook will be removed from this character.'`);
77647764 } catch {
77657765 console.error('Failed to parse character JSON data.');
77667766 }
@@ -7951,11 +7951,11 @@ async function createOrEditCharacter(e) {
79517951
79527952 if ($('#form_create').attr('actiontype') == 'createcharacter') {
79537953 if (String($('#character_name_pole').val()).length === 0) {
79547954 toastr.error('t`Name is required'`);
79557955 return;
79567956 }
79577957 if (is_group_generating || is_send_press) {
79587958 toastr.error('t`Cannot create characters while generating. Stop the request and try again.'`, 't`Creation aborted'`);
79597959 return;
79607960 }
79617961 try {
@@ -8039,7 +8039,7 @@ async function createOrEditCharacter(e) {
80398039
80408040 } catch (error) {
80418041 console.error('Error creating character', error);
80428042 toastr.error('t`Failed to create character'`);
80438043 }
80448044 } else {
80458045 try {
@@ -8098,7 +8098,7 @@ async function createOrEditCharacter(e) {
80988098 }
80998099 } catch (error) {
81008100 console.log(error);
81018101 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.'`);
81028102 }
81038103 }
81048104}
@@ -8604,7 +8604,7 @@ async function selectContextCallback(args, name) {
86048604 const result = fuse.search(name);
86058605
86068606 if (result.length === 0) {
86078607 !quiet && toastr.warning(t`Context template "'${name}"' not found`);
86088608 return '';
86098609 }
86108610
@@ -8624,7 +8624,7 @@ async function selectInstructCallback(args, name) {
86248624 const result = fuse.search(name);
86258625
86268626 if (result.length === 0) {
86278627 !quiet && toastr.warning(t`Instruct template "'${name}"' not found`);
86288628 return '';
86298629 }
86308630
@@ -8676,7 +8676,7 @@ async function connectAPISlash(args, text) {
86768676
86778677 const apiConfig = CONNECT_API_MAP[text.toLowerCase()];
86788678 if (!apiConfig) {
86798679 toastr.error(t`Error: ${text} is not a valid API`);
86808680 return '';
86818681 }
86828682
@@ -8705,7 +8705,7 @@ async function connectAPISlash(args, text) {
87058705 }
87068706
87078707 const quiet = isTrueBoolean(args?.quiet);
87088708 const toast = quiet ? jQuery() : toastr.info(t`API set to ${text}, trying to connect..`);
87098709
87108710 try {
87118711 await waitUntilCondition(() => online_status !== 'no_connection', 10000, 100);
@@ -8744,7 +8744,7 @@ export async function processDroppedFiles(files, data = new Map()) {
87448744 const preservedName = data instanceof Map && data.get(file);
87458745 await importCharacter(file, preservedName);
87468746 } else {
87478747 toastr.warning('t`Unsupported file type: '` + file.name);
87488748 }
87498749 }
87508750}
@@ -8757,7 +8757,7 @@ export async function processDroppedFiles(files, data = new Map()) {
87578757 */
87588758async function importCharacter(file, preserveFileName = '') {
87598759 if (is_group_generating || is_send_press) {
87608760 toastr.error('t`Cannot import characters while generating. Stop the request and try again.'`, 't`Import aborted'`);
87618761 throw new Error('Cannot import character while generating');
87628762 }
87638763
@@ -8784,7 +8784,7 @@ async function importCharacter(file, preserveFileName = '') {
87848784 });
87858785
87868786 if (data.error) {
87878787 toastr.error('t`The file is likely invalid or corrupted.'`, 't`Could not import character'`);
87888788 return;
87898789 }
87908790
@@ -8837,7 +8837,7 @@ async function doImpersonate(args, prompt) {
88378837 await waitUntilCondition(() => !is_send_press && !is_group_generating, 10000, 100);
88388838 } catch {
88398839 console.warn('Timeout waiting for generation unlock');
88408840 toastr.warning('t`Cannot run /impersonate command while the reply is being generated.'`);
88418841 return '';
88428842 }
88438843
@@ -8899,19 +8899,19 @@ async function doDeleteChat() {
88998899
89008900async function doRenameChat(_, chatName) {
89018901 if (!chatName) {
89028902 toastr.warning('t`Name must be provided as an argument to rename this chat.'`);
89038903 return '';
89048904 }
89058905
89068906 const currentChatName = getCurrentChatId();
89078907 if (!currentChatName) {
89088908 toastr.warning('t`No chat selected that can be renamed.'`);
89098909 return '';
89108910 }
89118911
89128912 await renameChat(currentChatName, chatName);
89138913
89148914 toastr.success(t`Successfully renamed chat to: ${chatName}`);
89158915 return '';
89168916}
89178917
@@ -9026,7 +9026,7 @@ export async function deleteCharacter(characterKey, { deleteChats = true } = {})
90269026 for (const key of characterKey) {
90279027 const character = characters.find(x => x.avatar == key);
90289028 if (!character) {
90299029 toastr.warning(t`Character ${key} not found. Skipping deletion.`);
90309030 continue;
90319031 }
90329032
@@ -9043,7 +9043,7 @@ export async function deleteCharacter(characterKey, { deleteChats = true } = {})
90439043 });
90449044
90459045 if (!response.ok) {
90469046 toastr.error(`${response.status} ${response.statusText}`, 't`Failed to delete character'`);
90479047 continue;
90489048 }
90499049
@@ -9802,12 +9802,7 @@ jQuery(async function () {
98029802
98039803 let deleteChats = false;
98049804
98059805 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>`, {
98119806 onClose: () => deleteChats = !!$('#del_char_checkbox').prop('checked'),
98129807 });
98139808 if (!confirm) {
public/scripts/PromptManager.js+7 -6
@@ -8,6 +8,7 @@ import { debounce, waitUntilCondition, escapeHtml } from './utils.js';
88import { debounce_timeout } from './constants.js';
99import { renderTemplateAsync } from './templates.js';
1010import { Popup } from './popup.js';
11+import { t } from './i18n.js';
1112
1213function debouncePromise(func, delay) {
1314 let timeoutId;
@@ -455,7 +456,7 @@ class PromptManager {
455456
456457 // Delete selected prompt from list form and close edit form
457458 this.handleDeletePrompt = async (event) => {
458459 Popup.show.confirm('t`Are you sure you want to delete this prompt?'`, null).then((userChoice) => {
459460 if (!userChoice) return;
460461 const promptID = document.getElementById(this.configuration.prefix + 'prompt_manager_footer_append_prompt').value;
461462 const prompt = this.getPromptById(promptID);
@@ -531,7 +532,7 @@ class PromptManager {
531532
532533 // Import prompts for the selected character
533534 this.handleImport = () => {
534535 Popup.show.confirm('t`Existing prompts with the same ID will be overridden. Do you want to proceed?'`, null)
535536 .then(userChoice => {
536537 if (!userChoice) return;
537538
@@ -552,7 +553,7 @@ class PromptManager {
552553 const data = JSON.parse(fileContent);
553554 this.import(data);
554555 } catch (err) {
555556 toastr.error('t`An error occurred while importing prompts. More info available in console.'`);
556557 console.log('An error occurred while importing prompts');
557558 console.log(err.toString());
558559 }
@@ -567,7 +568,7 @@ class PromptManager {
567568
568569 // Restore default state of a characters prompt order
569570 this.handleCharacterReset = () => {
570571 Popup.show.confirm('t`This will reset the prompt order for this character. You will not lose any prompts.'`, null)
571572 .then(userChoice => {
572573 if (!userChoice) return;
573574
@@ -1649,7 +1650,7 @@ class PromptManager {
16491650 };
16501651
16511652 if (false === this.validateObject(controlObj, importData)) {
16521653 toastr.warning('t`Could not import prompts. Export failed validation.'`);
16531654 return;
16541655 }
16551656
@@ -1672,7 +1673,7 @@ class PromptManager {
16721673 throw new Error('Prompt order strategy not supported.');
16731674 }
16741675
16751676 toastr.success('t`Prompt import complete.'`);
16761677 this.saveServiceSettings().then(() => this.render());
16771678 }
16781679
public/scripts/authors-note.js+10 -9
@@ -16,6 +16,7 @@ import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
1616import { SlashCommand } from './slash-commands/SlashCommand.js';
1717import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js';
1818export { MODULE_NAME as NOTE_MODULE_NAME };
19+import { t } from './i18n.js';
1920
2021const MODULE_NAME = '2_floating_prompt'; // <= Deliberate, for sorting lower than memory
2122
@@ -37,7 +38,7 @@ const chara_note_position = {
3738
3839function setNoteTextCommand(_, text) {
3940 $('#extension_floating_prompt').val(text).trigger('input');
4041 toastr.success('t`Author\'s Note text updated'`);
4142 return '';
4243}
4344
@@ -45,12 +46,12 @@ function setNoteDepthCommand(_, text) {
4546 const value = Number(text);
4647
4748 if (Number.isNaN(value)) {
4849 toastr.error('t`Not a valid number'`);
4950 return;
5051 }
5152
5253 $('#extension_floating_depth').val(Math.abs(value)).trigger('input');
5354 toastr.success('t`Author\'s Note depth updated'`);
5455 return '';
5556}
5657
@@ -58,12 +59,12 @@ function setNoteIntervalCommand(_, text) {
5859 const value = Number(text);
5960
6061 if (Number.isNaN(value)) {
6162 toastr.error('t`Not a valid number'`);
6263 return;
6364 }
6465
6566 $('#extension_floating_interval').val(Math.abs(value)).trigger('input');
6667 toastr.success('t`Author\'s Note frequency updated'`);
6768 return '';
6869}
6970
@@ -76,12 +77,12 @@ function setNotePositionCommand(_, text) {
7677 const position = validPositions[text?.trim()];
7778
7879 if (Number.isNaN(position)) {
7980 toastr.error('t`Not a valid position'`);
8081 return;
8182 }
8283
8384 $(`input[name="extension_floating_position"][value="${position}"]`).prop('checked', true).trigger('input');
8485 toastr.info('t`Author\'s Note position updated'`);
8586 return '';
8687}
8788
@@ -206,7 +207,7 @@ function onExtensionFloatingCharaPromptInput() {
206207 extension_settings.note.chara.push(tempCharaNote);
207208 } else {
208209 console.log('Character author\'s note error: No avatar name key could be found.');
209210 toastr.error('t`Something went wrong. Could not save character\'s author\'s note.'`);
210211
211212 // Don't save settings if something went wrong
212213 return;
@@ -397,7 +398,7 @@ function onANMenuItemClick() {
397398 //because this listener takes priority
398399 $('#options').stop().fadeOut(animation_duration);
399400 } else {
400401 toastr.warning('t`Select a character before trying to use Author\'s Note'`, '', { timeOut: 2000 });
401402 }
402403}
403404
public/scripts/chats.js+13 -12
@@ -40,6 +40,7 @@ import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js';
4040import { ScraperManager } from './scrapers.js';
4141import { DragAndDropHandler } from './dragdrop.js';
4242import { renderTemplateAsync } from './templates.js';
43+import { t } from './i18n.js';
4344
4445/**
4546 * @typedef {Object} FileAttachment
@@ -206,7 +207,7 @@ export async function populateFileAttachment(message, inputId = 'file_form_input
206207 const fileText = await converter(file);
207208 base64Data = window.btoa(unescape(encodeURIComponent(fileText)));
208209 } catch (error) {
209210 toastr.error(String(error), 't`Could not convert file'`);
210211 console.error('Could not convert file', error);
211212 }
212213 }
@@ -257,7 +258,7 @@ export async function uploadFileAttachment(fileName, base64Data) {
257258 const responseData = await result.json();
258259 return responseData.path;
259260 } catch (error) {
260261 toastr.error(String(error), 't`Could not upload file'`);
261262 console.error('Could not upload file', error);
262263 }
263264}
@@ -283,7 +284,7 @@ export async function getFileAttachment(url) {
283284 const text = await result.text();
284285 return text;
285286 } catch (error) {
286287 toastr.error(error, 't`Could not download file'`);
287288 console.error('Could not download file', error);
288289 }
289290}
@@ -299,13 +300,13 @@ async function validateFile(file) {
299300 const isBinary = /^[\x00-\x08\x0E-\x1F\x7F-\xFF]*$/.test(fileText);
300301
301302 if (!isImage && file.size > fileSizeLimit) {
302303 toastr.error(t`File is too big. Maximum size is ${humanFileSize(fileSizeLimit)}.`);
303304 return false;
304305 }
305306
306307 // If file is binary
307308 if (isBinary && !isImage && !isConvertible(file.type)) {
308309 toastr.error('t`Binary files are not supported. Select a text file or image.'`);
309310 return false;
310311 }
311312
@@ -521,7 +522,7 @@ async function openExternalMediaOverridesDialog() {
521522 const entityId = getCurrentEntityId();
522523
523524 if (!entityId) {
524525 toastr.info('t`No character or group selected'`);
525526 return;
526527 }
527528
@@ -646,7 +647,7 @@ async function deleteFileFromServer(url, silent = false) {
646647 await eventSource.emit(event_types.FILE_ATTACHMENT_DELETED, url);
647648 return true;
648649 } catch (error) {
649650 toastr.error(String(error), 't`Could not delete file'`);
650651 console.error('Could not delete file', error);
651652 return false;
652653 }
@@ -1054,7 +1055,7 @@ async function openAttachmentManager() {
10541055 const selectedAttachments = document.querySelectorAll('.attachmentListItemCheckboxContainer .attachmentListItemCheckbox:checked');
10551056
10561057 if (selectedAttachments.length === 0) {
10571058 toastr.info('t`No attachments selected.'`, 't`Data Bank'`);
10581059 return;
10591060 }
10601061
@@ -1168,7 +1169,7 @@ async function runScraper(scraperId, target, callback) {
11681169
11691170 if (files.length === 0) {
11701171 console.warn('Scraping returned no files');
11711172 toastr.info('t`No files were scraped.'`, 't`Data Bank'`);
11721173 return;
11731174 }
11741175
@@ -1176,12 +1177,12 @@ async function runScraper(scraperId, target, callback) {
11761177 await uploadFileAttachmentToServer(file, target);
11771178 }
11781179
11791180 toastr.success(t`Scraped ${files.length} files from ${scraperId} to ${target}.`, 't`Data Bank'`);
11801181 callback();
11811182 }
11821183 catch (error) {
11831184 console.error('Scraping failed', error);
11841185 toastr.error('t`Check browser console for details.'`, 't`Scraping failed'`);
11851186 }
11861187}
11871188
@@ -1208,7 +1209,7 @@ export async function uploadFileAttachmentToServer(file, target) {
12081209 const fileText = await converter(file);
12091210 base64Data = window.btoa(unescape(encodeURIComponent(fileText)));
12101211 } catch (error) {
12111212 toastr.error(String(error), 't`Could not convert file'`);
12121213 console.error('Could not convert file', error);
12131214 }
12141215 } else {
public/scripts/extensions/regex/editor.html+1 -1
@@ -132,7 +132,7 @@
132132 <small data-i18n="ext_regex_other_options" data-i18n="Ephemerality">Ephemerality</small>
133133 <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>
134134 </span>
135135 <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.">
136136 <input type="checkbox" name="only_format_display" />
137137 <span data-i18n="Only Format Display">Alter Chat Display</span>
138138 </label>
public/scripts/group-chats.js+10 -10
@@ -189,8 +189,8 @@ async function validateGroup(group) {
189189 group.members = group.members.filter(member => {
190190 const character = characters.find(x => x.avatar === member || x.name === member);
191191 if (!character) {
192192 const msg = t`Warning: Listed member ${member} does not exist as a character. It will be removed from the group.`;
193193 toastr.warning(msg, 't`Group Validation'`);
194194 console.warn(msg);
195195 dirty = true;
196196 }
@@ -522,7 +522,7 @@ async function saveGroupChat(groupId, shouldSaveGroup) {
522522 });
523523
524524 if (!response.ok) {
525525 toastr.error('t`Check the server connection and reload the page to prevent data loss.'`, 't`Group Chat could not be saved'`);
526526 console.error('Group chat could not be saved', response);
527527 return;
528528 }
@@ -837,7 +837,7 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
837837 activatedMembers = activateSwipe(group.members);
838838
839839 if (activatedMembers.length === 0) {
840840 toastr.warning('t`Deleted group member swiped. To get a reply, add them back to the group.'`);
841841 throw new Error('Deleted group member swiped');
842842 }
843843 }
@@ -1368,15 +1368,15 @@ function isGroupMemberDisabled(avatarId) {
13681368
13691369async function onDeleteGroupClick() {
13701370 if (!openGroupId) {
13711371 toastr.warning('t`Currently no group selected.'`);
13721372 return;
13731373 }
13741374 if (is_group_generating) {
13751375 toastr.warning('t`Not so fast! Wait for the characters to stop typing before deleting the group.'`);
13761376 return;
13771377 }
13781378
13791379 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>');
13801380 if (confirm) {
13811381 deleteGroup(openGroupId);
13821382 }
@@ -1630,7 +1630,7 @@ function updateFavButtonState(state) {
16301630
16311631export async function openGroupById(groupId) {
16321632 if (isChatSaving) {
16331633 toastr.info('t`Please wait until the chat is saved before switching characters.'`, 't`Your chat is still saving...'`);
16341634 return;
16351635 }
16361636
@@ -1659,7 +1659,7 @@ export async function openGroupById(groupId) {
16591659
16601660function openCharacterDefinition(characterSelect) {
16611661 if (is_group_generating) {
16621662 toastr.warning('t`Can\'t peek a character while group reply is being generated'`);
16631663 console.warn('Can\'t peek a character def while group reply is being generated');
16641664 return;
16651665 }
@@ -1908,7 +1908,7 @@ export async function saveGroupBookmarkChat(groupId, name, metadata, mesId) {
19081908 });
19091909
19101910 if (!response.ok) {
19111911 toastr.error('t`Check the server connection and reload the page to prevent data loss.'`, 't`Group chat could not be saved'`);
19121912 console.error('Group chat could not be saved', response);
19131913 }
19141914}
public/scripts/openai.js+25 -28
@@ -388,7 +388,7 @@ async function validateReverseProxy() {
388388 new URL(oai_settings.reverse_proxy);
389389 }
390390 catch (err) {
391391 toastr.error('t`Entered reverse proxy address is not a valid URL'`);
392392 setOnlineStatus('no_connection');
393393 resultCheckStatus();
394394 throw err;
@@ -399,7 +399,7 @@ async function validateReverseProxy() {
399399 const confirmation = skipConfirm || await Popup.show.confirm(t`Connecting To Proxy`, await renderTemplateAsync('proxyConnectionWarning', { proxyURL: DOMPurify.sanitize(oai_settings.reverse_proxy) }));
400400
401401 if (!confirmation) {
402402 toastr.error('t`Update or remove your reverse proxy settings.'`);
403403 setOnlineStatus('no_connection');
404404 resultCheckStatus();
405405 throw new Error('Proxy connection denied.');
@@ -1231,15 +1231,15 @@ export async function prepareOpenAIMessages({
12311231 await populateChatCompletion(prompts, chatCompletion, { bias, quietPrompt, quietImage, type, cyclePrompt, messages, messageExamples });
12321232 } catch (error) {
12331233 if (error instanceof TokenBudgetExceededError) {
12341234 toastr.error('t`An error occurred while counting tokens: Token budget exceeded.'`);
12351235 chatCompletion.log('Token budget exceeded.');
12361236 promptManager.error = 't`Not enough free tokens for mandatory prompts. Raise your token Limit or disable custom prompts.'`;
12371237 } else if (error instanceof InvalidCharacterNameError) {
12381238 toastr.warning('t`An error occurred while counting tokens: Invalid character name'`);
12391239 chatCompletion.log('Invalid character name');
12401240 promptManager.error = 't`The name of at least one character contained whitespaces or special characters. Please check your user and character name.'`;
12411241 } else {
12421242 toastr.error('t`An unknown error occurred while counting tokens. Further information may be available in console.'`);
12431243 chatCompletion.log('----- Unexpected error while preparing prompts -----');
12441244 chatCompletion.log(error);
12451245 chatCompletion.log(error.stack);
@@ -1293,11 +1293,8 @@ function tryParseStreamingError(response, decoded) {
12931293 }
12941294}
12951295
12961296async 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
13021299 if (!data) {
13031300 return;
@@ -1933,11 +1930,11 @@ async function sendOpenAIRequest(type, messages, signal) {
19331930 else {
19341931 const data = await response.json();
19351932
19361933 await checkQuotaError(data);
19371934 checkModerationError(data);
19381935
19391936 if (data.error) {
19401937 toastr.error(data.error.message || response.statusText, 't`API returned an error'`);
19411938 throw new Error(data);
19421939 }
19431940
@@ -2184,7 +2181,7 @@ function parseOpenAITextLogprobs(logprobs) {
21842181
21852182function handleWindowError(err) {
21862183 const text = parseWindowError(err);
21872184 toastr.error(text, 't`Window.ai returned an error'`);
21882185 throw err;
21892186}
21902187
@@ -3257,7 +3254,7 @@ async function getStatusOpen() {
32573254}
32583255
32593256function showWindowExtensionError() {
32603257 toastr.error('t`Get it here:` + ' <a href="https://windowai.io/" target="_blank">windowai.io</a>', 't`Extension is not installed'`, {
32613258 escapeHtml: false,
32623259 timeOut: 0,
32633260 extendedTimeOut: 0,
@@ -3376,7 +3373,7 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
33763373 if (triggerUi) $('#settings_preset_openai').append(option).trigger('change');
33773374 }
33783375 } else {
33793376 toastr.error('t`Failed to save preset'`);
33803377 throw new Error('Failed to save preset');
33813378 }
33823379}
@@ -3455,7 +3452,7 @@ async function createNewLogitBiasPreset() {
34553452 }
34563453
34573454 if (name in oai_settings.bias_presets) {
34583455 toastr.error('t`Preset name should be unique.'`);
34593456 return;
34603457 }
34613458
@@ -3499,7 +3496,7 @@ async function onPresetImportFileChange(e) {
34993496 try {
35003497 presetBody = JSON.parse(importedFile);
35013498 } catch (err) {
35023499 toastr.error('t`Invalid file'`);
35033500 return;
35043501 }
35053502
@@ -3540,7 +3537,7 @@ async function onPresetImportFileChange(e) {
35403537 });
35413538
35423539 if (!savePresetSettings.ok) {
35433540 toastr.error('t`Failed to save preset'`);
35443541 return;
35453542 }
35463543
@@ -3565,7 +3562,7 @@ async function onPresetImportFileChange(e) {
35653562
35663563async function onExportPresetClick() {
35673564 if (!oai_settings.preset_settings_openai) {
35683565 toastr.error('t`No preset selected'`);
35693566 return;
35703567 }
35713568
@@ -3606,12 +3603,12 @@ async function onLogitBiasPresetImportFileChange(e) {
36063603 e.target.value = '';
36073604
36083605 if (name in oai_settings.bias_presets) {
36093606 toastr.error('t`Preset name should be unique.'`);
36103607 return;
36113608 }
36123609
36133610 if (!Array.isArray(importedFile)) {
36143611 toastr.error('t`Invalid logit bias preset file.'`);
36153612 return;
36163613 }
36173614
@@ -3670,16 +3667,16 @@ async function onDeletePresetClick() {
36703667 });
36713668
36723669 if (!response.ok) {
36733670 toastr.warning('t`Preset was not deleted from server'`);
36743671 } else {
36753672 toastr.success('t`Preset deleted'`);
36763673 }
36773674
36783675 saveSettingsDebounced();
36793676}
36803677
36813678async function onLogitBiasPresetDeleteClick() {
36823679 const value = await callPopup('t`Delete the preset?'`, 'confirm');
36833680
36843681 if (!value) {
36853682 return;
@@ -4816,7 +4813,7 @@ function runProxyCallback(_, value) {
48164813 const result = fuse.search(value);
48174814
48184815 if (result.length === 0) {
48194816 toastr.warning(t`Proxy preset "'${value}"' not found`);
48204817 return '';
48214818 }
48224819
@@ -4990,7 +4987,7 @@ export function initOpenAI() {
49904987 $('#update_oai_preset').on('click', async function () {
49914988 const name = oai_settings.preset_settings_openai;
49924989 await saveOpenAIPreset(name, oai_settings);
49934990 toastr.success('t`Preset updated'`);
49944991 });
49954992
49964993 $('#impersonation_prompt_restore').on('click', function () {
public/scripts/templates/assistantNote.html+1 -1
@@ -1,3 +1,3 @@
11<div>
22 <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>
33</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>
5 \ No newline at end of file
public/scripts/templates/duplicateConfirm.html+2 -2
@@ -1,5 +1,5 @@
11<div>
22 <h3 data-i18n="Are you sure you want to duplicate this character?">Are you sure you want to duplicate this character?</h3>
33 <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>
44 <br>
55</div>
public/scripts/templates/hotkeys.html+7 -7
@@ -14,18 +14,18 @@
1414 <li><kbd data-i18n="help_hotkeys_19">Ctrl+Shift+Down</kbd> = <span data-i18n="help_hotkeys_20">Scroll chat to bottom</span></li>
1515</ul>
1616<div>
1717 <strong data-i18n="help_hotkeys_20">Markdown Hotkeys</strong>
1818</div>
1919<div>
2020 <small>
2121 <span data-i18n="help_hotkeys_21">Works in the chatbar and textareas marked with this icon:</span>
2222 <code><i class="fa-brands fa-markdown"></i></code>
2323 </small>
2424</div>
2525<ul>
2626 <li><kbd>Ctrl+B</kbd> = <span data-i18n="help_hotkeys_22">**bold**</span></li>
2727 <li><kbd>Ctrl+I</kbd> = <span data-i18n="help_hotkeys_23">*italic*</span></li>
2828 <li><kbd>Ctrl+U</kbd> = <span data-i18n="help_hotkeys_24">__underline__</span></li>
2929 <li><kbd>Ctrl+K</kbd> = <span data-i18n="help_hotkeys_25">`inline code`</span></li>
3030 <li><kbd>Ctrl+Shift+~</kbd> = <span data-i18n="help_hotkeys_26">~~strikethrough~~</span></li>
3131</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>
4 \ No newline at end of file