Work on translation

1d5cf8d25c738801b8a922df4a4e290122719733

Yokayo <nonameutubecomm@gmail.com>

8 files changed, +188 -77Ignore whitespace
public/index.html+9 -9
@@ -2278,7 +2278,7 @@
2278 <h4 data-i18n="Model Providers">Model Providers</h4>2278 <h4 data-i18n="Model Providers">Model Providers</h4>
2279 <select id="openrouter_providers_text" class="openrouter_providers" multiple>2279 <select id="openrouter_providers_text" class="openrouter_providers" multiple>
2280 </select>2280 </select>
2281 <label class="checkbox_label" for="openrouter_allow_fallbacks_textgenerationwebui" title="Automatically chooses an alternative provider if chosen providers can't serve your request.">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 <input id="openrouter_allow_fallbacks_textgenerationwebui" type="checkbox" />2282 <input id="openrouter_allow_fallbacks_textgenerationwebui" type="checkbox" />
2283 <span data-i18n="Allow fallback providers">Allow fallback providers</span>2283 <span data-i18n="Allow fallback providers">Allow fallback providers</span>
2284 </label>2284 </label>
@@ -2902,7 +2902,7 @@
2902 <div>2902 <div>
2903 <h4 data-i18n="Window AI Model">Window AI Model</h4>2903 <h4 data-i18n="Window AI Model">Window AI Model</h4>
2904 <select id="model_windowai_select">2904 <select id="model_windowai_select">
2905 <option value="">Use extension settings</option>2905 <option data-i18n="Use extension settings" value="">Use extension settings</option>
2906 <option value="openai/gpt-3.5-turbo">openai/gpt-3.5-turbo</option>2906 <option value="openai/gpt-3.5-turbo">openai/gpt-3.5-turbo</option>
2907 <option value="openai/gpt-3.5-turbo-16k">openai/gpt-3.5-turbo-16k</option>2907 <option value="openai/gpt-3.5-turbo-16k">openai/gpt-3.5-turbo-16k</option>
2908 <option value="openai/gpt-4">openai/gpt-4</option>2908 <option value="openai/gpt-4">openai/gpt-4</option>
@@ -2977,14 +2977,14 @@
2977 <h4 data-i18n="Model Providers">Model Providers</h4>2977 <h4 data-i18n="Model Providers">Model Providers</h4>
2978 <select id="openrouter_providers_chat" class="openrouter_providers" multiple>2978 <select id="openrouter_providers_chat" class="openrouter_providers" multiple>
2979 </select>2979 </select>
2980 <label class="checkbox_label marginTopBot5" for="openrouter_allow_fallbacks" title="Automatically chooses an alternative provider if chosen providers can't serve your request.">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 <input id="openrouter_allow_fallbacks" type="checkbox" />2981 <input id="openrouter_allow_fallbacks" type="checkbox" />
2982 <span data-i18n="Allow fallback providers">Allow fallback providers</span>2982 <span data-i18n="Allow fallback providers">Allow fallback providers</span>
2983 </label>2983 </label>
2984 </div>2984 </div>
2985 <small class="marginTopBot5">2985 <small class="marginTopBot5">
2986 <i class="fa-solid fa-lightbulb"></i>2986 <i class="fa-solid fa-lightbulb"></i>
2987 <span>To use instruct formatting, switch to OpenRouter under Text Completion API.</span>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 </small>2988 </small>
2989 </form>2989 </form>
2990 <form id="scale_form" data-source="scale" action="javascript:void(null);" method="post" enctype="multipart/form-data">2990 <form id="scale_form" data-source="scale" action="javascript:void(null);" method="post" enctype="multipart/form-data">
@@ -4601,7 +4601,7 @@
4601 </div>4601 </div>
4602 </div>4602 </div>
4603 <div nane="AutoContiueBlock" class="inline-drawer wide100p flexFlowColumn">4603 <div nane="AutoContiueBlock" class="inline-drawer wide100p flexFlowColumn">
4604 <div class="inline-drawer-toggle inline-drawer-header userSettingsInnerExpandable" title="Automatically 'continue' a response if the model stopped before reaching a certain amount of tokens.">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 <b><span data-i18n="Auto-Continue">Auto-Continue</span></b>4605 <b><span data-i18n="Auto-Continue">Auto-Continue</span></b>
4606 <div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>4606 <div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
4607 </div>4607 </div>
@@ -5734,7 +5734,7 @@
5734 <span class="drag-handle">&#9776;</span>5734 <span class="drag-handle">&#9776;</span>
5735 <div class="gap5px world_entry_thin_controls wide100p alignitemscenter">5735 <div class="gap5px world_entry_thin_controls wide100p alignitemscenter">
5736 <div class="inline-drawer-toggle fa-fw fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>5736 <div class="inline-drawer-toggle fa-fw fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
5737 <div class="fa-solid fa-toggle-on killSwitch" name="entryKillSwitch" title="Toggle entry's active state."></div>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 <div class="flex-container alignitemscenter wide100p">5738 <div class="flex-container alignitemscenter wide100p">
5739 <div class="WIEntryTitleAndStatus flex-container flex1 alignitemscenter">5739 <div class="WIEntryTitleAndStatus flex-container flex1 alignitemscenter">
5740 <div class="flex-container flex1">5740 <div class="flex-container flex1">
@@ -5967,7 +5967,7 @@
5967 </small>5967 </small>
5968 </div>5968 </div>
5969 <div class="range-block-range">5969 <div class="range-block-range">
5970 <input class="text_pole margin0" name="sticky" type="number" placeholder="Non-sticky" min="0" max="999999">5970 <input class="text_pole margin0" name="sticky" type="number" data-i18n="[placeholder]No-sticky" placeholder="Non-sticky" min="0" max="999999">
5971 </div>5971 </div>
5972 </div>5972 </div>
5973 <div class="flex2 flex-container flexFlowColumn flexNoGap" data-i18n="[title]Entries with a cooldown can't be activated N messages after being triggered." title="Entries with a cooldown can't be activated N messages after being triggered.">5973 <div class="flex2 flex-container flexFlowColumn flexNoGap" data-i18n="[title]Entries with a cooldown can't be activated N messages after being triggered." title="Entries with a cooldown can't be activated N messages after being triggered.">
@@ -5980,7 +5980,7 @@
5980 </small>5980 </small>
5981 </div>5981 </div>
5982 <div class="range-block-range">5982 <div class="range-block-range">
5983 <input class="text_pole margin0" name="cooldown" type="number" placeholder="No cooldown" min="0" max="999999">5983 <input class="text_pole margin0" name="cooldown" type="number" data-i18n="[placeholder]No cooldown" placeholder="No cooldown" min="0" max="999999">
5984 </div>5984 </div>
5985 </div>5985 </div>
5986 <div class="flex2 flex-container flexFlowColumn flexNoGap" data-i18n="[title]Entries with a delay can't be activated until there are N messages present in the chat." title="Entries with a delay can't be activated until there are N messages present in the chat.">5986 <div class="flex2 flex-container flexFlowColumn flexNoGap" data-i18n="[title]Entries with a delay can't be activated until there are N messages present in the chat." title="Entries with a delay can't be activated until there are N messages present in the chat.">
@@ -5993,7 +5993,7 @@
5993 </small>5993 </small>
5994 </div>5994 </div>
5995 <div class="range-block-range">5995 <div class="range-block-range">
5996 <input class="text_pole margin0" name="delay" type="number" placeholder="No delay" min="0" max="999999">5996 <input class="text_pole margin0" name="delay" type="number" data-i18n="[placeholder]No delay" placeholder="No delay" min="0" max="999999">
5997 </div>5997 </div>
5998 </div>5998 </div>
5999 </div>5999 </div>
public/locales/ru-ru.json+123 -14
@@ -294,7 +294,7 @@
294 "Avoid sending sensitive information to the Horde.": "Избегайте отправки личной информации Horde",294 "Avoid sending sensitive information to the Horde.": "Избегайте отправки личной информации Horde",
295 "Review the Privacy statement": "Ознакомиться с заявлением о конфиденциальности",295 "Review the Privacy statement": "Ознакомиться с заявлением о конфиденциальности",
296 "Trusted workers only": "Только доверенные рабочие машины",296 "Trusted workers only": "Только доверенные рабочие машины",
297 "For privacy reasons, your API key will be hidden after you reload the page.": "По причинам безопасности ваш API-ключ будет скрыт после перезагрузки страницы.",297 "For privacy reasons, your API key will be hidden after you reload the page.": "Из соображений безопасности ваш API-ключ будет скрыт после перезагрузки страницы.",
298 "-- Horde models not loaded --": "--Модель Horde не загружена--",298 "-- Horde models not loaded --": "--Модель Horde не загружена--",
299 "Example: http://127.0.0.1:5000/api ": "Пример: http://127.0.0.1:5000/api",299 "Example: http://127.0.0.1:5000/api ": "Пример: http://127.0.0.1:5000/api",
300 "No connection...": "Нет соединения...",300 "No connection...": "Нет соединения...",
@@ -331,7 +331,6 @@
331 "UID ↘": "UID ↘",331 "UID ↘": "UID ↘",
332 "Trigger% ↗": "Триггер% ↗",332 "Trigger% ↗": "Триггер% ↗",
333 "Trigger% ↘": "Триггер% ↘",333 "Trigger% ↘": "Триггер% ↘",
334 "Order:": "Порядок:",
335 "Depth:": "Глубина:",334 "Depth:": "Глубина:",
336 "Character Lore First": "Сначала лор персонажа",335 "Character Lore First": "Сначала лор персонажа",
337 "Global Lore First": "Сначала глобальный лор",336 "Global Lore First": "Сначала глобальный лор",
@@ -347,7 +346,7 @@
347 "After Char Defs": "↓Перс.",346 "After Char Defs": "↓Перс.",
348 "Before AN": "↑АЗ",347 "Before AN": "↑АЗ",
349 "After AN": "↓АЗ",348 "After AN": "↓АЗ",
350 "Order": "Порядок:",349 "Order": "Очерёдность:",
351 "Update a theme file": "Обновить файл темы",350 "Update a theme file": "Обновить файл темы",
352 "Save as a new theme": "Сохранить как новую тему",351 "Save as a new theme": "Сохранить как новую тему",
353 "Minimum number of blacklisted words detected to trigger an auto-swipe": "Минимальное количество обнаруженных запрещённых слов, при котором срабатывает авто-свайп.",352 "Minimum number of blacklisted words detected to trigger an auto-swipe": "Минимальное количество обнаруженных запрещённых слов, при котором срабатывает авто-свайп.",
@@ -497,7 +496,7 @@
497 "What this keyword should mean to the AI, sent verbatim": "Что это ключевое слово должно означать для ИИ, отправляется дословно",496 "What this keyword should mean to the AI, sent verbatim": "Что это ключевое слово должно означать для ИИ, отправляется дословно",
498 "Filter to Character(s)": "Фильтр по персонажу(ам)",497 "Filter to Character(s)": "Фильтр по персонажу(ам)",
499 "Character Exclusion": "Исключить персонажей",498 "Character Exclusion": "Исключить персонажей",
500 "Inclusion Group": "Включить персонажей",499 "Inclusion Group": "Группа записей",
501 "Only one entry with the same label will be activated": "Будет активна только одна запись с одинаковой меткой",500 "Only one entry with the same label will be activated": "Будет активна только одна запись с одинаковой меткой",
502 "-- Characters not found --": "-- Персонажей не найдено --",501 "-- Characters not found --": "-- Персонажей не найдено --",
503 "(This will be the first message from the character that starts every chat)": "(Это будет первое сообщение от персонажа, когда вы начинаете новый чат)",502 "(This will be the first message from the character that starts every chat)": "(Это будет первое сообщение от персонажа, когда вы начинаете новый чат)",
@@ -541,7 +540,7 @@
541 "NOT ANY": "НЕ ЛЮБОЙ",540 "NOT ANY": "НЕ ЛЮБОЙ",
542 "Optional Filter": "Дополнительный фильтр",541 "Optional Filter": "Дополнительный фильтр",
543 "New Entry": "Новая запись",542 "New Entry": "Новая запись",
544 "Fill empty Memo/Titles with Keywords": "Заполните пустые Заметки/Названия ключевыми словами",543 "Fill empty Memo/Titles with Keywords": "Заполнить пустые названия ключевыми словами",
545 "AI Response Formatting": "Формат ответа ИИ",544 "AI Response Formatting": "Формат ответа ИИ",
546 "Change Background Image": "Изменить фон",545 "Change Background Image": "Изменить фон",
547 "Extensions": "Расширения",546 "Extensions": "Расширения",
@@ -628,7 +627,7 @@
628 "UI Theme": "Тема UI",627 "UI Theme": "Тема UI",
629 "This message is invisible for the AI": "Это сообщение невидимо для ИИ",628 "This message is invisible for the AI": "Это сообщение невидимо для ИИ",
630 "Sampler Priority": "Приоритет сэмплеров",629 "Sampler Priority": "Приоритет сэмплеров",
631 "Ooba only. Determines the order of samplers.": "Только Ooba. Определяет порядок сэмплеров.",630 "Ooba only. Determines the order of samplers.": "Только oobabooga. Определяет порядок сэмплеров.",
632 "Load default order": "Загрузить стандартный порядок",631 "Load default order": "Загрузить стандартный порядок",
633 "Max Tokens Second": "Макс. кол-во токенов в секунду",632 "Max Tokens Second": "Макс. кол-во токенов в секунду",
634 "CFG": "CFG",633 "CFG": "CFG",
@@ -1342,7 +1341,7 @@
1342 "Chat Lorebook": "Chat Lorebook for",1341 "Chat Lorebook": "Chat Lorebook for",
1343 "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.",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 "Use tag as folder": "Tag as folder",1343 "Use tag as folder": "Tag as folder",
1345 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "Статус записи:\n 🔵 Постоянная\n 🟢 Обычная\n 🔗 Векторизованная\n ❌ Отключена",1344 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized": "Статус записи:\n 🔵 Постоянная\n 🟢 Обычная\n 🔗 Векторизованная",
1346 "WI_Entry_Status_Constant": "Постоянная",1345 "WI_Entry_Status_Constant": "Постоянная",
1347 "WI_Entry_Status_Normal": "Обычная",1346 "WI_Entry_Status_Normal": "Обычная",
1348 "WI_Entry_Status_Vectorized": "Векторизованная",1347 "WI_Entry_Status_Vectorized": "Векторизованная",
@@ -1366,13 +1365,7 @@
1366 "Can be used to automatically activate Quick Replies": "Can be used to automatically activate Quick Replies",1365 "Can be used to automatically activate Quick Replies": "Can be used to automatically activate Quick Replies",
1367 "Automation ID": "Automation ID",1366 "Automation ID": "Automation ID",
1368 "( None )": "( None )",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 "Add Memo": "Add Memo",1368 "Add Memo": "Add Memo",
1375 "close": "close",
1376 "reset": "reset",1369 "reset": "reset",
1377 "save": "save",1370 "save": "save",
1378 "Open checkpoint chat": "Открыть чат из чекпоинта",1371 "Open checkpoint chat": "Открыть чат из чекпоинта",
@@ -2035,5 +2028,121 @@
2035 "tags_sorting_desc": "Автоматически сортировать теги по алфавиту после создания или переименования одного из них.\nПри выключении новые теги будут просто добавляться в конец.\n\nЕсли вручную перетащить один из тегов на другое место, автосортировка отключится.",2028 "tags_sorting_desc": "Автоматически сортировать теги по алфавиту после создания или переименования одного из них.\nПри выключении новые теги будут просто добавляться в конец.\n\nЕсли вручную перетащить один из тегов на другое место, автосортировка отключится.",
2036 "Imported tags:": "Импортируемые теги:",2029 "Imported tags:": "Импортируемые теги:",
2037 "Importing Tags": "Импорт тегов",2030 "Importing Tags": "Импорт тегов",
2038 "Couldn't import tags:": "Не удалось импортировать теги:"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 "No-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}
public/scripts/extensions.js+11 -11
@@ -682,9 +682,9 @@ function getExtensionData(extension) {
682 * @return {string} - The HTML string for the module information.682 * @return {string} - The HTML string for the module information.
683 */683 */
684function getModuleInformation() {684function getModuleInformation() {
685 let moduleInfo = modules.length ? `<p>${DOMPurify.sanitize(modules.join(', '))}</p>` : '<p class="failure">Not connected to the API!</p>';685 let moduleInfo = modules.length ? `<p>${DOMPurify.sanitize(modules.join(', '))}</p>` : '<p class="failure">' + t`Not connected to the API!` + '</p>';
686 return `686 return `
687 <h3>Modules provided by your Extras API:</h3>687 <h3>` + t`Modules provided by your Extras API:` + `</h3>
688 ${moduleInfo}688 ${moduleInfo}
689 `;689 `;
690}690}
@@ -703,11 +703,11 @@ async function showExtensionsDetails() {
703 initialScrollTop = oldPopup.content.scrollTop;703 initialScrollTop = oldPopup.content.scrollTop;
704 await oldPopup.completeCancelled();704 await oldPopup.completeCancelled();
705 }705 }
706 const htmlDefault = $('<div class="marginBot10"><h3 class="textAlignCenter">Built-in Extensions:</h3></div>');706 const htmlDefault = $('<div class="marginBot10"><h3 class="textAlignCenter">' + t`Built-in Extensions:` + '</h3></div>');
707 const htmlExternal = $('<div class="marginBot10"><h3 class="textAlignCenter">Installed Extensions:</h3></div>');707 const htmlExternal = $('<div class="marginBot10"><h3 class="textAlignCenter">' + t`Installed Extensions:` + '</h3></div>');
708 const htmlLoading = $(`<div class="flex-container alignItemsCenter justifyCenter marginTop10 marginBot5">708 const htmlLoading = $(`<div class="flex-container alignItemsCenter justifyCenter marginTop10 marginBot5">
709 <i class="fa-solid fa-spinner fa-spin"></i>709 <i class="fa-solid fa-spinner fa-spin"></i>
710 <span>Loading third-party extensions... Please wait...</span>710 <span>` + t`Loading third-party extensions... Please wait...` + `</span>
711 </div>`);711 </div>`);
712712
713 htmlExternal.append(htmlLoading);713 htmlExternal.append(htmlLoading);
@@ -728,7 +728,7 @@ async function showExtensionsDetails() {
728728
729 /** @type {import('./popup.js').CustomPopupButton} */729 /** @type {import('./popup.js').CustomPopupButton} */
730 const updateAllButton = {730 const updateAllButton = {
731 text: 'Update all',731 text: t`Update all`,
732 appendAtEnd: true,732 appendAtEnd: true,
733 action: async () => {733 action: async () => {
734 requiresReload = true;734 requiresReload = true;
@@ -740,7 +740,7 @@ async function showExtensionsDetails() {
740 let waitingForSave = false;740 let waitingForSave = false;
741741
742 const popup = new Popup(html, POPUP_TYPE.TEXT, '', {742 const popup = new Popup(html, POPUP_TYPE.TEXT, '', {
743 okButton: 'Close',743 okButton: t`Close`,
744 wide: true,744 wide: true,
745 large: true,745 large: true,
746 customButtons: [updateAllButton],746 customButtons: [updateAllButton],
@@ -833,7 +833,7 @@ async function updateExtension(extensionName, quiet) {
833 toastr.success('Extension is already up to date');833 toastr.success('Extension is already up to date');
834 }834 }
835 } else {835 } else {
836 toastr.success(`Extension ${extensionName} updated to ${data.shortCommitHash}`, 'Reload the page to apply updates');836 toastr.success(t`Extension ${extensionName} updated to ${data.shortCommitHash}`, t`Reload the page to apply updates`);
837 }837 }
838 } catch (error) {838 } catch (error) {
839 console.error('Error:', error);839 console.error('Error:', error);
@@ -1001,7 +1001,7 @@ export async function installExtension(url, global) {
1001 }1001 }
10021002
1003 const response = await request.json();1003 const response = await request.json();
1004 toastr.success(`Extension "${response.display_name}" by ${response.author} (version ${response.version}) has been installed successfully!`, 'Extension installation successful');1004 toastr.success(t`Extension '${response.display_name}' by ${response.author} (version ${response.version}) has been installed successfully!`, t`Extension installation successful`);
1005 console.debug(`Extension "${response.display_name}" has been installed successfully at ${response.extensionPath}`);1005 console.debug(`Extension "${response.display_name}" has been installed successfully at ${response.extensionPath}`);
1006 await loadExtensionSettings({}, false, false);1006 await loadExtensionSettings({}, false, false);
1007 await eventSource.emit(event_types.EXTENSION_SETTINGS_LOADED);1007 await eventSource.emit(event_types.EXTENSION_SETTINGS_LOADED);
@@ -1175,7 +1175,7 @@ async function checkForExtensionUpdates(force) {
1175 await Promise.allSettled(promises);1175 await Promise.allSettled(promises);
11761176
1177 if (updatesAvailable.length > 0) {1177 if (updatesAvailable.length > 0) {
1178 toastr.info(`${updatesAvailable.map(x => `• ${x}`).join('\n')}`, 'Extension updates available');1178 toastr.info(`${updatesAvailable.map(x => `• ${x}`).join('\n')}`, t`Extension updates available`);
1179 }1179 }
1180}1180}
11811181
@@ -1189,7 +1189,7 @@ async function autoUpdateExtensions(forceAll) {
1189 return;1189 return;
1190 }1190 }
11911191
1192 const banner = toastr.info('Auto-updating extensions. This may take several minutes.', 'Please wait...', { timeOut: 10000, extendedTimeOut: 10000 });1192 const banner = toastr.info(t`Auto-updating extensions. This may take several minutes.`, t`Please wait...`, { timeOut: 10000, extendedTimeOut: 10000 });
1193 const isCurrentUserAdmin = isAdmin();1193 const isCurrentUserAdmin = isAdmin();
1194 const promises = [];1194 const promises = [];
1195 for (const [id, manifest] of Object.entries(manifests)) {1195 for (const [id, manifest] of Object.entries(manifests)) {
public/scripts/extensions/connection-manager/edit.html+2 -2
@@ -1,10 +1,10 @@
1<div>1<div>
2 <h3>Included settings:</h3>2 <h3 data-i18n="Included settings:">Included settings:</h3>
3 <div class="justifyLeft flex-container flexFlowColumn flexNoGap">3 <div class="justifyLeft flex-container flexFlowColumn flexNoGap">
4 {{#each settings}}4 {{#each settings}}
5 <label class="checkbox_label">5 <label class="checkbox_label">
6 <input type="checkbox" value="{{@key}}" name="exclude"{{#if this}} checked{{/if}}>6 <input type="checkbox" value="{{@key}}" name="exclude"{{#if this}} checked{{/if}}>
7 <span>{{@key}}</span>7 <span data-i18n="{{@key}}">{{@key}}</span>
8 </label>8 </label>
9 {{/each}}9 {{/each}}
10 </div>10 </div>
public/scripts/extensions/connection-manager/profile.html+2 -2
@@ -12,8 +12,8 @@
12 </div>12 </div>
13 <div class="marginTop5">13 <div class="marginTop5">
14 <small>14 <small>
15 <b>Hint:</b>15 <b data-i18n="Hint:">Hint:</b>
16 <i>Click on the setting name to omit it from the profile.</i>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 </small>17 </small>
18 </div>18 </div>
19 <h3 data-i18n="Enter a name:">19 <h3 data-i18n="Enter a name:">
public/scripts/openai.js+2 -2
@@ -5491,8 +5491,8 @@ export function initOpenAI() {
54915491
5492 if (!isMobile()) {5492 if (!isMobile()) {
5493 $('#model_openrouter_select').select2({5493 $('#model_openrouter_select').select2({
5494 placeholder: 'Select a model',5494 placeholder: t`Select a model`,
5495 searchInputPlaceholder: 'Search models...',5495 searchInputPlaceholder: t`Search models...`,
5496 searchInputCssClass: 'text_pole',5496 searchInputCssClass: 'text_pole',
5497 width: '100%',5497 width: '100%',
5498 templateResult: getOpenRouterModelTemplate,5498 templateResult: getOpenRouterModelTemplate,
public/scripts/textgen-models.js+21 -20
@@ -5,6 +5,7 @@ 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';
89
9let mancerModels = [];10let mancerModels = [];
10let togetherModels = [];11let togetherModels = [];
@@ -936,71 +937,71 @@ export function initTextGenModels() {
936937
937 if (!isMobile()) {938 if (!isMobile()) {
938 $('#mancer_model').select2({939 $('#mancer_model').select2({
939 placeholder: 'Select a model',940 placeholder: t`Select a model`,
940 searchInputPlaceholder: 'Search models...',941 searchInputPlaceholder: t`Search models...`,
941 searchInputCssClass: 'text_pole',942 searchInputCssClass: 'text_pole',
942 width: '100%',943 width: '100%',
943 templateResult: getMancerModelTemplate,944 templateResult: getMancerModelTemplate,
944 });945 });
945 $('#model_togetherai_select').select2({946 $('#model_togetherai_select').select2({
946 placeholder: 'Select a model',947 placeholder: t`Select a model`,
947 searchInputPlaceholder: 'Search models...',948 searchInputPlaceholder: t`Search models...`,
948 searchInputCssClass: 'text_pole',949 searchInputCssClass: 'text_pole',
949 width: '100%',950 width: '100%',
950 templateResult: getTogetherModelTemplate,951 templateResult: getTogetherModelTemplate,
951 });952 });
952 $('#ollama_model').select2({953 $('#ollama_model').select2({
953 placeholder: 'Select a model',954 placeholder: t`Select a model`,
954 searchInputPlaceholder: 'Search models...',955 searchInputPlaceholder: t`Search models...`,
955 searchInputCssClass: 'text_pole',956 searchInputCssClass: 'text_pole',
956 width: '100%',957 width: '100%',
957 });958 });
958 $('#tabby_model').select2({959 $('#tabby_model').select2({
959 placeholder: '[Currently loaded]',960 placeholder: t`[Currently loaded]`,
960 searchInputPlaceholder: 'Search models...',961 searchInputPlaceholder: t`Search models...`,
961 searchInputCssClass: 'text_pole',962 searchInputCssClass: 'text_pole',
962 width: '100%',963 width: '100%',
963 allowClear: true,964 allowClear: true,
964 });965 });
965 $('#model_infermaticai_select').select2({966 $('#model_infermaticai_select').select2({
966 placeholder: 'Select a model',967 placeholder: t`Select a model`,
967 searchInputPlaceholder: 'Search models...',968 searchInputPlaceholder: t`Search models...`,
968 searchInputCssClass: 'text_pole',969 searchInputCssClass: 'text_pole',
969 width: '100%',970 width: '100%',
970 templateResult: getInfermaticAIModelTemplate,971 templateResult: getInfermaticAIModelTemplate,
971 });972 });
972 $('#model_dreamgen_select').select2({973 $('#model_dreamgen_select').select2({
973 placeholder: 'Select a model',974 placeholder: t`Select a model`,
974 searchInputPlaceholder: 'Search models...',975 searchInputPlaceholder: t`Search models...`,
975 searchInputCssClass: 'text_pole',976 searchInputCssClass: 'text_pole',
976 width: '100%',977 width: '100%',
977 templateResult: getDreamGenModelTemplate,978 templateResult: getDreamGenModelTemplate,
978 });979 });
979 $('#openrouter_model').select2({980 $('#openrouter_model').select2({
980 placeholder: 'Select a model',981 placeholder: t`Select a model`,
981 searchInputPlaceholder: 'Search models...',982 searchInputPlaceholder: t`Search models...`,
982 searchInputCssClass: 'text_pole',983 searchInputCssClass: 'text_pole',
983 width: '100%',984 width: '100%',
984 templateResult: getOpenRouterModelTemplate,985 templateResult: getOpenRouterModelTemplate,
985 });986 });
986 $('#vllm_model').select2({987 $('#vllm_model').select2({
987 placeholder: 'Select a model',988 placeholder: t`Select a model`,
988 searchInputPlaceholder: 'Search models...',989 searchInputPlaceholder: t`Search models...`,
989 searchInputCssClass: 'text_pole',990 searchInputCssClass: 'text_pole',
990 width: '100%',991 width: '100%',
991 templateResult: getVllmModelTemplate,992 templateResult: getVllmModelTemplate,
992 });993 });
993 $('#aphrodite_model').select2({994 $('#aphrodite_model').select2({
994 placeholder: 'Select a model',995 placeholder: t`Select a model`,
995 searchInputPlaceholder: 'Search models...',996 searchInputPlaceholder: t`Search models...`,
996 searchInputCssClass: 'text_pole',997 searchInputCssClass: 'text_pole',
997 width: '100%',998 width: '100%',
998 templateResult: getAphroditeModelTemplate,999 templateResult: getAphroditeModelTemplate,
999 });1000 });
1000 providersSelect.select2({1001 providersSelect.select2({
1001 sorter: data => data.sort((a, b) => a.text.localeCompare(b.text)),1002 sorter: data => data.sort((a, b) => a.text.localeCompare(b.text)),
1002 placeholder: 'Select providers. No selection = all providers.',1003 placeholder: t`Select providers. No selection = all providers.`,
1003 searchInputPlaceholder: 'Search providers...',1004 searchInputPlaceholder: t`Search providers...`,
1004 searchInputCssClass: 'text_pole',1005 searchInputCssClass: 'text_pole',
1005 width: '100%',1006 width: '100%',
1006 closeOnSelect: false,1007 closeOnSelect: false,
public/scripts/world-info.js+18 -17
@@ -20,6 +20,7 @@ import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js';
20import { callGenericPopup, Popup, POPUP_TYPE } from './popup.js';20import { callGenericPopup, Popup, POPUP_TYPE } from './popup.js';
21import { StructuredCloneMap } from './util/StructuredCloneMap.js';21import { StructuredCloneMap } from './util/StructuredCloneMap.js';
22import { renderTemplateAsync } from './templates.js';22import { renderTemplateAsync } from './templates.js';
23import { t } from './i18n.js';
2324
24export const world_info_insertion_strategy = {25export const world_info_insertion_strategy = {
25 evenly: 0,26 evenly: 0,
@@ -909,21 +910,21 @@ function registerWorldInfoSlashCommands() {
909910
910 async function getEntriesFromFile(file) {911 async function getEntriesFromFile(file) {
911 if (!file || !world_names.includes(file)) {912 if (!file || !world_names.includes(file)) {
912 toastr.warning('Valid World Info file name is required');913 toastr.warning(t`Valid World Info file name is required`);
913 return '';914 return '';
914 }915 }
915916
916 const data = await loadWorldInfo(file);917 const data = await loadWorldInfo(file);
917918
918 if (!data || !('entries' in data)) {919 if (!data || !('entries' in data)) {
919 toastr.warning('World Info file has an invalid format');920 toastr.warning(t`World Info file has an invalid format`);
920 return '';921 return '';
921 }922 }
922923
923 const entries = Object.values(data.entries);924 const entries = Object.values(data.entries);
924925
925 if (!entries || entries.length === 0) {926 if (!entries || entries.length === 0) {
926 toastr.warning('World Info file has no entries');927 toastr.warning(t`World Info file has no entries`);
927 return '';928 return '';
928 }929 }
929930
@@ -951,7 +952,7 @@ function registerWorldInfoSlashCommands() {
951 name = String(name ?? '') || context.characters[context.characterId]?.avatar || null;952 name = String(name ?? '') || context.characters[context.characterId]?.avatar || null;
952 const character = findChar({ name });953 const character = findChar({ name });
953 if (!character) {954 if (!character) {
954 toastr.error('Character not found.');955 toastr.error(t`Character not found.`);
955 return '';956 return '';
956 }957 }
957 const books = [];958 const books = [];
@@ -977,7 +978,7 @@ function registerWorldInfoSlashCommands() {
977 const chatId = getCurrentChatId();978 const chatId = getCurrentChatId();
978979
979 if (!chatId) {980 if (!chatId) {
980 toastr.warning('Open a chat to get a name of the chat-bound lorebook');981 toastr.warning(t`Open a chat to get a name of the chat-bound lorebook`);
981 return '';982 return '';
982 }983 }
983984
@@ -4773,7 +4774,7 @@ export async function importEmbeddedWorldInfo(skipPopup = false) {
4773 const bookName = characters[chid]?.data?.character_book?.name || `${characters[chid]?.name}'s Lorebook`;4774 const bookName = characters[chid]?.data?.character_book?.name || `${characters[chid]?.name}'s Lorebook`;
47744775
4775 if (!skipPopup) {4776 if (!skipPopup) {
4776 const confirmation = await Popup.show.confirm(`Are you sure you want to import "${bookName}"?`, world_names.includes(bookName) ? 'It will overwrite the World/Lorebook with the same name.' : '');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 if (!confirmation) {4778 if (!confirmation) {
4778 return;4779 return;
4779 }4780 }
@@ -4785,7 +4786,7 @@ export async function importEmbeddedWorldInfo(skipPopup = false) {
4785 await updateWorldInfoList();4786 await updateWorldInfoList();
4786 $('#character_world').val(bookName).trigger('change');4787 $('#character_world').val(bookName).trigger('change');
47874788
4788 toastr.success(`The world "${bookName}" has been imported and linked to the character successfully.`, 'World/Lorebook imported');4789 toastr.success(t`The world '${bookName}' has been imported and linked to the character successfully.`, t`World/Lorebook imported`);
47894790
4790 const newIndex = world_names.indexOf(bookName);4791 const newIndex = world_names.indexOf(bookName);
4791 if (newIndex >= 0) {4792 if (newIndex >= 0) {
@@ -4813,9 +4814,9 @@ export function onWorldInfoChange(args, text) {
4813 if (selected_world_info.includes(name)) {4814 if (selected_world_info.includes(name)) {
4814 selected_world_info.splice(selected_world_info.indexOf(name), 1);4815 selected_world_info.splice(selected_world_info.indexOf(name), 1);
4815 wiElement.prop('selected', false);4816 wiElement.prop('selected', false);
4816 if (!silent) toastr.success(`Deactivated world: ${name}`);4817 if (!silent) toastr.success(t`Deactivated world: ${name}`);
4817 } else {4818 } else {
4818 if (!silent) toastr.error(`World was not active: ${name}`);4819 if (!silent) toastr.error(t`World was not active: ${name}`);
4819 }4820 }
4820 break;4821 break;
4821 }4822 }
@@ -4823,11 +4824,11 @@ export function onWorldInfoChange(args, text) {
4823 if (selected_world_info.includes(name)) {4824 if (selected_world_info.includes(name)) {
4824 selected_world_info.splice(selected_world_info.indexOf(name), 1);4825 selected_world_info.splice(selected_world_info.indexOf(name), 1);
4825 wiElement.prop('selected', false);4826 wiElement.prop('selected', false);
4826 if (!silent) toastr.success(`Deactivated world: ${name}`);4827 if (!silent) toastr.success(t`Deactivated world: ${name}`);
4827 } else {4828 } else {
4828 selected_world_info.push(name);4829 selected_world_info.push(name);
4829 wiElement.prop('selected', true);4830 wiElement.prop('selected', true);
4830 if (!silent) toastr.success(`Activated world: ${name}`);4831 if (!silent) toastr.success(t`Activated world: ${name}`);
4831 }4832 }
4832 break;4833 break;
4833 }4834 }
@@ -4835,16 +4836,16 @@ export function onWorldInfoChange(args, text) {
4835 default: {4836 default: {
4836 selected_world_info.push(name);4837 selected_world_info.push(name);
4837 wiElement.prop('selected', true);4838 wiElement.prop('selected', true);
4838 if (!silent) toastr.success(`Activated world: ${name}`);4839 if (!silent) toastr.success(t`Activated world: ${name}`);
4839 }4840 }
4840 }4841 }
4841 } else {4842 } else {
4842 if (!silent) toastr.error(`No world found named: ${worldName}`);4843 if (!silent) toastr.error(t`No world found named: ${worldName}`);
4843 }4844 }
4844 });4845 });
4845 $('#world_info').trigger('change');4846 $('#world_info').trigger('change');
4846 } else { // if no args, unset all worlds4847 } else { // if no args, unset all worlds
4847 if (!silent) toastr.success('Deactivated all worlds');4848 if (!silent) toastr.success(t`Deactivated all worlds`);
4848 selected_world_info = [];4849 selected_world_info = [];
4849 $('#world_info').val(null).trigger('change');4850 $('#world_info').val(null).trigger('change');
4850 }4851 }
@@ -4860,7 +4861,7 @@ export function onWorldInfoChange(args, text) {
4860 } else {4861 } else {
4861 const wiElement = getWIElement(existingWorldName);4862 const wiElement = getWIElement(existingWorldName);
4862 wiElement.prop('selected', false);4863 wiElement.prop('selected', false);
4863 toastr.error(`The world with ${existingWorldName} is invalid or corrupted.`);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 }
48934894
4894 if (jsonData === undefined || jsonData === null) {4895 if (jsonData === undefined || jsonData === null) {
4895 toastr.error(`File is not valid: ${file.name}`);4896 toastr.error(t`File is not valid: ${file.name}`);
4896 return;4897 return;
4897 }4898 }
48984899
@@ -5038,7 +5039,7 @@ jQuery(() => {
50385039
5039 $('#world_create_button').on('click', async () => {5040 $('#world_create_button').on('click', async () => {
5040 const tempName = getFreeWorldName();5041 const tempName = getFreeWorldName();
5041 const finalName = await Popup.show.input('Create a new World Info', 'Enter a name for the new file:', tempName);5042 const finalName = await Popup.show.input(t`Create a new World Info`, t`Enter a name for the new file:`, tempName);
50425043
5043 if (finalName) {5044 if (finalName) {
5044 await createNewWorldInfo(finalName, { interactive: true });5045 await createNewWorldInfo(finalName, { interactive: true });