Work on tl
| @@ -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,142 @@ | |||
| 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 | "quota_error_1": "При обработке вашего запроса возникла ошибка.", | ||
| 1768 | "quota_error_2": "Убедитесь, что на вашем", | ||
| 1769 | "quota_error_3": "аккаунте OpenAI", | ||
| 1770 | "quota_error_4": "имеется достаточно кредитов.", | ||
| 1771 | "quota_error_5": "Если кредитов достаточно, то повторите попытку позднее.", | ||
| 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 | "assistant_note_1": "Примечание:", | ||
| 1784 | "assistant_note_2": "это временный чат, он будет удалён, как только вы из него выйдете.", | ||
| 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 Server", | ||
| 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 | "duplicate_confirm_1": "Вы точно хотите клонировать этого персонажа?", | ||
| 1867 | "duplicate_confirm_2": "Если вы хотите просто создать новый чат, воспользуйтесь кнопкой \"Начать новый чат\" в меню слева внизу.", | ||
| 1868 | "delete_confirm_1": "ОТМЕНИТЬ БУДЕТ НЕВОЗМОЖНО!", | ||
| 1869 | "delete_confirm_2": "Также удалить файлы чатов", | ||
| 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": "Невозможно открыть карточку персонажа во время генерации ответа" | ||
| 1751 | } | 1888 | } |
| @@ -383,8 +383,8 @@ DOMPurify.addHook('uponSanitizeElement', (node, _, config) => { | |||
| 383 | 383 | ||
| 384 | if (localStorage.getItem(warningShownKey) === null) { | 384 | if (localStorage.getItem(warningShownKey) === null) { |
| 385 | const warningToast = toastr.warning( | 385 | const warningToast = toastr.warning( |
| 386 | 'Use the "Ext. Media" button to allow it. Click on this message to dismiss.', | 386 | t`Use the 'Ext. Media' button to allow it. Click on this message to dismiss.`, |
| 387 | 'External media has been blocked', | 387 | t`External media has been blocked`, |
| 388 | { | 388 | { |
| 389 | timeOut: 0, | 389 | timeOut: 0, |
| 390 | preventDuplicates: true, | 390 | preventDuplicates: true, |
| @@ -924,7 +924,7 @@ async function firstLoadInit() { | |||
| 924 | token = tokenData.token; | 924 | token = tokenData.token; |
| 925 | } catch { | 925 | } catch { |
| 926 | hideLoader(); | 926 | hideLoader(); |
| 927 | toastr.error('Couldn\'t get CSRF token. Please refresh the page.', 'Error', { timeOut: 0, extendedTimeOut: 0, preventDuplicates: true }); | 927 | toastr.error(t`Couldn't get CSRF token. Please refresh the page.`, t`Error`, { timeOut: 0, extendedTimeOut: 0, preventDuplicates: true }); |
| 928 | throw new Error('Initialization failed'); | 928 | throw new Error('Initialization failed'); |
| 929 | } | 929 | } |
| 930 | 930 | ||
| @@ -1140,7 +1140,7 @@ async function getStatusKobold() { | |||
| 1140 | 1140 | ||
| 1141 | // We didn't get a 200 status code, but the endpoint has an explanation. Which means it DID connect, but I digress. | 1141 | // We didn't get a 200 status code, but the endpoint has an explanation. Which means it DID connect, but I digress. |
| 1142 | if (online_status === 'no_connection' && data.response) { | 1142 | if (online_status === 'no_connection' && data.response) { |
| 1143 | toastr.error(data.response, 'API Error', { timeOut: 5000, preventDuplicates: true }); | 1143 | toastr.error(data.response, t`API Error`, { timeOut: 5000, preventDuplicates: true }); |
| 1144 | } | 1144 | } |
| 1145 | } catch (err) { | 1145 | } catch (err) { |
| 1146 | console.error('Error getting status', err); | 1146 | console.error('Error getting status', err); |
| @@ -1226,7 +1226,7 @@ async function getStatusTextgen() { | |||
| 1226 | 1226 | ||
| 1227 | // We didn't get a 200 status code, but the endpoint has an explanation. Which means it DID connect, but I digress. | 1227 | // We didn't get a 200 status code, but the endpoint has an explanation. Which means it DID connect, but I digress. |
| 1228 | if (online_status === 'no_connection' && data.response) { | 1228 | if (online_status === 'no_connection' && data.response) { |
| 1229 | toastr.error(data.response, 'API Error', { timeOut: 5000, preventDuplicates: true }); | 1229 | toastr.error(data.response, t`API Error`, { timeOut: 5000, preventDuplicates: true }); |
| 1230 | } | 1230 | } |
| 1231 | } catch (err) { | 1231 | } catch (err) { |
| 1232 | if (err instanceof AbortReason) { | 1232 | if (err instanceof AbortReason) { |
| @@ -1278,7 +1278,7 @@ export async function selectCharacterById(id) { | |||
| 1278 | } | 1278 | } |
| 1279 | 1279 | ||
| 1280 | if (isChatSaving) { | 1280 | if (isChatSaving) { |
| 1281 | toastr.info('Please wait until the chat is saved before switching characters.', 'Your chat is still saving...'); | 1281 | toastr.info(t`Please wait until the chat is saved before switching characters.`, t`Your chat is still saving...`); |
| 1282 | return; | 1282 | return; |
| 1283 | } | 1283 | } |
| 1284 | 1284 | ||
| @@ -1632,7 +1632,7 @@ export async function getOneCharacter(avatarUrl) { | |||
| 1632 | if (indexOf !== -1) { | 1632 | if (indexOf !== -1) { |
| 1633 | characters[indexOf] = getData; | 1633 | characters[indexOf] = getData; |
| 1634 | } else { | 1634 | } else { |
| 1635 | toastr.error(`Character ${avatarUrl} not found in the list`, 'Error', { timeOut: 5000, preventDuplicates: true }); | 1635 | toastr.error(t`Character ${avatarUrl} not found in the list`, t`Error`, { timeOut: 5000, preventDuplicates: true }); |
| 1636 | } | 1636 | } |
| 1637 | } | 1637 | } |
| 1638 | } | 1638 | } |
| @@ -3374,7 +3374,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | |||
| 3374 | await eventSource.emit(event_types.GENERATION_AFTER_COMMANDS, type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage }, dryRun); | 3374 | await eventSource.emit(event_types.GENERATION_AFTER_COMMANDS, type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage }, dryRun); |
| 3375 | 3375 | ||
| 3376 | if (main_api == 'kobold' && kai_settings.streaming_kobold && !kai_flags.can_use_streaming) { | 3376 | if (main_api == 'kobold' && kai_settings.streaming_kobold && !kai_flags.can_use_streaming) { |
| 3377 | toastr.error('Streaming is enabled, but the version of Kobold used does not support token streaming.', undefined, { timeOut: 10000, preventDuplicates: true }); | 3377 | toastr.error(t`Streaming is enabled, but the version of Kobold used does not support token streaming.`, undefined, { timeOut: 10000, preventDuplicates: true }); |
| 3378 | unblockGeneration(type); | 3378 | unblockGeneration(type); |
| 3379 | return Promise.resolve(); | 3379 | return Promise.resolve(); |
| 3380 | } | 3380 | } |
| @@ -3383,7 +3383,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | |||
| 3383 | textgen_settings.streaming && | 3383 | textgen_settings.streaming && |
| 3384 | textgen_settings.legacy_api && | 3384 | textgen_settings.legacy_api && |
| 3385 | textgen_settings.type === OOBA) { | 3385 | textgen_settings.type === OOBA) { |
| 3386 | toastr.error('Streaming is not supported for the Legacy API. Update Ooba and use new API to enable streaming.', undefined, { timeOut: 10000, preventDuplicates: true }); | 3386 | 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 }); |
| 3387 | unblockGeneration(type); | 3387 | unblockGeneration(type); |
| 3388 | return Promise.resolve(); | 3388 | return Promise.resolve(); |
| 3389 | } | 3389 | } |
| @@ -3399,7 +3399,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | |||
| 3399 | 3399 | ||
| 3400 | if (!pingResult) { | 3400 | if (!pingResult) { |
| 3401 | unblockGeneration(type); | 3401 | unblockGeneration(type); |
| 3402 | toastr.error('Verify that the server is running and accessible.', 'ST Server cannot be reached'); | 3402 | toastr.error(t`Verify that the server is running and accessible.`, t`ST Server cannot be reached`); |
| 3403 | throw new Error('Server unreachable'); | 3403 | throw new Error('Server unreachable'); |
| 3404 | } | 3404 | } |
| 3405 | 3405 | ||
| @@ -4427,7 +4427,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | |||
| 4427 | generatedPromptCache = ''; | 4427 | generatedPromptCache = ''; |
| 4428 | 4428 | ||
| 4429 | if (data?.response) { | 4429 | if (data?.response) { |
| 4430 | toastr.error(data.response, 'API Error', { preventDuplicates: true }); | 4430 | toastr.error(data.response, t`API Error`, { preventDuplicates: true }); |
| 4431 | } | 4431 | } |
| 4432 | throw new Error(data?.response); | 4432 | throw new Error(data?.response); |
| 4433 | } | 4433 | } |
| @@ -4518,7 +4518,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | |||
| 4518 | 4518 | ||
| 4519 | function onError(exception) { | 4519 | function onError(exception) { |
| 4520 | if (typeof exception?.error?.message === 'string') { | 4520 | if (typeof exception?.error?.message === 'string') { |
| 4521 | toastr.error(exception.error.message, 'Error', { timeOut: 10000, extendedTimeOut: 20000 }); | 4521 | toastr.error(exception.error.message, t`Error`, { timeOut: 10000, extendedTimeOut: 20000 }); |
| 4522 | } | 4522 | } |
| 4523 | 4523 | ||
| 4524 | generatedPromptCache = ''; | 4524 | generatedPromptCache = ''; |
| @@ -4920,7 +4920,7 @@ function addChatsSeparator(mesSendString) { | |||
| 4920 | 4920 | ||
| 4921 | async function duplicateCharacter() { | 4921 | async function duplicateCharacter() { |
| 4922 | if (!this_chid) { | 4922 | if (!this_chid) { |
| 4923 | toastr.warning('You must first select a character to duplicate!'); | 4923 | toastr.warning(t`You must first select a character to duplicate!`); |
| 4924 | return ''; | 4924 | return ''; |
| 4925 | } | 4925 | } |
| 4926 | 4926 | ||
| @@ -4939,7 +4939,7 @@ async function duplicateCharacter() { | |||
| 4939 | body: JSON.stringify(body), | 4939 | body: JSON.stringify(body), |
| 4940 | }); | 4940 | }); |
| 4941 | if (response.ok) { | 4941 | if (response.ok) { |
| 4942 | toastr.success('Character Duplicated'); | 4942 | toastr.success(t`Character Duplicated`); |
| 4943 | const data = await response.json(); | 4943 | const data = await response.json(); |
| 4944 | await eventSource.emit(event_types.CHARACTER_DUPLICATED, { oldAvatar: body.avatar_url, newAvatar: data.path }); | 4944 | await eventSource.emit(event_types.CHARACTER_DUPLICATED, { oldAvatar: body.avatar_url, newAvatar: data.path }); |
| 4945 | await getCharacters(); | 4945 | await getCharacters(); |
| @@ -5786,23 +5786,23 @@ export function setSendButtonState(value) { | |||
| 5786 | 5786 | ||
| 5787 | export async function renameCharacter(name = null, { silent = false, renameChats = null } = {}) { | 5787 | export async function renameCharacter(name = null, { silent = false, renameChats = null } = {}) { |
| 5788 | if (!name && silent) { | 5788 | if (!name && silent) { |
| 5789 | toastr.warning('No character name provided.', 'Rename Character'); | 5789 | toastr.warning(t`No character name provided.`, t`Rename Character`); |
| 5790 | return false; | 5790 | return false; |
| 5791 | } | 5791 | } |
| 5792 | if (this_chid === undefined) { | 5792 | if (this_chid === undefined) { |
| 5793 | toastr.warning('No character selected.', 'Rename Character'); | 5793 | toastr.warning(t`No character selected.`, t`Rename Character`); |
| 5794 | return false; | 5794 | return false; |
| 5795 | } | 5795 | } |
| 5796 | 5796 | ||
| 5797 | const oldAvatar = characters[this_chid].avatar; | 5797 | const oldAvatar = characters[this_chid].avatar; |
| 5798 | const newValue = name || await callGenericPopup('<h3>New name:</h3>', POPUP_TYPE.INPUT, characters[this_chid].name); | 5798 | const newValue = name || await callGenericPopup('<h3>' + t`New name:` + '</h3>', POPUP_TYPE.INPUT, characters[this_chid].name); |
| 5799 | 5799 | ||
| 5800 | if (!newValue) { | 5800 | if (!newValue) { |
| 5801 | toastr.warning('No character name provided.', 'Rename Character'); | 5801 | toastr.warning(t`No character name provided.`, t`Rename Character`); |
| 5802 | return false; | 5802 | return false; |
| 5803 | } | 5803 | } |
| 5804 | if (newValue === characters[this_chid].name) { | 5804 | if (newValue === characters[this_chid].name) { |
| 5805 | toastr.info('Same character name provided, so name did not change.', 'Rename Character'); | 5805 | toastr.info(t`Same character name provided, so name did not change.`, t`Rename Character`); |
| 5806 | return false; | 5806 | return false; |
| 5807 | } | 5807 | } |
| 5808 | 5808 | ||
| @@ -5849,9 +5849,9 @@ export async function renameCharacter(name = null, { silent = false, renameChats | |||
| 5849 | if (renamePastChatsConfirm) { | 5849 | if (renamePastChatsConfirm) { |
| 5850 | await renamePastChats(newAvatar, newValue); | 5850 | await renamePastChats(newAvatar, newValue); |
| 5851 | await reloadCurrentChat(); | 5851 | await reloadCurrentChat(); |
| 5852 | toastr.success('Character renamed and past chats updated!', 'Rename Character'); | 5852 | toastr.success(t`Character renamed and past chats updated!`, t`Rename Character`); |
| 5853 | } else { | 5853 | } else { |
| 5854 | toastr.success('Character renamed!', 'Rename Character'); | 5854 | toastr.success(t`Character renamed!`, t`Rename Character`); |
| 5855 | } | 5855 | } |
| 5856 | } | 5856 | } |
| 5857 | else { | 5857 | else { |
| @@ -5864,8 +5864,8 @@ export async function renameCharacter(name = null, { silent = false, renameChats | |||
| 5864 | } | 5864 | } |
| 5865 | catch (error) { | 5865 | catch (error) { |
| 5866 | // Reloading to prevent data corruption | 5866 | // Reloading to prevent data corruption |
| 5867 | if (!silent) await callPopup('Something went wrong. The page will be reloaded.', 'text'); | 5867 | if (!silent) await callPopup(t`Something went wrong. The page will be reloaded.`, 'text'); |
| 5868 | else toastr.error('Something went wrong. The page will be reloaded.', 'Rename Character'); | 5868 | else toastr.error(t`Something went wrong. The page will be reloaded.`, t`Rename Character`); |
| 5869 | 5869 | ||
| 5870 | console.log('Renaming character error:', error); | 5870 | console.log('Renaming character error:', error); |
| 5871 | location.reload(); | 5871 | location.reload(); |
| @@ -5922,7 +5922,7 @@ async function renamePastChats(newAvatar, newValue) { | |||
| 5922 | } | 5922 | } |
| 5923 | } | 5923 | } |
| 5924 | } catch (error) { | 5924 | } catch (error) { |
| 5925 | toastr.error(`Past chat could not be updated: ${file_name}`); | 5925 | toastr.error(t`Past chat could not be updated: ${file_name}`); |
| 5926 | console.error(error); | 5926 | console.error(error); |
| 5927 | } | 5927 | } |
| 5928 | } | 5928 | } |
| @@ -5971,7 +5971,7 @@ export async function saveChat(chatName, withMetadata, mesId) { | |||
| 5971 | characters[this_chid]['date_last_chat'] = Date.now(); | 5971 | characters[this_chid]['date_last_chat'] = Date.now(); |
| 5972 | chat.forEach(function (item, i) { | 5972 | chat.forEach(function (item, i) { |
| 5973 | if (item['is_group']) { | 5973 | if (item['is_group']) { |
| 5974 | toastr.error('Trying to save group chat with regular saveChat function. Aborting to prevent corruption.'); | 5974 | toastr.error(t`Trying to save group chat with regular saveChat function. Aborting to prevent corruption.`); |
| 5975 | throw new Error('Group chat saved from saveChat'); | 5975 | throw new Error('Group chat saved from saveChat'); |
| 5976 | } | 5976 | } |
| 5977 | /* | 5977 | /* |
| @@ -6016,7 +6016,7 @@ export async function saveChat(chatName, withMetadata, mesId) { | |||
| 6016 | contentType: 'application/json', | 6016 | contentType: 'application/json', |
| 6017 | success: function (data) { }, | 6017 | success: function (data) { }, |
| 6018 | error: function (jqXHR, exception) { | 6018 | error: function (jqXHR, exception) { |
| 6019 | toastr.error('Check the server connection and reload the page to prevent data loss.', 'Chat could not be saved'); | 6019 | toastr.error(t`Check the server connection and reload the page to prevent data loss.`, t`Chat could not be saved`); |
| 6020 | console.log(exception); | 6020 | console.log(exception); |
| 6021 | console.log(jqXHR); | 6021 | console.log(jqXHR); |
| 6022 | }, | 6022 | }, |
| @@ -6413,7 +6413,7 @@ export async function getSettings() { | |||
| 6413 | 6413 | ||
| 6414 | if (!response.ok) { | 6414 | if (!response.ok) { |
| 6415 | reloadLoop(); | 6415 | reloadLoop(); |
| 6416 | toastr.error('Settings could not be loaded after multiple attempts. Please try again later.'); | 6416 | toastr.error(t`Settings could not be loaded after multiple attempts. Please try again later.`); |
| 6417 | throw new Error('Error getting settings'); | 6417 | throw new Error('Error getting settings'); |
| 6418 | } | 6418 | } |
| 6419 | 6419 | ||
| @@ -6640,7 +6640,7 @@ export async function saveSettings(type) { | |||
| 6640 | eventSource.emit(event_types.SETTINGS_UPDATED); | 6640 | eventSource.emit(event_types.SETTINGS_UPDATED); |
| 6641 | }, | 6641 | }, |
| 6642 | error: function (jqXHR, exception) { | 6642 | error: function (jqXHR, exception) { |
| 6643 | toastr.error('Check the server connection and reload the page to prevent data loss.', 'Settings could not be saved'); | 6643 | toastr.error(t`Check the server connection and reload the page to prevent data loss.t`, t`Settings could not be saved`); |
| 6644 | console.log(exception); | 6644 | console.log(exception); |
| 6645 | console.log(jqXHR); | 6645 | console.log(jqXHR); |
| 6646 | }, | 6646 | }, |
| @@ -6906,7 +6906,7 @@ export async function displayPastChats() { | |||
| 6906 | const data = await (selected_group ? getGroupPastChats(selected_group) : getPastCharacterChats()); | 6906 | const data = await (selected_group ? getGroupPastChats(selected_group) : getPastCharacterChats()); |
| 6907 | 6907 | ||
| 6908 | if (!data) { | 6908 | if (!data) { |
| 6909 | toastr.error('Could not load chat data. Try reloading the page.'); | 6909 | toastr.error(t`Could not load chat data. Try reloading the page.`); |
| 6910 | return; | 6910 | return; |
| 6911 | } | 6911 | } |
| 6912 | 6912 | ||
| @@ -7039,7 +7039,7 @@ export function selectRightMenuWithAnimation(selectedMenuId) { | |||
| 7039 | 7039 | ||
| 7040 | export function select_rm_info(type, charId, previousCharId = null) { | 7040 | export function select_rm_info(type, charId, previousCharId = null) { |
| 7041 | if (!type) { | 7041 | if (!type) { |
| 7042 | toastr.error('Invalid process (no \'type\')'); | 7042 | toastr.error(t`Invalid process (no 'type')`); |
| 7043 | return; | 7043 | return; |
| 7044 | } | 7044 | } |
| 7045 | if (type !== 'group_create') { | 7045 | if (type !== 'group_create') { |
| @@ -7047,20 +7047,20 @@ export function select_rm_info(type, charId, previousCharId = null) { | |||
| 7047 | } | 7047 | } |
| 7048 | 7048 | ||
| 7049 | if (type === 'char_delete') { | 7049 | if (type === 'char_delete') { |
| 7050 | toastr.warning(`Character Deleted: ${displayName}`); | 7050 | toastr.warning(t`Character Deleted: ${displayName}`); |
| 7051 | } | 7051 | } |
| 7052 | if (type === 'char_create') { | 7052 | if (type === 'char_create') { |
| 7053 | toastr.success(`Character Created: ${displayName}`); | 7053 | toastr.success(t`Character Created: ${displayName}`); |
| 7054 | } | 7054 | } |
| 7055 | if (type === 'group_create') { | 7055 | if (type === 'group_create') { |
| 7056 | toastr.success('Group Created'); | 7056 | toastr.success(t`Group Created`); |
| 7057 | } | 7057 | } |
| 7058 | if (type === 'group_delete') { | 7058 | if (type === 'group_delete') { |
| 7059 | toastr.warning('Group Deleted'); | 7059 | toastr.warning(t`Group Deleted`); |
| 7060 | } | 7060 | } |
| 7061 | 7061 | ||
| 7062 | if (type === 'char_import') { | 7062 | if (type === 'char_import') { |
| 7063 | toastr.success(`Character Imported: ${displayName}`); | 7063 | toastr.success(t`Character Imported: ${displayName}`); |
| 7064 | } | 7064 | } |
| 7065 | 7065 | ||
| 7066 | selectRightMenuWithAnimation('rm_characters_block'); | 7066 | selectRightMenuWithAnimation('rm_characters_block'); |
| @@ -7515,25 +7515,25 @@ export function hideSwipeButtons() { | |||
| 7515 | */ | 7515 | */ |
| 7516 | export async function deleteSwipe(swipeId = null) { | 7516 | export async function deleteSwipe(swipeId = null) { |
| 7517 | if (swipeId && (isNaN(swipeId) || swipeId < 0)) { | 7517 | if (swipeId && (isNaN(swipeId) || swipeId < 0)) { |
| 7518 | toastr.warning(`Invalid swipe ID: ${swipeId + 1}`); | 7518 | toastr.warning(t`Invalid swipe ID: ${swipeId + 1}`); |
| 7519 | return; | 7519 | return; |
| 7520 | } | 7520 | } |
| 7521 | 7521 | ||
| 7522 | const lastMessage = chat[chat.length - 1]; | 7522 | const lastMessage = chat[chat.length - 1]; |
| 7523 | if (!lastMessage || !Array.isArray(lastMessage.swipes) || !lastMessage.swipes.length) { | 7523 | if (!lastMessage || !Array.isArray(lastMessage.swipes) || !lastMessage.swipes.length) { |
| 7524 | toastr.warning('No messages to delete swipes from.'); | 7524 | toastr.warning(t`No messages to delete swipes from.`); |
| 7525 | return; | 7525 | return; |
| 7526 | } | 7526 | } |
| 7527 | 7527 | ||
| 7528 | if (lastMessage.swipes.length <= 1) { | 7528 | if (lastMessage.swipes.length <= 1) { |
| 7529 | toastr.warning('Can\'t delete the last swipe.'); | 7529 | toastr.warning(t`Can't delete the last swipe.`); |
| 7530 | return; | 7530 | return; |
| 7531 | } | 7531 | } |
| 7532 | 7532 | ||
| 7533 | swipeId = swipeId ?? lastMessage.swipe_id; | 7533 | swipeId = swipeId ?? lastMessage.swipe_id; |
| 7534 | 7534 | ||
| 7535 | if (swipeId < 0 || swipeId >= lastMessage.swipes.length) { | 7535 | if (swipeId < 0 || swipeId >= lastMessage.swipes.length) { |
| 7536 | toastr.warning(`Invalid swipe ID: ${swipeId + 1}`); | 7536 | toastr.warning(t`Invalid swipe ID: ${swipeId + 1}`); |
| 7537 | return; | 7537 | return; |
| 7538 | } | 7538 | } |
| 7539 | 7539 | ||
| @@ -7672,7 +7672,7 @@ export function setGenerationProgress(progress) { | |||
| 7672 | 7672 | ||
| 7673 | function isHordeGenerationNotAllowed() { | 7673 | function isHordeGenerationNotAllowed() { |
| 7674 | if (main_api == 'koboldhorde' && preset_settings == 'gui') { | 7674 | if (main_api == 'koboldhorde' && preset_settings == 'gui') { |
| 7675 | toastr.error('GUI Settings preset is not supported for Horde. Please select another preset.'); | 7675 | toastr.error(t`GUI Settings preset is not supported for Horde. Please select another preset.`); |
| 7676 | return true; | 7676 | return true; |
| 7677 | } | 7677 | } |
| 7678 | 7678 | ||
| @@ -7721,7 +7721,7 @@ function openCharacterWorldPopup() { | |||
| 7721 | } | 7721 | } |
| 7722 | 7722 | ||
| 7723 | $('#character_json_data').val(JSON.stringify(data)); | 7723 | $('#character_json_data').val(JSON.stringify(data)); |
| 7724 | toastr.info('Embedded lorebook will be removed from this character.'); | 7724 | toastr.info(t`Embedded lorebook will be removed from this character.`); |
| 7725 | } catch { | 7725 | } catch { |
| 7726 | console.error('Failed to parse character JSON data.'); | 7726 | console.error('Failed to parse character JSON data.'); |
| 7727 | } | 7727 | } |
| @@ -7912,11 +7912,11 @@ async function createOrEditCharacter(e) { | |||
| 7912 | 7912 | ||
| 7913 | if ($('#form_create').attr('actiontype') == 'createcharacter') { | 7913 | if ($('#form_create').attr('actiontype') == 'createcharacter') { |
| 7914 | if (String($('#character_name_pole').val()).length === 0) { | 7914 | if (String($('#character_name_pole').val()).length === 0) { |
| 7915 | toastr.error('Name is required'); | 7915 | toastr.error(t`Name is required`); |
| 7916 | return; | 7916 | return; |
| 7917 | } | 7917 | } |
| 7918 | if (is_group_generating || is_send_press) { | 7918 | if (is_group_generating || is_send_press) { |
| 7919 | toastr.error('Cannot create characters while generating. Stop the request and try again.', 'Creation aborted'); | 7919 | toastr.error(t`Cannot create characters while generating. Stop the request and try again.`, t`Creation aborted`); |
| 7920 | return; | 7920 | return; |
| 7921 | } | 7921 | } |
| 7922 | try { | 7922 | try { |
| @@ -8000,7 +8000,7 @@ async function createOrEditCharacter(e) { | |||
| 8000 | 8000 | ||
| 8001 | } catch (error) { | 8001 | } catch (error) { |
| 8002 | console.error('Error creating character', error); | 8002 | console.error('Error creating character', error); |
| 8003 | toastr.error('Failed to create character'); | 8003 | toastr.error(t`Failed to create character`); |
| 8004 | } | 8004 | } |
| 8005 | } else { | 8005 | } else { |
| 8006 | try { | 8006 | try { |
| @@ -8059,7 +8059,7 @@ async function createOrEditCharacter(e) { | |||
| 8059 | } | 8059 | } |
| 8060 | } catch (error) { | 8060 | } catch (error) { |
| 8061 | console.log(error); | 8061 | console.log(error); |
| 8062 | 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.'); | 8062 | 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.`); |
| 8063 | } | 8063 | } |
| 8064 | } | 8064 | } |
| 8065 | } | 8065 | } |
| @@ -8551,7 +8551,7 @@ async function selectContextCallback(args, name) { | |||
| 8551 | const result = fuse.search(name); | 8551 | const result = fuse.search(name); |
| 8552 | 8552 | ||
| 8553 | if (result.length === 0) { | 8553 | if (result.length === 0) { |
| 8554 | !quiet && toastr.warning(`Context template "${name}" not found`); | 8554 | !quiet && toastr.warning(t`Context template '${name}' not found`); |
| 8555 | return ''; | 8555 | return ''; |
| 8556 | } | 8556 | } |
| 8557 | 8557 | ||
| @@ -8571,7 +8571,7 @@ async function selectInstructCallback(args, name) { | |||
| 8571 | const result = fuse.search(name); | 8571 | const result = fuse.search(name); |
| 8572 | 8572 | ||
| 8573 | if (result.length === 0) { | 8573 | if (result.length === 0) { |
| 8574 | !quiet && toastr.warning(`Instruct template "${name}" not found`); | 8574 | !quiet && toastr.warning(t`Instruct template '${name}' not found`); |
| 8575 | return ''; | 8575 | return ''; |
| 8576 | } | 8576 | } |
| 8577 | 8577 | ||
| @@ -8623,7 +8623,7 @@ async function connectAPISlash(args, text) { | |||
| 8623 | 8623 | ||
| 8624 | const apiConfig = CONNECT_API_MAP[text.toLowerCase()]; | 8624 | const apiConfig = CONNECT_API_MAP[text.toLowerCase()]; |
| 8625 | if (!apiConfig) { | 8625 | if (!apiConfig) { |
| 8626 | toastr.error(`Error: ${text} is not a valid API`); | 8626 | toastr.error(t`Error: ${text} is not a valid API`); |
| 8627 | return ''; | 8627 | return ''; |
| 8628 | } | 8628 | } |
| 8629 | 8629 | ||
| @@ -8652,7 +8652,7 @@ async function connectAPISlash(args, text) { | |||
| 8652 | } | 8652 | } |
| 8653 | 8653 | ||
| 8654 | const quiet = isTrueBoolean(args?.quiet); | 8654 | const quiet = isTrueBoolean(args?.quiet); |
| 8655 | const toast = quiet ? jQuery() : toastr.info(`API set to ${text}, trying to connect..`); | 8655 | const toast = quiet ? jQuery() : toastr.info(t`API set to ${text}, trying to connect..`); |
| 8656 | 8656 | ||
| 8657 | try { | 8657 | try { |
| 8658 | await waitUntilCondition(() => online_status !== 'no_connection', 10000, 100); | 8658 | await waitUntilCondition(() => online_status !== 'no_connection', 10000, 100); |
| @@ -8691,7 +8691,7 @@ export async function processDroppedFiles(files, data = new Map()) { | |||
| 8691 | const preservedName = data instanceof Map && data.get(file); | 8691 | const preservedName = data instanceof Map && data.get(file); |
| 8692 | await importCharacter(file, preservedName); | 8692 | await importCharacter(file, preservedName); |
| 8693 | } else { | 8693 | } else { |
| 8694 | toastr.warning('Unsupported file type: ' + file.name); | 8694 | toastr.warning(t`Unsupported file type: ` + file.name); |
| 8695 | } | 8695 | } |
| 8696 | } | 8696 | } |
| 8697 | } | 8697 | } |
| @@ -8704,7 +8704,7 @@ export async function processDroppedFiles(files, data = new Map()) { | |||
| 8704 | */ | 8704 | */ |
| 8705 | async function importCharacter(file, preserveFileName = '') { | 8705 | async function importCharacter(file, preserveFileName = '') { |
| 8706 | if (is_group_generating || is_send_press) { | 8706 | if (is_group_generating || is_send_press) { |
| 8707 | toastr.error('Cannot import characters while generating. Stop the request and try again.', 'Import aborted'); | 8707 | toastr.error(t`Cannot import characters while generating. Stop the request and try again.`, t`Import aborted`); |
| 8708 | throw new Error('Cannot import character while generating'); | 8708 | throw new Error('Cannot import character while generating'); |
| 8709 | } | 8709 | } |
| 8710 | 8710 | ||
| @@ -8731,7 +8731,7 @@ async function importCharacter(file, preserveFileName = '') { | |||
| 8731 | }); | 8731 | }); |
| 8732 | 8732 | ||
| 8733 | if (data.error) { | 8733 | if (data.error) { |
| 8734 | toastr.error('The file is likely invalid or corrupted.', 'Could not import character'); | 8734 | toastr.error(t`The file is likely invalid or corrupted.`, t`Could not import character`); |
| 8735 | return; | 8735 | return; |
| 8736 | } | 8736 | } |
| 8737 | 8737 | ||
| @@ -8784,7 +8784,7 @@ async function doImpersonate(args, prompt) { | |||
| 8784 | await waitUntilCondition(() => !is_send_press && !is_group_generating, 10000, 100); | 8784 | await waitUntilCondition(() => !is_send_press && !is_group_generating, 10000, 100); |
| 8785 | } catch { | 8785 | } catch { |
| 8786 | console.warn('Timeout waiting for generation unlock'); | 8786 | console.warn('Timeout waiting for generation unlock'); |
| 8787 | toastr.warning('Cannot run /impersonate command while the reply is being generated.'); | 8787 | toastr.warning(t`Cannot run /impersonate command while the reply is being generated.`); |
| 8788 | return ''; | 8788 | return ''; |
| 8789 | } | 8789 | } |
| 8790 | 8790 | ||
| @@ -8846,19 +8846,19 @@ async function doDeleteChat() { | |||
| 8846 | 8846 | ||
| 8847 | async function doRenameChat(_, chatName) { | 8847 | async function doRenameChat(_, chatName) { |
| 8848 | if (!chatName) { | 8848 | if (!chatName) { |
| 8849 | toastr.warning('Name must be provided as an argument to rename this chat.'); | 8849 | toastr.warning(t`Name must be provided as an argument to rename this chat.`); |
| 8850 | return ''; | 8850 | return ''; |
| 8851 | } | 8851 | } |
| 8852 | 8852 | ||
| 8853 | const currentChatName = getCurrentChatId(); | 8853 | const currentChatName = getCurrentChatId(); |
| 8854 | if (!currentChatName) { | 8854 | if (!currentChatName) { |
| 8855 | toastr.warning('No chat selected that can be renamed.'); | 8855 | toastr.warning(t`No chat selected that can be renamed.`); |
| 8856 | return ''; | 8856 | return ''; |
| 8857 | } | 8857 | } |
| 8858 | 8858 | ||
| 8859 | await renameChat(currentChatName, chatName); | 8859 | await renameChat(currentChatName, chatName); |
| 8860 | 8860 | ||
| 8861 | toastr.success(`Successfully renamed chat to: ${chatName}`); | 8861 | toastr.success(t`Successfully renamed chat to: ${chatName}`); |
| 8862 | return ''; | 8862 | return ''; |
| 8863 | } | 8863 | } |
| 8864 | 8864 | ||
| @@ -8973,7 +8973,7 @@ export async function deleteCharacter(characterKey, { deleteChats = true } = {}) | |||
| 8973 | for (const key of characterKey) { | 8973 | for (const key of characterKey) { |
| 8974 | const character = characters.find(x => x.avatar == key); | 8974 | const character = characters.find(x => x.avatar == key); |
| 8975 | if (!character) { | 8975 | if (!character) { |
| 8976 | toastr.warning(`Character ${key} not found. Skipping deletion.`); | 8976 | toastr.warning(t`Character ${key} not found. Skipping deletion.`); |
| 8977 | continue; | 8977 | continue; |
| 8978 | } | 8978 | } |
| 8979 | 8979 | ||
| @@ -8990,7 +8990,7 @@ export async function deleteCharacter(characterKey, { deleteChats = true } = {}) | |||
| 8990 | }); | 8990 | }); |
| 8991 | 8991 | ||
| 8992 | if (!response.ok) { | 8992 | if (!response.ok) { |
| 8993 | toastr.error(`${response.status} ${response.statusText}`, 'Failed to delete character'); | 8993 | toastr.error(`${response.status} ${response.statusText}`, t`Failed to delete character`); |
| 8994 | continue; | 8994 | continue; |
| 8995 | } | 8995 | } |
| 8996 | 8996 | ||
| @@ -9665,12 +9665,7 @@ jQuery(async function () { | |||
| 9665 | 9665 | ||
| 9666 | let deleteChats = false; | 9666 | let deleteChats = false; |
| 9667 | 9667 | ||
| 9668 | const confirm = await Popup.show.confirm('Delete the character?', ` | 9668 | const confirm = await Popup.show.confirm(t`Delete the character?`, await renderTemplateAsync('deleteConfirm'), { |
| 9669 | <b>THIS IS PERMANENT!<br><br> | ||
| 9670 | <label for="del_char_checkbox" class="checkbox_label justifyCenter"> | ||
| 9671 | <input type="checkbox" id="del_char_checkbox" /> | ||
| 9672 | <small>Also delete the chat files</small> | ||
| 9673 | </label></b>`, { | ||
| 9674 | onClose: () => deleteChats = !!$('#del_char_checkbox').prop('checked'), | 9669 | onClose: () => deleteChats = !!$('#del_char_checkbox').prop('checked'), |
| 9675 | }); | 9670 | }); |
| 9676 | if (!confirm) { | 9671 | if (!confirm) { |
| @@ -8,6 +8,7 @@ import { debounce, waitUntilCondition, escapeHtml } from './utils.js'; | |||
| 8 | import { debounce_timeout } from './constants.js'; | 8 | import { debounce_timeout } from './constants.js'; |
| 9 | import { renderTemplateAsync } from './templates.js'; | 9 | import { renderTemplateAsync } from './templates.js'; |
| 10 | import { Popup } from './popup.js'; | 10 | import { Popup } from './popup.js'; |
| 11 | import { t } from './i18n.js'; | ||
| 11 | 12 | ||
| 12 | function debouncePromise(func, delay) { | 13 | function debouncePromise(func, delay) { |
| 13 | let timeoutId; | 14 | let timeoutId; |
| @@ -455,7 +456,7 @@ class PromptManager { | |||
| 455 | 456 | ||
| 456 | // Delete selected prompt from list form and close edit form | 457 | // 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 { | |||
| 531 | 532 | ||
| 532 | // Import prompts for the selected character | 533 | // 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; |
| 537 | 538 | ||
| @@ -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 { | |||
| 567 | 568 | ||
| 568 | // Restore default state of a characters prompt order | 569 | // 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; |
| 573 | 574 | ||
| @@ -1649,7 +1650,7 @@ class PromptManager { | |||
| 1649 | }; | 1650 | }; |
| 1650 | 1651 | ||
| 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 | } |
| 1655 | 1656 | ||
| @@ -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 | } |
| 1674 | 1675 | ||
| 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 | } |
| 1678 | 1679 | ||
| @@ -16,6 +16,7 @@ import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; | |||
| 16 | import { SlashCommand } from './slash-commands/SlashCommand.js'; | 16 | import { SlashCommand } from './slash-commands/SlashCommand.js'; |
| 17 | import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js'; | 17 | import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js'; |
| 18 | export { MODULE_NAME as NOTE_MODULE_NAME }; | 18 | export { MODULE_NAME as NOTE_MODULE_NAME }; |
| 19 | import { t } from './i18n.js'; | ||
| 19 | 20 | ||
| 20 | const MODULE_NAME = '2_floating_prompt'; // <= Deliberate, for sorting lower than memory | 21 | const MODULE_NAME = '2_floating_prompt'; // <= Deliberate, for sorting lower than memory |
| 21 | 22 | ||
| @@ -37,7 +38,7 @@ const chara_note_position = { | |||
| 37 | 38 | ||
| 38 | function setNoteTextCommand(_, text) { | 39 | function 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 | } |
| 43 | 44 | ||
| @@ -45,12 +46,12 @@ function setNoteDepthCommand(_, text) { | |||
| 45 | const value = Number(text); | 46 | const value = Number(text); |
| 46 | 47 | ||
| 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 | } |
| 51 | 52 | ||
| 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 | } |
| 56 | 57 | ||
| @@ -58,12 +59,12 @@ function setNoteIntervalCommand(_, text) { | |||
| 58 | const value = Number(text); | 59 | const value = Number(text); |
| 59 | 60 | ||
| 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 | } |
| 64 | 65 | ||
| 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 | } |
| 69 | 70 | ||
| @@ -76,12 +77,12 @@ function setNotePositionCommand(_, text) { | |||
| 76 | const position = validPositions[text?.trim()]; | 77 | const position = validPositions[text?.trim()]; |
| 77 | 78 | ||
| 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 | } |
| 82 | 83 | ||
| 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 | } |
| 87 | 88 | ||
| @@ -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.`); |
| 210 | 211 | ||
| 211 | // Don't save settings if something went wrong | 212 | // Don't save settings if something went wrong |
| 212 | return; | 213 | return; |
| @@ -397,7 +398,7 @@ function onANMenuItemClick() { | |||
| 397 | //because this listener takes priority | 398 | //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 | } |
| 403 | 404 | ||
| @@ -40,6 +40,7 @@ import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js'; | |||
| 40 | import { ScraperManager } from './scrapers.js'; | 40 | import { ScraperManager } from './scrapers.js'; |
| 41 | import { DragAndDropHandler } from './dragdrop.js'; | 41 | import { DragAndDropHandler } from './dragdrop.js'; |
| 42 | import { renderTemplateAsync } from './templates.js'; | 42 | import { renderTemplateAsync } from './templates.js'; |
| 43 | import { t } from './i18n.js'; | ||
| 43 | 44 | ||
| 44 | /** | 45 | /** |
| 45 | * @typedef {Object} FileAttachment | 46 | * @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); |
| 300 | 301 | ||
| 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 | } |
| 305 | 306 | ||
| 306 | // If file is binary | 307 | // 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 | } |
| 311 | 312 | ||
| @@ -521,7 +522,7 @@ async function openExternalMediaOverridesDialog() { | |||
| 521 | const entityId = getCurrentEntityId(); | 522 | const entityId = getCurrentEntityId(); |
| 522 | 523 | ||
| 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 | } |
| 527 | 528 | ||
| @@ -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'); |
| 1055 | 1056 | ||
| 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 | } |
| 1060 | 1061 | ||
| @@ -1168,7 +1169,7 @@ async function runScraper(scraperId, target, callback) { | |||
| 1168 | 1169 | ||
| 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 | } |
| 1174 | 1175 | ||
| @@ -1176,12 +1177,12 @@ async function runScraper(scraperId, target, callback) { | |||
| 1176 | await uploadFileAttachmentToServer(file, target); | 1177 | await uploadFileAttachmentToServer(file, target); |
| 1177 | } | 1178 | } |
| 1178 | 1179 | ||
| 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 | } |
| 1187 | 1188 | ||
| @@ -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 { |
| @@ -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. 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. 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> |
| @@ -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 | }); |
| 523 | 523 | ||
| 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); |
| 838 | 838 | ||
| 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) { | |||
| 1368 | 1368 | ||
| 1369 | async function onDeleteGroupClick() { | 1369 | async 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 | } |
| 1378 | 1378 | ||
| 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) { | |||
| 1630 | 1630 | ||
| 1631 | export async function openGroupById(groupId) { | 1631 | export 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 | } |
| 1636 | 1636 | ||
| @@ -1659,7 +1659,7 @@ export async function openGroupById(groupId) { | |||
| 1659 | 1659 | ||
| 1660 | function openCharacterDefinition(characterSelect) { | 1660 | function 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 | }); |
| 1909 | 1909 | ||
| 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 | } |
| @@ -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) })); |
| 400 | 400 | ||
| 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.'); |
| @@ -1225,15 +1225,15 @@ export async function prepareOpenAIMessages({ | |||
| 1225 | await populateChatCompletion(prompts, chatCompletion, { bias, quietPrompt, quietImage, type, cyclePrompt, messages, messageExamples }); | 1225 | await populateChatCompletion(prompts, chatCompletion, { bias, quietPrompt, quietImage, type, cyclePrompt, messages, messageExamples }); |
| 1226 | } catch (error) { | 1226 | } catch (error) { |
| 1227 | if (error instanceof TokenBudgetExceededError) { | 1227 | if (error instanceof TokenBudgetExceededError) { |
| 1228 | toastr.error('An error occurred while counting tokens: Token budget exceeded.'); | 1228 | toastr.error(t`An error occurred while counting tokens: Token budget exceeded.`); |
| 1229 | chatCompletion.log('Token budget exceeded.'); | 1229 | chatCompletion.log('Token budget exceeded.'); |
| 1230 | promptManager.error = 'Not enough free tokens for mandatory prompts. Raise your token Limit or disable custom prompts.'; | 1230 | promptManager.error = t`Not enough free tokens for mandatory prompts. Raise your token Limit or disable custom prompts.`; |
| 1231 | } else if (error instanceof InvalidCharacterNameError) { | 1231 | } else if (error instanceof InvalidCharacterNameError) { |
| 1232 | toastr.warning('An error occurred while counting tokens: Invalid character name'); | 1232 | toastr.warning(t`An error occurred while counting tokens: Invalid character name`); |
| 1233 | chatCompletion.log('Invalid character name'); | 1233 | chatCompletion.log('Invalid character name'); |
| 1234 | promptManager.error = 'The name of at least one character contained whitespaces or special characters. Please check your user and character name.'; | 1234 | promptManager.error = t`The name of at least one character contained whitespaces or special characters. Please check your user and character name.`; |
| 1235 | } else { | 1235 | } else { |
| 1236 | toastr.error('An unknown error occurred while counting tokens. Further information may be available in console.'); | 1236 | toastr.error(t`An unknown error occurred while counting tokens. Further information may be available in console.`); |
| 1237 | chatCompletion.log('----- Unexpected error while preparing prompts -----'); | 1237 | chatCompletion.log('----- Unexpected error while preparing prompts -----'); |
| 1238 | chatCompletion.log(error); | 1238 | chatCompletion.log(error); |
| 1239 | chatCompletion.log(error.stack); | 1239 | chatCompletion.log(error.stack); |
| @@ -1287,11 +1287,8 @@ function tryParseStreamingError(response, decoded) { | |||
| 1287 | } | 1287 | } |
| 1288 | } | 1288 | } |
| 1289 | 1289 | ||
| 1290 | function checkQuotaError(data) { | 1290 | async function checkQuotaError(data) { |
| 1291 | const errorText = `<h3>Encountered an error while processing your request.<br> | 1291 | const errorText = await renderTemplateAsync('quotaError'); |
| 1292 | Check you have credits available on your | ||
| 1293 | <a href="https://platform.openai.com/account/usage" target="_blank">OpenAI account</a>.<br> | ||
| 1294 | If you have sufficient credits, please try again later.</h3>`; | ||
| 1295 | 1292 | ||
| 1296 | if (!data) { | 1293 | if (!data) { |
| 1297 | return; | 1294 | return; |
| @@ -1927,11 +1924,11 @@ async function sendOpenAIRequest(type, messages, signal) { | |||
| 1927 | else { | 1924 | else { |
| 1928 | const data = await response.json(); | 1925 | const data = await response.json(); |
| 1929 | 1926 | ||
| 1930 | checkQuotaError(data); | 1927 | await checkQuotaError(data); |
| 1931 | checkModerationError(data); | 1928 | checkModerationError(data); |
| 1932 | 1929 | ||
| 1933 | if (data.error) { | 1930 | if (data.error) { |
| 1934 | toastr.error(data.error.message || response.statusText, 'API returned an error'); | 1931 | toastr.error(data.error.message || response.statusText, t`API returned an error`); |
| 1935 | throw new Error(data); | 1932 | throw new Error(data); |
| 1936 | } | 1933 | } |
| 1937 | 1934 | ||
| @@ -2178,7 +2175,7 @@ function parseOpenAITextLogprobs(logprobs) { | |||
| 2178 | 2175 | ||
| 2179 | function handleWindowError(err) { | 2176 | function handleWindowError(err) { |
| 2180 | const text = parseWindowError(err); | 2177 | const text = parseWindowError(err); |
| 2181 | toastr.error(text, 'Window.ai returned an error'); | 2178 | toastr.error(text, t`Window.ai returned an error`); |
| 2182 | throw err; | 2179 | throw err; |
| 2183 | } | 2180 | } |
| 2184 | 2181 | ||
| @@ -3251,7 +3248,7 @@ async function getStatusOpen() { | |||
| 3251 | } | 3248 | } |
| 3252 | 3249 | ||
| 3253 | function showWindowExtensionError() { | 3250 | function showWindowExtensionError() { |
| 3254 | toastr.error('Get it here: <a href="https://windowai.io/" target="_blank">windowai.io</a>', 'Extension is not installed', { | 3251 | toastr.error(t`Get it here:` + ' <a href="https://windowai.io/" target="_blank">windowai.io</a>', t`Extension is not installed`, { |
| 3255 | escapeHtml: false, | 3252 | escapeHtml: false, |
| 3256 | timeOut: 0, | 3253 | timeOut: 0, |
| 3257 | extendedTimeOut: 0, | 3254 | extendedTimeOut: 0, |
| @@ -3370,7 +3367,7 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) { | |||
| 3370 | if (triggerUi) $('#settings_preset_openai').append(option).trigger('change'); | 3367 | if (triggerUi) $('#settings_preset_openai').append(option).trigger('change'); |
| 3371 | } | 3368 | } |
| 3372 | } else { | 3369 | } else { |
| 3373 | toastr.error('Failed to save preset'); | 3370 | toastr.error(t`Failed to save preset`); |
| 3374 | throw new Error('Failed to save preset'); | 3371 | throw new Error('Failed to save preset'); |
| 3375 | } | 3372 | } |
| 3376 | } | 3373 | } |
| @@ -3449,7 +3446,7 @@ async function createNewLogitBiasPreset() { | |||
| 3449 | } | 3446 | } |
| 3450 | 3447 | ||
| 3451 | if (name in oai_settings.bias_presets) { | 3448 | if (name in oai_settings.bias_presets) { |
| 3452 | toastr.error('Preset name should be unique.'); | 3449 | toastr.error(t`Preset name should be unique.`); |
| 3453 | return; | 3450 | return; |
| 3454 | } | 3451 | } |
| 3455 | 3452 | ||
| @@ -3493,7 +3490,7 @@ async function onPresetImportFileChange(e) { | |||
| 3493 | try { | 3490 | try { |
| 3494 | presetBody = JSON.parse(importedFile); | 3491 | presetBody = JSON.parse(importedFile); |
| 3495 | } catch (err) { | 3492 | } catch (err) { |
| 3496 | toastr.error('Invalid file'); | 3493 | toastr.error(t`Invalid file`); |
| 3497 | return; | 3494 | return; |
| 3498 | } | 3495 | } |
| 3499 | 3496 | ||
| @@ -3534,7 +3531,7 @@ async function onPresetImportFileChange(e) { | |||
| 3534 | }); | 3531 | }); |
| 3535 | 3532 | ||
| 3536 | if (!savePresetSettings.ok) { | 3533 | if (!savePresetSettings.ok) { |
| 3537 | toastr.error('Failed to save preset'); | 3534 | toastr.error(t`Failed to save preset`); |
| 3538 | return; | 3535 | return; |
| 3539 | } | 3536 | } |
| 3540 | 3537 | ||
| @@ -3559,7 +3556,7 @@ async function onPresetImportFileChange(e) { | |||
| 3559 | 3556 | ||
| 3560 | async function onExportPresetClick() { | 3557 | async function onExportPresetClick() { |
| 3561 | if (!oai_settings.preset_settings_openai) { | 3558 | if (!oai_settings.preset_settings_openai) { |
| 3562 | toastr.error('No preset selected'); | 3559 | toastr.error(t`No preset selected`); |
| 3563 | return; | 3560 | return; |
| 3564 | } | 3561 | } |
| 3565 | 3562 | ||
| @@ -3600,12 +3597,12 @@ async function onLogitBiasPresetImportFileChange(e) { | |||
| 3600 | e.target.value = ''; | 3597 | e.target.value = ''; |
| 3601 | 3598 | ||
| 3602 | if (name in oai_settings.bias_presets) { | 3599 | if (name in oai_settings.bias_presets) { |
| 3603 | toastr.error('Preset name should be unique.'); | 3600 | toastr.error(t`Preset name should be unique.`); |
| 3604 | return; | 3601 | return; |
| 3605 | } | 3602 | } |
| 3606 | 3603 | ||
| 3607 | if (!Array.isArray(importedFile)) { | 3604 | if (!Array.isArray(importedFile)) { |
| 3608 | toastr.error('Invalid logit bias preset file.'); | 3605 | toastr.error(t`Invalid logit bias preset file.`); |
| 3609 | return; | 3606 | return; |
| 3610 | } | 3607 | } |
| 3611 | 3608 | ||
| @@ -3664,16 +3661,16 @@ async function onDeletePresetClick() { | |||
| 3664 | }); | 3661 | }); |
| 3665 | 3662 | ||
| 3666 | if (!response.ok) { | 3663 | if (!response.ok) { |
| 3667 | toastr.warning('Preset was not deleted from server'); | 3664 | toastr.warning(t`Preset was not deleted from server`); |
| 3668 | } else { | 3665 | } else { |
| 3669 | toastr.success('Preset deleted'); | 3666 | toastr.success(t`Preset deleted`); |
| 3670 | } | 3667 | } |
| 3671 | 3668 | ||
| 3672 | saveSettingsDebounced(); | 3669 | saveSettingsDebounced(); |
| 3673 | } | 3670 | } |
| 3674 | 3671 | ||
| 3675 | async function onLogitBiasPresetDeleteClick() { | 3672 | async function onLogitBiasPresetDeleteClick() { |
| 3676 | const value = await callPopup('Delete the preset?', 'confirm'); | 3673 | const value = await callPopup(t`Delete the preset?`, 'confirm'); |
| 3677 | 3674 | ||
| 3678 | if (!value) { | 3675 | if (!value) { |
| 3679 | return; | 3676 | return; |
| @@ -4808,7 +4805,7 @@ function runProxyCallback(_, value) { | |||
| 4808 | const result = fuse.search(value); | 4805 | const result = fuse.search(value); |
| 4809 | 4806 | ||
| 4810 | if (result.length === 0) { | 4807 | if (result.length === 0) { |
| 4811 | toastr.warning(`Proxy preset "${value}" not found`); | 4808 | toastr.warning(t`Proxy preset '${value}' not found`); |
| 4812 | return ''; | 4809 | return ''; |
| 4813 | } | 4810 | } |
| 4814 | 4811 | ||
| @@ -4982,7 +4979,7 @@ export function initOpenAI() { | |||
| 4982 | $('#update_oai_preset').on('click', async function () { | 4979 | $('#update_oai_preset').on('click', async function () { |
| 4983 | const name = oai_settings.preset_settings_openai; | 4980 | const name = oai_settings.preset_settings_openai; |
| 4984 | await saveOpenAIPreset(name, oai_settings); | 4981 | await saveOpenAIPreset(name, oai_settings); |
| 4985 | toastr.success('Preset updated'); | 4982 | toastr.success(t`Preset updated`); |
| 4986 | }); | 4983 | }); |
| 4987 | 4984 | ||
| 4988 | $('#impersonation_prompt_restore').on('click', function () { | 4985 | $('#impersonation_prompt_restore').on('click', function () { |
| @@ -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="assistant_note_1">Note:</b> <span data-i18n="assistant_note_2">this chat is temporary and will be deleted as soon as you leave it.</span> |
| 3 | </div> | 3 | </div> |
| @@ -0,0 +1,5 @@ | |||
| 1 | <b><span data-i18n="delete_confirm_1">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="delete_confirm_2">Also delete the chat files</small> | ||
| 5 | </label></b> | ||
| \ No newline at end of file | 5 | \ No newline at end of file | |
| @@ -1,5 +1,5 @@ | |||
| 1 | <div> | 1 | <div> |
| 2 | <h3>Are you sure you want to duplicate this character?</h3> | 2 | <h3 data-i18n="duplicate_confirm_1">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="duplicate_confirm_2">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> |
| @@ -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> |
| @@ -0,0 +1,4 @@ | |||
| 1 | <h3><span data-i18n="quota_error_1">Encountered an error while processing your request.</span><br> | ||
| 2 | <span data-i18n="quota_error_2">Check you have credits available on your</span> | ||
| 3 | <a href="https://platform.openai.com/account/usage" target="_blank" data-i18n="quota_error_3">OpenAI account</a><span data-i18n="quota_error_4">.</span><br> | ||
| 4 | <span data-i18n="quota_error_5">If you have sufficient credits, please try again later.</span></h3> | ||
| \ No newline at end of file | 4 | \ No newline at end of file | |