Work on tl

11908f7363651bf434cf23c1d8bb39feab12e9ad

Yokayo <nonameutubecomm@gmail.com>

13 files changed, +191 -61Ignore whitespace
public/css/rm-groups.css+2 -2
@@ -87,7 +87,7 @@
87}87}
8888
89#rm_group_members:empty::before {89#rm_group_members:empty::before {
90 content: 'Group is empty';90 content: attr(group_empty_text);
9191
92 font-weight: bolder;92 font-weight: bolder;
93 width: 100%;93 width: 100%;
@@ -115,7 +115,7 @@
115}115}
116116
117#rm_group_add_members:empty::before {117#rm_group_add_members:empty::before {
118 content: 'No characters available';118 content: attr(no_characters_text);
119119
120 font-weight: bolder;120 font-weight: bolder;
121 width: 100%;121 width: 100%;
public/index.html+8 -8
@@ -2052,8 +2052,8 @@
2052 <div class="flex-container oneline-dropdown" title="Constrains effort on reasoning for reasoning models.&#10;Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response." data-i18n="[title]Constrains effort on reasoning for reasoning models.">2052 <div class="flex-container oneline-dropdown" title="Constrains effort on reasoning for reasoning models.&#10;Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response." data-i18n="[title]Constrains effort on reasoning for reasoning models.">
2053 <label for="openai_reasoning_effort">2053 <label for="openai_reasoning_effort">
2054 <span data-i18n="Reasoning Effort">Reasoning Effort</span>2054 <span data-i18n="Reasoning Effort">Reasoning Effort</span>
2055 <i data-source="openai,custom,xai,openrouter" class="opacity50p fa-solid fa-circle-info" title="OpenAI-style options: low, medium, high. Minimum and maximum are aliased to low and high. Auto does not send an effort level."></i>2055 <i data-source="openai,custom,xai,openrouter" class="opacity50p fa-solid fa-circle-info" title="OpenAI-style options: low, medium, high. Minimum and maximum are aliased to low and high. Auto does not send an effort level." data-i18n="[title]OpenAI-style options: low, medium, high. Minimum and maximum are aliased to low and high. Auto does not send an effort level."></i>
2056 <i data-source="claude,makersuite" class="opacity50p fa-solid fa-circle-info" title="Allocates a portion of the response length for thinking (low: 10%, medium: 25%, high: 50%). Other options are model-dependent."></i>2056 <i data-source="claude,makersuite" class="opacity50p fa-solid fa-circle-info" title="Allocates a portion of the response length for thinking (low: 10%, medium: 25%, high: 50%). Other options are model-dependent." data-i18n="[title]Allocates a portion of the response length for thinking (low: 10%, medium: 25%, high: 50%). Other options are model-dependent."></i>
2057 </label>2057 </label>
2058 <select id="openai_reasoning_effort">2058 <select id="openai_reasoning_effort">
2059 <option data-i18n="openai_reasoning_effort_auto" value="auto">Auto</option>2059 <option data-i18n="openai_reasoning_effort_auto" value="auto">Auto</option>
@@ -2498,8 +2498,8 @@
2498 <option value="search" data-i18n="Search" hidden>A-Z</option>2498 <option value="search" data-i18n="Search" hidden>A-Z</option>
2499 <option value="asc">A-Z</option>2499 <option value="asc">A-Z</option>
2500 <option value="desc">Z-A</option>2500 <option value="desc">Z-A</option>
2501 <option value="date_asc">Date Asc</option>2501 <option data-i18n="Date Asc" value="date_asc">Date Asc</option>
2502 <option value="date_desc">Date Desc</option>2502 <option data-i18n="Date Desc" value="date_desc">Date Desc</option>
2503 </select>2503 </select>
2504 <select id="featherless_category_selection" class="text_pole">2504 <select id="featherless_category_selection" class="text_pole">
2505 <option value="" disabled selected data-i18n="category">category</option>2505 <option value="" disabled selected data-i18n="category">category</option>
@@ -2509,7 +2509,7 @@
2509 <option value="All" data-i18n="All">All</option>2509 <option value="All" data-i18n="All">All</option>
2510 </select>2510 </select>
2511 <select id="featherless_class_selection" class="text_pole">2511 <select id="featherless_class_selection" class="text_pole">
2512 <option value="" selected data-i18n="class">All Classes</option>2512 <option value="" selected data-i18n="All Classes">All Classes</option>
2513 </select>2513 </select>
2514 <div id="featherless_model_pagination_container" class="flex1"></div>2514 <div id="featherless_model_pagination_container" class="flex1"></div>
2515 <i id="featherless_model_grid_toggle" class="fa-solid fa-table-cells-large menu_button" data-i18n="[title]Toggle grid view" title="Toggle grid view"></i>2515 <i id="featherless_model_grid_toggle" class="fa-solid fa-table-cells-large menu_button" data-i18n="[title]Toggle grid view" title="Toggle grid view"></i>
@@ -5534,7 +5534,7 @@
5534 <div class="inline-drawer-content">5534 <div class="inline-drawer-content">
5535 <div id="currentGroupMembers" name="Current Group Members" class="flex-container flexFlowColumn overflowYAuto flex1">5535 <div id="currentGroupMembers" name="Current Group Members" class="flex-container flexFlowColumn overflowYAuto flex1">
5536 <div id="rm_group_members_pagination" class="rm_group_members_pagination group_pagination"></div>5536 <div id="rm_group_members_pagination" class="rm_group_members_pagination group_pagination"></div>
5537 <div id="rm_group_members" class="rm_group_members overflowYAuto flex-container"></div>5537 <div id="rm_group_members" class="rm_group_members overflowYAuto flex-container" group_empty_text="Group is empty." data-i18n="[group_empty_text]Group is empty."></div>
5538 </div>5538 </div>
5539 </div>5539 </div>
5540 </div>5540 </div>
@@ -5552,7 +5552,7 @@
5552 <div class="tags rm_tag_filter"></div>5552 <div class="tags rm_tag_filter"></div>
5553 </div>5553 </div>
5554 <div id="rm_group_add_members_pagination" class="group_pagination"></div>5554 <div id="rm_group_add_members_pagination" class="group_pagination"></div>
5555 <div id="rm_group_add_members" class="overflowYAuto flex-container"></div>5555 <div id="rm_group_add_members" class="overflowYAuto flex-container" no_characters_text="No characters available" data-i18n="[no_characters_text]No characters available"></div>
5556 </div>5556 </div>
5557 </div>5557 </div>
5558 </div>5558 </div>
@@ -5972,7 +5972,7 @@
5972 <div class="tag_view_color_picker" data-value="color"></div>5972 <div class="tag_view_color_picker" data-value="color"></div>
5973 <div class="tag_view_color_picker" data-value="color2"></div>5973 <div class="tag_view_color_picker" data-value="color2"></div>
5974 <div class="tag_view_name" contenteditable="true"></div>5974 <div class="tag_view_name" contenteditable="true"></div>
5975 <div class="tag_view_counter"><span class="tag_view_counter_value"></span>&nbsp;entries</div>5975 <div class="tag_view_counter"><span class="tag_view_counter_value"></span>&nbsp;<span data-i18n="tag_entries">entries</span></div>
5976 <div title="Delete tag" class="tag_delete fa-solid fa-trash-can right_menu_button" data-i18n="[title]Delete tag"></div>5976 <div title="Delete tag" class="tag_delete fa-solid fa-trash-can right_menu_button" data-i18n="[title]Delete tag"></div>
5977 </div>5977 </div>
5978 </div>5978 </div>
public/locales/ru-ru.json+104 -22
@@ -52,7 +52,7 @@
52 "Presence Penalty": "Штраф за присутствие",52 "Presence Penalty": "Штраф за присутствие",
53 "Top A": "Top А",53 "Top A": "Top А",
54 "Tail Free Sampling": "Tail Free Sampling",54 "Tail Free Sampling": "Tail Free Sampling",
55 "Rep. Pen. Slope": "Rep. Pen. Slope",55 "Rep. Pen. Slope": "Рост штрафа за повтор к концу промпта",
56 "Top K": "Top K",56 "Top K": "Top K",
57 "Top P": "Top P",57 "Top P": "Top P",
58 "Do Sample": "Включить сэмплинг",58 "Do Sample": "Включить сэмплинг",
@@ -162,9 +162,9 @@
162 "Story String": "Строка истории",162 "Story String": "Строка истории",
163 "Example Separator": "Разделитель примеров сообщений",163 "Example Separator": "Разделитель примеров сообщений",
164 "Chat Start": "Начало чата",164 "Chat Start": "Начало чата",
165 "Activation Regex": "Regex для активации",165 "Activation Regex": "Рег. выражение для активации",
166 "Instruct Mode": "Режим Instruct",166 "Instruct Mode": "Режим Instruct",
167 "Wrap Sequences with Newline": "Отделять строки символом новой строки",167 "Wrap Sequences with Newline": "Каждая строка из шаблона на новой строке",
168 "Include Names": "Добавлять имена",168 "Include Names": "Добавлять имена",
169 "Force for Groups and Personas": "Также для групп и персон",169 "Force for Groups and Personas": "Также для групп и персон",
170 "System Prompt": "Системный промпт",170 "System Prompt": "Системный промпт",
@@ -299,7 +299,7 @@
299 "AI Horde": "AI Horde",299 "AI Horde": "AI Horde",
300 "NovelAI": "NovelAI",300 "NovelAI": "NovelAI",
301 "OpenAI API key": "Ключ для API OpenAI",301 "OpenAI API key": "Ключ для API OpenAI",
302 "Trim spaces": "Обрезать пробелы",302 "Trim spaces": "Обрезать пробелы в начале и конце",
303 "Trim Incomplete Sentences": "Удалять неоконченные предложения",303 "Trim Incomplete Sentences": "Удалять неоконченные предложения",
304 "Include Newline": "Добавлять новую строку",304 "Include Newline": "Добавлять новую строку",
305 "Non-markdown strings": "Строки без разметки",305 "Non-markdown strings": "Строки без разметки",
@@ -510,7 +510,7 @@
510 "New preset": "Новый пресет",510 "New preset": "Новый пресет",
511 "Delete preset": "Удалить пресет",511 "Delete preset": "Удалить пресет",
512 "API Connections": "Соединения с API",512 "API Connections": "Соединения с API",
513 "Can help with bad responses by queueing only the approved workers. May slowdown the response time.": "Может помочь с плохими ответами ставя в очередь только подтвержденных работников. Может замедлить время ответа.",513 "Can help with bad responses by queueing only the approved workers. May slowdown the response time.": "Может помочь при плохих ответах, делая запросы только к доверенным рабочим машинам. Может замедлить время ответа.",
514 "Clear your API key": "Стереть ключ от API",514 "Clear your API key": "Стереть ключ от API",
515 "Refresh models": "Обновить модели",515 "Refresh models": "Обновить модели",
516 "Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai": "Получите свой OpenRouter API токен используя OAuth. У вас будет открыта вкладка openrouter.ai",516 "Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai": "Получите свой OpenRouter API токен используя OAuth. У вас будет открыта вкладка openrouter.ai",
@@ -551,7 +551,7 @@
551 "Token counts may be inaccurate and provided just for reference.": "Счетчик токенов может быть неточным, используйте как ориентир",551 "Token counts may be inaccurate and provided just for reference.": "Счетчик токенов может быть неточным, используйте как ориентир",
552 "Click to select a new avatar for this character": "Нажмите чтобы выбрать новый аватар для этого персонажа",552 "Click to select a new avatar for this character": "Нажмите чтобы выбрать новый аватар для этого персонажа",
553 "Example: [{{user}} is a 28-year-old Romanian cat girl.]": "Пример:\n [{{user}} is a 28-year-old Romanian cat girl.]",553 "Example: [{{user}} is a 28-year-old Romanian cat girl.]": "Пример:\n [{{user}} is a 28-year-old Romanian cat girl.]",
554 "Toggle grid view": "Переключить вид сетки",554 "Toggle grid view": "Сменить вид сетки",
555 "Add to Favorites": "Добавить в Избранное",555 "Add to Favorites": "Добавить в Избранное",
556 "Advanced Definition": "Расширенное описание",556 "Advanced Definition": "Расширенное описание",
557 "Character Lore": "Лор персонажа",557 "Character Lore": "Лор персонажа",
@@ -624,7 +624,7 @@
624 "UI Theme": "Тема UI",624 "UI Theme": "Тема UI",
625 "This message is invisible for the AI": "Это сообщение невидимо для ИИ",625 "This message is invisible for the AI": "Это сообщение невидимо для ИИ",
626 "Sampler Priority": "Приоритет сэмплеров",626 "Sampler Priority": "Приоритет сэмплеров",
627 "Ooba only. Determines the order of samplers.": "Только oobabooga. Определяет порядок сэмплеров.",627 "Ooba only. Determines the order of samplers.": "Только для oobabooga. Определяет порядок сэмплеров.",
628 "Load default order": "Загрузить стандартный порядок",628 "Load default order": "Загрузить стандартный порядок",
629 "Max Tokens Second": "Макс. кол-во токенов в секунду",629 "Max Tokens Second": "Макс. кол-во токенов в секунду",
630 "CFG": "CFG",630 "CFG": "CFG",
@@ -695,7 +695,7 @@
695 "Medium": "Средний",695 "Medium": "Средний",
696 "Aggressive": "Агрессивный",696 "Aggressive": "Агрессивный",
697 "Very aggressive": "Очень агрессивный",697 "Very aggressive": "Очень агрессивный",
698 "Eta_Cutoff_desc": "Eta cutoff - основной параметр специальной техники сэмплинга под названием Eta Sampling.&#13;В единицах 1e-4; разумное значение - 3.&#13;Установите в 0, чтобы отключить.&#13;См. статью Truncation Sampling as Language Model Desmoothing от Хьюитт и др. (2022) для получения подробной информации.",698 "Eta_Cutoff_desc": "Eta cutoff - основной параметр специальной техники сэмплинга под названием Eta Sampling.\nВ единицах 1e-4; разумное значение - 3.\nУстановите в 0, чтобы отключить.\nСм. статью Truncation Sampling as Language Model Desmoothing от Хьюитт и др. (2022) для получения подробной информации.",
699 "Learn how to contribute your idle GPU cycles to the Horde": "Узнайте, как использовать время простоя вашего GPU для помощи Horde",699 "Learn how to contribute your idle GPU cycles to the Horde": "Узнайте, как использовать время простоя вашего GPU для помощи Horde",
700 "Use the appropriate tokenizer for Google models via their API. Slower prompt processing, but offers much more accurate token counting.": "Используйте соответствующий токенизатор для моделей Google через их API. Медленная обработка подсказок, но предлагает намного более точный подсчет токенов.",700 "Use the appropriate tokenizer for Google models via their API. Slower prompt processing, but offers much more accurate token counting.": "Используйте соответствующий токенизатор для моделей Google через их API. Медленная обработка подсказок, но предлагает намного более точный подсчет токенов.",
701 "Load koboldcpp order": "Загрузить порядок из koboldcpp",701 "Load koboldcpp order": "Загрузить порядок из koboldcpp",
@@ -964,7 +964,7 @@
964 "char_import_3": "Персонаж с JanitorAI (прямая ссылка или UUID)",964 "char_import_3": "Персонаж с JanitorAI (прямая ссылка или UUID)",
965 "char_import_4": "Персонаж с Pygmalion.chat (прямая ссылка или UUID)",965 "char_import_4": "Персонаж с Pygmalion.chat (прямая ссылка или UUID)",
966 "char_import_5": "Персонаж с AICharacterCards.com (прямая ссылка или ID)",966 "char_import_5": "Персонаж с AICharacterCards.com (прямая ссылка или ID)",
967 "char_import_6": "Прямая ссылка на PNG-файл (чтобы узнать список разрешённых хостов, загляните в",967 "char_import_6": "Прямая ссылка на PNG-файл (список разрешённых хостов находится в",
968 "char_import_7": ")",968 "char_import_7": ")",
969 "Grammar String": "Грамматика",969 "Grammar String": "Грамматика",
970 "GBNF or EBNF, depends on the backend in use. If you're using this you should know which.": "GBNF или EBNF, зависит от бэкенда. Если вы это используете, то, скорее всего, сами знаете, какой именно.",970 "GBNF or EBNF, depends on the backend in use. If you're using this you should know which.": "GBNF или EBNF, зависит от бэкенда. Если вы это используете, то, скорее всего, сами знаете, какой именно.",
@@ -1016,7 +1016,7 @@
1016 "prompt_manager_relative": "Относительная",1016 "prompt_manager_relative": "Относительная",
1017 "prompt_manager_depth": "Глубина",1017 "prompt_manager_depth": "Глубина",
1018 "Injection depth. 0 = after the last message, 1 = before the last message, etc.": "Глубина вставки. 0 = после последнего сообщения, 1 = перед последним сообщением, и т.д.",1018 "Injection depth. 0 = after the last message, 1 = before the last message, etc.": "Глубина вставки. 0 = после последнего сообщения, 1 = перед последним сообщением, и т.д.",
1019 "The prompt to be sent.": "Отправляемый ИИ промпт.",1019 "The prompt to be sent.": "Текст промпта.",
1020 "prompt_manager_forbid_overrides": "Запретить перезапись",1020 "prompt_manager_forbid_overrides": "Запретить перезапись",
1021 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "Карточка персонажа не сможет перезаписать этот промпт, даже если настройки отдают приоритет именно ей.",1021 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "Карточка персонажа не сможет перезаписать этот промпт, даже если настройки отдают приоритет именно ей.",
1022 "image_inlining_hint_1": "Отправлять картинки как часть промпта, если позволяет модель (такой функционал поддерживают GPT-4V, Claude 3 или Llava 13B). Чтобы добавить в чат изображение, используйте на нужном сообщении действие",1022 "image_inlining_hint_1": "Отправлять картинки как часть промпта, если позволяет модель (такой функционал поддерживают GPT-4V, Claude 3 или Llava 13B). Чтобы добавить в чат изображение, используйте на нужном сообщении действие",
@@ -1232,7 +1232,7 @@
1232 "Top P & Min P": "Top P & Min P",1232 "Top P & Min P": "Top P & Min P",
1233 "llama.cpp only. Determines the order of samplers. If Mirostat mode is not 0, sampler order is ignored.": "llama.cpp only. Determines the order of samplers. If Mirostat mode is not 0, sampler order is ignored.",1233 "llama.cpp only. Determines the order of samplers. If Mirostat mode is not 0, sampler order is ignored.": "llama.cpp only. Determines the order of samplers. If Mirostat mode is not 0, sampler order is ignored.",
1234 "Helps the model to associate messages with characters.": "Помогает модели связывать сообщения с персонажами.",1234 "Helps the model to associate messages with characters.": "Помогает модели связывать сообщения с персонажами.",
1235 "character_names_default": "Except for groups and past personas. Otherwise, make sure you provide names in the prompt.",1235 "character_names_default": "Добавлять префиксы для групповых чатов и предыдущих персон. В остальных случаях указывайте имена в промпте иными способами.",
1236 "Completion": "Completion Object",1236 "Completion": "Completion Object",
1237 "character_names_completion": "Только латинские буквы, цифры и знак подчёркивания. Работает не для всех бэкендов, в частности для Claude, MistralAI, Google.",1237 "character_names_completion": "Только латинские буквы, цифры и знак подчёркивания. Работает не для всех бэкендов, в частности для Claude, MistralAI, Google.",
1238 "Use AI21 Tokenizer": "Использовать токенайзер AI21",1238 "Use AI21 Tokenizer": "Использовать токенайзер AI21",
@@ -1278,7 +1278,7 @@
1278 "Will be inserted as a last prompt line when using system/neutral generation.": "Will be inserted as a last prompt line when using system/neutral generation.",1278 "Will be inserted as a last prompt line when using system/neutral generation.": "Will be inserted as a last prompt line when using system/neutral generation.",
1279 "If a stop sequence is generated, everything past it will be removed from the output (inclusive).": "Если ИИ генерирует стоп-строку, то всё после неё будет вырезано из ответа (включая и саму стоп-строку).",1279 "If a stop sequence is generated, everything past it will be removed from the output (inclusive).": "Если ИИ генерирует стоп-строку, то всё после неё будет вырезано из ответа (включая и саму стоп-строку).",
1280 "Will be inserted at the start of the chat history if it doesn't start with a User message.": "Вставляется в начале истории чата, если она начинается не с сообщения пользователя.",1280 "Will be inserted at the start of the chat history if it doesn't start with a User message.": "Вставляется в начале истории чата, если она начинается не с сообщения пользователя.",
1281 "Global World Info/Lorebook activation settings": "Настройки активации глобального лорбука / Информации о мире",1281 "Global World Info/Lorebook activation settings": "Глобальные настройки активации лорбука / Информации о мире",
1282 "Click to expand": "Щёлкните, чтобы развернуть",1282 "Click to expand": "Щёлкните, чтобы развернуть",
1283 "Insertion Strategy": "Как инжектить",1283 "Insertion Strategy": "Как инжектить",
1284 "Only the entries with the most number of key matches will be selected for Inclusion Group filtering": "Only the entries with the most number of key matches will be selected for Inclusion Group filtering",1284 "Only the entries with the most number of key matches will be selected for Inclusion Group filtering": "Only the entries with the most number of key matches will be selected for Inclusion Group filtering",
@@ -1647,8 +1647,8 @@
1647 "mui_reset": "Сброс",1647 "mui_reset": "Сброс",
1648 "Quick 'Impersonate' button": "Быстрое перевоплощение",1648 "Quick 'Impersonate' button": "Быстрое перевоплощение",
1649 "Show a button in the input area to ask the AI to impersonate your character for a single message": "Показать в поле ввода кнопку, по нажатии на которую ИИ сгенерирует одно сообщение от лица вашего персонажа.",1649 "Show a button in the input area to ask the AI to impersonate your character for a single message": "Показать в поле ввода кнопку, по нажатии на которую ИИ сгенерирует одно сообщение от лица вашего персонажа.",
1650 "Separators as Stop Strings": "Разделители как стоп-строки",1650 "Separators as Stop Strings": "Разделители в качестве стоп-строк",
1651 "Names as Stop Strings": "Имена как стоп-строки",1651 "Names as Stop Strings": "Имена в качестве стоп-строк",
1652 "Add Character and User names to a list of stopping strings.": "Добавлять имена персонажа и пользователя в список стоп-строк.",1652 "Add Character and User names to a list of stopping strings.": "Добавлять имена персонажа и пользователя в список стоп-строк.",
1653 "Allow Post-History Instructions": "Разрешить инструкции после истории",1653 "Allow Post-History Instructions": "Разрешить инструкции после истории",
1654 "context_allow_post_history_instructions": "Добавлять в конец промпта инструкции после истории. Работает только при наличии таких инструкций в карточке И при включенной опции ''Приоритет инструкциям из карточек''.\nНЕ РЕКОМЕНДУЕТСЯ ДЛЯ МОДЕЛЕЙ TEXT COMPLETION, МОЖЕТ ПОРТИТЬ ВЫХОДНОЙ ТЕКСТ.",1654 "context_allow_post_history_instructions": "Добавлять в конец промпта инструкции после истории. Работает только при наличии таких инструкций в карточке И при включенной опции ''Приоритет инструкциям из карточек''.\nНЕ РЕКОМЕНДУЕТСЯ ДЛЯ МОДЕЛЕЙ TEXT COMPLETION, МОЖЕТ ПОРТИТЬ ВЫХОДНОЙ ТЕКСТ.",
@@ -1916,8 +1916,8 @@
1916 "Cannot restore GUI preset": "Пресет для Gui восстановить нельзя",1916 "Cannot restore GUI preset": "Пресет для Gui восстановить нельзя",
1917 "Default preset cannot be restored": "Невозможно восстановить пресет по умолчанию",1917 "Default preset cannot be restored": "Невозможно восстановить пресет по умолчанию",
1918 "Default template cannot be restored": "Невозможно восстановить шаблон по умолчанию",1918 "Default template cannot be restored": "Невозможно восстановить шаблон по умолчанию",
1919 "Resetting a <b>default preset</b> will restore the default settings": "Сброс <b>стандартного пресета</b> восстановит настройки по умолчанию.",1919 "Resetting a <b>default preset</b> will restore the default settings.": "Сброс <b>комплектного пресета</b> восстановит настройки по умолчанию.",
1920 "Resetting a <b>default template</b> will restore the default settings.": "Сброс <b>стандартного шаблона</b> восстановит настройки по умолчанию.",1920 "Resetting a <b>default template</b> will restore the default settings.": "Сброс <b>комплектного шаблона</b> восстановит настройки по умолчанию.",
1921 "Are you sure?": "Вы уверены?",1921 "Are you sure?": "Вы уверены?",
1922 "Default preset restored": "Стандартный пресет восстановлен",1922 "Default preset restored": "Стандартный пресет восстановлен",
1923 "Default template restored": "Стандартный шаблон восстановлен",1923 "Default template restored": "Стандартный шаблон восстановлен",
@@ -2048,11 +2048,11 @@
2048 "prompt_post_processing_merge": "Объединять идущие подряд сообщения с одной ролью",2048 "prompt_post_processing_merge": "Объединять идущие подряд сообщения с одной ролью",
2049 "prompt_post_processing_semi": "Semi-strict (чередовать роли)",2049 "prompt_post_processing_semi": "Semi-strict (чередовать роли)",
2050 "prompt_post_processing_strict": "Strict (чередовать роли, сначала пользователь)",2050 "prompt_post_processing_strict": "Strict (чередовать роли, сначала пользователь)",
2051 "Select Horde models": "Выбрать модель из Horde",2051 "Select Horde models": "Выберите модель из Horde",
2052 "Model ID (optional)": "Идентификатор модели (необязательно)",2052 "Model ID (optional)": "Идентификатор модели (необязательно)",
2053 "Derive context size from backend": "Использовать бэкенд для определения размера контекста",2053 "Derive context size from backend": "Использовать бэкенд для определения размера контекста",
2054 "Rename current preset": "Переименовать пресет",2054 "Rename current preset": "Переименовать пресет",
2055 "No Worlds active. Click here to select.": "Нет активных миров. Нажмите, чтобы выбрать.",2055 "No Worlds active. Click here to select.": "Активных миров нет, ЛКМ для выбора.",
2056 "Title/Memo": "Название",2056 "Title/Memo": "Название",
2057 "Strategy": "Статус",2057 "Strategy": "Статус",
2058 "Position": "Позиция",2058 "Position": "Позиция",
@@ -2171,7 +2171,7 @@
2171 "instruct_derived": "Считывать из метаданных модели (по возможности)",2171 "instruct_derived": "Считывать из метаданных модели (по возможности)",
2172 "Confirm token parsing with": "Чтобы убедиться в правильности выделения токенов, используйте",2172 "Confirm token parsing with": "Чтобы убедиться в правильности выделения токенов, используйте",
2173 "Reasoning Effort": "Рассуждения",2173 "Reasoning Effort": "Рассуждения",
2174 "Constrains effort on reasoning for reasoning models.": "Регулирует объём внутренних рассуждений модели (reasoning), для моделей которые поддерживают эту возможность.\nНа данный момент поддерживаются три значения: Подробные, Обычные, Поверхностные.\nПри менее подробном рассуждении ответ получается быстрее, а также экономятся токены, уходящие на рассуждения.",2174 "Constrains effort on reasoning for reasoning models.": "Регулирует объём внутренних рассуждений модели (reasoning), для моделей, которые поддерживают эту возможность.\nПри менее подробном рассуждении ответ получается быстрее, а также экономятся токены, уходящие на рассуждения.",
2175 "openai_reasoning_effort_low": "Поверхностные",2175 "openai_reasoning_effort_low": "Поверхностные",
2176 "openai_reasoning_effort_medium": "Обычные",2176 "openai_reasoning_effort_medium": "Обычные",
2177 "openai_reasoning_effort_high": "Подробные",2177 "openai_reasoning_effort_high": "Подробные",
@@ -2276,8 +2276,8 @@
2276 "Persona Name Not Set": "У персоны отсутствует имя",2276 "Persona Name Not Set": "У персоны отсутствует имя",
2277 "You must bind a name to this persona before you can set a lorebook.": "Перед привязкой лорбука персоне необходимо присвоить имя.",2277 "You must bind a name to this persona before you can set a lorebook.": "Перед привязкой лорбука персоне необходимо присвоить имя.",
2278 "Default Persona Removed": "Персона по умолчанию снята",2278 "Default Persona Removed": "Персона по умолчанию снята",
2279 "Persona is locked to the current character": "Персона закреплена за этим персонажем",2279 "Persona is locked to the current character": "Персона закреплена за текущим персонажем",
2280 "Persona is locked to the current chat": "Персона закреплена за этим чатом",2280 "Persona is locked to the current chat": "Персона закреплена за текущим чатом",
2281 "characters": "перс.",2281 "characters": "перс.",
2282 "character": "персонаж",2282 "character": "персонаж",
2283 "in this group": "в группе",2283 "in this group": "в группе",
@@ -2338,5 +2338,87 @@
2338 "Reasoning already exists.": "Рассуждения уже присутствуют.",2338 "Reasoning already exists.": "Рассуждения уже присутствуют.",
2339 "Edit Message": "Редактирование",2339 "Edit Message": "Редактирование",
2340 "Status check bypassed": "Проверка статуса отключена",2340 "Status check bypassed": "Проверка статуса отключена",
2341 "Valid": "Работает"2341 "Valid": "Работает",
2342 "Use Group Scoring": "Использовать Group Scoring",
2343 "Only the entries with the most number of key matches will be selected for Inclusion Group filtering": "До групповых фильтров будут допущены только записи с наибольшим кол-вом совпадений",
2344 "Can be used to automatically activate Quick Replies": "Используется для автоматической активации быстрых ответов (Quick Replies)",
2345 "( None )": "(Отсутствует)",
2346 "Tie this entry to specific characters or characters with specific tags": "Привязать запись к опред. персонажам или персонажам с заданными тегами",
2347 "Move Entry to Another Lorebook": "Переместить запись в другой лорбук",
2348 "There are no other lorebooks to move to.": "Некуда перемещать: не найдено других лорбуков.",
2349 "Select Target Lorebook": "Выберите куда переместить",
2350 "Move '${0}' to:": "Переместить '${0}' в:",
2351 "Please select a target lorebook.": "Выберите лорбук, в который будет перемещена запись.",
2352 "Scan depth cannot be negative": "Глубина сканирования не может быть отрицательной",
2353 "Scan depth cannot exceed ${0}": "Глубина сканирования не может превышать ${0}",
2354 "Select your current Reasoning Template": "Выберите текущий Шаблон рассуждений",
2355 "Delete template": "Удалить шаблон",
2356 "Reasoning Template": "Шаблон рассуждений",
2357 "openai_reasoning_effort_auto": "Авто",
2358 "openai_reasoning_effort_minimum": "Минимальные",
2359 "openai_reasoning_effort_maximum": "Максимальные",
2360 "OpenAI-style options: low, medium, high. Minimum and maximum are aliased to low and high. Auto does not send an effort level.": "OpenAI принимает следующее: low (Поверхностные), medium (Обычные), high (Подробные). Minimum (Минимальные) - то же самое, что low. Maximum (Максимальные) - то же самое, что high. При выборе Auto (Авто) значение не отсылается вообще.",
2361 "Allocates a portion of the response length for thinking (low: 10%, medium: 25%, high: 50%). Other options are model-dependent.": "Резервирует часть ответа для рассуждений (Поверхностные: 10% ответа, Обычные: 25%, Подробные: 50%). Остальные значения зависят от конкретной модели.",
2362 "xAI Model": "Модель xAI",
2363 "xAI API Key": "Ключ от API xAI",
2364 "HuggingFace Token": "Токен HuggingFace",
2365 "Endpoint URL": "Адрес эндпоинта",
2366 "Example: https://****.endpoints.huggingface.cloud": "Пример: https://****.endpoints.huggingface.cloud",
2367 "Featherless Model Selection": "Выбор модели из Featherless",
2368 "category": "категория",
2369 "Top": "Топовые",
2370 "All Classes": "Все классы",
2371 "Date Asc": "Дата, возрастание",
2372 "Date Desc": "Дата, убывание",
2373 "Background Image": "Фоновое изображение",
2374 "Delete the background?": "Удалить фон?",
2375 "Tags_as_Folders_desc": "Чтобы тег отображался как папка, его нужно отметить таковым в меню управления тегами. Нажмите сюда, чтобы открыть его.",
2376 "tag_entries": "раз исп.",
2377 "Multiple personas are connected to this character.\nSelect a persona to use for this chat.": "К этому персонажу привязано несколько персон.\nВыберите персону, которую хотите использовать в этом чате.",
2378 "Select Persona": "Выберите персону",
2379 "Completion Object": "Как часть Completion Object",
2380 "Move ${0} to:": "Переместить '${0}' в:",
2381 "Chat Scenario Override": "Перезапись сценария чата",
2382 "Unique to this chat.": "Действует только в рамках текущего чата.",
2383 "All group members will use the following scenario text instead of what is specified in their character cards.": "Все участники группы будут использовать этот сценарий вместо того, который указан в карточке.",
2384 "Checkpoints inherit the scenario override from their parent, and can be changed individually after that.": "Чекпоинты наследуют сценарий родителя, после отделения его можно менять.",
2385 "Delete Tag": "Удалить тег",
2386 "Do you want to delete the tag": "Вы точно хотите удалить тег",
2387 "If you want to merge all references to this tag into another tag, select it below:": "Если хотите заменить ссылки на этот тег на какой-то другой, то выберите из списка:",
2388 "Open Folder (Show all characters even if not selected)": "Открытая папка (показать всех персонажей, включая невыбранных)",
2389 "Closed Folder (Hide all characters unless selected)": "Закрытая папка (скрыть всех персонажей, кроме выбранных)",
2390 "No Folder": "Не папка",
2391 "Show only favorites": "Показать только избранных персонажей",
2392 "Show only groups": "Показать только группы",
2393 "Show only folders": "Показать только папки",
2394 "Manage tags": "Панель управления тегами",
2395 "Show Tag List": "Показать список тегов",
2396 "Clear all filters": "Сбросить все фильтры",
2397 "There are no items to display.": "Отображать абсолютно нечего.",
2398 "Characters and groups hidden by filters or closed folders": "Персонажи и группы скрыты настройками фильтров либо закрытыми папками",
2399 "Otterly empty": "Всё что можно, всё выдрано",
2400 "Here be dragons": "Список настолько очистился, что в него вернулись драконы",
2401 "Kiwibunga": "Настолько пусто, что киви прилетела посидеть",
2402 "Pump-a-Rum": "Пу-пу-пу",
2403 "Croak it": "Только кваканье лягушек и стрёкот сверчков",
2404 "${0} ${1} hidden.": "Персонажей скрыто: ${0}.",
2405 "pagination_of": "из",
2406 "/ page": "/ стр.",
2407 "Context Length": "Размер контекста",
2408 "Added On": "Добавлена",
2409 "Class": "Класс",
2410 "Bulk_edit_characters": "Массовое редактирование персонажей\n\nЛКМ, чтобы выделить либо отменить выделение персонажа\nShift+ЛКМ, чтобы массово выделить либо отменить выделение персонажей\nПКМ, чтобы выбрать действие",
2411 "Bulk select all characters": "Выбрать всех персонажей",
2412 "Duplicate": "Клонировать",
2413 "Next page": "След. страница",
2414 "Previous page": "Пред. страница",
2415 "Group:": "Группа:",
2416 "You deleted a character/chat and arrived back here for safety reasons! Pick another character!": "Вы удалили персонажа или чат, и мы из соображений безопасности перенесли вас на эту страницу! Выберите другого персонажа!",
2417 "Group is empty.": "Группа пуста.",
2418 "No characters available": "Персонажей нет",
2419 "Choose what to export": "Выберите, что экспортировать",
2420 "Text Completion Preset": "Пресет для режима Text Completion",
2421 "Update enabled": "Обновить включенные",
2422 "Could not connect to API": "Не удалось подключиться к API",
2423 "Connected to API": "Соединение с API установлено"
2342}2424}
public/script.js+9 -5
@@ -143,6 +143,7 @@ import {
143 getHordeModels,143 getHordeModels,
144 adjustHordeGenerationParams,144 adjustHordeGenerationParams,
145 MIN_LENGTH,145 MIN_LENGTH,
146 initHorde
146} from './scripts/horde.js';147} from './scripts/horde.js';
147148
148import {149import {
@@ -175,6 +176,7 @@ import {
175 saveBase64AsFile,176 saveBase64AsFile,
176 uuidv4,177 uuidv4,
177 equalsIgnoreCaseAndAccents,178 equalsIgnoreCaseAndAccents,
179 localizePagination
178} from './scripts/utils.js';180} from './scripts/utils.js';
179import { debounce_timeout, IGNORE_SYMBOL } from './scripts/constants.js';181import { debounce_timeout, IGNORE_SYMBOL } from './scripts/constants.js';
180182
@@ -229,7 +231,7 @@ import {
229 instruct_presets,231 instruct_presets,
230 selectContextPreset,232 selectContextPreset,
231} from './scripts/instruct-mode.js';233} from './scripts/instruct-mode.js';
232import { initLocales, t } from './scripts/i18n.js';234import { initLocales, t, translate } from './scripts/i18n.js';
233import { getFriendlyTokenizerName, getTokenCount, getTokenCountAsync, initTokenizers, saveTokenCache, TOKENIZER_SUPPORTED_KEY } from './scripts/tokenizers.js';235import { getFriendlyTokenizerName, getTokenCount, getTokenCountAsync, initTokenizers, saveTokenCache, TOKENIZER_SUPPORTED_KEY } from './scripts/tokenizers.js';
234import {236import {
235 user_avatar,237 user_avatar,
@@ -994,6 +996,7 @@ async function firstLoadInit() {
994 initAuthorsNote();996 initAuthorsNote();
995 await initPersonas();997 await initPersonas();
996 initWorldInfo();998 initWorldInfo();
999 initHorde();
997 initRossMods();1000 initRossMods();
998 initStats();1001 initStats();
999 initCfg();1002 initCfg();
@@ -1425,13 +1428,13 @@ function getBackBlock() {
14251428
1426function getEmptyBlock() {1429function getEmptyBlock() {
1427 const icons = ['fa-dragon', 'fa-otter', 'fa-kiwi-bird', 'fa-crow', 'fa-frog'];1430 const icons = ['fa-dragon', 'fa-otter', 'fa-kiwi-bird', 'fa-crow', 'fa-frog'];
1428 const texts = ['Here be dragons', 'Otterly empty', 'Kiwibunga', 'Pump-a-Rum', 'Croak it'];1431 const texts = [t`Here be dragons`, t`Otterly empty`, t`Kiwibunga`, t`Pump-a-Rum`, t`Croak it`];
1429 const roll = new Date().getMinutes() % icons.length;1432 const roll = new Date().getMinutes() % icons.length;
1430 const emptyBlock = `1433 const emptyBlock = `
1431 <div class="text_block empty_block">1434 <div class="text_block empty_block">
1432 <i class="fa-solid ${icons[roll]} fa-4x"></i>1435 <i class="fa-solid ${icons[roll]} fa-4x"></i>
1433 <h1>${texts[roll]}</h1>1436 <h1>${texts[roll]}</h1>
1434 <p>There are no items to display.</p>1437 <p>` + t`There are no items to display.` + `</p>
1435 </div>`;1438 </div>`;
1436 return $(emptyBlock);1439 return $(emptyBlock);
1437}1440}
@@ -1443,7 +1446,7 @@ function getHiddenBlock(hidden) {
1443 const hiddenBlock = `1446 const hiddenBlock = `
1444 <div class="text_block hidden_block">1447 <div class="text_block hidden_block">
1445 <small>1448 <small>
1446 <p>${hidden} ${hidden > 1 ? 'characters' : 'character'} hidden.</p>1449 <p>` + t`${hidden} ${hidden > 1 ? 'characters' : 'character'} hidden.` + `</p>
1447 <div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]Characters and groups hidden by filters or closed folders" title="Characters and groups hidden by filters or closed folders"></div>1450 <div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]Characters and groups hidden by filters or closed folders" title="Characters and groups hidden by filters or closed folders"></div>
1448 </small>1451 </small>
1449 </div>`;1452 </div>`;
@@ -1568,6 +1571,7 @@ export async function printCharacters(fullRefresh = false) {
1568 if (hidden > 0 && entitiesFilter.hasAnyFilter()) {1571 if (hidden > 0 && entitiesFilter.hasAnyFilter()) {
1569 $(listId).append(getHiddenBlock(hidden));1572 $(listId).append(getHiddenBlock(hidden));
1570 }1573 }
1574 localizePagination($('#rm_print_characters_pagination'));
15711575
1572 eventSource.emit(event_types.CHARACTER_PAGE_LOADED);1576 eventSource.emit(event_types.CHARACTER_PAGE_LOADED);
1573 },1577 },
@@ -8429,7 +8433,7 @@ export function callPopup(text, type, inputValue = '', { okButton, rows, wide, w
8429 } else if (['delete_extension'].includes(popup_type)) {8433 } else if (['delete_extension'].includes(popup_type)) {
8430 return okButton ?? 'Ok';8434 return okButton ?? 'Ok';
8431 } else if (['new_chat', 'confirm'].includes(popup_type)) {8435 } else if (['new_chat', 'confirm'].includes(popup_type)) {
8432 return okButton ?? 'Yes';8436 return okButton ?? t`Yes`;
8433 } else if (['input'].includes(popup_type)) {8437 } else if (['input'].includes(popup_type)) {
8434 return okButton ?? t`Save`;8438 return okButton ?? t`Save`;
8435 }8439 }
public/scripts/backgrounds.js+1 -1
@@ -291,7 +291,7 @@ async function onDeleteBackgroundClick(e) {
291 const bgToDelete = $(this).closest('.bg_example');291 const bgToDelete = $(this).closest('.bg_example');
292 const url = bgToDelete.data('url');292 const url = bgToDelete.data('url');
293 const isCustom = bgToDelete.attr('custom') === 'true';293 const isCustom = bgToDelete.attr('custom') === 'true';
294 const confirm = await callPopup('<h3>Delete the background?</h3>', 'confirm');294 const confirm = await callPopup('<h3>' + t`Delete the background?` + '</h3>', 'confirm');
295 const bg = bgToDelete.attr('bgfile');295 const bg = bgToDelete.attr('bgfile');
296296
297 if (confirm) {297 if (confirm) {
public/scripts/group-chats.js+5 -1
@@ -13,6 +13,7 @@ import {
13 getBase64Async,13 getBase64Async,
14 resetScrollHeight,14 resetScrollHeight,
15 initScrollHeight,15 initScrollHeight,
16 localizePagination
16} from './utils.js';17} from './utils.js';
17import { RA_CountCharTokens, humanizedDateTime, dragElement, favsToHotswap, getMessageTimeStamp } from './RossAscends-mods.js';18import { RA_CountCharTokens, humanizedDateTime, dragElement, favsToHotswap, getMessageTimeStamp } from './RossAscends-mods.js';
18import { power_user, loadMovingUIState, sortEntitiesList } from './power-user.js';19import { power_user, loadMovingUIState, sortEntitiesList } from './power-user.js';
@@ -1394,6 +1395,7 @@ function printGroupCandidates() {
1394 for (const i of data) {1395 for (const i of data) {
1395 $('#rm_group_add_members').append(getGroupCharacterBlock(i.item));1396 $('#rm_group_add_members').append(getGroupCharacterBlock(i.item));
1396 }1397 }
1398 localizePagination($('#rm_group_add_members_pagination'));
1397 },1399 },
1398 });1400 });
1399}1401}
@@ -1401,6 +1403,7 @@ function printGroupCandidates() {
1401function printGroupMembers() {1403function printGroupMembers() {
1402 const storageKey = 'GroupMembers_PerPage';1404 const storageKey = 'GroupMembers_PerPage';
1403 $('.rm_group_members_pagination').each(function () {1405 $('.rm_group_members_pagination').each(function () {
1406 let that = this;
1404 $(this).pagination({1407 $(this).pagination({
1405 dataSource: getGroupCharacters({ doFilter: false, onlyMembers: true }),1408 dataSource: getGroupCharacters({ doFilter: false, onlyMembers: true }),
1406 pageRange: 1,1409 pageRange: 1,
@@ -1421,6 +1424,7 @@ function printGroupMembers() {
1421 for (const i of data) {1424 for (const i of data) {
1422 $('.rm_group_members').append(getGroupCharacterBlock(i.item));1425 $('.rm_group_members').append(getGroupCharacterBlock(i.item));
1423 }1426 }
1427 localizePagination($(that));
1424 },1428 },
1425 });1429 });
1426 });1430 });
@@ -1804,7 +1808,7 @@ async function createGroup() {
1804 const memberNames = characters.filter(x => members.includes(x.avatar)).map(x => x.name).join(', ');1808 const memberNames = characters.filter(x => members.includes(x.avatar)).map(x => x.name).join(', ');
18051809
1806 if (!name) {1810 if (!name) {
1807 name = `Group: ${memberNames}`;1811 name = t`Group:` + ` ${memberNames}`;
1808 }1812 }
18091813
1810 const avatar_url = $('#group_avatar_preview img').attr('src');1814 const avatar_url = $('#group_avatar_preview img').attr('src');
public/scripts/horde.js+3 -3
@@ -394,7 +394,7 @@ function getHordeModelTemplate(option) {
394 `));394 `));
395}395}
396396
397jQuery(function () {397export function initHorde () {
398 $('#horde_model').on('mousedown change', async function (e) {398 $('#horde_model').on('mousedown change', async function (e) {
399 console.log('Horde model change', e);399 console.log('Horde model change', e);
400 horde_settings.models = $('#horde_model').val();400 horde_settings.models = $('#horde_model').val();
@@ -441,7 +441,7 @@ jQuery(function () {
441 if (!isMobile()) {441 if (!isMobile()) {
442 $('#horde_model').select2({442 $('#horde_model').select2({
443 width: '100%',443 width: '100%',
444 placeholder: 'Select Horde models',444 placeholder: t`Select Horde models`,
445 allowClear: true,445 allowClear: true,
446 closeOnSelect: false,446 closeOnSelect: false,
447 templateSelection: function (data) {447 templateSelection: function (data) {
@@ -451,5 +451,5 @@ jQuery(function () {
451 templateResult: getHordeModelTemplate,451 templateResult: getHordeModelTemplate,
452 });452 });
453 }453 }
454});454};
455455
public/scripts/openai.js+1 -1
@@ -3851,7 +3851,7 @@ function createLogitBiasListItem(entry) {
3851}3851}
38523852
3853async function createNewLogitBiasPreset() {3853async function createNewLogitBiasPreset() {
3854 const name = await callPopup('Preset name:', 'input');3854 const name = await callPopup(t`Preset name:`, 'input');
38553855
3856 if (!name) {3856 if (!name) {
3857 return;3857 return;
public/scripts/personas.js+4 -2
@@ -22,12 +22,12 @@ import {
22} from '../script.js';22} from '../script.js';
23import { persona_description_positions, power_user } from './power-user.js';23import { persona_description_positions, power_user } from './power-user.js';
24import { getTokenCountAsync } from './tokenizers.js';24import { getTokenCountAsync } from './tokenizers.js';
25import { PAGINATION_TEMPLATE, clearInfoBlock, debounce, delay, download, ensureImageFormatSupported, flashHighlight, getBase64Async, getCharIndex, isFalseBoolean, isTrueBoolean, onlyUnique, parseJsonFile, setInfoBlock } from './utils.js';25import { PAGINATION_TEMPLATE, clearInfoBlock, debounce, delay, download, ensureImageFormatSupported, flashHighlight, getBase64Async, getCharIndex, isFalseBoolean, isTrueBoolean, onlyUnique, parseJsonFile, setInfoBlock, localizePagination } from './utils.js';
26import { debounce_timeout } from './constants.js';26import { debounce_timeout } from './constants.js';
27import { FILTER_TYPES, FilterHelper } from './filters.js';27import { FILTER_TYPES, FilterHelper } from './filters.js';
28import { groups, selected_group } from './group-chats.js';28import { groups, selected_group } from './group-chats.js';
29import { POPUP_TYPE, Popup, callGenericPopup } from './popup.js';29import { POPUP_TYPE, Popup, callGenericPopup } from './popup.js';
30import { t } from './i18n.js';30import { t, translate } from './i18n.js';
31import { openWorldInfoEditor, world_names } from './world-info.js';31import { openWorldInfoEditor, world_names } from './world-info.js';
32import { renderTemplateAsync } from './templates.js';32import { renderTemplateAsync } from './templates.js';
33import { saveMetadataDebounced } from './extensions.js';33import { saveMetadataDebounced } from './extensions.js';
@@ -263,6 +263,7 @@ export async function getUserAvatars(doRender = true, openPageAt = '') {
263 prevText: '<',263 prevText: '<',
264 nextText: '>',264 nextText: '>',
265 formatNavigator: PAGINATION_TEMPLATE,265 formatNavigator: PAGINATION_TEMPLATE,
266 afterRender: function(a) {console.log(a)},
266 showNavigator: true,267 showNavigator: true,
267 callback: function (data) {268 callback: function (data) {
268 $(listId).empty();269 $(listId).empty();
@@ -270,6 +271,7 @@ export async function getUserAvatars(doRender = true, openPageAt = '') {
270 $(listId).append(getUserAvatarBlock(item));271 $(listId).append(getUserAvatarBlock(item));
271 }272 }
272 updatePersonaUIStates();273 updatePersonaUIStates();
274 localizePagination($('#persona_pagination_container'));
273 },275 },
274 afterSizeSelectorChange: function (e) {276 afterSizeSelectorChange: function (e) {
275 accountStorage.setItem(storageKey, e.target.value);277 accountStorage.setItem(storageKey, e.target.value);
public/scripts/tags.js+6 -5
@@ -27,7 +27,7 @@ import { debounce_timeout } from './constants.js';
27import { INTERACTABLE_CONTROL_CLASS } from './keyboard.js';27import { INTERACTABLE_CONTROL_CLASS } from './keyboard.js';
28import { commonEnumProviders } from './slash-commands/SlashCommandCommonEnumsProvider.js';28import { commonEnumProviders } from './slash-commands/SlashCommandCommonEnumsProvider.js';
29import { renderTemplateAsync } from './templates.js';29import { renderTemplateAsync } from './templates.js';
30import { t } from './i18n.js';30import { t, translate } from './i18n.js';
3131
32export {32export {
33 TAG_FOLDER_TYPES,33 TAG_FOLDER_TYPES,
@@ -1057,7 +1057,7 @@ function appendTagToList(listElement, tag, { removable = false, isFilter = false
1057 tagElement.attr('title', tag.title);1057 tagElement.attr('title', tag.title);
1058 }1058 }
1059 if (tag.icon) {1059 if (tag.icon) {
1060 tagElement.find('.tag_name').text('').attr('title', `${tag.name} ${tag.title || ''}`.trim()).addClass(tag.icon);1060 tagElement.find('.tag_name').text('').attr('title', `${translate(tag.name)} ${tag.title || ''}`.trim()).addClass(tag.icon);
1061 tagElement.addClass('actionable');1061 tagElement.addClass('actionable');
1062 }1062 }
10631063
@@ -1644,6 +1644,7 @@ function updateDrawTagFolder(element, tag) {
16441644
1645 // Draw/update css attributes for this class1645 // Draw/update css attributes for this class
1646 folderElement.attr('title', tagFolder.tooltip);1646 folderElement.attr('title', tagFolder.tooltip);
1647 folderElement.attr('data-i18n', '[title]' + tagFolder.tooltip);
1647 const indicator = folderElement.find('.tag_folder_indicator');1648 const indicator = folderElement.find('.tag_folder_indicator');
1648 indicator.text(tagFolder.icon);1649 indicator.text(tagFolder.icon);
1649 indicator.css('color', tagFolder.color);1650 indicator.css('color', tagFolder.color);
@@ -1656,9 +1657,9 @@ async function onTagDeleteClick() {
1656 const otherTags = sortTags(tags.filter(x => x.id !== id).map(x => ({ id: x.id, name: x.name })));1657 const otherTags = sortTags(tags.filter(x => x.id !== id).map(x => ({ id: x.id, name: x.name })));
16571658
1658 const popupContent = $(`1659 const popupContent = $(`
1659 <h3>Delete Tag</h3>1660 <h3>` + t`Delete Tag` + `</h3>
1660 <div>Do you want to delete the tag <div id="tag_to_delete" class="tags_inline inline-flex margin-r2"></div>?</div>1661 <div>` + t`Do you want to delete the tag` + ` <div id="tag_to_delete" class="tags_inline inline-flex margin-r2"></div>?</div>
1661 <div class="m-t-2 marginBot5">If you want to merge all references to this tag into another tag, select it below:</div>1662 <div class="m-t-2 marginBot5">` + t`If you want to merge all references to this tag into another tag, select it below:` + `</div>
1662 <select id="merge_tag_select">1663 <select id="merge_tag_select">
1663 <option value="">--- None ---</option>1664 <option value="">--- None ---</option>
1664 ${otherTags.map(x => `<option value="${x.id}">${x.name}</option>`).join('')}1665 ${otherTags.map(x => `<option value="${x.id}">${x.name}</option>`).join('')}
public/scripts/textgen-models.js+16 -5
@@ -5,8 +5,9 @@ import { textgenerationwebui_settings as textgen_settings, textgen_types } from
5import { tokenizers } from './tokenizers.js';5import { tokenizers } from './tokenizers.js';
6import { renderTemplateAsync } from './templates.js';6import { renderTemplateAsync } from './templates.js';
7import { POPUP_TYPE, callGenericPopup } from './popup.js';7import { POPUP_TYPE, callGenericPopup } from './popup.js';
8import { t } from './i18n.js';8import { t, translate } from './i18n.js';
9import { accountStorage } from './util/AccountStorage.js';9import { accountStorage } from './util/AccountStorage.js';
10import { localizePagination } from './utils.js';
1011
11let mancerModels = [];12let mancerModels = [];
12let togetherModels = [];13let togetherModels = [];
@@ -368,7 +369,16 @@ export async function loadFeatherlessModels(data) {
368 prevText: '<',369 prevText: '<',
369 nextText: '>',370 nextText: '>',
370 formatNavigator: function (currentPage, totalPage) {371 formatNavigator: function (currentPage, totalPage) {
371 return (currentPage - 1) * perPage + 1 + ' - ' + currentPage * perPage + ' of ' + totalPage * perPage;372 let translated_of;
373 try {
374 translated_of = translate('pagination_of');
375 if (translated_of == 'pagination_of') {
376 translated_of = 'of';
377 }
378 } catch (e) {
379 translated_of = 'of';
380 }
381 return (currentPage - 1) * perPage + 1 + ' - ' + currentPage * perPage + ` ${translated_of} ` + totalPage * perPage;
372 },382 },
373 showNavigator: true,383 showNavigator: true,
374 callback: function (modelsOnPage, pagination) {384 callback: function (modelsOnPage, pagination) {
@@ -391,15 +401,15 @@ export async function loadFeatherlessModels(data) {
391401
392 const modelClassDiv = document.createElement('div');402 const modelClassDiv = document.createElement('div');
393 modelClassDiv.classList.add('model-class');403 modelClassDiv.classList.add('model-class');
394 modelClassDiv.textContent = `Class: ${model.model_class || 'N/A'}`;404 modelClassDiv.textContent = t`Class` + `: ${model.model_class || 'N/A'}`;
395405
396 const contextLengthDiv = document.createElement('div');406 const contextLengthDiv = document.createElement('div');
397 contextLengthDiv.classList.add('model-context-length');407 contextLengthDiv.classList.add('model-context-length');
398 contextLengthDiv.textContent = `Context Length: ${model.context_length}`;408 contextLengthDiv.textContent = t`Context Length` + `: ${model.context_length}`;
399409
400 const dateAddedDiv = document.createElement('div');410 const dateAddedDiv = document.createElement('div');
401 dateAddedDiv.classList.add('model-date-added');411 dateAddedDiv.classList.add('model-date-added');
402 dateAddedDiv.textContent = `Added On: ${new Date(model.created * 1000).toLocaleDateString()}`;412 dateAddedDiv.textContent = t`Added On` + `: ${new Date(model.created * 1000).toLocaleDateString()}`;
403413
404 detailsContainer.appendChild(modelClassDiv);414 detailsContainer.appendChild(modelClassDiv);
405 detailsContainer.appendChild(contextLengthDiv);415 detailsContainer.appendChild(contextLengthDiv);
@@ -423,6 +433,7 @@ export async function loadFeatherlessModels(data) {
423433
424 // Update the current page value whenever the page changes434 // Update the current page value whenever the page changes
425 featherlessCurrentPage = pagination.pageNumber;435 featherlessCurrentPage = pagination.pageNumber;
436 localizePagination(paginationContainer);
426 },437 },
427 afterSizeSelectorChange: function (e) {438 afterSizeSelectorChange: function (e) {
428 const newPerPage = e.target.value;439 const newPerPage = e.target.value;
public/scripts/utils.js+30 -4
@@ -14,13 +14,39 @@ import { Popup, POPUP_RESULT, POPUP_TYPE } from './popup.js';
14import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js';14import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js';
15import { getTagsList } from './tags.js';15import { getTagsList } from './tags.js';
16import { groups, selected_group } from './group-chats.js';16import { groups, selected_group } from './group-chats.js';
17import { getCurrentLocale, t } from './i18n.js';17import { getCurrentLocale, t, translate } from './i18n.js';
1818
19/**19/**
20 * Pagination status string template.20 * Function returning pagination status string template.
21 * @type {string}21 * @type {function}
22 */22 */
23export const PAGINATION_TEMPLATE = '<%= rangeStart %>-<%= rangeEnd %> of <%= totalNumber %>';23export const PAGINATION_TEMPLATE = function() {
24 let translated_of;
25 try {
26 translated_of = translate('pagination_of');
27 if (translated_of == 'pagination_of') {
28 translated_of = 'of';
29 }
30 } catch (e) {
31 translated_of = 'of';
32 }
33 return `<%= rangeStart %>-<%= rangeEnd %> ${translated_of} <%= totalNumber %>`
34};
35
36export const localizePagination = function(container) {
37 let options = container.find('option');
38 for (let option of options) {
39 option = $(option);
40 try {
41 option.text(option.text().replace('/ page', translate('/ page')));
42 } catch (e) {
43 console.error(e);
44 break;
45 }
46 }
47 container.find("[title='Next page']").attr('title', translate('Next page'));
48 container.find("[title='Previous page']").attr('title', translate('Previous page'));
49}
2450
25/**51/**
26 * Navigation options for pagination.52 * Navigation options for pagination.
public/scripts/world-info.js+2 -2
@@ -2658,7 +2658,7 @@ export async function getWorldEntry(name, data, entry) {
2658 if (!isMobile()) {2658 if (!isMobile()) {
2659 $(characterFilter).select2({2659 $(characterFilter).select2({
2660 width: '100%',2660 width: '100%',
2661 placeholder: 'Tie this entry to specific characters or characters with specific tags',2661 placeholder: t`Tie this entry to specific characters or characters with specific tags`,
2662 allowClear: true,2662 allowClear: true,
2663 closeOnSelect: false,2663 closeOnSelect: false,
2664 });2664 });
@@ -3258,7 +3258,7 @@ export async function getWorldEntry(name, data, entry) {
32583258
3259 // Create wrapper div3259 // Create wrapper div
3260 const wrapper = document.createElement('div');3260 const wrapper = document.createElement('div');
3261 wrapper.textContent = t`Move "${sourceName}" to:`;3261 wrapper.textContent = t`Move '${sourceName}' to:`;
32623262
3263 // Create container and append elements3263 // Create container and append elements
3264 const container = document.createElement('div');3264 const container = document.createElement('div');