diff --git a/public/locales/ru-ru.json b/public/locales/ru-ru.json
index 7472d199d..a633db208 100644
--- a/public/locales/ru-ru.json
+++ b/public/locales/ru-ru.json
@@ -56,7 +56,7 @@
"Rep. Pen. Slope": "Rep. Pen. Slope",
"Top K": "Top K",
"Top P": "Top P",
- "Do Sample": "Сделать образец",
+ "Do Sample": "Включить сэмплинг",
"Add BOS Token": "Добавлять BOS-токен",
"Add the bos_token to the beginning of prompts. Disabling this can make the replies more creative": "Добавлять BOS-токен в начале промпта. Если выключить, ответы могут стать более креативными.",
"Ban EOS Token": "Запретить EOS-токен",
@@ -451,8 +451,8 @@
"Creator's Notes": "Заметки создателя",
"A-Z": "A-Z",
"Z-A": "Z-A",
- "Newest": "Новейшие",
- "Oldest": "Старейшие",
+ "Newest": "Сначала новые",
+ "Oldest": "Сначала старые",
"Favorites": "Избранные",
"Recent": "Последние",
"Most chats": "Больше всего чатов",
@@ -564,7 +564,7 @@
"Advanced Definition": "Расширенное описание",
"Character Lore": "Лор персонажа",
"Export and Download": "Экспортировать и скачать",
- "Duplicate Character": "Дублировать персонажа",
+ "Duplicate Character": "Клонировать персонажа",
"Create Character": "Создать персонажа",
"Delete Character": "Удалить персонажа",
"View all tags": "Показать все тэги",
@@ -1690,7 +1690,7 @@
"Delete Message": "Удалить сообщение",
"Delete Swipe": "Удалить свайп",
"Could not get a reply from API. Check your connection settings / API key and try again.": "Не удалось получить ответ от API. Проверьте настройки соединения и API-ключ и повторите попытку.",
- "Connecting To Proxy": "Подключение к прокси",
+ "Connecting To Proxy": "Подключиться к прокси",
"Are you sure you want to connect to the following proxy URL?": "Вы точно хотите соединиться с прокси по этому адресу?",
"API connection successful!": "Соединение с API установлено!",
"Proxy Saved": "Прокси сохранена",
@@ -1747,5 +1747,151 @@
"Markdown Hotkeys": "Горячие клавиши для разметки",
"markdown_hotkeys_desc": "Включить горячие клавиши для вставки символов разметки в некоторых полях ввода. См. '/help hotkeys'.",
"Save and Update": "Сохранить и обновить",
- "Profile name:": "Название профиля:"
+ "Profile name:": "Название профиля:",
+ "API returned an error": "API вернуло ошибку",
+ "Failed to save preset": "Не удалось сохранить пресет",
+ "Preset name should be unique.": "Название пресета должно быть уникальным.",
+ "Invalid file": "Невалидный файл",
+ "No preset selected": "Пресет не выбран",
+ "Invalid logit bias preset file.": "Файл пресета невалиден.",
+ "Preset was not deleted from server": "Пресет не удалён с сервера",
+ "Preset deleted": "Пресет удалён",
+ "Delete the preset?": "Удалить пресет?",
+ "Preset updated": "Пресет обновлён",
+ "Entered reverse proxy address is not a valid URL": "Введённый адрес прокси невалиден",
+ "An error occurred while counting tokens: Token budget exceeded.": "Ошибка при подсчёте токенов: Превышен бюджет токенов",
+ "An error occurred while counting tokens: Invalid character name": "Ошибка при подсчёте токенов: Невалидное имя персонажа",
+ "Not enough free tokens for mandatory prompts. Raise your token Limit or disable custom prompts.": "Недостаточно токенов для всех выбранных промптов. Повысьте лимит токенов или отключите часть промптов.",
+ "The name of at least one character contained whitespaces or special characters. Please check your user and character name.": "В имени одного из персонажей содержится пробел или иной спецсимвол. Проверьте имена пользователя и персонажа.",
+ "An unknown error occurred while counting tokens. Further information may be available in console.": "Неизвестная ошибка при подсчёте токенов. Проверьте консоль, возможно, подробная информация есть там.",
+ "Encountered an error while processing your request.": "При обработке вашего запроса возникла ошибка.",
+ "Check you have credits available on your": "Убедитесь, что на вашем",
+ "OpenAI account quora_error": "аккаунте OpenAI",
+ "dot quota_error": "имеется достаточно кредитов.",
+ "If you have sufficient credits, please try again later.": "Если кредитов достаточно, то повторите попытку позднее.",
+ "Proxy preset '${0}' not found": "Пресет '${0}' не найден",
+ "Window.ai returned an error": "Window.ai вернул ошибку",
+ "Get it here:": "Загрузите здесь:",
+ "Extension is not installed": "Расширение не установлено",
+ "Update or remove your reverse proxy settings.": "Измените или удалите ваши настройки прокси.",
+ "An error occurred while importing prompts. More info available in console.": "В процессе импорта произошла ошибка. Подробную информацию см. в консоли.",
+ "Could not import prompts. Export failed validation.": "Не удалось импортировать промпты. Не пройдена валидация при экспорте.",
+ "Prompt import complete.": "Импорт завершён.",
+ "Are you sure you want to delete this prompt?": "Вы точно хотите удалить этот промпт?",
+ "Existing prompts with the same ID will be overridden. Do you want to proceed?": "Имеющиеся промпты с совпадающими идентификаторами будут перезаписаны. Продолжить?",
+ "This will reset the prompt order for this character. You will not lose any prompts.": "Будет сброшен порядок промптов для этого персонажа. Сами промпты вы не потеряете.",
+ "Note:": "Примечание:",
+ "this chat is temporary and will be deleted as soon as you leave it.": "это временный чат, он будет удалён, как только вы из него выйдете.",
+ "help_hotkeys_20": "Горячие клавиши для разметки",
+ "help_hotkeys_21": "Работают в окне ввода чата, а также в полях, отмеченных этим значком:",
+ "help_hotkeys_22": "**полужирный**",
+ "help_hotkeys_23": "*курсив*",
+ "help_hotkeys_24": "__подчёркивание__",
+ "help_hotkeys_25": "`inline-код`",
+ "help_hotkeys_26": "~~зачёркнутый~~",
+ "ext_regex_only_format_visual_desc": "Содержимое файла с историей чата останется нетронутым, изменения будут лишь визуальными (в UI).",
+ "Could not convert file": "Не удалось сконвертировать файл",
+ "Could not upload file": "Не удалось загрузить файл",
+ "Could not download file": "Не удалось скачать файл",
+ "File is too big. Maximum size is ${0}.": "Слишком большой файл. Максимальный размер: ${0}.",
+ "Binary files are not supported. Select a text file or image.": "Бинарные файлы не поддерживаются. Выберите текстовый файл или изображение.",
+ "No character or group selected": "Не выбрано ни одного персонажа или группы",
+ "Could not delete file": "Не удалось удалить файл",
+ "No attachments selected.": "Вложение не выбрано.",
+ "Data Bank": "Банк данных",
+ "No files were scraped.": "Скрапинг не выполнен.",
+ "Scraped ${0} files from ${1} to ${2}.": "Соскраплено из ${0} файлов из ${1} в ${2}.",
+ "Check browser console for details.": "Подробности см. в консоли браузера.",
+ "Scraping failed": "Ошибка скрапинга",
+ "External media has been blocked": "Внешние медиа отключены",
+ "Use the 'Ext. Media' button to allow it. Click on this message to dismiss.": "Разрешить можно с помощью кнопки 'Внешн. медиа'. Нажмите на это сообщение, чтобы его скрыть.",
+ "Couldn't get CSRF token. Please refresh the page.": "Не удалось получить CSRF токен. Попробуйте перезагрузить страницу.",
+ "Error": "Ошибка",
+ "API Error": "Ошибка API",
+ "Please wait until the chat is saved before switching characters.": "Пожалуйста, дождитесь сохранения чата, прежде чем переключать персонажа.",
+ "Your chat is still saving...": "Чат всё ещё сохраняется...",
+ "Character ${0} not found in the list": "Персонаж ${0} не найден в списке",
+ "Streaming is enabled, but the version of Kobold used does not support token streaming.": "Включён стриминг текста, но ваша версия Kobold не поддерживает стриминг токенов.",
+ "Streaming is not supported for the Legacy API. Update Ooba and use new API to enable streaming.": "Для устаревшего API стриминг недоступен. Обновите oobaboga и используйте новый API, чтобы включить стриминг.",
+ "Verify that the server is running and accessible.": "Убедитесь, что сервер запущен и доступен по сети.",
+ "ST Server cannot be reached": "Не удалось соединиться с сервером ST",
+ "You must first select a character to duplicate!": "Вы не выбрали персонажа, которого хотите клонировать!",
+ "Character Duplicated": "Персонаж склонирован",
+ "No character name provided.": "Вы не ввели имя персонажа.",
+ "Rename Character": "Переименование",
+ "No character selected.": "Вы не выбрали персонажа.",
+ "New name:": "Новое имя:",
+ "Same character name provided, so name did not change.": "Введено то же самое имя, ничего не изменилось.",
+ "Character renamed and past chats updated!": "Персонаж переименован, а чаты обновлены!",
+ "Character renamed!": "Персонаж переименован!",
+ "Something went wrong. The page will be reloaded.": "Что-то пошло не так. Страница будет перезагружена.",
+ "Past chat could not be updated: ${0}": "Не удалось обновить чат ${0}",
+ "Trying to save group chat with regular saveChat function. Aborting to prevent corruption.": "Произошла попытка сохранения группового чата функцией saveChat. Откатываем изменения, чтобы предотвратить потерю данных.",
+ "Check the server connection and reload the page to prevent data loss.": "Проверьте связь с сервером и перезагрузите страницу, чтобы избежать потери данных.",
+ "Chat could not be saved": "Не удалось сохранить чат",
+ "Settings could not be loaded after multiple attempts. Please try again later.": "Не удалось загрузить настройки за несколько попыток. Попробуйте позднее.",
+ "Settings could not be saved": "Не удалось сохранить настройки",
+ "Could not load chat data. Try reloading the page.": "Не удалось загрузить чат. Попробуйте обновить страницу.",
+ "Invalid process (no 'type')": "Невалидный процесс (нет параметра 'type')",
+ "Character Deleted: ${0}": "Персонаж удалён: ${0}",
+ "Character Created: ${0}": "Персонаж создан: ${0}",
+ "Group Created": "Группа создана",
+ "Group Deleted": "Группа удалена",
+ "Character Imported: ${0}": "Персонаж импортирован: ${0}",
+ "Invalid swipe ID: ${0}": "Некорректный идентификатор свайпа: ${0}",
+ "No messages to delete swipes from.": "Сообщение, из которого требуется удалить свайп, не найдено.",
+ "Can't delete the last swipe.": "Невозможно удалить единственный свайп.",
+ "GUI Settings preset is not supported for Horde. Please select another preset.": "Для Horde не поддерживаются пресеты настроек GUI. Пожалуйста, выберите другой пресет.",
+ "Embedded lorebook will be removed from this character.": "Встроенный лорбук будет удалён из персонажа.",
+ "Name is required": "Введите имя",
+ "Cannot create characters while generating. Stop the request and try again.": "Во время генерации ответа создать персонажа невозможно. Остановите запрос и повторите попытку.",
+ "Creation aborted": "Процесс создания прерван",
+ "Failed to create character": "Не удалось создать персонажа",
+ "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.",
+ "Context template '${0}' not found": "Шаблон контекста '${0}' не найден",
+ "Instruct template '${0}' not found": "Шаблон Instruct-режима '${0}' не найден",
+ "Error: ${0} is not a valid API": "Ошибка: ${0} не является валидным API",
+ "API set to ${0}, trying to connect..": "Установлено API ${0}, пробуем подключиться...",
+ "Unsupported file type: ": "Неподдерживаемый тип файла: ",
+ "Cannot import characters while generating. Stop the request and try again.": "Во время генерации ответа импорт персонажа невозможен. Остановите запрос и повторите попытку.",
+ "Import aborted": "Процесс импорта прерван",
+ "The file is likely invalid or corrupted.": "Вероятно, файл невалиден или повреждён.",
+ "Could not import character": "Не удалось импортировать персонажа",
+ "Cannot run /impersonate command while the reply is being generated.": "Во время генерации ответа выполнить /impersonate невозможно.",
+ "Name must be provided as an argument to rename this chat.": "Для переименования чата необходимо предоставить новое имя в качестве аргумента.",
+ "No chat selected that can be renamed.": "Чат, который требуется переименовать, не найден.",
+ "Successfully renamed chat to: ${0}": "Чат успешно переименован в: ${0}",
+ "Character ${0} not found. Skipping deletion.": "Персонаж ${0} не найден. Удаление пропускается.",
+ "Failed to delete character": "При удалении персонажа произошло ошибка",
+ "Are you sure you want to duplicate this character?": "Вы точно хотите клонировать этого персонажа?",
+ "If you just want to start a new chat with the same character...": "Если вы хотите просто создать новый чат, воспользуйтесь кнопкой \"Начать новый чат\" в меню слева внизу.",
+ "THIS IS PERMANENT!": "ОТМЕНИТЬ БУДЕТ НЕВОЗМОЖНО!",
+ "Also delete the chat files": "Также удалить файлы чатов",
+ "Delete the character?": "Удалить персонажа?",
+ "Not a valid number": "Некорректное число",
+ "Author's Note depth updated": "Глубина заметок автора обновлена",
+ "Author's Note frequency updated": "Частота заметок автора обновлена",
+ "Not a valid position": "Некорректная позиция",
+ "Author's Note position updated": "Позиция заметок автора обновлена",
+ "Something went wrong. Could not save character's author's note.": "Что-то пошло не так. Не удалось сохранить заметки автора для этого персонажа.",
+ "Select a character before trying to use Author's Note": "Сначала необходимо выбрать персонажа",
+ "Author's Note text updated": "Текст заметок автора обновлён",
+ "Group Validation": "Валидация группы",
+ "Warning: Listed member ${0} does not exist as a character. It will be removed from the group.": "Предупреждение: персонаж ${0} не существует в виде карточки. Он будет удалён из группы.",
+ "Group Chat could not be saved": "Не удалось сохранить групповой чат",
+ "Deleted group member swiped. To get a reply, add them back to the group.": "Вы пытаетесь свайпнуть удалённого члена группы. Чтобы получить ответ, добавьте этого персонажа обратно в группу.",
+ "Currently no group selected.": "В данный момент не выбрано ни одной группы.",
+ "Not so fast! Wait for the characters to stop typing before deleting the group.": "Чуть помедленнее! Перед удалением группы дождитесь, пока персонаж закончит печатать.",
+ "Delete the group?": "Удалить группу?",
+ "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.": "Вместе с ней будут удалены и все её чаты. Если требуется удалить только один чат, воспользуйтесь кнопкой \"Все чаты\" в меню в левом нижнем углу.",
+ "Can't peek a character while group reply is being generated": "Невозможно открыть карточку персонажа во время генерации ответа",
+ "Threshold": "Порог",
+ "DRY Repetition Penalty": "DRY Штраф за повтор",
+ "Multiplier": "Множитель",
+ "DRY_Multiplier_desc": "Поставьте в положение > 0, чтобы включить DRY. Определяет величину штрафа для кратчайшей \"штрафуемой\" строки.",
+ "DRY_Repetition_Penalty_desc": "DRY налагает штраф на токены, генерация которых приведёт к появлению строки, которая уже была в тексте раньше. Установите множитель = 0, чтобы отключить.",
+ "Base": "Основание",
+ "DRY_Base_desc": "Определяет, насколько быстро возрастает штраф с увеличением длины строки.",
+ "Allowed Length": "Допустимая длина",
+ "DRY_Allowed_Length_desc": "Длина повторяющейся строки, при превышении которой DRY начинает налагать штраф."
}
diff --git a/public/script.js b/public/script.js
index 7651cf3fd..872932e63 100644
--- a/public/script.js
+++ b/public/script.js
@@ -391,8 +391,8 @@ DOMPurify.addHook('uponSanitizeElement', (node, _, config) => {
if (localStorage.getItem(warningShownKey) === null) {
const warningToast = toastr.warning(
- 'Use the "Ext. Media" button to allow it. Click on this message to dismiss.',
- 'External media has been blocked',
+ t`Use the 'Ext. Media' button to allow it. Click on this message to dismiss.`,
+ t`External media has been blocked`,
{
timeOut: 0,
preventDuplicates: true,
@@ -933,7 +933,7 @@ async function firstLoadInit() {
token = tokenData.token;
} catch {
hideLoader();
- toastr.error('Couldn\'t get CSRF token. Please refresh the page.', 'Error', { timeOut: 0, extendedTimeOut: 0, preventDuplicates: true });
+ toastr.error(t`Couldn't get CSRF token. Please refresh the page.`, t`Error`, { timeOut: 0, extendedTimeOut: 0, preventDuplicates: true });
throw new Error('Initialization failed');
}
@@ -1149,7 +1149,7 @@ async function getStatusKobold() {
// We didn't get a 200 status code, but the endpoint has an explanation. Which means it DID connect, but I digress.
if (online_status === 'no_connection' && data.response) {
- toastr.error(data.response, 'API Error', { timeOut: 5000, preventDuplicates: true });
+ toastr.error(data.response, t`API Error`, { timeOut: 5000, preventDuplicates: true });
}
} catch (err) {
console.error('Error getting status', err);
@@ -1235,7 +1235,7 @@ async function getStatusTextgen() {
// We didn't get a 200 status code, but the endpoint has an explanation. Which means it DID connect, but I digress.
if (online_status === 'no_connection' && data.response) {
- toastr.error(data.response, 'API Error', { timeOut: 5000, preventDuplicates: true });
+ toastr.error(data.response, t`API Error`, { timeOut: 5000, preventDuplicates: true });
}
} catch (err) {
if (err instanceof AbortReason) {
@@ -1287,7 +1287,7 @@ export async function selectCharacterById(id) {
}
if (isChatSaving) {
- toastr.info('Please wait until the chat is saved before switching characters.', 'Your chat is still saving...');
+ toastr.info(t`Please wait until the chat is saved before switching characters.`, t`Your chat is still saving...`);
return;
}
@@ -1641,7 +1641,7 @@ export async function getOneCharacter(avatarUrl) {
if (indexOf !== -1) {
characters[indexOf] = getData;
} else {
- toastr.error(`Character ${avatarUrl} not found in the list`, 'Error', { timeOut: 5000, preventDuplicates: true });
+ toastr.error(t`Character ${avatarUrl} not found in the list`, t`Error`, { timeOut: 5000, preventDuplicates: true });
}
}
}
@@ -3408,7 +3408,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
await eventSource.emit(event_types.GENERATION_AFTER_COMMANDS, type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage }, dryRun);
if (main_api == 'kobold' && kai_settings.streaming_kobold && !kai_flags.can_use_streaming) {
- toastr.error('Streaming is enabled, but the version of Kobold used does not support token streaming.', undefined, { timeOut: 10000, preventDuplicates: true });
+ toastr.error(t`Streaming is enabled, but the version of Kobold used does not support token streaming.`, undefined, { timeOut: 10000, preventDuplicates: true });
unblockGeneration(type);
return Promise.resolve();
}
@@ -3417,7 +3417,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
textgen_settings.streaming &&
textgen_settings.legacy_api &&
textgen_settings.type === OOBA) {
- toastr.error('Streaming is not supported for the Legacy API. Update Ooba and use new API to enable streaming.', undefined, { timeOut: 10000, preventDuplicates: true });
+ 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 });
unblockGeneration(type);
return Promise.resolve();
}
@@ -3433,7 +3433,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
if (!pingResult) {
unblockGeneration(type);
- toastr.error('Verify that the server is running and accessible.', 'ST Server cannot be reached');
+ toastr.error(t`Verify that the server is running and accessible.`, t`ST Server cannot be reached`);
throw new Error('Server unreachable');
}
@@ -4461,7 +4461,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
generatedPromptCache = '';
if (data?.response) {
- toastr.error(data.response, 'API Error', { preventDuplicates: true });
+ toastr.error(data.response, t`API Error`, { preventDuplicates: true });
}
throw new Error(data?.response);
}
@@ -4552,7 +4552,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
function onError(exception) {
if (typeof exception?.error?.message === 'string') {
- toastr.error(exception.error.message, 'Error', { timeOut: 10000, extendedTimeOut: 20000 });
+ toastr.error(exception.error.message, t`Error`, { timeOut: 10000, extendedTimeOut: 20000 });
}
generatedPromptCache = '';
@@ -4956,7 +4956,7 @@ function addChatsSeparator(mesSendString) {
async function duplicateCharacter() {
if (!this_chid) {
- toastr.warning('You must first select a character to duplicate!');
+ toastr.warning(t`You must first select a character to duplicate!`);
return '';
}
@@ -4975,7 +4975,7 @@ async function duplicateCharacter() {
body: JSON.stringify(body),
});
if (response.ok) {
- toastr.success('Character Duplicated');
+ toastr.success(t`Character Duplicated`);
const data = await response.json();
await eventSource.emit(event_types.CHARACTER_DUPLICATED, { oldAvatar: body.avatar_url, newAvatar: data.path });
await getCharacters();
@@ -5822,23 +5822,23 @@ export function setSendButtonState(value) {
export async function renameCharacter(name = null, { silent = false, renameChats = null } = {}) {
if (!name && silent) {
- toastr.warning('No character name provided.', 'Rename Character');
+ toastr.warning(t`No character name provided.`, t`Rename Character`);
return false;
}
if (this_chid === undefined) {
- toastr.warning('No character selected.', 'Rename Character');
+ toastr.warning(t`No character selected.`, t`Rename Character`);
return false;
}
const oldAvatar = characters[this_chid].avatar;
- const newValue = name || await callGenericPopup('
', POPUP_TYPE.INPUT, characters[this_chid].name);
if (!newValue) {
- toastr.warning('No character name provided.', 'Rename Character');
+ toastr.warning(t`No character name provided.`, t`Rename Character`);
return false;
}
if (newValue === characters[this_chid].name) {
- toastr.info('Same character name provided, so name did not change.', 'Rename Character');
+ toastr.info(t`Same character name provided, so name did not change.`, t`Rename Character`);
return false;
}
@@ -5885,9 +5885,9 @@ export async function renameCharacter(name = null, { silent = false, renameChats
if (renamePastChatsConfirm) {
await renamePastChats(newAvatar, newValue);
await reloadCurrentChat();
- toastr.success('Character renamed and past chats updated!', 'Rename Character');
+ toastr.success(t`Character renamed and past chats updated!`, t`Rename Character`);
} else {
- toastr.success('Character renamed!', 'Rename Character');
+ toastr.success(t`Character renamed!`, t`Rename Character`);
}
}
else {
@@ -5900,8 +5900,8 @@ export async function renameCharacter(name = null, { silent = false, renameChats
}
catch (error) {
// Reloading to prevent data corruption
- if (!silent) await callPopup('Something went wrong. The page will be reloaded.', 'text');
- else toastr.error('Something went wrong. The page will be reloaded.', 'Rename Character');
+ if (!silent) await callPopup(t`Something went wrong. The page will be reloaded.`, 'text');
+ else toastr.error(t`Something went wrong. The page will be reloaded.`, t`Rename Character`);
console.log('Renaming character error:', error);
location.reload();
@@ -5958,7 +5958,7 @@ async function renamePastChats(newAvatar, newValue) {
}
}
} catch (error) {
- toastr.error(`Past chat could not be updated: ${file_name}`);
+ toastr.error(t`Past chat could not be updated: ${file_name}`);
console.error(error);
}
}
@@ -6007,7 +6007,7 @@ export async function saveChat(chatName, withMetadata, mesId) {
characters[this_chid]['date_last_chat'] = Date.now();
chat.forEach(function (item, i) {
if (item['is_group']) {
- toastr.error('Trying to save group chat with regular saveChat function. Aborting to prevent corruption.');
+ toastr.error(t`Trying to save group chat with regular saveChat function. Aborting to prevent corruption.`);
throw new Error('Group chat saved from saveChat');
}
/*
@@ -6052,7 +6052,7 @@ export async function saveChat(chatName, withMetadata, mesId) {
contentType: 'application/json',
success: function (data) { },
error: function (jqXHR, exception) {
- toastr.error('Check the server connection and reload the page to prevent data loss.', 'Chat could not be saved');
+ toastr.error(t`Check the server connection and reload the page to prevent data loss.`, t`Chat could not be saved`);
console.log(exception);
console.log(jqXHR);
},
@@ -6449,7 +6449,7 @@ export async function getSettings() {
if (!response.ok) {
reloadLoop();
- toastr.error('Settings could not be loaded after multiple attempts. Please try again later.');
+ toastr.error(t`Settings could not be loaded after multiple attempts. Please try again later.`);
throw new Error('Error getting settings');
}
@@ -6676,7 +6676,7 @@ export async function saveSettings(type) {
eventSource.emit(event_types.SETTINGS_UPDATED);
},
error: function (jqXHR, exception) {
- toastr.error('Check the server connection and reload the page to prevent data loss.', 'Settings could not be saved');
+ toastr.error(t`Check the server connection and reload the page to prevent data loss.t`, t`Settings could not be saved`);
console.log(exception);
console.log(jqXHR);
},
@@ -6942,7 +6942,7 @@ export async function displayPastChats() {
const data = await (selected_group ? getGroupPastChats(selected_group) : getPastCharacterChats());
if (!data) {
- toastr.error('Could not load chat data. Try reloading the page.');
+ toastr.error(t`Could not load chat data. Try reloading the page.`);
return;
}
@@ -7075,7 +7075,7 @@ export function selectRightMenuWithAnimation(selectedMenuId) {
export function select_rm_info(type, charId, previousCharId = null) {
if (!type) {
- toastr.error('Invalid process (no \'type\')');
+ toastr.error(t`Invalid process (no 'type')`);
return;
}
if (type !== 'group_create') {
@@ -7083,20 +7083,20 @@ export function select_rm_info(type, charId, previousCharId = null) {
}
if (type === 'char_delete') {
- toastr.warning(`Character Deleted: ${displayName}`);
+ toastr.warning(t`Character Deleted: ${displayName}`);
}
if (type === 'char_create') {
- toastr.success(`Character Created: ${displayName}`);
+ toastr.success(t`Character Created: ${displayName}`);
}
if (type === 'group_create') {
- toastr.success('Group Created');
+ toastr.success(t`Group Created`);
}
if (type === 'group_delete') {
- toastr.warning('Group Deleted');
+ toastr.warning(t`Group Deleted`);
}
if (type === 'char_import') {
- toastr.success(`Character Imported: ${displayName}`);
+ toastr.success(t`Character Imported: ${displayName}`);
}
selectRightMenuWithAnimation('rm_characters_block');
@@ -7554,25 +7554,25 @@ export function hideSwipeButtons() {
*/
export async function deleteSwipe(swipeId = null) {
if (swipeId && (isNaN(swipeId) || swipeId < 0)) {
- toastr.warning(`Invalid swipe ID: ${swipeId + 1}`);
+ toastr.warning(t`Invalid swipe ID: ${swipeId + 1}`);
return;
}
const lastMessage = chat[chat.length - 1];
if (!lastMessage || !Array.isArray(lastMessage.swipes) || !lastMessage.swipes.length) {
- toastr.warning('No messages to delete swipes from.');
+ toastr.warning(t`No messages to delete swipes from.`);
return;
}
if (lastMessage.swipes.length <= 1) {
- toastr.warning('Can\'t delete the last swipe.');
+ toastr.warning(t`Can't delete the last swipe.`);
return;
}
swipeId = swipeId ?? lastMessage.swipe_id;
if (swipeId < 0 || swipeId >= lastMessage.swipes.length) {
- toastr.warning(`Invalid swipe ID: ${swipeId + 1}`);
+ toastr.warning(t`Invalid swipe ID: ${swipeId + 1}`);
return;
}
@@ -7711,7 +7711,7 @@ export function setGenerationProgress(progress) {
function isHordeGenerationNotAllowed() {
if (main_api == 'koboldhorde' && preset_settings == 'gui') {
- toastr.error('GUI Settings preset is not supported for Horde. Please select another preset.');
+ toastr.error(t`GUI Settings preset is not supported for Horde. Please select another preset.`);
return true;
}
@@ -7760,7 +7760,7 @@ function openCharacterWorldPopup() {
}
$('#character_json_data').val(JSON.stringify(data));
- toastr.info('Embedded lorebook will be removed from this character.');
+ toastr.info(t`Embedded lorebook will be removed from this character.`);
} catch {
console.error('Failed to parse character JSON data.');
}
@@ -7951,11 +7951,11 @@ async function createOrEditCharacter(e) {
if ($('#form_create').attr('actiontype') == 'createcharacter') {
if (String($('#character_name_pole').val()).length === 0) {
- toastr.error('Name is required');
+ toastr.error(t`Name is required`);
return;
}
if (is_group_generating || is_send_press) {
- toastr.error('Cannot create characters while generating. Stop the request and try again.', 'Creation aborted');
+ toastr.error(t`Cannot create characters while generating. Stop the request and try again.`, t`Creation aborted`);
return;
}
try {
@@ -8039,7 +8039,7 @@ async function createOrEditCharacter(e) {
} catch (error) {
console.error('Error creating character', error);
- toastr.error('Failed to create character');
+ toastr.error(t`Failed to create character`);
}
} else {
try {
@@ -8098,7 +8098,7 @@ async function createOrEditCharacter(e) {
}
} catch (error) {
console.log(error);
- 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.');
+ 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.`);
}
}
}
@@ -8604,7 +8604,7 @@ async function selectContextCallback(args, name) {
const result = fuse.search(name);
if (result.length === 0) {
- !quiet && toastr.warning(`Context template "${name}" not found`);
+ !quiet && toastr.warning(t`Context template '${name}' not found`);
return '';
}
@@ -8624,7 +8624,7 @@ async function selectInstructCallback(args, name) {
const result = fuse.search(name);
if (result.length === 0) {
- !quiet && toastr.warning(`Instruct template "${name}" not found`);
+ !quiet && toastr.warning(t`Instruct template '${name}' not found`);
return '';
}
@@ -8676,7 +8676,7 @@ async function connectAPISlash(args, text) {
const apiConfig = CONNECT_API_MAP[text.toLowerCase()];
if (!apiConfig) {
- toastr.error(`Error: ${text} is not a valid API`);
+ toastr.error(t`Error: ${text} is not a valid API`);
return '';
}
@@ -8705,7 +8705,7 @@ async function connectAPISlash(args, text) {
}
const quiet = isTrueBoolean(args?.quiet);
- const toast = quiet ? jQuery() : toastr.info(`API set to ${text}, trying to connect..`);
+ const toast = quiet ? jQuery() : toastr.info(t`API set to ${text}, trying to connect..`);
try {
await waitUntilCondition(() => online_status !== 'no_connection', 10000, 100);
@@ -8744,7 +8744,7 @@ export async function processDroppedFiles(files, data = new Map()) {
const preservedName = data instanceof Map && data.get(file);
await importCharacter(file, preservedName);
} else {
- toastr.warning('Unsupported file type: ' + file.name);
+ toastr.warning(t`Unsupported file type: ` + file.name);
}
}
}
@@ -8757,7 +8757,7 @@ export async function processDroppedFiles(files, data = new Map()) {
*/
async function importCharacter(file, preserveFileName = '') {
if (is_group_generating || is_send_press) {
- toastr.error('Cannot import characters while generating. Stop the request and try again.', 'Import aborted');
+ toastr.error(t`Cannot import characters while generating. Stop the request and try again.`, t`Import aborted`);
throw new Error('Cannot import character while generating');
}
@@ -8784,7 +8784,7 @@ async function importCharacter(file, preserveFileName = '') {
});
if (data.error) {
- toastr.error('The file is likely invalid or corrupted.', 'Could not import character');
+ toastr.error(t`The file is likely invalid or corrupted.`, t`Could not import character`);
return;
}
@@ -8837,7 +8837,7 @@ async function doImpersonate(args, prompt) {
await waitUntilCondition(() => !is_send_press && !is_group_generating, 10000, 100);
} catch {
console.warn('Timeout waiting for generation unlock');
- toastr.warning('Cannot run /impersonate command while the reply is being generated.');
+ toastr.warning(t`Cannot run /impersonate command while the reply is being generated.`);
return '';
}
@@ -8899,19 +8899,19 @@ async function doDeleteChat() {
async function doRenameChat(_, chatName) {
if (!chatName) {
- toastr.warning('Name must be provided as an argument to rename this chat.');
+ toastr.warning(t`Name must be provided as an argument to rename this chat.`);
return '';
}
const currentChatName = getCurrentChatId();
if (!currentChatName) {
- toastr.warning('No chat selected that can be renamed.');
+ toastr.warning(t`No chat selected that can be renamed.`);
return '';
}
await renameChat(currentChatName, chatName);
- toastr.success(`Successfully renamed chat to: ${chatName}`);
+ toastr.success(t`Successfully renamed chat to: ${chatName}`);
return '';
}
@@ -9026,7 +9026,7 @@ export async function deleteCharacter(characterKey, { deleteChats = true } = {})
for (const key of characterKey) {
const character = characters.find(x => x.avatar == key);
if (!character) {
- toastr.warning(`Character ${key} not found. Skipping deletion.`);
+ toastr.warning(t`Character ${key} not found. Skipping deletion.`);
continue;
}
@@ -9043,7 +9043,7 @@ export async function deleteCharacter(characterKey, { deleteChats = true } = {})
});
if (!response.ok) {
- toastr.error(`${response.status} ${response.statusText}`, 'Failed to delete character');
+ toastr.error(`${response.status} ${response.statusText}`, t`Failed to delete character`);
continue;
}
@@ -9802,12 +9802,7 @@ jQuery(async function () {
let deleteChats = false;
- const confirm = await Popup.show.confirm('Delete the character?', `
- THIS IS PERMANENT!
- `, {
+ const confirm = await Popup.show.confirm(t`Delete the character?`, await renderTemplateAsync('deleteConfirm'), {
onClose: () => deleteChats = !!$('#del_char_checkbox').prop('checked'),
});
if (!confirm) {
diff --git a/public/scripts/PromptManager.js b/public/scripts/PromptManager.js
index 551b78328..00a34c86b 100644
--- a/public/scripts/PromptManager.js
+++ b/public/scripts/PromptManager.js
@@ -8,6 +8,7 @@ import { debounce, waitUntilCondition, escapeHtml } from './utils.js';
import { debounce_timeout } from './constants.js';
import { renderTemplateAsync } from './templates.js';
import { Popup } from './popup.js';
+import { t } from './i18n.js';
function debouncePromise(func, delay) {
let timeoutId;
@@ -455,7 +456,7 @@ class PromptManager {
// Delete selected prompt from list form and close edit form
this.handleDeletePrompt = async (event) => {
- Popup.show.confirm('Are you sure you want to delete this prompt?', null).then((userChoice) => {
+ Popup.show.confirm(t`Are you sure you want to delete this prompt?`, null).then((userChoice) => {
if (!userChoice) return;
const promptID = document.getElementById(this.configuration.prefix + 'prompt_manager_footer_append_prompt').value;
const prompt = this.getPromptById(promptID);
@@ -531,7 +532,7 @@ class PromptManager {
// Import prompts for the selected character
this.handleImport = () => {
- Popup.show.confirm('Existing prompts with the same ID will be overridden. Do you want to proceed?', null)
+ Popup.show.confirm(t`Existing prompts with the same ID will be overridden. Do you want to proceed?`, null)
.then(userChoice => {
if (!userChoice) return;
@@ -552,7 +553,7 @@ class PromptManager {
const data = JSON.parse(fileContent);
this.import(data);
} catch (err) {
- toastr.error('An error occurred while importing prompts. More info available in console.');
+ toastr.error(t`An error occurred while importing prompts. More info available in console.`);
console.log('An error occurred while importing prompts');
console.log(err.toString());
}
@@ -567,7 +568,7 @@ class PromptManager {
// Restore default state of a characters prompt order
this.handleCharacterReset = () => {
- Popup.show.confirm('This will reset the prompt order for this character. You will not lose any prompts.', null)
+ Popup.show.confirm(t`This will reset the prompt order for this character. You will not lose any prompts.`, null)
.then(userChoice => {
if (!userChoice) return;
@@ -1649,7 +1650,7 @@ class PromptManager {
};
if (false === this.validateObject(controlObj, importData)) {
- toastr.warning('Could not import prompts. Export failed validation.');
+ toastr.warning(t`Could not import prompts. Export failed validation.`);
return;
}
@@ -1672,7 +1673,7 @@ class PromptManager {
throw new Error('Prompt order strategy not supported.');
}
- toastr.success('Prompt import complete.');
+ toastr.success(t`Prompt import complete.`);
this.saveServiceSettings().then(() => this.render());
}
diff --git a/public/scripts/authors-note.js b/public/scripts/authors-note.js
index 31a5719e1..5e1cf44a3 100644
--- a/public/scripts/authors-note.js
+++ b/public/scripts/authors-note.js
@@ -16,6 +16,7 @@ import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
import { SlashCommand } from './slash-commands/SlashCommand.js';
import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js';
export { MODULE_NAME as NOTE_MODULE_NAME };
+import { t } from './i18n.js';
const MODULE_NAME = '2_floating_prompt'; // <= Deliberate, for sorting lower than memory
@@ -37,7 +38,7 @@ const chara_note_position = {
function setNoteTextCommand(_, text) {
$('#extension_floating_prompt').val(text).trigger('input');
- toastr.success('Author\'s Note text updated');
+ toastr.success(t`Author's Note text updated`);
return '';
}
@@ -45,12 +46,12 @@ function setNoteDepthCommand(_, text) {
const value = Number(text);
if (Number.isNaN(value)) {
- toastr.error('Not a valid number');
+ toastr.error(t`Not a valid number`);
return;
}
$('#extension_floating_depth').val(Math.abs(value)).trigger('input');
- toastr.success('Author\'s Note depth updated');
+ toastr.success(t`Author's Note depth updated`);
return '';
}
@@ -58,12 +59,12 @@ function setNoteIntervalCommand(_, text) {
const value = Number(text);
if (Number.isNaN(value)) {
- toastr.error('Not a valid number');
+ toastr.error(t`Not a valid number`);
return;
}
$('#extension_floating_interval').val(Math.abs(value)).trigger('input');
- toastr.success('Author\'s Note frequency updated');
+ toastr.success(t`Author's Note frequency updated`);
return '';
}
@@ -76,12 +77,12 @@ function setNotePositionCommand(_, text) {
const position = validPositions[text?.trim()];
if (Number.isNaN(position)) {
- toastr.error('Not a valid position');
+ toastr.error(t`Not a valid position`);
return;
}
$(`input[name="extension_floating_position"][value="${position}"]`).prop('checked', true).trigger('input');
- toastr.info('Author\'s Note position updated');
+ toastr.info(t`Author's Note position updated`);
return '';
}
@@ -206,7 +207,7 @@ function onExtensionFloatingCharaPromptInput() {
extension_settings.note.chara.push(tempCharaNote);
} else {
console.log('Character author\'s note error: No avatar name key could be found.');
- toastr.error('Something went wrong. Could not save character\'s author\'s note.');
+ toastr.error(t`Something went wrong. Could not save character's author's note.`);
// Don't save settings if something went wrong
return;
@@ -397,7 +398,7 @@ function onANMenuItemClick() {
//because this listener takes priority
$('#options').stop().fadeOut(animation_duration);
} else {
- toastr.warning('Select a character before trying to use Author\'s Note', '', { timeOut: 2000 });
+ toastr.warning(t`Select a character before trying to use Author's Note`, '', { timeOut: 2000 });
}
}
diff --git a/public/scripts/chats.js b/public/scripts/chats.js
index 75ad746b9..d8f1e8dad 100644
--- a/public/scripts/chats.js
+++ b/public/scripts/chats.js
@@ -40,6 +40,7 @@ import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js';
import { ScraperManager } from './scrapers.js';
import { DragAndDropHandler } from './dragdrop.js';
import { renderTemplateAsync } from './templates.js';
+import { t } from './i18n.js';
/**
* @typedef {Object} FileAttachment
@@ -206,7 +207,7 @@ export async function populateFileAttachment(message, inputId = 'file_form_input
const fileText = await converter(file);
base64Data = window.btoa(unescape(encodeURIComponent(fileText)));
} catch (error) {
- toastr.error(String(error), 'Could not convert file');
+ toastr.error(String(error), t`Could not convert file`);
console.error('Could not convert file', error);
}
}
@@ -257,7 +258,7 @@ export async function uploadFileAttachment(fileName, base64Data) {
const responseData = await result.json();
return responseData.path;
} catch (error) {
- toastr.error(String(error), 'Could not upload file');
+ toastr.error(String(error), t`Could not upload file`);
console.error('Could not upload file', error);
}
}
@@ -283,7 +284,7 @@ export async function getFileAttachment(url) {
const text = await result.text();
return text;
} catch (error) {
- toastr.error(error, 'Could not download file');
+ toastr.error(error, t`Could not download file`);
console.error('Could not download file', error);
}
}
@@ -299,13 +300,13 @@ async function validateFile(file) {
const isBinary = /^[\x00-\x08\x0E-\x1F\x7F-\xFF]*$/.test(fileText);
if (!isImage && file.size > fileSizeLimit) {
- toastr.error(`File is too big. Maximum size is ${humanFileSize(fileSizeLimit)}.`);
+ toastr.error(t`File is too big. Maximum size is ${humanFileSize(fileSizeLimit)}.`);
return false;
}
// If file is binary
if (isBinary && !isImage && !isConvertible(file.type)) {
- toastr.error('Binary files are not supported. Select a text file or image.');
+ toastr.error(t`Binary files are not supported. Select a text file or image.`);
return false;
}
@@ -521,7 +522,7 @@ async function openExternalMediaOverridesDialog() {
const entityId = getCurrentEntityId();
if (!entityId) {
- toastr.info('No character or group selected');
+ toastr.info(t`No character or group selected`);
return;
}
@@ -646,7 +647,7 @@ async function deleteFileFromServer(url, silent = false) {
await eventSource.emit(event_types.FILE_ATTACHMENT_DELETED, url);
return true;
} catch (error) {
- toastr.error(String(error), 'Could not delete file');
+ toastr.error(String(error), t`Could not delete file`);
console.error('Could not delete file', error);
return false;
}
@@ -1054,7 +1055,7 @@ async function openAttachmentManager() {
const selectedAttachments = document.querySelectorAll('.attachmentListItemCheckboxContainer .attachmentListItemCheckbox:checked');
if (selectedAttachments.length === 0) {
- toastr.info('No attachments selected.', 'Data Bank');
+ toastr.info(t`No attachments selected.`, t`Data Bank`);
return;
}
@@ -1168,7 +1169,7 @@ async function runScraper(scraperId, target, callback) {
if (files.length === 0) {
console.warn('Scraping returned no files');
- toastr.info('No files were scraped.', 'Data Bank');
+ toastr.info(t`No files were scraped.`, t`Data Bank`);
return;
}
@@ -1176,12 +1177,12 @@ async function runScraper(scraperId, target, callback) {
await uploadFileAttachmentToServer(file, target);
}
- toastr.success(`Scraped ${files.length} files from ${scraperId} to ${target}.`, 'Data Bank');
+ toastr.success(t`Scraped ${files.length} files from ${scraperId} to ${target}.`, t`Data Bank`);
callback();
}
catch (error) {
console.error('Scraping failed', error);
- toastr.error('Check browser console for details.', 'Scraping failed');
+ toastr.error(t`Check browser console for details.`, t`Scraping failed`);
}
}
@@ -1208,7 +1209,7 @@ export async function uploadFileAttachmentToServer(file, target) {
const fileText = await converter(file);
base64Data = window.btoa(unescape(encodeURIComponent(fileText)));
} catch (error) {
- toastr.error(String(error), 'Could not convert file');
+ toastr.error(String(error), t`Could not convert file`);
console.error('Could not convert file', error);
}
} else {
diff --git a/public/scripts/extensions/regex/editor.html b/public/scripts/extensions/regex/editor.html
index 5552504e1..aa207ffd9 100644
--- a/public/scripts/extensions/regex/editor.html
+++ b/public/scripts/extensions/regex/editor.html
@@ -132,7 +132,7 @@
Ephemerality
-