Merge pull request #3053 from Yokayo/staging Update ru-ru translation
Signed| @@ -333,7 +333,7 @@ | |||
| 333 | } | 333 | } |
| 334 | 334 | ||
| 335 | .completion_prompt_manager_popup_entry_form_control:has(#completion_prompt_manager_popup_entry_form_prompt:disabled)>div:first-child::after { | 335 | .completion_prompt_manager_popup_entry_form_control:has(#completion_prompt_manager_popup_entry_form_prompt:disabled)>div:first-child::after { |
| 336 | content: 'The content of this prompt is pulled from elsewhere and cannot be edited here.'; | 336 | content: attr(external_piece_text); |
| 337 | display: block; | 337 | display: block; |
| 338 | width: 100%; | 338 | width: 100%; |
| 339 | font-weight: 600; | 339 | font-weight: 600; |
| @@ -2782,7 +2782,7 @@ | |||
| 2782 | </div> | 2782 | </div> |
| 2783 | </form> | 2783 | </form> |
| 2784 | <form id="claude_form" data-source="claude" action="javascript:void(null);" method="post" enctype="multipart/form-data"> | 2784 | <form id="claude_form" data-source="claude" action="javascript:void(null);" method="post" enctype="multipart/form-data"> |
| 2785 | <h4>Claude API Key</h4> | 2785 | <h4 data-i18n="Claude API Key">Claude API Key</h4> |
| 2786 | <div> | 2786 | <div> |
| 2787 | <span data-i18n="Get your key from">Get your key from </span> <a target="_blank" href="https://console.anthropic.com/account/keys" data-i18n="Anthropic's developer console">Anthropic's developer console</a>. | 2787 | <span data-i18n="Get your key from">Get your key from </span> <a target="_blank" href="https://console.anthropic.com/account/keys" data-i18n="Anthropic's developer console">Anthropic's developer console</a>. |
| 2788 | </div> | 2788 | </div> |
| @@ -6001,7 +6001,7 @@ | |||
| 6001 | </div> | 6001 | </div> |
| 6002 | </div> | 6002 | </div> |
| 6003 | <div class="completion_prompt_manager_popup_entry_form_control"> | 6003 | <div class="completion_prompt_manager_popup_entry_form_control"> |
| 6004 | <div class="flex-container alignItemsCenter"> | 6004 | <div class="flex-container alignItemsCenter" data-i18n="[external_piece_text]The content of this prompt is pulled from elsewhere and cannot be edited here." external_piece_text="The content of this prompt is pulled from elsewhere and cannot be edited here."> |
| 6005 | <div class="flex1"> | 6005 | <div class="flex1"> |
| 6006 | <label for="completion_prompt_manager_popup_entry_form_prompt"> | 6006 | <label for="completion_prompt_manager_popup_entry_form_prompt"> |
| 6007 | <span data-i18n="Prompt">Prompt</span> | 6007 | <span data-i18n="Prompt">Prompt</span> |
| @@ -177,7 +177,7 @@ | |||
| 177 | "(Saved to Context Template)": "(Сохраняется в шаблоне контекста)", | 177 | "(Saved to Context Template)": "(Сохраняется в шаблоне контекста)", |
| 178 | "Tokenizer": "Токенайзер", | 178 | "Tokenizer": "Токенайзер", |
| 179 | "Token Padding": "Кол-во добавочных токенов", | 179 | "Token Padding": "Кол-во добавочных токенов", |
| 180 | "Save preset as": "Сохранить пресет как", | 180 | "Save preset as": "Сохранить пресет как...", |
| 181 | "Always add character's name to prompt": "Всегда добавлять имя персонажа в промпт", | 181 | "Always add character's name to prompt": "Всегда добавлять имя персонажа в промпт", |
| 182 | "Use as Stop Strings": "Использовать в качестве стоп-строк", | 182 | "Use as Stop Strings": "Использовать в качестве стоп-строк", |
| 183 | "Bind to Context": "Привязка к контексту", | 183 | "Bind to Context": "Привязка к контексту", |
| @@ -275,12 +275,12 @@ | |||
| 275 | "Chatty": "Разговорчивый", | 275 | "Chatty": "Разговорчивый", |
| 276 | "Examples of dialogue": "Примеры диалога", | 276 | "Examples of dialogue": "Примеры диалога", |
| 277 | "Save": "Сохранить", | 277 | "Save": "Сохранить", |
| 278 | "Chat History": "История чата", | 278 | "Chat History": "История чатов", |
| 279 | "Content": "Содержание", | 279 | "Content": "Содержание", |
| 280 | "Selective": "Выборочно", | 280 | "Selective": "Выборочно", |
| 281 | "Disable": "Отключено", | 281 | "Disable": "Отключено", |
| 282 | "Back to parent chat": "Вернуться в основной чат", | 282 | "Back to parent chat": "Вернуться в основной чат", |
| 283 | "Convert to group": "Превратить в группу", | 283 | "Convert to group": "Сделать групповым", |
| 284 | "Start new chat": "Начать новый чат", | 284 | "Start new chat": "Начать новый чат", |
| 285 | "Delete messages": "Удалить сообщения", | 285 | "Delete messages": "Удалить сообщения", |
| 286 | "Impersonate": "Перевоплощение", | 286 | "Impersonate": "Перевоплощение", |
| @@ -434,7 +434,7 @@ | |||
| 434 | "Persona Description": "Описание персоны", | 434 | "Persona Description": "Описание персоны", |
| 435 | "Your Persona": "Ваша персона", | 435 | "Your Persona": "Ваша персона", |
| 436 | "Show notifications on switching personas": "Показывать уведомления при смене персоны", | 436 | "Show notifications on switching personas": "Показывать уведомления при смене персоны", |
| 437 | "In Story String / Prompt Manager": "В строке истории / Менеджер промптов", | 437 | "In Story String / Prompt Manager": "В строке истории / Менеджере промптов", |
| 438 | "Top of Author's Note": "Сверху от заметок автора", | 438 | "Top of Author's Note": "Сверху от заметок автора", |
| 439 | "Bottom of Author's Note": "Снизу от заметок автора", | 439 | "Bottom of Author's Note": "Снизу от заметок автора", |
| 440 | "How do I use this?": "Как пользоваться?", | 440 | "How do I use this?": "Как пользоваться?", |
| @@ -1160,7 +1160,8 @@ | |||
| 1160 | "ext_regex_global_scripts_desc": "Распространяются на всех персонажей. Сохраняются как часть настроек.", | 1160 | "ext_regex_global_scripts_desc": "Распространяются на всех персонажей. Сохраняются как часть настроек.", |
| 1161 | "ext_regex_scoped_scripts": "Локальные скрипты", | 1161 | "ext_regex_scoped_scripts": "Локальные скрипты", |
| 1162 | "ext_regex_scoped_scripts_desc": "Распространяются только на этого персонажа. Сохраняются в его карточку.", | 1162 | "ext_regex_scoped_scripts_desc": "Распространяются только на этого персонажа. Сохраняются в его карточку.", |
| 1163 | "ext_regex_allow_scoped": "Разрешить использование локальных скриптов", | 1163 | "ext_regex_allow_scoped": "Включить локальные скрипты", |
| 1164 | "ext_regex_disallow_scoped": "Отключить локальные скрипты", | ||
| 1164 | "ext_regex_move_to_global": "Сделать глобальным", | 1165 | "ext_regex_move_to_global": "Сделать глобальным", |
| 1165 | "openai_logit_bias_no_items": "Правил нет", | 1166 | "openai_logit_bias_no_items": "Правил нет", |
| 1166 | "Enable function calling": "Включить функции", | 1167 | "Enable function calling": "Включить функции", |
| @@ -1903,7 +1904,7 @@ | |||
| 1903 | "No sections selected for export": "Не выбрано ни одной секции для экспорта", | 1904 | "No sections selected for export": "Не выбрано ни одной секции для экспорта", |
| 1904 | "Cannot update GUI preset": "Пресет для GUI обновить невозможно", | 1905 | "Cannot update GUI preset": "Пресет для GUI обновить невозможно", |
| 1905 | "Template updated": "Шаблон сохранён", | 1906 | "Template updated": "Шаблон сохранён", |
| 1906 | "Hint: Use a character/group name to bind preset to a specific chat.": "Совет: введите имя персонажа/группы, чтобы привязать пресет к определённому чату.", | 1907 | "Hint: Use a character/group name to bind preset to a specific chat.": "Совет: введите имя персонажа/группы, чтобы привязать пресет к определённым чатам.", |
| 1907 | "Preset name:": "Название пресета:", | 1908 | "Preset name:": "Название пресета:", |
| 1908 | "Template name:": "Название шаблона:", | 1909 | "Template name:": "Название шаблона:", |
| 1909 | "Preset saved": "Пресет сохранён", | 1910 | "Preset saved": "Пресет сохранён", |
| @@ -1951,5 +1952,88 @@ | |||
| 1951 | "from other websites": "с других сайтов.", | 1952 | "from other websites": "с других сайтов.", |
| 1952 | "Go to the": "Загляните в", | 1953 | "Go to the": "Загляните в", |
| 1953 | "to install additional features.": ", чтобы установить разные дополнительные ресурсы.", | 1954 | "to install additional features.": ", чтобы установить разные дополнительные ресурсы.", |
| 1954 | "or_welcome": "; также доступен" | 1955 | "or_welcome": "; также доступен", |
| 1956 | "Claude API Key": "Ключ от API Claude", | ||
| 1957 | "Block Entropy API Key": "Ключ от API Block Entropy", | ||
| 1958 | "Select a Model": "Выберите модель", | ||
| 1959 | "The content of this prompt is pulled from elsewhere and cannot be edited here.": "Эта часть промпта подтягивается откуда-то извне. Отредактировать её здесь невозможно.", | ||
| 1960 | "save": "сохранить", | ||
| 1961 | "reset": "сбросить", | ||
| 1962 | "close": "закрыть", | ||
| 1963 | "Your preset contains proxy and/or custom endpoint settings.": "Ваш пресет содержит настройки прокси и/или настройки кастомного эндпоинта.", | ||
| 1964 | "Do you want to remove these fields before exporting?": "Желаете ли удалить эти поля перед экспортом?", | ||
| 1965 | "Save": "Сохранить", | ||
| 1966 | "Chat Lorebook": "Лорбук для чата", | ||
| 1967 | "chat_world_template_txt": "Выбранный мир будет привязан к этому чату. Будет добавляться в промпт наряду с глобальным лорбуком и лором персонажа.", | ||
| 1968 | "world_button_title": "Лор персонажа\n\nНажмите, чтобы загрузить\nShift + клик, чтобы открыть диалог привязки мира", | ||
| 1969 | "No auxillary Lorebooks set. Click here to select.": "Вспомогательный лорбук не выбран. Нажмите, чтобы выбрать.", | ||
| 1970 | "ext_regex_user_input_desc": "Отправленные вами сообщения.", | ||
| 1971 | "ext_regex_ai_input_desc": "Полученные от API ответы.", | ||
| 1972 | "ext_regex_slash_desc": "Сообщения, отправленные с помощью команд STscript.", | ||
| 1973 | "ext_regex_wi_desc": "Содержимое лорбуков/информации о мире. Работает только со включенной галочкой \"Только промпт\"!", | ||
| 1974 | "ext_regex_run_on_edit_desc": "Выполнять скрипт при редактировании принадлежащих обозначенной роли (ролям) сообщений.", | ||
| 1975 | "Convert to group chat": "Сделать чат групповым", | ||
| 1976 | "Are you sure you want to convert this chat to a group chat?": "Вы точно хотите сделать этот чат групповым?", | ||
| 1977 | "This cannot be reverted.": "Отменить будет невозможно.", | ||
| 1978 | "Enter a name for this persona:": "Введите имя для этой персоны:", | ||
| 1979 | "Cancel if you're just uploading an avatar.": "Отмените, если просто загружаете аватарку.", | ||
| 1980 | "Set the crop position of the avatar image": "Обозначьте зону, по которой будет обрезана аватарка", | ||
| 1981 | "popup-button-crop": "Обрезать", | ||
| 1982 | "Duplicate persona": "Клонировать персону", | ||
| 1983 | "Are you sure you want to duplicate this persona?": "Вы точно хотите клонировать эту персону?", | ||
| 1984 | "Enter a description for this persona:": "Введите описание для этой персоны:", | ||
| 1985 | "You can always add or change it later.": "Его можно будет добавить или изменить в любое время.", | ||
| 1986 | "You can now pick ${0} as a persona in the Persona Management menu.": "Теперь ${0} может быть выбран(а) в качестве персоны в меню \"Управление персоной\".", | ||
| 1987 | "Persona Created": "Персона создана", | ||
| 1988 | "Overwrite Existing Persona": "Перезаписать существующую персону", | ||
| 1989 | "This character exists as a persona already. Do you want to overwrite it?": "Этот персонаж уже существует в виде персоны. Желаете перезаписать?", | ||
| 1990 | "Persona Description Macros": "Макросы в описании персоны", | ||
| 1991 | "This character has a description that uses <code>{{char}}</code> or <code>{{user}}</code> macros. Do you want to swap them in the persona description?": "В описании персонажа присутствуют макросы <code>{{char}}</code> и/или <code>{{user}}</code>. Желаете поменять их местами?", | ||
| 1992 | "(If empty name is provided, this will unbind the name from this avatar)": "(если оставить пустым, то имя будет отвязано от этой аватарки)", | ||
| 1993 | "To permanently set \"${0}\" as the selected persona, unlock and relock it using the \"Lock\" button. Otherwise, the selection resets upon reloading the chat.": "Чтобы перманентно привязать персону \"${0}\" к этому чату, открепите текущую персону и затем закрепите новую кнопкой \"Закрепить\". Иначе при следующем заходе персона будет сброшена.", | ||
| 1994 | "This chat is locked to a different persona (${0}).": "К этому чату уже привязана другая персона (${0}).", | ||
| 1995 | "User persona is now unlocked for this chat. Click the \"Lock\" again to revert.": "Персона откреплена. Нажмите \"Закрепить\", чтобы снова зафиксировать её в этом чате.", | ||
| 1996 | "Persona unlocked": "Персона откреплена", | ||
| 1997 | "Creating a new persona for currently selected user name and avatar...": "Создаём новую персону для этого имени и аватарки...", | ||
| 1998 | "Persona not set for this avatar": "К этой аватарке не привязано ни одной персоны", | ||
| 1999 | "User persona is locked to ${0} in this chat": "${0} установлен(а) в качестве фиксированной персоны в этом чате", | ||
| 2000 | "You cannot delete the avatar you are currently using": "Невозможно удалить аватарку, которую вы используете прямо сейчас", | ||
| 2001 | "Warning": "Внимание", | ||
| 2002 | "Are you sure you want to delete this avatar?": "Вы точно хотите удалить эту аватарку?", | ||
| 2003 | "All information associated with its linked persona will be lost.": "Вся информация о связанной с ней персоне будет утеряна.", | ||
| 2004 | "The default persona was deleted. You will need to set a new default persona.": "Удалена персона по умолчанию. Вам будет необходимо выбрать новую вместо неё.", | ||
| 2005 | "Default persona deleted": "Удалена персона по умолчанию", | ||
| 2006 | "The locked persona was deleted. You will need to set a new persona for this chat.": "Удалена привязанная к чату персона. Вам будет необходимо выбрать новую фиксированную персону для этого чата.", | ||
| 2007 | "Persona deleted": "Персона удалена", | ||
| 2008 | "You must bind a name to this persona before you can set it as the default.": "Прежде чем установить эту персону в качестве персоны по умолчанию, ей необходимо задать имя.", | ||
| 2009 | "Persona name not set": "У персоны отсутствует имя", | ||
| 2010 | "Are you sure you want to remove the default persona?": "Вы точно хотите снять статус персоны по умолчанию?", | ||
| 2011 | "This persona will no longer be used by default when you open a new chat.": "Эта персона больше не будет автоматически выбираться при старте нового чата", | ||
| 2012 | "Default persona removed": "Персона по умолчанию снята", | ||
| 2013 | "Are you sure you want to set \"${0}\" as the default persona?": "Вы точно хотите установить \"${0}\" в качестве персоны по умолчанию?", | ||
| 2014 | "This name and avatar will be used for all new chats, as well as existing chats where the user persona is not locked.": "Это имя и аватарка будут автоматически выбираться при старте нового чата, а также в любом существующем чате, где нет фиксированной персоны.", | ||
| 2015 | "This persona will be used by default when you open a new chat.": "Эта персона будет выбираться автоматически при старте нового чата.", | ||
| 2016 | "Default persona set to ${0}": "${0} установлен(а) в качестве персоны по умолчанию", | ||
| 2017 | "Invalid file selected": "Загружен невалидный файл", | ||
| 2018 | "Invalid file format": "Некорректный формат файла", | ||
| 2019 | "Personas restored with warnings. Check console for details.": "Персона восстановлена, но были сгенерированы варнинги. Подробности см. в консоли.", | ||
| 2020 | "Personas restored successfully.": "Персона успешно восстановлена.", | ||
| 2021 | "All user-sent messages in this chat will be attributed to ${0}.": "Все ваши сообщения в этом чате будут приписаны ${0}.", | ||
| 2022 | "Forbid Media Override explanation": "Способность текущего персонажа/группы использовать в чате внешние медиа.", | ||
| 2023 | "forbid_media_global_state_forbidden": "(запрещены)", | ||
| 2024 | "forbid_media_global_state_allowed": "(разрешены)", | ||
| 2025 | "Always forbidden": "Всегда запрещены", | ||
| 2026 | "Always allowed": "Всегда разрешены", | ||
| 2027 | "Forbid Media Override subtitle": "Под медиа подразумеваются картинки, видео, аудио. Под внешними - любые, не хостящиеся на локальном сервере.", | ||
| 2028 | "Tag Management": "Управление тегами", | ||
| 2029 | "Save your tags to a file": "Сохранить ваши теги в файл", | ||
| 2030 | "Restore tags from a file": "Восстановить теги из файла", | ||
| 2031 | "Create a new tag": "Создать новый тег", | ||
| 2032 | "Drag handle to reorder. Click name to rename. Click color to change display.": "Перемещайте мышкой, чтобы менять порядок. Нажмите, чтобы переименовать. Нажмите на цвет, чтобы сменить его.", | ||
| 2033 | "Click on the folder icon to use this tag as a folder.": "Нажмите на иконку папки, чтобы использовать тег как папку.", | ||
| 2034 | "Use alphabetical sorting": "Сортировать по алфавиту", | ||
| 2035 | "tags_sorting_desc": "Автоматически сортировать теги по алфавиту после создания или переименования одного из них.\nПри выключении новые теги будут просто добавляться в конец.\n\nЕсли вручную перетащить один из тегов на другое место, автосортировка отключится.", | ||
| 2036 | "Imported tags:": "Импортируемые теги:", | ||
| 2037 | "Importing Tags": "Импорт тегов", | ||
| 2038 | "Couldn't import tags:": "Не удалось импортировать теги:" | ||
| 1955 | } | 2039 | } |
| @@ -7491,7 +7491,7 @@ export function callPopup(text, type, inputValue = '', { okButton, rows, wide, w | |||
| 7491 | } else if (['new_chat', 'confirm'].includes(popup_type)) { | 7491 | } else if (['new_chat', 'confirm'].includes(popup_type)) { |
| 7492 | return okButton ?? 'Yes'; | 7492 | return okButton ?? 'Yes'; |
| 7493 | } else if (['input'].includes(popup_type)) { | 7493 | } else if (['input'].includes(popup_type)) { |
| 7494 | return okButton ?? 'Save'; | 7494 | return okButton ?? t`Save`; |
| 7495 | } | 7495 | } |
| 7496 | return okButton ?? 'Delete'; | 7496 | return okButton ?? 'Delete'; |
| 7497 | } | 7497 | } |
| @@ -7861,7 +7861,7 @@ function openCharacterWorldPopup() { | |||
| 7861 | if (!isMobile()) { | 7861 | if (!isMobile()) { |
| 7862 | $(extraSelect).select2({ | 7862 | $(extraSelect).select2({ |
| 7863 | width: '100%', | 7863 | width: '100%', |
| 7864 | placeholder: 'No auxillary Lorebooks set. Click here to select.', | 7864 | placeholder: t`No auxillary Lorebooks set. Click here to select.`, |
| 7865 | allowClear: true, | 7865 | allowClear: true, |
| 7866 | closeOnSelect: false, | 7866 | closeOnSelect: false, |
| 7867 | }); | 7867 | }); |
| @@ -32,6 +32,7 @@ import { commonEnumProviders } from './slash-commands/SlashCommandCommonEnumsPro | |||
| 32 | import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; | 32 | import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; |
| 33 | import { createTagMapFromList } from './tags.js'; | 33 | import { createTagMapFromList } from './tags.js'; |
| 34 | import { renderTemplateAsync } from './templates.js'; | 34 | import { renderTemplateAsync } from './templates.js'; |
| 35 | import { t } from './i18n.js'; | ||
| 35 | 36 | ||
| 36 | import { | 37 | import { |
| 37 | getUniqueName, | 38 | getUniqueName, |
| @@ -263,7 +264,7 @@ export async function convertSoloToGroupChat() { | |||
| 263 | return; | 264 | return; |
| 264 | } | 265 | } |
| 265 | 266 | ||
| 266 | const confirm = await Popup.show.confirm('Convert to group chat', 'Are you sure you want to convert this chat to a group chat?<br />This cannot be reverted.'); | 267 | const confirm = await Popup.show.confirm(t`Convert to group chat`, t`Are you sure you want to convert this chat to a group chat?` + `<br />` + t`This cannot be reverted.`); |
| 267 | if (!confirm) { | 268 | if (!confirm) { |
| 268 | return; | 269 | return; |
| 269 | } | 270 | } |
| @@ -38,7 +38,7 @@ | |||
| 38 | <strong class="flex1" data-i18n="ext_regex_scoped_scripts">Scoped Scripts</strong> | 38 | <strong class="flex1" data-i18n="ext_regex_scoped_scripts">Scoped Scripts</strong> |
| 39 | <label id="toggle_scoped_regex" class="checkbox flex-container" for="regex_scoped_toggle"> | 39 | <label id="toggle_scoped_regex" class="checkbox flex-container" for="regex_scoped_toggle"> |
| 40 | <input type="checkbox" id="regex_scoped_toggle" class="enable_scoped" /> | 40 | <input type="checkbox" id="regex_scoped_toggle" class="enable_scoped" /> |
| 41 | <span class="regex-toggle-on fa-solid fa-toggle-on fa-lg" title="Disallow using scoped regex"></span> | 41 | <span class="regex-toggle-on fa-solid fa-toggle-on fa-lg" data-i18n="[title]ext_regex_disallow_scoped" title="Disallow using scoped regex"></span> |
| 42 | <span class="regex-toggle-off fa-solid fa-toggle-off fa-lg" data-i18n="[title]ext_regex_allow_scoped" title="Allow using scoped regex"></span> | 42 | <span class="regex-toggle-off fa-solid fa-toggle-off fa-lg" data-i18n="[title]ext_regex_allow_scoped" title="Allow using scoped regex"></span> |
| 43 | </label> | 43 | </label> |
| 44 | </div> | 44 | </div> |
| @@ -70,25 +70,25 @@ | |||
| 70 | <div class="flex-container"> | 70 | <div class="flex-container"> |
| 71 | <div class="flex1 wi-enter-footer-text flex-container flexFlowColumn flexNoGap alignitemsstart"> | 71 | <div class="flex1 wi-enter-footer-text flex-container flexFlowColumn flexNoGap alignitemsstart"> |
| 72 | <small data-i18n="ext_regex_affects">Affects</small> | 72 | <small data-i18n="ext_regex_affects">Affects</small> |
| 73 | <div title="Messages sent by the user."> | 73 | <div data-i18n="[title]ext_regex_user_input_desc" title="Messages sent by the user."> |
| 74 | <label class="checkbox flex-container"> | 74 | <label class="checkbox flex-container"> |
| 75 | <input type="checkbox" name="replace_position" value="1"> | 75 | <input type="checkbox" name="replace_position" value="1"> |
| 76 | <span data-i18n="ext_regex_user_input">User Input</span> | 76 | <span data-i18n="ext_regex_user_input">User Input</span> |
| 77 | </label> | 77 | </label> |
| 78 | </div> | 78 | </div> |
| 79 | <div title="Messages received from the Generation API."> | 79 | <div data-i18n="[title]ext_regex_ai_input_desc" title="Messages received from the Generation API."> |
| 80 | <label class="checkbox flex-container"> | 80 | <label class="checkbox flex-container"> |
| 81 | <input type="checkbox" name="replace_position" value="2"> | 81 | <input type="checkbox" name="replace_position" value="2"> |
| 82 | <span data-i18n="ext_regex_ai_output">AI Output</span> | 82 | <span data-i18n="ext_regex_ai_output">AI Output</span> |
| 83 | </label> | 83 | </label> |
| 84 | </div> | 84 | </div> |
| 85 | <div title="Messages sent using STscript commands."> | 85 | <div data-i18n="[title]ext_regex_slash_desc" title="Messages sent using STscript commands."> |
| 86 | <label class="checkbox flex-container"> | 86 | <label class="checkbox flex-container"> |
| 87 | <input type="checkbox" name="replace_position" value="3"> | 87 | <input type="checkbox" name="replace_position" value="3"> |
| 88 | <span data-i18n="Slash Commands">Slash Commands</span> | 88 | <span data-i18n="Slash Commands">Slash Commands</span> |
| 89 | </label> | 89 | </label> |
| 90 | </div> | 90 | </div> |
| 91 | <div title="Lorebook/World Info entry contents. Requires 'Only Format Prompt' to be checked!"> | 91 | <div data-i18n="[title]ext_regex_wi_desc" title="Lorebook/World Info entry contents. Requires 'Only Format Prompt' to be checked!"> |
| 92 | <label class="checkbox flex-container"> | 92 | <label class="checkbox flex-container"> |
| 93 | <input type="checkbox" name="replace_position" value="5"> | 93 | <input type="checkbox" name="replace_position" value="5"> |
| 94 | <span data-i18n="World Info">World Info</span> | 94 | <span data-i18n="World Info">World Info</span> |
| @@ -117,7 +117,7 @@ | |||
| 117 | <input type="checkbox" name="disabled" /> | 117 | <input type="checkbox" name="disabled" /> |
| 118 | <span data-i18n="Disabled">Disabled</span> | 118 | <span data-i18n="Disabled">Disabled</span> |
| 119 | </label> | 119 | </label> |
| 120 | <label class="checkbox flex-container" title="Run the regex script when the message belonging a to specified role(s) is edited."> | 120 | <label class="checkbox flex-container" data-i18n="[title]ext_regex_run_on_edit_desc" title="Run the regex script when the message belonging a to specified role(s) is edited."> |
| 121 | <input type="checkbox" name="run_on_edit" /> | 121 | <input type="checkbox" name="run_on_edit" /> |
| 122 | <span data-i18n="Run On Edit">Run On Edit</span> | 122 | <span data-i18n="Run On Edit">Run On Edit</span> |
| 123 | </label> | 123 | </label> |
| @@ -9,6 +9,7 @@ import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashComm | |||
| 9 | import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js'; | 9 | import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js'; |
| 10 | import { download, getFileText, getSortableDelay, uuidv4 } from '../../utils.js'; | 10 | import { download, getFileText, getSortableDelay, uuidv4 } from '../../utils.js'; |
| 11 | import { regex_placement, runRegexScript } from './engine.js'; | 11 | import { regex_placement, runRegexScript } from './engine.js'; |
| 12 | import { t } from '../../i18n.js'; | ||
| 12 | 13 | ||
| 13 | /** | 14 | /** |
| 14 | * @typedef {object} RegexScript | 15 | * @typedef {object} RegexScript |
| @@ -275,7 +276,7 @@ async function onRegexEditorOpenClick(existingId, isScoped) { | |||
| 275 | 276 | ||
| 276 | editorHtml.find('input, textarea, select').on('input', updateTestResult); | 277 | editorHtml.find('input, textarea, select').on('input', updateTestResult); |
| 277 | 278 | ||
| 278 | const popupResult = await callPopup(editorHtml, 'confirm', undefined, { okButton: 'Save' }); | 279 | const popupResult = await callPopup(editorHtml, 'confirm', undefined, { okButton: t`Save` }); |
| 279 | if (popupResult) { | 280 | if (popupResult) { |
| 280 | const newRegexScript = { | 281 | const newRegexScript = { |
| 281 | id: existingId ? String(existingId) : uuidv4(), | 282 | id: existingId ? String(existingId) : uuidv4(), |
| @@ -3612,8 +3612,8 @@ async function onExportPresetClick() { | |||
| 3612 | 3612 | ||
| 3613 | const fieldValues = sensitiveFields.filter(field => preset[field]).map(field => `<b>${field}</b>: <code>${preset[field]}</code>`); | 3613 | const fieldValues = sensitiveFields.filter(field => preset[field]).map(field => `<b>${field}</b>: <code>${preset[field]}</code>`); |
| 3614 | const shouldConfirm = fieldValues.length > 0; | 3614 | const shouldConfirm = fieldValues.length > 0; |
| 3615 | const textHeader = 'Your preset contains proxy and/or custom endpoint settings.'; | 3615 | const textHeader = t`Your preset contains proxy and/or custom endpoint settings.`; |
| 3616 | const textMessage = `<div>Do you want to remove these fields before exporting?</div><br>${DOMPurify.sanitize(fieldValues.join('<br>'))}`; | 3616 | const textMessage = `<div>` + t`Do you want to remove these fields before exporting?` + `</div><br>${DOMPurify.sanitize(fieldValues.join('<br>'))}`; |
| 3617 | const cancelButton = { text: 'Cancel', result: POPUP_RESULT.CANCELLED, appendAtEnd: true }; | 3617 | const cancelButton = { text: 'Cancel', result: POPUP_RESULT.CANCELLED, appendAtEnd: true }; |
| 3618 | const popupOptions = { customButtons: [cancelButton] }; | 3618 | const popupOptions = { customButtons: [cancelButton] }; |
| 3619 | const popupResult = await Popup.show.confirm(textHeader, textMessage, popupOptions); | 3619 | const popupResult = await Popup.show.confirm(textHeader, textMessage, popupOptions); |
| @@ -4369,8 +4369,8 @@ async function onOpenrouterModelSortChange() { | |||
| 4369 | 4369 | ||
| 4370 | async function onNewPresetClick() { | 4370 | async function onNewPresetClick() { |
| 4371 | const popupText = ` | 4371 | const popupText = ` |
| 4372 | <h3>Preset name:</h3> | 4372 | <h3>` + t`Preset name:` + `</h3> |
| 4373 | <h4>Hint: Use a character/group name to bind preset to a specific chat.</h4>`; | 4373 | <h4>` + t`Hint: Use a character/group name to bind preset to a specific chat.` + `</h4>`; |
| 4374 | const name = await callPopup(popupText, 'input', oai_settings.preset_settings_openai); | 4374 | const name = await callPopup(popupText, 'input', oai_settings.preset_settings_openai); |
| 4375 | 4375 | ||
| 4376 | if (!name) { | 4376 | if (!name) { |
| @@ -22,6 +22,7 @@ import { debounce_timeout } from './constants.js'; | |||
| 22 | import { FILTER_TYPES, FilterHelper } from './filters.js'; | 22 | import { FILTER_TYPES, FilterHelper } from './filters.js'; |
| 23 | import { selected_group } from './group-chats.js'; | 23 | import { selected_group } from './group-chats.js'; |
| 24 | import { POPUP_RESULT, POPUP_TYPE, Popup } from './popup.js'; | 24 | import { POPUP_RESULT, POPUP_TYPE, Popup } from './popup.js'; |
| 25 | import { t } from './i18n.js'; | ||
| 25 | 26 | ||
| 26 | let savePersonasPage = 0; | 27 | let savePersonasPage = 0; |
| 27 | const GRID_STORAGE_KEY = 'Personas_GridView'; | 28 | const GRID_STORAGE_KEY = 'Personas_GridView'; |
| @@ -276,7 +277,7 @@ async function changeUserAvatar(e) { | |||
| 276 | let url = '/api/avatars/upload'; | 277 | let url = '/api/avatars/upload'; |
| 277 | 278 | ||
| 278 | if (!power_user.never_resize_avatars) { | 279 | if (!power_user.never_resize_avatars) { |
| 279 | const dlg = new Popup('Set the crop position of the avatar image', POPUP_TYPE.CROP, '', { cropImage: dataUrl }); | 280 | const dlg = new Popup(t`Set the crop position of the avatar image`, POPUP_TYPE.CROP, '', { cropImage: dataUrl }); |
| 280 | const result = await dlg.show(); | 281 | const result = await dlg.show(); |
| 281 | 282 | ||
| 282 | if (!result) { | 283 | if (!result) { |
| @@ -331,23 +332,23 @@ async function changeUserAvatar(e) { | |||
| 331 | * @returns {Promise} Promise that resolves when the persona is set | 332 | * @returns {Promise} Promise that resolves when the persona is set |
| 332 | */ | 333 | */ |
| 333 | export async function createPersona(avatarId) { | 334 | export async function createPersona(avatarId) { |
| 334 | const personaName = await Popup.show.input('Enter a name for this persona:', 'Cancel if you\'re just uploading an avatar.', ''); | 335 | const personaName = await Popup.show.input(t`Enter a name for this persona:`, t`Cancel if you're just uploading an avatar.`, ''); |
| 335 | 336 | ||
| 336 | if (!personaName) { | 337 | if (!personaName) { |
| 337 | console.debug('User cancelled creating a persona'); | 338 | console.debug('User cancelled creating a persona'); |
| 338 | return; | 339 | return; |
| 339 | } | 340 | } |
| 340 | 341 | ||
| 341 | const personaDescription = await Popup.show.input('Enter a description for this persona:', 'You can always add or change it later.', '', { rows: 4 }); | 342 | const personaDescription = await Popup.show.input(t`Enter a description for this persona:`, t`You can always add or change it later.`, '', { rows: 4 }); |
| 342 | 343 | ||
| 343 | initPersona(avatarId, personaName, personaDescription); | 344 | initPersona(avatarId, personaName, personaDescription); |
| 344 | if (power_user.persona_show_notifications) { | 345 | if (power_user.persona_show_notifications) { |
| 345 | toastr.success(`You can now pick ${personaName} as a persona in the Persona Management menu.`, 'Persona Created'); | 346 | toastr.success(t`You can now pick ${personaName} as a persona in the Persona Management menu.`, t`Persona Created`); |
| 346 | } | 347 | } |
| 347 | } | 348 | } |
| 348 | 349 | ||
| 349 | async function createDummyPersona() { | 350 | async function createDummyPersona() { |
| 350 | const personaName = await Popup.show.input('Enter a name for this persona:', null); | 351 | const personaName = await Popup.show.input(t`Enter a name for this persona:`, null); |
| 351 | 352 | ||
| 352 | if (!personaName) { | 353 | if (!personaName) { |
| 353 | console.debug('User cancelled creating dummy persona'); | 354 | console.debug('User cancelled creating dummy persona'); |
| @@ -393,7 +394,7 @@ export async function convertCharacterToPersona(characterId = null) { | |||
| 393 | const overwriteName = `${name} (Persona).png`; | 394 | const overwriteName = `${name} (Persona).png`; |
| 394 | 395 | ||
| 395 | if (overwriteName in power_user.personas) { | 396 | if (overwriteName in power_user.personas) { |
| 396 | const confirm = await Popup.show.confirm('Overwrite Existing Persona', 'This character exists as a persona already. Do you want to overwrite it?'); | 397 | const confirm = await Popup.show.confirm(t`Overwrite Existing Persona`, t`This character exists as a persona already. Do you want to overwrite it?`); |
| 397 | if (!confirm) { | 398 | if (!confirm) { |
| 398 | console.log('User cancelled the overwrite of the persona'); | 399 | console.log('User cancelled the overwrite of the persona'); |
| 399 | return; | 400 | return; |
| @@ -401,7 +402,7 @@ export async function convertCharacterToPersona(characterId = null) { | |||
| 401 | } | 402 | } |
| 402 | 403 | ||
| 403 | if (description.includes('{{char}}') || description.includes('{{user}}')) { | 404 | if (description.includes('{{char}}') || description.includes('{{user}}')) { |
| 404 | const confirm = await Popup.show.confirm('Persona Description Macros', 'This character has a description that uses <code>{{char}}</code> or <code>{{user}}</code> macros. Do you want to swap them in the persona description?'); | 405 | const confirm = await Popup.show.confirm(t`Persona Description Macros`, t`This character has a description that uses <code>{{char}}</code> or <code>{{user}}</code> macros. Do you want to swap them in the persona description?`); |
| 405 | if (confirm) { | 406 | if (confirm) { |
| 406 | description = description.replace(/{{char}}/gi, '{{personaChar}}').replace(/{{user}}/gi, '{{personaUser}}'); | 407 | description = description.replace(/{{char}}/gi, '{{personaChar}}').replace(/{{user}}/gi, '{{personaUser}}'); |
| 407 | description = description.replace(/{{personaUser}}/gi, '{{char}}').replace(/{{personaChar}}/gi, '{{user}}'); | 408 | description = description.replace(/{{personaUser}}/gi, '{{char}}').replace(/{{personaChar}}/gi, '{{user}}'); |
| @@ -427,7 +428,7 @@ export async function convertCharacterToPersona(characterId = null) { | |||
| 427 | saveSettingsDebounced(); | 428 | saveSettingsDebounced(); |
| 428 | 429 | ||
| 429 | console.log('Persona for character created'); | 430 | console.log('Persona for character created'); |
| 430 | toastr.success(`You can now select ${name} as a persona in the Persona Management menu.`, 'Persona Created'); | 431 | toastr.success(t`You can now pick ${name} as a persona in the Persona Management menu.`, t`Persona Created`); |
| 431 | 432 | ||
| 432 | // Refresh the persona selector | 433 | // Refresh the persona selector |
| 433 | await getUserAvatars(true, overwriteName); | 434 | await getUserAvatars(true, overwriteName); |
| @@ -509,8 +510,8 @@ async function bindUserNameToPersona(e) { | |||
| 509 | let personaUnbind = false; | 510 | let personaUnbind = false; |
| 510 | const existingPersona = power_user.personas[avatarId]; | 511 | const existingPersona = power_user.personas[avatarId]; |
| 511 | const personaName = await Popup.show.input( | 512 | const personaName = await Popup.show.input( |
| 512 | 'Enter a name for this persona:', | 513 | t`Enter a name for this persona:`, |
| 513 | '(If empty name is provided, this will unbind the name from this avatar)', | 514 | t`(If empty name is provided, this will unbind the name from this avatar)`, |
| 514 | existingPersona || '', | 515 | existingPersona || '', |
| 515 | { onClose: (p) => { personaUnbind = p.value === '' && p.result === POPUP_RESULT.AFFIRMATIVE; } }); | 516 | { onClose: (p) => { personaUnbind = p.value === '' && p.result === POPUP_RESULT.AFFIRMATIVE; } }); |
| 516 | 517 | ||
| @@ -560,8 +561,8 @@ function selectCurrentPersona() { | |||
| 560 | const lockedPersona = chat_metadata['persona']; | 561 | const lockedPersona = chat_metadata['persona']; |
| 561 | if (lockedPersona && lockedPersona !== user_avatar && power_user.persona_show_notifications) { | 562 | if (lockedPersona && lockedPersona !== user_avatar && power_user.persona_show_notifications) { |
| 562 | toastr.info( | 563 | toastr.info( |
| 563 | `To permanently set "${personaName}" as the selected persona, unlock and relock it using the "Lock" button. Otherwise, the selection resets upon reloading the chat.`, | 564 | t`To permanently set "${personaName}" as the selected persona, unlock and relock it using the "Lock" button. Otherwise, the selection resets upon reloading the chat.`, |
| 564 | `This chat is locked to a different persona (${power_user.personas[lockedPersona]}).`, | 565 | t`This chat is locked to a different persona (${power_user.personas[lockedPersona]}).`, |
| 565 | { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true }, | 566 | { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true }, |
| 566 | ); | 567 | ); |
| 567 | } | 568 | } |
| @@ -626,7 +627,7 @@ async function unlockPersona() { | |||
| 626 | delete chat_metadata['persona']; | 627 | delete chat_metadata['persona']; |
| 627 | await saveMetadata(); | 628 | await saveMetadata(); |
| 628 | if (power_user.persona_show_notifications) { | 629 | if (power_user.persona_show_notifications) { |
| 629 | toastr.info('User persona is now unlocked for this chat. Click the "Lock" again to revert.', 'Persona unlocked'); | 630 | toastr.info(t`User persona is now unlocked for this chat. Click the "Lock" again to revert.`, t`Persona unlocked`); |
| 630 | } | 631 | } |
| 631 | updateUserLockIcon(); | 632 | updateUserLockIcon(); |
| 632 | } | 633 | } |
| @@ -640,8 +641,8 @@ async function lockPersona() { | |||
| 640 | console.log(`Creating a new persona ${user_avatar}`); | 641 | console.log(`Creating a new persona ${user_avatar}`); |
| 641 | if (power_user.persona_show_notifications) { | 642 | if (power_user.persona_show_notifications) { |
| 642 | toastr.info( | 643 | toastr.info( |
| 643 | 'Creating a new persona for currently selected user name and avatar...', | 644 | t`Creating a new persona for currently selected user name and avatar...`, |
| 644 | 'Persona not set for this avatar', | 645 | t`Persona not set for this avatar`, |
| 645 | { timeOut: 10000, extendedTimeOut: 20000 }, | 646 | { timeOut: 10000, extendedTimeOut: 20000 }, |
| 646 | ); | 647 | ); |
| 647 | } | 648 | } |
| @@ -659,7 +660,7 @@ async function lockPersona() { | |||
| 659 | saveSettingsDebounced(); | 660 | saveSettingsDebounced(); |
| 660 | console.log(`Locking persona for this chat ${user_avatar}`); | 661 | console.log(`Locking persona for this chat ${user_avatar}`); |
| 661 | if (power_user.persona_show_notifications) { | 662 | if (power_user.persona_show_notifications) { |
| 662 | toastr.success(`User persona is locked to ${name1} in this chat`); | 663 | toastr.success(t`User persona is locked to ${name1} in this chat`); |
| 663 | } | 664 | } |
| 664 | updateUserLockIcon(); | 665 | updateUserLockIcon(); |
| 665 | } | 666 | } |
| @@ -676,11 +677,11 @@ async function deleteUserAvatar(e) { | |||
| 676 | 677 | ||
| 677 | if (avatarId == user_avatar) { | 678 | if (avatarId == user_avatar) { |
| 678 | console.warn(`User tried to delete their current avatar ${avatarId}`); | 679 | console.warn(`User tried to delete their current avatar ${avatarId}`); |
| 679 | toastr.warning('You cannot delete the avatar you are currently using', 'Warning'); | 680 | toastr.warning(t`You cannot delete the avatar you are currently using`, t`Warning`); |
| 680 | return; | 681 | return; |
| 681 | } | 682 | } |
| 682 | 683 | ||
| 683 | const confirm = await Popup.show.confirm('Are you sure you want to delete this avatar?', 'All information associated with its linked persona will be lost.'); | 684 | const confirm = await Popup.show.confirm(t`Are you sure you want to delete this avatar?`, t`All information associated with its linked persona will be lost.`); |
| 684 | 685 | ||
| 685 | if (!confirm) { | 686 | if (!confirm) { |
| 686 | console.debug('User cancelled deleting avatar'); | 687 | console.debug('User cancelled deleting avatar'); |
| @@ -701,12 +702,12 @@ async function deleteUserAvatar(e) { | |||
| 701 | delete power_user.persona_descriptions[avatarId]; | 702 | delete power_user.persona_descriptions[avatarId]; |
| 702 | 703 | ||
| 703 | if (avatarId === power_user.default_persona) { | 704 | if (avatarId === power_user.default_persona) { |
| 704 | toastr.warning('The default persona was deleted. You will need to set a new default persona.', 'Default persona deleted'); | 705 | toastr.warning(t`The default persona was deleted. You will need to set a new default persona.`, t`Default persona deleted`); |
| 705 | power_user.default_persona = null; | 706 | power_user.default_persona = null; |
| 706 | } | 707 | } |
| 707 | 708 | ||
| 708 | if (avatarId === chat_metadata['persona']) { | 709 | if (avatarId === chat_metadata['persona']) { |
| 709 | toastr.warning('The locked persona was deleted. You will need to set a new persona for this chat.', 'Persona deleted'); | 710 | toastr.warning(t`The locked persona was deleted. You will need to set a new persona for this chat.`, t`Persona deleted`); |
| 710 | delete chat_metadata['persona']; | 711 | delete chat_metadata['persona']; |
| 711 | await saveMetadata(); | 712 | await saveMetadata(); |
| 712 | } | 713 | } |
| @@ -807,14 +808,14 @@ async function setDefaultPersona(e) { | |||
| 807 | 808 | ||
| 808 | if (power_user.personas[avatarId] === undefined) { | 809 | if (power_user.personas[avatarId] === undefined) { |
| 809 | console.warn(`No persona name found for avatar ${avatarId}`); | 810 | console.warn(`No persona name found for avatar ${avatarId}`); |
| 810 | toastr.warning('You must bind a name to this persona before you can set it as the default.', 'Persona name not set'); | 811 | toastr.warning(t`You must bind a name to this persona before you can set it as the default.`, t`Persona name not set`); |
| 811 | return; | 812 | return; |
| 812 | } | 813 | } |
| 813 | 814 | ||
| 814 | const personaName = power_user.personas[avatarId]; | 815 | const personaName = power_user.personas[avatarId]; |
| 815 | 816 | ||
| 816 | if (avatarId === currentDefault) { | 817 | if (avatarId === currentDefault) { |
| 817 | const confirm = await Popup.show.confirm('Are you sure you want to remove the default persona?', personaName); | 818 | const confirm = await Popup.show.confirm(t`Are you sure you want to remove the default persona?`, personaName); |
| 818 | 819 | ||
| 819 | if (!confirm) { | 820 | if (!confirm) { |
| 820 | console.debug('User cancelled removing default persona'); | 821 | console.debug('User cancelled removing default persona'); |
| @@ -823,11 +824,11 @@ async function setDefaultPersona(e) { | |||
| 823 | 824 | ||
| 824 | console.log(`Removing default persona ${avatarId}`); | 825 | console.log(`Removing default persona ${avatarId}`); |
| 825 | if (power_user.persona_show_notifications) { | 826 | if (power_user.persona_show_notifications) { |
| 826 | toastr.info('This persona will no longer be used by default when you open a new chat.', 'Default persona removed'); | 827 | toastr.info(t`This persona will no longer be used by default when you open a new chat.`, t`Default persona removed`); |
| 827 | } | 828 | } |
| 828 | delete power_user.default_persona; | 829 | delete power_user.default_persona; |
| 829 | } else { | 830 | } else { |
| 830 | const confirm = await Popup.show.confirm(`Are you sure you want to set "${personaName}" as the default persona?`, 'This name and avatar will be used for all new chats, as well as existing chats where the user persona is not locked.'); | 831 | const confirm = await Popup.show.confirm(t`Are you sure you want to set "${personaName}" as the default persona?`, t`This name and avatar will be used for all new chats, as well as existing chats where the user persona is not locked.`); |
| 831 | 832 | ||
| 832 | if (!confirm) { | 833 | if (!confirm) { |
| 833 | console.debug('User cancelled setting default persona'); | 834 | console.debug('User cancelled setting default persona'); |
| @@ -836,7 +837,7 @@ async function setDefaultPersona(e) { | |||
| 836 | 837 | ||
| 837 | power_user.default_persona = avatarId; | 838 | power_user.default_persona = avatarId; |
| 838 | if (power_user.persona_show_notifications) { | 839 | if (power_user.persona_show_notifications) { |
| 839 | toastr.success('This persona will be used by default when you open a new chat.', `Default persona set to ${personaName}`); | 840 | toastr.success(t`This persona will be used by default when you open a new chat.`, t`Default persona set to ${personaName}`); |
| 840 | } | 841 | } |
| 841 | } | 842 | } |
| 842 | 843 | ||
| @@ -918,13 +919,13 @@ async function onPersonasRestoreInput(e) { | |||
| 918 | const data = await parseJsonFile(file); | 919 | const data = await parseJsonFile(file); |
| 919 | 920 | ||
| 920 | if (!data) { | 921 | if (!data) { |
| 921 | toastr.warning('Invalid file selected', 'Persona Management'); | 922 | toastr.warning(t`Invalid file selected`, t`Persona Management`); |
| 922 | console.debug('Invalid file selected'); | 923 | console.debug('Invalid file selected'); |
| 923 | return; | 924 | return; |
| 924 | } | 925 | } |
| 925 | 926 | ||
| 926 | if (!data.personas || !data.persona_descriptions || typeof data.personas !== 'object' || typeof data.persona_descriptions !== 'object') { | 927 | if (!data.personas || !data.persona_descriptions || typeof data.personas !== 'object' || typeof data.persona_descriptions !== 'object') { |
| 927 | toastr.warning('Invalid file format', 'Persona Management'); | 928 | toastr.warning(t`Invalid file format`, t`Persona Management`); |
| 928 | console.debug('Invalid file selected'); | 929 | console.debug('Invalid file selected'); |
| 929 | return; | 930 | return; |
| 930 | } | 931 | } |
| @@ -972,10 +973,10 @@ async function onPersonasRestoreInput(e) { | |||
| 972 | } | 973 | } |
| 973 | 974 | ||
| 974 | if (warnings.length) { | 975 | if (warnings.length) { |
| 975 | toastr.success('Personas restored with warnings. Check console for details.'); | 976 | toastr.success(t`Personas restored with warnings. Check console for details.`); |
| 976 | console.warn(`PERSONA RESTORE REPORT\n====================\n${warnings.join('\n')}`); | 977 | console.warn(`PERSONA RESTORE REPORT\n====================\n${warnings.join('\n')}`); |
| 977 | } else { | 978 | } else { |
| 978 | toastr.success('Personas restored successfully.'); | 979 | toastr.success(t`Personas restored successfully.`); |
| 979 | } | 980 | } |
| 980 | 981 | ||
| 981 | await getUserAvatars(); | 982 | await getUserAvatars(); |
| @@ -985,7 +986,7 @@ async function onPersonasRestoreInput(e) { | |||
| 985 | } | 986 | } |
| 986 | 987 | ||
| 987 | async function syncUserNameToPersona() { | 988 | async function syncUserNameToPersona() { |
| 988 | const confirmation = await Popup.show.confirm('Are you sure?', `All user-sent messages in this chat will be attributed to ${name1}.`); | 989 | const confirmation = await Popup.show.confirm(t`Are you sure?`, t`All user-sent messages in this chat will be attributed to ${name1}.`); |
| 989 | 990 | ||
| 990 | if (!confirmation) { | 991 | if (!confirmation) { |
| 991 | return; | 992 | return; |
| @@ -1021,7 +1022,7 @@ async function duplicatePersona(avatarId) { | |||
| 1021 | return; | 1022 | return; |
| 1022 | } | 1023 | } |
| 1023 | 1024 | ||
| 1024 | const confirm = await Popup.show.confirm('Are you sure you want to duplicate this persona?', personaName); | 1025 | const confirm = await Popup.show.confirm(t`Are you sure you want to duplicate this persona?`, personaName); |
| 1025 | 1026 | ||
| 1026 | if (!confirm) { | 1027 | if (!confirm) { |
| 1027 | console.debug('User cancelled duplicating persona'); | 1028 | console.debug('User cancelled duplicating persona'); |
| @@ -728,9 +728,9 @@ async function importTags(character, { importSetting = null } = {}) { | |||
| 728 | const added = addTagsToEntity(tagsToImport, character.avatar); | 728 | const added = addTagsToEntity(tagsToImport, character.avatar); |
| 729 | 729 | ||
| 730 | if (added) { | 730 | if (added) { |
| 731 | toastr.success(`Imported tags:<br />${tagsToImport.map(x => x.name).join(', ')}`, 'Importing Tags', { escapeHtml: false }); | 731 | toastr.success(t`Imported tags:` + `<br />${tagsToImport.map(x => x.name).join(', ')}`, t`Importing Tags`, { escapeHtml: false }); |
| 732 | } else { | 732 | } else { |
| 733 | toastr.error(`Couldn't import tags:<br />${tagsToImport.map(x => x.name).join(', ')}`, 'Importing Tags', { escapeHtml: false }); | 733 | toastr.error(t`Couldn't import tags:` + `<br />${tagsToImport.map(x => x.name).join(', ')}`, t`Importing Tags`, { escapeHtml: false }); |
| 734 | } | 734 | } |
| 735 | 735 | ||
| 736 | return added; | 736 | return added; |
| @@ -1299,40 +1299,7 @@ export function createTagInput(inputSelector, listSelector, tagListOptions = {}) | |||
| 1299 | async function onViewTagsListClick() { | 1299 | async function onViewTagsListClick() { |
| 1300 | const html = $(document.createElement('div')); | 1300 | const html = $(document.createElement('div')); |
| 1301 | html.attr('id', 'tag_view_list'); | 1301 | html.attr('id', 'tag_view_list'); |
| 1302 | html.append(` | 1302 | html.append(await renderTemplateAsync('tagManagement', {bogus_folders: power_user.bogus_folders, auto_sort_tags: power_user.auto_sort_tags})); |
| 1303 | <div class="title_restorable alignItemsBaseline"> | ||
| 1304 | <h3>Tag Management</h3> | ||
| 1305 | <div class="flex-container alignItemsBaseline"> | ||
| 1306 | <div class="menu_button menu_button_icon tag_view_backup" title="Save your tags to a file"> | ||
| 1307 | <i class="fa-solid fa-file-export"></i> | ||
| 1308 | <span data-i18n="Backup">Backup</span> | ||
| 1309 | </div> | ||
| 1310 | <div class="menu_button menu_button_icon tag_view_restore" title="Restore tags from a file"> | ||
| 1311 | <i class="fa-solid fa-file-import"></i> | ||
| 1312 | <span data-i18n="Restore">Restore</span> | ||
| 1313 | </div> | ||
| 1314 | <div class="menu_button menu_button_icon tag_view_create" title="Create a new tag"> | ||
| 1315 | <i class="fa-solid fa-plus"></i> | ||
| 1316 | <span data-i18n="Create">Create</span> | ||
| 1317 | </div> | ||
| 1318 | <input type="file" id="tag_view_restore_input" hidden accept=".json"> | ||
| 1319 | </div> | ||
| 1320 | </div> | ||
| 1321 | <div class="justifyLeft m-b-1"> | ||
| 1322 | <small> | ||
| 1323 | Drag handle to reorder. Click name to rename. Click color to change display.<br> | ||
| 1324 | ${(power_user.bogus_folders ? 'Click on the folder icon to use this tag as a folder.<br>' : '')} | ||
| 1325 | <label class="checkbox flex-container alignitemscenter flexNoGap m-t-1" for="auto_sort_tags"> | ||
| 1326 | <input type="checkbox" id="auto_sort_tags" name="auto_sort_tags" ${power_user.auto_sort_tags ? ' checked' : ''} /> | ||
| 1327 | <span data-i18n="Use alphabetical sorting"> | ||
| 1328 | Use alphabetical sorting | ||
| 1329 | <div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]If enabled, tags will automatically be sorted alphabetically on creation or rename.\nIf disabled, new tags will be appended at the end.\n\nIf a tag is manually reordered by dragging, automatic sorting will be disabled." | ||
| 1330 | title="If enabled, tags will automatically be sorted alphabetically on creation or rename.\nIf disabled, new tags will be appended at the end.\n\nIf a tag is manually reordered by dragging, automatic sorting will be disabled."> | ||
| 1331 | </div> | ||
| 1332 | </span> | ||
| 1333 | </label> | ||
| 1334 | </small> | ||
| 1335 | </div>`); | ||
| 1336 | 1303 | ||
| 1337 | const tagContainer = $('<div class="tag_view_list_tags ui-sortable"></div>'); | 1304 | const tagContainer = $('<div class="tag_view_list_tags ui-sortable"></div>'); |
| 1338 | html.append(tagContainer); | 1305 | html.append(tagContainer); |
| @@ -9,8 +9,8 @@ | |||
| 9 | <input type="radio" id="forbid_media_override_global" name="forbid_media_override" /> | 9 | <input type="radio" id="forbid_media_override_global" name="forbid_media_override" /> |
| 10 | <span> | 10 | <span> |
| 11 | <span data-i18n="Use global setting">Use global setting</span> | 11 | <span data-i18n="Use global setting">Use global setting</span> |
| 12 | <b class="forbid_media_global_state_forbidden">(forbidden)</b> | 12 | <b data-i18n="forbid_media_global_state_forbidden" class="forbid_media_global_state_forbidden">(forbidden)</b> |
| 13 | <b class="forbid_media_global_state_allowed">(allowed)</b> | 13 | <b data-i18n="forbid_media_global_state_allowed" class="forbid_media_global_state_allowed">(allowed)</b> |
| 14 | </span> | 14 | </span> |
| 15 | </label> | 15 | </label> |
| 16 | <label class="checkbox_label" for="forbid_media_override_forbidden"> | 16 | <label class="checkbox_label" for="forbid_media_override_forbidden"> |
| @@ -0,0 +1,33 @@ | |||
| 1 | <div class="title_restorable alignItemsBaseline"> | ||
| 2 | <h3 data-i18n="Tag Management">Tag Management</h3> | ||
| 3 | <div class="flex-container alignItemsBaseline"> | ||
| 4 | <div class="menu_button menu_button_icon tag_view_backup" data-i18n="[title]Save your tags to a file" title="Save your tags to a file"> | ||
| 5 | <i class="fa-solid fa-file-export"></i> | ||
| 6 | <span data-i18n="Backup">Backup</span> | ||
| 7 | </div> | ||
| 8 | <div class="menu_button menu_button_icon tag_view_restore" data-i18n="[title]Restore tags from a file" title="Restore tags from a file"> | ||
| 9 | <i class="fa-solid fa-file-import"></i> | ||
| 10 | <span data-i18n="Restore">Restore</span> | ||
| 11 | </div> | ||
| 12 | <div class="menu_button menu_button_icon tag_view_create" data-i18n="[title]Create a new tag" title="Create a new tag"> | ||
| 13 | <i class="fa-solid fa-plus"></i> | ||
| 14 | <span data-i18n="Create">Create</span> | ||
| 15 | </div> | ||
| 16 | <input type="file" id="tag_view_restore_input" hidden accept=".json"> | ||
| 17 | </div> | ||
| 18 | </div> | ||
| 19 | <div class="justifyLeft m-b-1"> | ||
| 20 | <small> | ||
| 21 | <span data-i18n="Drag handle to reorder. Click name to rename. Click color to change display.">Drag handle to reorder. Click name to rename. Click color to change display.</span><br> | ||
| 22 | {{#if bogus_folders}}<span data-i18n="Click on the folder icon to use this tag as a folder.">Click on the folder icon to use this tag as a folder.</span><br>{{/if}} | ||
| 23 | <label class="checkbox flex-container alignitemscenter flexNoGap m-t-1" for="auto_sort_tags"> | ||
| 24 | <input type="checkbox" id="auto_sort_tags" name="auto_sort_tags" {{#if auto_sort_tags}} checked{{/if}} /> | ||
| 25 | <span data-i18n="Use alphabetical sorting"> | ||
| 26 | Use alphabetical sorting | ||
| 27 | </span> | ||
| 28 | <div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]tags_sorting_desc" | ||
| 29 | title="If enabled, tags will automatically be sorted alphabetically on creation or rename.\nIf disabled, new tags will be appended at the end.\n\nIf a tag is manually reordered by dragging, automatic sorting will be disabled."> | ||
| 30 | </div> | ||
| 31 | </label> | ||
| 32 | </small> | ||
| 33 | </div> | ||
| \ No newline at end of file | 33 | \ No newline at end of file | |
| @@ -33,7 +33,7 @@ | |||
| 33 | <i class="fa-solid fa-image-portrait"></i> | 33 | <i class="fa-solid fa-image-portrait"></i> |
| 34 | <span data-i18n="Sample characters">Sample characters</span> | 34 | <span data-i18n="Sample characters">Sample characters</span> |
| 35 | </button> | 35 | </button> |
| 36 | <span data-i18n="or_welcome">or</span> | 36 | <span data-i18n="or;or_welcome">or</span> |
| 37 | <button class="external_import_button menu_button menu_button_icon inline-flex"> | 37 | <button class="external_import_button menu_button menu_button_icon inline-flex"> |
| 38 | <i class="fa-solid fa-cloud-arrow-down"></i> | 38 | <i class="fa-solid fa-cloud-arrow-down"></i> |
| 39 | <span data-i18n="Import Characters">Import characters</span> | 39 | <span data-i18n="Import Characters">Import characters</span> |