Merge pull request #3291 from Yokayo/staging Update ru-ru translation
Signed| @@ -2278,7 +2278,7 @@ | ||
| 2278 | 2278 | <h4 data-i18n="Model Providers">Model Providers</h4> |
| 2279 | 2279 | <select id="openrouter_providers_text" class="openrouter_providers" multiple> |
| 2280 | 2280 | </select> |
| 2281 | 2281 | <label class="checkbox_label" data-i18n="[title]Automatically chooses an alternative provider if chosen providers can't serve your request." for="openrouter_allow_fallbacks_textgenerationwebui" title="Automatically chooses an alternative provider if chosen providers can't serve your request."> |
| 2282 | 2282 | <input id="openrouter_allow_fallbacks_textgenerationwebui" type="checkbox" /> |
| 2283 | 2283 | <span data-i18n="Allow fallback providers">Allow fallback providers</span> |
| 2284 | 2284 | </label> |
| @@ -2902,7 +2902,7 @@ | ||
| 2902 | 2902 | <div> |
| 2903 | 2903 | <h4 data-i18n="Window AI Model">Window AI Model</h4> |
| 2904 | 2904 | <select id="model_windowai_select"> |
| 2905 | 2905 | <option data-i18n="Use extension settings" value="">Use extension settings</option> |
| 2906 | 2906 | <option value="openai/gpt-3.5-turbo">openai/gpt-3.5-turbo</option> |
| 2907 | 2907 | <option value="openai/gpt-3.5-turbo-16k">openai/gpt-3.5-turbo-16k</option> |
| 2908 | 2908 | <option value="openai/gpt-4">openai/gpt-4</option> |
| @@ -2977,14 +2977,14 @@ | ||
| 2977 | 2977 | <h4 data-i18n="Model Providers">Model Providers</h4> |
| 2978 | 2978 | <select id="openrouter_providers_chat" class="openrouter_providers" multiple> |
| 2979 | 2979 | </select> |
| 2980 | 2980 | <label class="checkbox_label marginTopBot5" for="openrouter_allow_fallbacks" data-i18n="[title]Automatically chooses an alternative provider if chosen providers can't serve your request." title="Automatically chooses an alternative provider if chosen providers can't serve your request."> |
| 2981 | 2981 | <input id="openrouter_allow_fallbacks" type="checkbox" /> |
| 2982 | 2982 | <span data-i18n="Allow fallback providers">Allow fallback providers</span> |
| 2983 | 2983 | </label> |
| 2984 | 2984 | </div> |
| 2985 | 2985 | <small class="marginTopBot5"> |
| 2986 | 2986 | <i class="fa-solid fa-lightbulb"></i> |
| 2987 | 2987 | <span data-i18n="To use instruct formatting, switch to OpenRouter under Text Completion API.">To use instruct formatting, switch to OpenRouter under Text Completion API.</span> |
| 2988 | 2988 | </small> |
| 2989 | 2989 | </form> |
| 2990 | 2990 | <form id="scale_form" data-source="scale" action="javascript:void(null);" method="post" enctype="multipart/form-data"> |
| @@ -4601,7 +4601,7 @@ | ||
| 4601 | 4601 | </div> |
| 4602 | 4602 | </div> |
| 4603 | 4603 | <div nane="AutoContiueBlock" class="inline-drawer wide100p flexFlowColumn"> |
| 4604 | 4604 | <div class="inline-drawer-toggle inline-drawer-header userSettingsInnerExpandable" data-i18n="[title]Automatically 'continue' a response if the model stopped before reaching a certain amount of tokens." title="Automatically 'continue' a response if the model stopped before reaching a certain amount of tokens."> |
| 4605 | 4605 | <b><span data-i18n="Auto-Continue">Auto-Continue</span></b> |
| 4606 | 4606 | <div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div> |
| 4607 | 4607 | </div> |
| @@ -5734,7 +5734,7 @@ | ||
| 5734 | 5734 | <span class="drag-handle">☰</span> |
| 5735 | 5735 | <div class="gap5px world_entry_thin_controls wide100p alignitemscenter"> |
| 5736 | 5736 | <div class="inline-drawer-toggle fa-fw fa-solid fa-circle-chevron-down inline-drawer-icon down"></div> |
| 5737 | 5737 | <div class="fa-solid fa-toggle-on killSwitch" name="entryKillSwitch" data-i18n="[title]Toggle entry's active state." title="Toggle entry's active state."></div> |
| 5738 | 5738 | <div class="flex-container alignitemscenter wide100p"> |
| 5739 | 5739 | <div class="WIEntryTitleAndStatus flex-container flex1 alignitemscenter"> |
| 5740 | 5740 | <div class="flex-container flex1"> |
| @@ -294,7 +294,7 @@ | ||
| 294 | 294 | "Avoid sending sensitive information to the Horde.": "Избегайте отправки личной информации Horde", |
| 295 | 295 | "Review the Privacy statement": "Ознакомиться с заявлением о конфиденциальности", |
| 296 | 296 | "Trusted workers only": "Только доверенные рабочие машины", |
| 297 | 297 | "For privacy reasons, your API key will be hidden after you reload the page.": "ПоИз причинамсоображений безопасности ваш API-ключ будет скрыт после перезагрузки страницы.", |
| 298 | 298 | "-- Horde models not loaded --": "--Модель Horde не загружена--", |
| 299 | 299 | "Example: http://127.0.0.1:5000/api ": "Пример: http://127.0.0.1:5000/api", |
| 300 | 300 | "No connection...": "Нет соединения...", |
| @@ -331,7 +331,6 @@ | ||
| 331 | 331 | "UID ↘": "UID ↘", |
| 332 | 332 | "Trigger% ↗": "Триггер% ↗", |
| 333 | 333 | "Trigger% ↘": "Триггер% ↘", |
| 334 | - "Order:": "Порядок:", | |
| 335 | 334 | "Depth:": "Глубина:", |
| 336 | 335 | "Character Lore First": "Сначала лор персонажа", |
| 337 | 336 | "Global Lore First": "Сначала глобальный лор", |
| @@ -347,7 +346,7 @@ | ||
| 347 | 346 | "After Char Defs": "↓Перс.", |
| 348 | 347 | "Before AN": "↑АЗ", |
| 349 | 348 | "After AN": "↓АЗ", |
| 350 | 349 | "Order": "ПорядокОчерёдность:", |
| 351 | 350 | "Update a theme file": "Обновить файл темы", |
| 352 | 351 | "Save as a new theme": "Сохранить как новую тему", |
| 353 | 352 | "Minimum number of blacklisted words detected to trigger an auto-swipe": "Минимальное количество обнаруженных запрещённых слов, при котором срабатывает авто-свайп.", |
| @@ -497,7 +496,7 @@ | ||
| 497 | 496 | "What this keyword should mean to the AI, sent verbatim": "Что это ключевое слово должно означать для ИИ, отправляется дословно", |
| 498 | 497 | "Filter to Character(s)": "Фильтр по персонажу(ам)", |
| 499 | 498 | "Character Exclusion": "Исключить персонажей", |
| 500 | 499 | "Inclusion Group": "ВключитьГруппа персонажейзаписей", |
| 501 | 500 | "Only one entry with the same label will be activated": "Будет активна только одна запись с одинаковой меткой", |
| 502 | 501 | "-- Characters not found --": "-- Персонажей не найдено --", |
| 503 | 502 | "(This will be the first message from the character that starts every chat)": "(Это будет первое сообщение от персонажа, когда вы начинаете новый чат)", |
| @@ -541,7 +540,7 @@ | ||
| 541 | 540 | "NOT ANY": "НЕ ЛЮБОЙ", |
| 542 | 541 | "Optional Filter": "Дополнительный фильтр", |
| 543 | 542 | "New Entry": "Новая запись", |
| 544 | 543 | "Fill empty Memo/Titles with Keywords": "ЗаполнитеЗаполнить пустые Заметки/Названияназвания ключевыми словами", |
| 545 | 544 | "AI Response Formatting": "Формат ответа ИИ", |
| 546 | 545 | "Change Background Image": "Изменить фон", |
| 547 | 546 | "Extensions": "Расширения", |
| @@ -628,7 +627,7 @@ | ||
| 628 | 627 | "UI Theme": "Тема UI", |
| 629 | 628 | "This message is invisible for the AI": "Это сообщение невидимо для ИИ", |
| 630 | 629 | "Sampler Priority": "Приоритет сэмплеров", |
| 631 | 630 | "Ooba only. Determines the order of samplers.": "Только Oobaoobabooga. Определяет порядок сэмплеров.", |
| 632 | 631 | "Load default order": "Загрузить стандартный порядок", |
| 633 | 632 | "Max Tokens Second": "Макс. кол-во токенов в секунду", |
| 634 | 633 | "CFG": "CFG", |
| @@ -1342,7 +1341,7 @@ | ||
| 1342 | 1341 | "Chat Lorebook": "Chat Lorebook for", |
| 1343 | 1342 | "chat_world_template_txt": "A selected World Info will be bound to this chat. When generating an AI reply,\n it will be combined with the entries from global and character lorebooks.", |
| 1344 | 1343 | "Use tag as folder": "Tag as folder", |
| 1345 | 1344 | "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "Статус записи:\n 🔵 Постоянная\n 🟢 Обычная\n 🔗 Векторизованная\n ❌ Отключена", |
| 1346 | 1345 | "WI_Entry_Status_Constant": "Постоянная", |
| 1347 | 1346 | "WI_Entry_Status_Normal": "Обычная", |
| 1348 | 1347 | "WI_Entry_Status_Vectorized": "Векторизованная", |
| @@ -1366,13 +1365,7 @@ | ||
| 1366 | 1365 | "Can be used to automatically activate Quick Replies": "Can be used to automatically activate Quick Replies", |
| 1367 | 1366 | "Automation ID": "Automation ID", |
| 1368 | 1367 | "( None )": "( None )", |
| 1369 | - "Delay until recursion (this entry can only be activated on recursive checking)": "Delay until recursion (this entry can only be activated on recursive checking)", | |
| 1370 | - "Inclusion Groups ensure only one entry from a group is activated at a time, if multiple are triggered.Documentation: World Info - Inclusion Group": "Inclusion Groups ensure only one entry from a group is activated at a time, if multiple are triggered.\rSupports multiple comma-separated groups.\r\rDocumentation: World Info - Inclusion Group", | |
| 1371 | - "Prioritize this entry: When checked, this entry is prioritized out of all selections.If multiple are prioritized, the one with the highest 'Order' is chosen.": "Prioritize this entry: When checked, this entry is prioritized out of all selections.\rIf multiple are prioritized, the one with the highest 'Order' is chosen.\r", | |
| 1372 | - "A relative likelihood of entry activation within the group": "A relative likelihood of entry activation within the group", | |
| 1373 | - "Group Weight": "Group Weight", | |
| 1374 | 1368 | "Add Memo": "Add Memo", |
| 1375 | - "close": "close", | |
| 1376 | 1369 | "reset": "reset", |
| 1377 | 1370 | "save": "save", |
| 1378 | 1371 | "Open checkpoint chat": "Открыть чат из чекпоинта", |
| @@ -2035,5 +2028,121 @@ | ||
| 2035 | 2028 | "tags_sorting_desc": "Автоматически сортировать теги по алфавиту после создания или переименования одного из них.\nПри выключении новые теги будут просто добавляться в конец.\n\nЕсли вручную перетащить один из тегов на другое место, автосортировка отключится.", |
| 2036 | 2029 | "Imported tags:": "Импортируемые теги:", |
| 2037 | 2030 | "Importing Tags": "Импорт тегов", |
| 2038 | 2031 | "Couldn't import tags:": "Не удалось импортировать теги:", |
| 2032 | + "Allow fallback models": "Разрешить fallback-модели", | |
| 2033 | + "Allow fallback providers": "Разрешить fallback-провайдеров", | |
| 2034 | + "To use instruct formatting, switch to OpenRouter under Text Completion API.": "Переключитесь на OpenRouter в Text Completion API, чтобы использовать форматирование Instruct-режима.", | |
| 2035 | + "Select providers. No selection = all providers.": "Выберите провайдера. Нет выбранного = выбраны все.", | |
| 2036 | + "Model Providers": "Провайдеры моделей", | |
| 2037 | + "Select a model": "Выберите модель", | |
| 2038 | + "Search models...": "Искать по моделям...", | |
| 2039 | + "[Currently loaded]": "[Загруженная сейчас]", | |
| 2040 | + "Search providers...": "Искать по провайдерам...", | |
| 2041 | + "Automatically chooses an alternative provider if chosen providers can't serve your request.": "Автоматически переключаться на другого провайдера, если текущий не может обслужить запрос.", | |
| 2042 | + "Example: 127.0.0.1:8000": "Пример: 127.0.0.1:8000", | |
| 2043 | + "Edit a connection profile": "Редактировать профиль соединения", | |
| 2044 | + "System Prompt Name": "Название системного промпта", | |
| 2045 | + "Use System Prompt": "Использовать системный промпт", | |
| 2046 | + "Hint:": "Подсказка:", | |
| 2047 | + "Click on the setting name to omit it from the profile.": "Нажмите на название настройки, чтобы исключить её из профиля", | |
| 2048 | + "Included settings:": "Сохранённые параметры:", | |
| 2049 | + "Server URL": "Адрес сервера", | |
| 2050 | + "NanoGPT API Key": "Ключ от API NanoGPT", | |
| 2051 | + "NanoGPT Model": "Модель NanoGPT", | |
| 2052 | + "Use extension settings": "Использовать настройки из расширения", | |
| 2053 | + "DeepSeek API Key": "Ключ от API DeepSeek", | |
| 2054 | + "DeepSeek Model": "Модель DeepSeek", | |
| 2055 | + "prompt_post_processing_merge": "Объединять идущие подряд сообщения с одной ролью", | |
| 2056 | + "prompt_post_processing_semi": "Semi-strict (чередовать роли)", | |
| 2057 | + "prompt_post_processing_strict": "Strict (чередовать роли, сначала пользователь)", | |
| 2058 | + "Select Horde models": "Выбрать модель из Horde", | |
| 2059 | + "Model ID (optional)": "Идентификатор модели (необязательно)", | |
| 2060 | + "Derive context size from backend": "Использовать бэкенд для определения размера контекста", | |
| 2061 | + "Rename current preset": "Переименовать пресет", | |
| 2062 | + "No Worlds active. Click here to select.": "Нет активных миров. Нажмите, чтобы выбрать.", | |
| 2063 | + "Title/Memo": "Название", | |
| 2064 | + "Strategy": "Статус", | |
| 2065 | + "Position": "Позиция", | |
| 2066 | + "Trigger %": "% срабатывания", | |
| 2067 | + "Use global": "Глоб. настройка", | |
| 2068 | + "Whole Words": "Целые слова", | |
| 2069 | + "Non-recursable (will not be activated by another)": "Не рекурсивная (не активируется другими)", | |
| 2070 | + "Delay until recursion (can only be activated on recursive checking)": "Рекурсивная (активируется только рекурсией)", | |
| 2071 | + "Toggle entry's active state.": "Вкл/выкл запись.", | |
| 2072 | + "Prioritize": "Важная", | |
| 2073 | + "Prioritize this entry: When checked, this entry is prioritized out of all selections.If multiple are prioritized, the one with the highest 'Order' is chosen.": "Важная запись получает приоритет среди всех выбранных. Если важных записей несколько, выбирается та, у которой выше \"Очерёдность\".", | |
| 2074 | + "Group Weight": "Вес в группе", | |
| 2075 | + "A relative likelihood of entry activation within the group": "Относительная вероятность активации записи в рамках группы", | |
| 2076 | + "Sticky": "Липучка", | |
| 2077 | + "Sticky entries will stay active for N messages after being triggered.": "Запись-\"липучка\" останется активной в течение N сообщений после срабатывания.", | |
| 2078 | + "Cooldown": "Кулдаун", | |
| 2079 | + "Entries with a cooldown can't be activated N messages after being triggered.": "Запись с заданным кулдауном не активируется следующие N сообщений после срабатывания.", | |
| 2080 | + "Delay": "Задержка", | |
| 2081 | + "Entries with a delay can't be activated until there are N messages present in the chat.": "Запись с заданной задержкой может активироваться только после того, как в чате наберётся N сообщений.", | |
| 2082 | + "Non-sticky": "Нет", | |
| 2083 | + "No cooldown": "Нет", | |
| 2084 | + "No delay": "Нет", | |
| 2085 | + "Filter to Characters or Tags": "Фильтровать по персонажам или тегам", | |
| 2086 | + "Switch to plaintext mode": "Вкл/выкл режим чистого текста", | |
| 2087 | + "Exclude": "Режим исключения", | |
| 2088 | + "Switch the Character/Tags filter around to exclude the listed characters and tags from matching for this entry": "Инвертировать логику: для выбранных в фильтре персонажей/тегов данная запись активна НЕ БУДЕТ", | |
| 2089 | + "Apply current sorting as Order": "Настроить Очерёдность в соответствии с текущей сортировкой", | |
| 2090 | + "Create a new World Info": "Создать новый мир", | |
| 2091 | + "Enter a name for the new file:": "Название нового файла:", | |
| 2092 | + "Inclusion Groups ensure only one entry from a group is activated at a time, if multiple are triggered.Documentation: World Info - Inclusion Group": "Если сразу несколько записей из одной группы окажутся активированными, по факту сработает только одна. Одна запись может входить в несколько групп, отделяются запятыми. Раздел в документации: World Info - Inclusion Group", | |
| 2093 | + "Valid World Info file name is required": "Требуется корректное имя для файла мира", | |
| 2094 | + "World Info file has an invalid format": "Файл мира имеет неизвестный формат", | |
| 2095 | + "World Info file has no entries": "В файле нет ни одной записи", | |
| 2096 | + "Character not found.": "Персонаж не найден", | |
| 2097 | + "Open a chat to get a name of the chat-bound lorebook": "Чтобы получить название привязанного к чату лорбука, требуется открыть чат.", | |
| 2098 | + "File is not valid: ${0}": "Файл повреждён или имеет неизвестный формат: ${0}", | |
| 2099 | + "The world with ${0} is invalid or corrupted.": "Мир ${0} повреждён или имеет неизвестный формат.", | |
| 2100 | + "Deactivated all worlds": "Все миры отключены", | |
| 2101 | + "No world found named: ${0}": "Не найдено мира с название ${0}", | |
| 2102 | + "Activated world: ${0}": "Мир ${0} включён", | |
| 2103 | + "Deactivated world: ${0}": "Мир ${0} отключён", | |
| 2104 | + "World was not active: ${0}": "Мир ${0} не включён", | |
| 2105 | + "The world '${0}' has been imported and linked to the character successfully.": "Мир ${0} успешно импортирован и привязан к персонажу.", | |
| 2106 | + "World/Lorebook imported": "Мир/лорбук импортирован", | |
| 2107 | + "Are you sure you want to import '${0}'?": "Вы точно хотите импортировать '${0}'?", | |
| 2108 | + "It will overwrite the World/Lorebook with the same name.": "Существующий мир с таким же названием будет перезаписан.", | |
| 2109 | + "Automatically 'continue' a response if the model stopped before reaching a certain amount of tokens.": "Автоматически \"продолжать\" ответ, если он оказался короче, чем целевая длина (в токенах).", | |
| 2110 | + "None (not injected)": "Никуда", | |
| 2111 | + "ext_sum_injection_position_none": "Пересказ не будет вставляться в промпт. Однако его по-прежнему можно будет вставить с помощью макроса {{summary}}", | |
| 2112 | + "ext_sum_include_wi_scan": "Учитывать при сканировании лорбуком", | |
| 2113 | + "ext_sum_include_wi_scan_desc": "Учитывать актуальный пересказ при сканировании промпта лорбуком.", | |
| 2114 | + "ext_sum_force_tip": "Отправить запрос на создание пересказа прямо сейчас.", | |
| 2115 | + "ext_sum_restore_tip": "Откатиться к предыдущему пересказу; используйте несколько раз, чтобы очистить историю пересказов для чата", | |
| 2116 | + "Built-in Extensions:": "Комплектные расширения:", | |
| 2117 | + "Installed Extensions:": "Установленные расширения:", | |
| 2118 | + "Loading third-party extensions... Please wait...": "Загрузка сторонних расширений... Пожалуйста, подождите...", | |
| 2119 | + "The page will be reloaded shortly...": "Страница будет перезагружена...", | |
| 2120 | + "Extensions state changed": "Изменено состояние расширения", | |
| 2121 | + "Error loading extensions. See browser console for details.": "Не удалось загрузить расширения. Подробности см. в консоли браузера.", | |
| 2122 | + "You don't have permission to update global extensions.": "Отсутствуют права на обновление глобальных расширений.", | |
| 2123 | + "Extension update failed": "Не удалось обновить расширение", | |
| 2124 | + "Extension ${0} updated to ${1}": "Расширение ${0} обновлено до ${1}", | |
| 2125 | + "Reload the page to apply updates": "Чтобы изменения вступили в силу, обновите страницу", | |
| 2126 | + "You don't have permission to delete global extensions.": "Отсутствуют права на удаление глобальных расширений.", | |
| 2127 | + "Are you sure you want to delete ${0}?": "Вы точно хотите удалить ${0}?", | |
| 2128 | + "You don't have permission to move extensions.": "Отсутствуют права на перемещение расширений.", | |
| 2129 | + "Are you sure you want to move ${0} to your local extensions? This will make it available only for you.": "Вы точно хотите сделать ${0} локальным расширением? После этого оно будет доступно только вам.", | |
| 2130 | + "Are you sure you want to move ${0} to the global extensions? This will make it available for all users.": "Вы точно хотите сделать ${0} глобальным расширением? После этого оно будет доступно всем пользователям.", | |
| 2131 | + "Extension ${0} moved.": "Расширение ${0} перемещено.", | |
| 2132 | + "Extension ${0} deleted": "Расширение ${0} удалено.", | |
| 2133 | + "Please wait...": "Пожалуйста, подождите...", | |
| 2134 | + "Installing extension": "Идёт установка расширения", | |
| 2135 | + "Extension installation failed": "Не удалось установить расширение", | |
| 2136 | + "Extension '${0}' by ${1} (version ${2}) has been installed successfully!": "Расширение ${0} от автора ${1} (версия ${2}) успешно установлено!", | |
| 2137 | + "Extension installation successful": "Расширение установлено", | |
| 2138 | + "Extension updates available": "Доступных обновлений расширений", | |
| 2139 | + "Auto-updating extensions. This may take several minutes.": "Запущено автоматическое обновление расширений. Это может занять несколько минут.", | |
| 2140 | + "Install just for me": "Установить только мне", | |
| 2141 | + "Install": "Установить", | |
| 2142 | + "Install for all users": "Установить для всех пользователей", | |
| 2143 | + "Modules provided by your Extras API:": "Модули из Extras API:", | |
| 2144 | + "Not connected to the API!": "Нет соединения с API!", | |
| 2145 | + "ext_type_system": "Это комплектное расширение. Его нельзя удалить, а обновляется оно вместе со всей системой.", | |
| 2146 | + "Update all": "Обновить все", | |
| 2147 | + "Close": "Закрыть" | |
| 2039 | 2148 | } |
| @@ -682,9 +682,9 @@ function getExtensionData(extension) { | ||
| 682 | 682 | * @return {string} - The HTML string for the module information. |
| 683 | 683 | */ |
| 684 | 684 | function getModuleInformation() { |
| 685 | 685 | let moduleInfo = modules.length ? `<p>${DOMPurify.sanitize(modules.join(', '))}</p>` : '<p class="failure">' + t`Not connected to the API!` + '</p>'; |
| 686 | 686 | return ` |
| 687 | 687 | <h3>` + t`Modules provided by your Extras API:` + `</h3> |
| 688 | 688 | ${moduleInfo} |
| 689 | 689 | `; |
| 690 | 690 | } |
| @@ -703,11 +703,11 @@ async function showExtensionsDetails() { | ||
| 703 | 703 | initialScrollTop = oldPopup.content.scrollTop; |
| 704 | 704 | await oldPopup.completeCancelled(); |
| 705 | 705 | } |
| 706 | 706 | const htmlDefault = $('<div class="marginBot10"><h3 class="textAlignCenter">' + t`Built-in Extensions:` + '</h3></div>'); |
| 707 | 707 | const htmlExternal = $('<div class="marginBot10"><h3 class="textAlignCenter">' + t`Installed Extensions:` + '</h3></div>'); |
| 708 | 708 | const htmlLoading = $(`<div class="flex-container alignItemsCenter justifyCenter marginTop10 marginBot5"> |
| 709 | 709 | <i class="fa-solid fa-spinner fa-spin"></i> |
| 710 | 710 | <span>` + t`Loading third-party extensions... Please wait...` + `</span> |
| 711 | 711 | </div>`); |
| 712 | 712 | |
| 713 | 713 | htmlExternal.append(htmlLoading); |
| @@ -728,7 +728,7 @@ async function showExtensionsDetails() { | ||
| 728 | 728 | |
| 729 | 729 | /** @type {import('./popup.js').CustomPopupButton} */ |
| 730 | 730 | const updateAllButton = { |
| 731 | 731 | text: 't`Update all'`, |
| 732 | 732 | appendAtEnd: true, |
| 733 | 733 | action: async () => { |
| 734 | 734 | requiresReload = true; |
| @@ -740,7 +740,7 @@ async function showExtensionsDetails() { | ||
| 740 | 740 | let waitingForSave = false; |
| 741 | 741 | |
| 742 | 742 | const popup = new Popup(html, POPUP_TYPE.TEXT, '', { |
| 743 | 743 | okButton: 't`Close'`, |
| 744 | 744 | wide: true, |
| 745 | 745 | large: true, |
| 746 | 746 | customButtons: [updateAllButton], |
| @@ -833,7 +833,7 @@ async function updateExtension(extensionName, quiet) { | ||
| 833 | 833 | toastr.success('Extension is already up to date'); |
| 834 | 834 | } |
| 835 | 835 | } else { |
| 836 | 836 | toastr.success(t`Extension ${extensionName} updated to ${data.shortCommitHash}`, 't`Reload the page to apply updates'`); |
| 837 | 837 | } |
| 838 | 838 | } catch (error) { |
| 839 | 839 | console.error('Error:', error); |
| @@ -1001,7 +1001,7 @@ export async function installExtension(url, global) { | ||
| 1001 | 1001 | } |
| 1002 | 1002 | |
| 1003 | 1003 | const response = await request.json(); |
| 1004 | 1004 | toastr.success(t`Extension "'${response.display_name}"' by ${response.author} (version ${response.version}) has been installed successfully!`, 't`Extension installation successful'`); |
| 1005 | 1005 | console.debug(`Extension "${response.display_name}" has been installed successfully at ${response.extensionPath}`); |
| 1006 | 1006 | await loadExtensionSettings({}, false, false); |
| 1007 | 1007 | await eventSource.emit(event_types.EXTENSION_SETTINGS_LOADED); |
| @@ -1175,7 +1175,7 @@ async function checkForExtensionUpdates(force) { | ||
| 1175 | 1175 | await Promise.allSettled(promises); |
| 1176 | 1176 | |
| 1177 | 1177 | if (updatesAvailable.length > 0) { |
| 1178 | 1178 | toastr.info(`${updatesAvailable.map(x => `• ${x}`).join('\n')}`, 't`Extension updates available'`); |
| 1179 | 1179 | } |
| 1180 | 1180 | } |
| 1181 | 1181 | |
| @@ -1189,7 +1189,7 @@ async function autoUpdateExtensions(forceAll) { | ||
| 1189 | 1189 | return; |
| 1190 | 1190 | } |
| 1191 | 1191 | |
| 1192 | 1192 | const banner = toastr.info('t`Auto-updating extensions. This may take several minutes.'`, 't`Please wait...'`, { timeOut: 10000, extendedTimeOut: 10000 }); |
| 1193 | 1193 | const isCurrentUserAdmin = isAdmin(); |
| 1194 | 1194 | const promises = []; |
| 1195 | 1195 | for (const [id, manifest] of Object.entries(manifests)) { |
| @@ -1,10 +1,10 @@ | ||
| 1 | 1 | <div> |
| 2 | 2 | <h3 data-i18n="Included settings:">Included settings:</h3> |
| 3 | 3 | <div class="justifyLeft flex-container flexFlowColumn flexNoGap"> |
| 4 | 4 | {{#each settings}} |
| 5 | 5 | <label class="checkbox_label"> |
| 6 | 6 | <input type="checkbox" value="{{@key}}" name="exclude"{{#if this}} checked{{/if}}> |
| 7 | 7 | <span data-i18n="{{@key}}">{{@key}}</span> |
| 8 | 8 | </label> |
| 9 | 9 | {{/each}} |
| 10 | 10 | </div> |
| @@ -12,8 +12,8 @@ | ||
| 12 | 12 | </div> |
| 13 | 13 | <div class="marginTop5"> |
| 14 | 14 | <small> |
| 15 | 15 | <b data-i18n="Hint:">Hint:</b> |
| 16 | 16 | <i data-i18n="Click on the setting name to omit it from the profile.">Click on the setting name to omit it from the profile.</i> |
| 17 | 17 | </small> |
| 18 | 18 | </div> |
| 19 | 19 | <h3 data-i18n="Enter a name:"> |
| @@ -5491,8 +5491,8 @@ export function initOpenAI() { | ||
| 5491 | 5491 | |
| 5492 | 5492 | if (!isMobile()) { |
| 5493 | 5493 | $('#model_openrouter_select').select2({ |
| 5494 | 5494 | placeholder: 't`Select a model'`, |
| 5495 | 5495 | searchInputPlaceholder: 't`Search models...'`, |
| 5496 | 5496 | searchInputCssClass: 'text_pole', |
| 5497 | 5497 | width: '100%', |
| 5498 | 5498 | templateResult: getOpenRouterModelTemplate, |
| @@ -5,6 +5,7 @@ import { textgenerationwebui_settings as textgen_settings, textgen_types } from | ||
| 5 | 5 | import { tokenizers } from './tokenizers.js'; |
| 6 | 6 | import { renderTemplateAsync } from './templates.js'; |
| 7 | 7 | import { POPUP_TYPE, callGenericPopup } from './popup.js'; |
| 8 | +import { t } from './i18n.js'; | |
| 8 | 9 | |
| 9 | 10 | let mancerModels = []; |
| 10 | 11 | let togetherModels = []; |
| @@ -936,71 +937,71 @@ export function initTextGenModels() { | ||
| 936 | 937 | |
| 937 | 938 | if (!isMobile()) { |
| 938 | 939 | $('#mancer_model').select2({ |
| 939 | 940 | placeholder: 't`Select a model'`, |
| 940 | 941 | searchInputPlaceholder: 't`Search models...'`, |
| 941 | 942 | searchInputCssClass: 'text_pole', |
| 942 | 943 | width: '100%', |
| 943 | 944 | templateResult: getMancerModelTemplate, |
| 944 | 945 | }); |
| 945 | 946 | $('#model_togetherai_select').select2({ |
| 946 | 947 | placeholder: 't`Select a model'`, |
| 947 | 948 | searchInputPlaceholder: 't`Search models...'`, |
| 948 | 949 | searchInputCssClass: 'text_pole', |
| 949 | 950 | width: '100%', |
| 950 | 951 | templateResult: getTogetherModelTemplate, |
| 951 | 952 | }); |
| 952 | 953 | $('#ollama_model').select2({ |
| 953 | 954 | placeholder: 't`Select a model'`, |
| 954 | 955 | searchInputPlaceholder: 't`Search models...'`, |
| 955 | 956 | searchInputCssClass: 'text_pole', |
| 956 | 957 | width: '100%', |
| 957 | 958 | }); |
| 958 | 959 | $('#tabby_model').select2({ |
| 959 | 960 | placeholder: 't`[Currently loaded]'`, |
| 960 | 961 | searchInputPlaceholder: 't`Search models...'`, |
| 961 | 962 | searchInputCssClass: 'text_pole', |
| 962 | 963 | width: '100%', |
| 963 | 964 | allowClear: true, |
| 964 | 965 | }); |
| 965 | 966 | $('#model_infermaticai_select').select2({ |
| 966 | 967 | placeholder: 't`Select a model'`, |
| 967 | 968 | searchInputPlaceholder: 't`Search models...'`, |
| 968 | 969 | searchInputCssClass: 'text_pole', |
| 969 | 970 | width: '100%', |
| 970 | 971 | templateResult: getInfermaticAIModelTemplate, |
| 971 | 972 | }); |
| 972 | 973 | $('#model_dreamgen_select').select2({ |
| 973 | 974 | placeholder: 't`Select a model'`, |
| 974 | 975 | searchInputPlaceholder: 't`Search models...'`, |
| 975 | 976 | searchInputCssClass: 'text_pole', |
| 976 | 977 | width: '100%', |
| 977 | 978 | templateResult: getDreamGenModelTemplate, |
| 978 | 979 | }); |
| 979 | 980 | $('#openrouter_model').select2({ |
| 980 | 981 | placeholder: 't`Select a model'`, |
| 981 | 982 | searchInputPlaceholder: 't`Search models...'`, |
| 982 | 983 | searchInputCssClass: 'text_pole', |
| 983 | 984 | width: '100%', |
| 984 | 985 | templateResult: getOpenRouterModelTemplate, |
| 985 | 986 | }); |
| 986 | 987 | $('#vllm_model').select2({ |
| 987 | 988 | placeholder: 't`Select a model'`, |
| 988 | 989 | searchInputPlaceholder: 't`Search models...'`, |
| 989 | 990 | searchInputCssClass: 'text_pole', |
| 990 | 991 | width: '100%', |
| 991 | 992 | templateResult: getVllmModelTemplate, |
| 992 | 993 | }); |
| 993 | 994 | $('#aphrodite_model').select2({ |
| 994 | 995 | placeholder: 't`Select a model'`, |
| 995 | 996 | searchInputPlaceholder: 't`Search models...'`, |
| 996 | 997 | searchInputCssClass: 'text_pole', |
| 997 | 998 | width: '100%', |
| 998 | 999 | templateResult: getAphroditeModelTemplate, |
| 999 | 1000 | }); |
| 1000 | 1001 | providersSelect.select2({ |
| 1001 | 1002 | sorter: data => data.sort((a, b) => a.text.localeCompare(b.text)), |
| 1002 | 1003 | placeholder: 't`Select providers. No selection = all providers.'`, |
| 1003 | 1004 | searchInputPlaceholder: 't`Search providers...'`, |
| 1004 | 1005 | searchInputCssClass: 'text_pole', |
| 1005 | 1006 | width: '100%', |
| 1006 | 1007 | closeOnSelect: false, |
| @@ -20,6 +20,7 @@ import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js'; | ||
| 20 | 20 | import { callGenericPopup, Popup, POPUP_TYPE } from './popup.js'; |
| 21 | 21 | import { StructuredCloneMap } from './util/StructuredCloneMap.js'; |
| 22 | 22 | import { renderTemplateAsync } from './templates.js'; |
| 23 | +import { t } from './i18n.js'; | |
| 23 | 24 | |
| 24 | 25 | export const world_info_insertion_strategy = { |
| 25 | 26 | evenly: 0, |
| @@ -909,21 +910,21 @@ function registerWorldInfoSlashCommands() { | ||
| 909 | 910 | |
| 910 | 911 | async function getEntriesFromFile(file) { |
| 911 | 912 | if (!file || !world_names.includes(file)) { |
| 912 | 913 | toastr.warning('t`Valid World Info file name is required'`); |
| 913 | 914 | return ''; |
| 914 | 915 | } |
| 915 | 916 | |
| 916 | 917 | const data = await loadWorldInfo(file); |
| 917 | 918 | |
| 918 | 919 | if (!data || !('entries' in data)) { |
| 919 | 920 | toastr.warning('t`World Info file has an invalid format'`); |
| 920 | 921 | return ''; |
| 921 | 922 | } |
| 922 | 923 | |
| 923 | 924 | const entries = Object.values(data.entries); |
| 924 | 925 | |
| 925 | 926 | if (!entries || entries.length === 0) { |
| 926 | 927 | toastr.warning('t`World Info file has no entries'`); |
| 927 | 928 | return ''; |
| 928 | 929 | } |
| 929 | 930 | |
| @@ -951,7 +952,7 @@ function registerWorldInfoSlashCommands() { | ||
| 951 | 952 | name = String(name ?? '') || context.characters[context.characterId]?.avatar || null; |
| 952 | 953 | const character = findChar({ name }); |
| 953 | 954 | if (!character) { |
| 954 | 955 | toastr.error('t`Character not found.'`); |
| 955 | 956 | return ''; |
| 956 | 957 | } |
| 957 | 958 | const books = []; |
| @@ -977,7 +978,7 @@ function registerWorldInfoSlashCommands() { | ||
| 977 | 978 | const chatId = getCurrentChatId(); |
| 978 | 979 | |
| 979 | 980 | if (!chatId) { |
| 980 | 981 | toastr.warning('t`Open a chat to get a name of the chat-bound lorebook'`); |
| 981 | 982 | return ''; |
| 982 | 983 | } |
| 983 | 984 | |
| @@ -4773,7 +4774,7 @@ export async function importEmbeddedWorldInfo(skipPopup = false) { | ||
| 4773 | 4774 | const bookName = characters[chid]?.data?.character_book?.name || `${characters[chid]?.name}'s Lorebook`; |
| 4774 | 4775 | |
| 4775 | 4776 | if (!skipPopup) { |
| 4776 | 4777 | const confirmation = await Popup.show.confirm(t`Are you sure you want to import "'${bookName}"'?`, world_names.includes(bookName) ? 't`It will overwrite the World/Lorebook with the same name.'` : ''); |
| 4777 | 4778 | if (!confirmation) { |
| 4778 | 4779 | return; |
| 4779 | 4780 | } |
| @@ -4785,7 +4786,7 @@ export async function importEmbeddedWorldInfo(skipPopup = false) { | ||
| 4785 | 4786 | await updateWorldInfoList(); |
| 4786 | 4787 | $('#character_world').val(bookName).trigger('change'); |
| 4787 | 4788 | |
| 4788 | 4789 | toastr.success(t`The world "'${bookName}"' has been imported and linked to the character successfully.`, 't`World/Lorebook imported'`); |
| 4789 | 4790 | |
| 4790 | 4791 | const newIndex = world_names.indexOf(bookName); |
| 4791 | 4792 | if (newIndex >= 0) { |
| @@ -4813,9 +4814,9 @@ export function onWorldInfoChange(args, text) { | ||
| 4813 | 4814 | if (selected_world_info.includes(name)) { |
| 4814 | 4815 | selected_world_info.splice(selected_world_info.indexOf(name), 1); |
| 4815 | 4816 | wiElement.prop('selected', false); |
| 4816 | 4817 | if (!silent) toastr.success(t`Deactivated world: ${name}`); |
| 4817 | 4818 | } else { |
| 4818 | 4819 | if (!silent) toastr.error(t`World was not active: ${name}`); |
| 4819 | 4820 | } |
| 4820 | 4821 | break; |
| 4821 | 4822 | } |
| @@ -4823,11 +4824,11 @@ export function onWorldInfoChange(args, text) { | ||
| 4823 | 4824 | if (selected_world_info.includes(name)) { |
| 4824 | 4825 | selected_world_info.splice(selected_world_info.indexOf(name), 1); |
| 4825 | 4826 | wiElement.prop('selected', false); |
| 4826 | 4827 | if (!silent) toastr.success(t`Deactivated world: ${name}`); |
| 4827 | 4828 | } else { |
| 4828 | 4829 | selected_world_info.push(name); |
| 4829 | 4830 | wiElement.prop('selected', true); |
| 4830 | 4831 | if (!silent) toastr.success(t`Activated world: ${name}`); |
| 4831 | 4832 | } |
| 4832 | 4833 | break; |
| 4833 | 4834 | } |
| @@ -4835,16 +4836,16 @@ export function onWorldInfoChange(args, text) { | ||
| 4835 | 4836 | default: { |
| 4836 | 4837 | selected_world_info.push(name); |
| 4837 | 4838 | wiElement.prop('selected', true); |
| 4838 | 4839 | if (!silent) toastr.success(t`Activated world: ${name}`); |
| 4839 | 4840 | } |
| 4840 | 4841 | } |
| 4841 | 4842 | } else { |
| 4842 | 4843 | if (!silent) toastr.error(t`No world found named: ${worldName}`); |
| 4843 | 4844 | } |
| 4844 | 4845 | }); |
| 4845 | 4846 | $('#world_info').trigger('change'); |
| 4846 | 4847 | } else { // if no args, unset all worlds |
| 4847 | 4848 | if (!silent) toastr.success('t`Deactivated all worlds'`); |
| 4848 | 4849 | selected_world_info = []; |
| 4849 | 4850 | $('#world_info').val(null).trigger('change'); |
| 4850 | 4851 | } |
| @@ -4860,7 +4861,7 @@ export function onWorldInfoChange(args, text) { | ||
| 4860 | 4861 | } else { |
| 4861 | 4862 | const wiElement = getWIElement(existingWorldName); |
| 4862 | 4863 | wiElement.prop('selected', false); |
| 4863 | 4864 | toastr.error(t`The world with ${existingWorldName} is invalid or corrupted.`); |
| 4864 | 4865 | } |
| 4865 | 4866 | }); |
| 4866 | 4867 | } |
| @@ -4892,7 +4893,7 @@ export async function importWorldInfo(file) { | ||
| 4892 | 4893 | } |
| 4893 | 4894 | |
| 4894 | 4895 | if (jsonData === undefined || jsonData === null) { |
| 4895 | 4896 | toastr.error(t`File is not valid: ${file.name}`); |
| 4896 | 4897 | return; |
| 4897 | 4898 | } |
| 4898 | 4899 | |
| @@ -5038,7 +5039,7 @@ jQuery(() => { | ||
| 5038 | 5039 | |
| 5039 | 5040 | $('#world_create_button').on('click', async () => { |
| 5040 | 5041 | const tempName = getFreeWorldName(); |
| 5041 | 5042 | const finalName = await Popup.show.input('t`Create a new World Info'`, 't`Enter a name for the new file:'`, tempName); |
| 5042 | 5043 | |
| 5043 | 5044 | if (finalName) { |
| 5044 | 5045 | await createNewWorldInfo(finalName, { interactive: true }); |