Merge pull request #3053 from Yokayo/staging

Update ru-ru translation
This commit is contained in:
Cohee 2024-11-11 23:04:05 +02:00 committed by GitHub
commit f1bda3fb22
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 181 additions and 94 deletions

View File

@ -333,7 +333,7 @@
}
.completion_prompt_manager_popup_entry_form_control:has(#completion_prompt_manager_popup_entry_form_prompt:disabled)>div:first-child::after {
content: 'The content of this prompt is pulled from elsewhere and cannot be edited here.';
content: attr(external_piece_text);
display: block;
width: 100%;
font-weight: 600;

View File

@ -2782,7 +2782,7 @@
</div>
</form>
<form id="claude_form" data-source="claude" action="javascript:void(null);" method="post" enctype="multipart/form-data">
<h4>Claude API Key</h4>
<h4 data-i18n="Claude API Key">Claude API Key</h4>
<div>
<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>.
</div>
@ -6001,7 +6001,7 @@
</div>
</div>
<div class="completion_prompt_manager_popup_entry_form_control">
<div class="flex-container alignItemsCenter">
<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.">
<div class="flex1">
<label for="completion_prompt_manager_popup_entry_form_prompt">
<span data-i18n="Prompt">Prompt</span>

View File

@ -177,7 +177,7 @@
"(Saved to Context Template)": "(Сохраняется в шаблоне контекста)",
"Tokenizer": "Токенайзер",
"Token Padding": "Кол-во добавочных токенов",
"Save preset as": "Сохранить пресет как",
"Save preset as": "Сохранить пресет как...",
"Always add character's name to prompt": "Всегда добавлять имя персонажа в промпт",
"Use as Stop Strings": "Использовать в качестве стоп-строк",
"Bind to Context": "Привязка к контексту",
@ -275,12 +275,12 @@
"Chatty": "Разговорчивый",
"Examples of dialogue": "Примеры диалога",
"Save": "Сохранить",
"Chat History": "История чата",
"Chat History": "История чатов",
"Content": "Содержание",
"Selective": "Выборочно",
"Disable": "Отключено",
"Back to parent chat": "Вернуться в основной чат",
"Convert to group": "Превратить в группу",
"Convert to group": "Сделать групповым",
"Start new chat": "Начать новый чат",
"Delete messages": "Удалить сообщения",
"Impersonate": "Перевоплощение",
@ -434,7 +434,7 @@
"Persona Description": "Описание персоны",
"Your Persona": "Ваша персона",
"Show notifications on switching personas": "Показывать уведомления при смене персоны",
"In Story String / Prompt Manager": "В строке истории / Менеджер промптов",
"In Story String / Prompt Manager": "В строке истории / Менеджере промптов",
"Top of Author's Note": "Сверху от заметок автора",
"Bottom of Author's Note": "Снизу от заметок автора",
"How do I use this?": "Как пользоваться?",
@ -1160,7 +1160,8 @@
"ext_regex_global_scripts_desc": "Распространяются на всех персонажей. Сохраняются как часть настроек.",
"ext_regex_scoped_scripts": "Локальные скрипты",
"ext_regex_scoped_scripts_desc": "Распространяются только на этого персонажа. Сохраняются в его карточку.",
"ext_regex_allow_scoped": "Разрешить использование локальных скриптов",
"ext_regex_allow_scoped": "Включить локальные скрипты",
"ext_regex_disallow_scoped": "Отключить локальные скрипты",
"ext_regex_move_to_global": "Сделать глобальным",
"openai_logit_bias_no_items": "Правил нет",
"Enable function calling": "Включить функции",
@ -1903,7 +1904,7 @@
"No sections selected for export": "Не выбрано ни одной секции для экспорта",
"Cannot update GUI preset": "Пресет для GUI обновить невозможно",
"Template updated": "Шаблон сохранён",
"Hint: Use a character/group name to bind preset to a specific chat.": "Совет: введите имя персонажа/группы, чтобы привязать пресет к определённому чату.",
"Hint: Use a character/group name to bind preset to a specific chat.": "Совет: введите имя персонажа/группы, чтобы привязать пресет к определённым чатам.",
"Preset name:": "Название пресета:",
"Template name:": "Название шаблона:",
"Preset saved": "Пресет сохранён",
@ -1951,5 +1952,88 @@
"from other websites": "с других сайтов.",
"Go to the": "Загляните в",
"to install additional features.": ", чтобы установить разные дополнительные ресурсы.",
"or_welcome": "; также доступен"
"or_welcome": "; также доступен",
"Claude API Key": "Ключ от API Claude",
"Block Entropy API Key": "Ключ от API Block Entropy",
"Select a Model": "Выберите модель",
"The content of this prompt is pulled from elsewhere and cannot be edited here.": "Эта часть промпта подтягивается откуда-то извне. Отредактировать её здесь невозможно.",
"save": "сохранить",
"reset": "сбросить",
"close": "закрыть",
"Your preset contains proxy and/or custom endpoint settings.": "Ваш пресет содержит настройки прокси и/или настройки кастомного эндпоинта.",
"Do you want to remove these fields before exporting?": "Желаете ли удалить эти поля перед экспортом?",
"Save": "Сохранить",
"Chat Lorebook": "Лорбук для чата",
"chat_world_template_txt": "Выбранный мир будет привязан к этому чату. Будет добавляться в промпт наряду с глобальным лорбуком и лором персонажа.",
"world_button_title": "Лор персонажа\n\nНажмите, чтобы загрузить\nShift + клик, чтобы открыть диалог привязки мира",
"No auxillary Lorebooks set. Click here to select.": "Вспомогательный лорбук не выбран. Нажмите, чтобы выбрать.",
"ext_regex_user_input_desc": "Отправленные вами сообщения.",
"ext_regex_ai_input_desc": "Полученные от API ответы.",
"ext_regex_slash_desc": "Сообщения, отправленные с помощью команд STscript.",
"ext_regex_wi_desc": "Содержимое лорбуков/информации о мире. Работает только со включенной галочкой \"Только промпт\"!",
"ext_regex_run_on_edit_desc": "Выполнять скрипт при редактировании принадлежащих обозначенной роли (ролям) сообщений.",
"Convert to group chat": "Сделать чат групповым",
"Are you sure you want to convert this chat to a group chat?": "Вы точно хотите сделать этот чат групповым?",
"This cannot be reverted.": "Отменить будет невозможно.",
"Enter a name for this persona:": "Введите имя для этой персоны:",
"Cancel if you're just uploading an avatar.": "Отмените, если просто загружаете аватарку.",
"Set the crop position of the avatar image": "Обозначьте зону, по которой будет обрезана аватарка",
"popup-button-crop": "Обрезать",
"Duplicate persona": "Клонировать персону",
"Are you sure you want to duplicate this persona?": "Вы точно хотите клонировать эту персону?",
"Enter a description for this persona:": "Введите описание для этой персоны:",
"You can always add or change it later.": "Его можно будет добавить или изменить в любое время.",
"You can now pick ${0} as a persona in the Persona Management menu.": "Теперь ${0} может быть выбран(а) в качестве персоны в меню \"Управление персоной\".",
"Persona Created": "Персона создана",
"Overwrite Existing Persona": "Перезаписать существующую персону",
"This character exists as a persona already. Do you want to overwrite it?": "Этот персонаж уже существует в виде персоны. Желаете перезаписать?",
"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?": "В описании персонажа присутствуют макросы <code>{{char}}</code> и/или <code>{{user}}</code>. Желаете поменять их местами?",
"(If empty name is provided, this will unbind the name from this avatar)": "(если оставить пустым, то имя будет отвязано от этой аватарки)",
"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}\" к этому чату, открепите текущую персону и затем закрепите новую кнопкой \"Закрепить\". Иначе при следующем заходе персона будет сброшена.",
"This chat is locked to a different persona (${0}).": "К этому чату уже привязана другая персона (${0}).",
"User persona is now unlocked for this chat. Click the \"Lock\" again to revert.": "Персона откреплена. Нажмите \"Закрепить\", чтобы снова зафиксировать её в этом чате.",
"Persona unlocked": "Персона откреплена",
"Creating a new persona for currently selected user name and avatar...": "Создаём новую персону для этого имени и аватарки...",
"Persona not set for this avatar": "К этой аватарке не привязано ни одной персоны",
"User persona is locked to ${0} in this chat": "${0} установлен(а) в качестве фиксированной персоны в этом чате",
"You cannot delete the avatar you are currently using": "Невозможно удалить аватарку, которую вы используете прямо сейчас",
"Warning": "Внимание",
"Are you sure you want to delete this avatar?": "Вы точно хотите удалить эту аватарку?",
"All information associated with its linked persona will be lost.": "Вся информация о связанной с ней персоне будет утеряна.",
"The default persona was deleted. You will need to set a new default persona.": "Удалена персона по умолчанию. Вам будет необходимо выбрать новую вместо неё.",
"Default persona deleted": "Удалена персона по умолчанию",
"The locked persona was deleted. You will need to set a new persona for this chat.": "Удалена привязанная к чату персона. Вам будет необходимо выбрать новую фиксированную персону для этого чата.",
"Persona deleted": "Персона удалена",
"You must bind a name to this persona before you can set it as the default.": "Прежде чем установить эту персону в качестве персоны по умолчанию, ей необходимо задать имя.",
"Persona name not set": "У персоны отсутствует имя",
"Are you sure you want to remove the default persona?": "Вы точно хотите снять статус персоны по умолчанию?",
"This persona will no longer be used by default when you open a new chat.": "Эта персона больше не будет автоматически выбираться при старте нового чата",
"Default persona removed": "Персона по умолчанию снята",
"Are you sure you want to set \"${0}\" as the default persona?": "Вы точно хотите установить \"${0}\" в качестве персоны по умолчанию?",
"This name and avatar will be used for all new chats, as well as existing chats where the user persona is not locked.": "Это имя и аватарка будут автоматически выбираться при старте нового чата, а также в любом существующем чате, где нет фиксированной персоны.",
"This persona will be used by default when you open a new chat.": "Эта персона будет выбираться автоматически при старте нового чата.",
"Default persona set to ${0}": "${0} установлен(а) в качестве персоны по умолчанию",
"Invalid file selected": "Загружен невалидный файл",
"Invalid file format": "Некорректный формат файла",
"Personas restored with warnings. Check console for details.": "Персона восстановлена, но были сгенерированы варнинги. Подробности см. в консоли.",
"Personas restored successfully.": "Персона успешно восстановлена.",
"All user-sent messages in this chat will be attributed to ${0}.": "Все ваши сообщения в этом чате будут приписаны ${0}.",
"Forbid Media Override explanation": "Способность текущего персонажа/группы использовать в чате внешние медиа.",
"forbid_media_global_state_forbidden": "(запрещены)",
"forbid_media_global_state_allowed": "(разрешены)",
"Always forbidden": "Всегда запрещены",
"Always allowed": "Всегда разрешены",
"Forbid Media Override subtitle": "Под медиа подразумеваются картинки, видео, аудио. Под внешними - любые, не хостящиеся на локальном сервере.",
"Tag Management": "Управление тегами",
"Save your tags to a file": "Сохранить ваши теги в файл",
"Restore tags from a file": "Восстановить теги из файла",
"Create a new tag": "Создать новый тег",
"Drag handle to reorder. Click name to rename. Click color to change display.": "Перемещайте мышкой, чтобы менять порядок. Нажмите, чтобы переименовать. Нажмите на цвет, чтобы сменить его.",
"Click on the folder icon to use this tag as a folder.": "Нажмите на иконку папки, чтобы использовать тег как папку.",
"Use alphabetical sorting": "Сортировать по алфавиту",
"tags_sorting_desc": "Автоматически сортировать теги по алфавиту после создания или переименования одного из них.\nПри выключении новые теги будут просто добавляться в конец.\n\nЕсли вручную перетащить один из тегов на другое место, автосортировка отключится.",
"Imported tags:": "Импортируемые теги:",
"Importing Tags": "Импорт тегов",
"Couldn't import tags:": "Не удалось импортировать теги:"
}

View File

@ -7491,7 +7491,7 @@ export function callPopup(text, type, inputValue = '', { okButton, rows, wide, w
} else if (['new_chat', 'confirm'].includes(popup_type)) {
return okButton ?? 'Yes';
} else if (['input'].includes(popup_type)) {
return okButton ?? 'Save';
return okButton ?? t`Save`;
}
return okButton ?? 'Delete';
}
@ -7861,7 +7861,7 @@ function openCharacterWorldPopup() {
if (!isMobile()) {
$(extraSelect).select2({
width: '100%',
placeholder: 'No auxillary Lorebooks set. Click here to select.',
placeholder: t`No auxillary Lorebooks set. Click here to select.`,
allowClear: true,
closeOnSelect: false,
});

View File

@ -32,6 +32,7 @@ import { commonEnumProviders } from './slash-commands/SlashCommandCommonEnumsPro
import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
import { createTagMapFromList } from './tags.js';
import { renderTemplateAsync } from './templates.js';
import { t } from './i18n.js';
import {
getUniqueName,
@ -263,7 +264,7 @@ export async function convertSoloToGroupChat() {
return;
}
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.');
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.`);
if (!confirm) {
return;
}

View File

@ -38,7 +38,7 @@
<strong class="flex1" data-i18n="ext_regex_scoped_scripts">Scoped Scripts</strong>
<label id="toggle_scoped_regex" class="checkbox flex-container" for="regex_scoped_toggle">
<input type="checkbox" id="regex_scoped_toggle" class="enable_scoped" />
<span class="regex-toggle-on fa-solid fa-toggle-on fa-lg" title="Disallow using scoped regex"></span>
<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>
<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>
</label>
</div>

View File

@ -70,25 +70,25 @@
<div class="flex-container">
<div class="flex1 wi-enter-footer-text flex-container flexFlowColumn flexNoGap alignitemsstart">
<small data-i18n="ext_regex_affects">Affects</small>
<div title="Messages sent by the user.">
<div data-i18n="[title]ext_regex_user_input_desc" title="Messages sent by the user.">
<label class="checkbox flex-container">
<input type="checkbox" name="replace_position" value="1">
<span data-i18n="ext_regex_user_input">User Input</span>
</label>
</div>
<div title="Messages received from the Generation API.">
<div data-i18n="[title]ext_regex_ai_input_desc" title="Messages received from the Generation API.">
<label class="checkbox flex-container">
<input type="checkbox" name="replace_position" value="2">
<span data-i18n="ext_regex_ai_output">AI Output</span>
</label>
</div>
<div title="Messages sent using STscript commands.">
<div data-i18n="[title]ext_regex_slash_desc" title="Messages sent using STscript commands.">
<label class="checkbox flex-container">
<input type="checkbox" name="replace_position" value="3">
<span data-i18n="Slash Commands">Slash Commands</span>
</label>
</div>
<div title="Lorebook/World Info entry contents. Requires 'Only Format Prompt' to be checked!">
<div data-i18n="[title]ext_regex_wi_desc" title="Lorebook/World Info entry contents. Requires 'Only Format Prompt' to be checked!">
<label class="checkbox flex-container">
<input type="checkbox" name="replace_position" value="5">
<span data-i18n="World Info">World Info</span>
@ -117,7 +117,7 @@
<input type="checkbox" name="disabled" />
<span data-i18n="Disabled">Disabled</span>
</label>
<label class="checkbox flex-container" title="Run the regex script when the message belonging a to specified role(s) is edited.">
<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.">
<input type="checkbox" name="run_on_edit" />
<span data-i18n="Run On Edit">Run On Edit</span>
</label>

View File

@ -9,6 +9,7 @@ import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashComm
import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js';
import { download, getFileText, getSortableDelay, uuidv4 } from '../../utils.js';
import { regex_placement, runRegexScript } from './engine.js';
import { t } from '../../i18n.js';
/**
* @typedef {object} RegexScript
@ -275,7 +276,7 @@ async function onRegexEditorOpenClick(existingId, isScoped) {
editorHtml.find('input, textarea, select').on('input', updateTestResult);
const popupResult = await callPopup(editorHtml, 'confirm', undefined, { okButton: 'Save' });
const popupResult = await callPopup(editorHtml, 'confirm', undefined, { okButton: t`Save` });
if (popupResult) {
const newRegexScript = {
id: existingId ? String(existingId) : uuidv4(),

View File

@ -3612,8 +3612,8 @@ async function onExportPresetClick() {
const fieldValues = sensitiveFields.filter(field => preset[field]).map(field => `<b>${field}</b>: <code>${preset[field]}</code>`);
const shouldConfirm = fieldValues.length > 0;
const textHeader = 'Your preset contains proxy and/or custom endpoint settings.';
const textMessage = `<div>Do you want to remove these fields before exporting?</div><br>${DOMPurify.sanitize(fieldValues.join('<br>'))}`;
const textHeader = t`Your preset contains proxy and/or custom endpoint settings.`;
const textMessage = `<div>` + t`Do you want to remove these fields before exporting?` + `</div><br>${DOMPurify.sanitize(fieldValues.join('<br>'))}`;
const cancelButton = { text: 'Cancel', result: POPUP_RESULT.CANCELLED, appendAtEnd: true };
const popupOptions = { customButtons: [cancelButton] };
const popupResult = await Popup.show.confirm(textHeader, textMessage, popupOptions);
@ -4369,8 +4369,8 @@ async function onOpenrouterModelSortChange() {
async function onNewPresetClick() {
const popupText = `
<h3>Preset name:</h3>
<h4>Hint: Use a character/group name to bind preset to a specific chat.</h4>`;
<h3>` + t`Preset name:` + `</h3>
<h4>` + t`Hint: Use a character/group name to bind preset to a specific chat.` + `</h4>`;
const name = await callPopup(popupText, 'input', oai_settings.preset_settings_openai);
if (!name) {

View File

@ -22,6 +22,7 @@ import { debounce_timeout } from './constants.js';
import { FILTER_TYPES, FilterHelper } from './filters.js';
import { selected_group } from './group-chats.js';
import { POPUP_RESULT, POPUP_TYPE, Popup } from './popup.js';
import { t } from './i18n.js';
let savePersonasPage = 0;
const GRID_STORAGE_KEY = 'Personas_GridView';
@ -276,7 +277,7 @@ async function changeUserAvatar(e) {
let url = '/api/avatars/upload';
if (!power_user.never_resize_avatars) {
const dlg = new Popup('Set the crop position of the avatar image', POPUP_TYPE.CROP, '', { cropImage: dataUrl });
const dlg = new Popup(t`Set the crop position of the avatar image`, POPUP_TYPE.CROP, '', { cropImage: dataUrl });
const result = await dlg.show();
if (!result) {
@ -331,23 +332,23 @@ async function changeUserAvatar(e) {
* @returns {Promise} Promise that resolves when the persona is set
*/
export async function createPersona(avatarId) {
const personaName = await Popup.show.input('Enter a name for this persona:', 'Cancel if you\'re just uploading an avatar.', '');
const personaName = await Popup.show.input(t`Enter a name for this persona:`, t`Cancel if you're just uploading an avatar.`, '');
if (!personaName) {
console.debug('User cancelled creating a persona');
return;
}
const personaDescription = await Popup.show.input('Enter a description for this persona:', 'You can always add or change it later.', '', { rows: 4 });
const personaDescription = await Popup.show.input(t`Enter a description for this persona:`, t`You can always add or change it later.`, '', { rows: 4 });
initPersona(avatarId, personaName, personaDescription);
if (power_user.persona_show_notifications) {
toastr.success(`You can now pick ${personaName} as a persona in the Persona Management menu.`, 'Persona Created');
toastr.success(t`You can now pick ${personaName} as a persona in the Persona Management menu.`, t`Persona Created`);
}
}
async function createDummyPersona() {
const personaName = await Popup.show.input('Enter a name for this persona:', null);
const personaName = await Popup.show.input(t`Enter a name for this persona:`, null);
if (!personaName) {
console.debug('User cancelled creating dummy persona');
@ -393,7 +394,7 @@ export async function convertCharacterToPersona(characterId = null) {
const overwriteName = `${name} (Persona).png`;
if (overwriteName in power_user.personas) {
const confirm = await Popup.show.confirm('Overwrite Existing Persona', 'This character exists as a persona already. Do you want to overwrite it?');
const confirm = await Popup.show.confirm(t`Overwrite Existing Persona`, t`This character exists as a persona already. Do you want to overwrite it?`);
if (!confirm) {
console.log('User cancelled the overwrite of the persona');
return;
@ -401,7 +402,7 @@ export async function convertCharacterToPersona(characterId = null) {
}
if (description.includes('{{char}}') || description.includes('{{user}}')) {
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?');
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?`);
if (confirm) {
description = description.replace(/{{char}}/gi, '{{personaChar}}').replace(/{{user}}/gi, '{{personaUser}}');
description = description.replace(/{{personaUser}}/gi, '{{char}}').replace(/{{personaChar}}/gi, '{{user}}');
@ -427,7 +428,7 @@ export async function convertCharacterToPersona(characterId = null) {
saveSettingsDebounced();
console.log('Persona for character created');
toastr.success(`You can now select ${name} as a persona in the Persona Management menu.`, 'Persona Created');
toastr.success(t`You can now pick ${name} as a persona in the Persona Management menu.`, t`Persona Created`);
// Refresh the persona selector
await getUserAvatars(true, overwriteName);
@ -509,8 +510,8 @@ async function bindUserNameToPersona(e) {
let personaUnbind = false;
const existingPersona = power_user.personas[avatarId];
const personaName = await Popup.show.input(
'Enter a name for this persona:',
'(If empty name is provided, this will unbind the name from this avatar)',
t`Enter a name for this persona:`,
t`(If empty name is provided, this will unbind the name from this avatar)`,
existingPersona || '',
{ onClose: (p) => { personaUnbind = p.value === '' && p.result === POPUP_RESULT.AFFIRMATIVE; } });
@ -560,8 +561,8 @@ function selectCurrentPersona() {
const lockedPersona = chat_metadata['persona'];
if (lockedPersona && lockedPersona !== user_avatar && power_user.persona_show_notifications) {
toastr.info(
`To permanently set "${personaName}" as the selected persona, unlock and relock it using the "Lock" button. Otherwise, the selection resets upon reloading the chat.`,
`This chat is locked to a different persona (${power_user.personas[lockedPersona]}).`,
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.`,
t`This chat is locked to a different persona (${power_user.personas[lockedPersona]}).`,
{ timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true },
);
}
@ -626,7 +627,7 @@ async function unlockPersona() {
delete chat_metadata['persona'];
await saveMetadata();
if (power_user.persona_show_notifications) {
toastr.info('User persona is now unlocked for this chat. Click the "Lock" again to revert.', 'Persona unlocked');
toastr.info(t`User persona is now unlocked for this chat. Click the "Lock" again to revert.`, t`Persona unlocked`);
}
updateUserLockIcon();
}
@ -640,8 +641,8 @@ async function lockPersona() {
console.log(`Creating a new persona ${user_avatar}`);
if (power_user.persona_show_notifications) {
toastr.info(
'Creating a new persona for currently selected user name and avatar...',
'Persona not set for this avatar',
t`Creating a new persona for currently selected user name and avatar...`,
t`Persona not set for this avatar`,
{ timeOut: 10000, extendedTimeOut: 20000 },
);
}
@ -659,7 +660,7 @@ async function lockPersona() {
saveSettingsDebounced();
console.log(`Locking persona for this chat ${user_avatar}`);
if (power_user.persona_show_notifications) {
toastr.success(`User persona is locked to ${name1} in this chat`);
toastr.success(t`User persona is locked to ${name1} in this chat`);
}
updateUserLockIcon();
}
@ -676,11 +677,11 @@ async function deleteUserAvatar(e) {
if (avatarId == user_avatar) {
console.warn(`User tried to delete their current avatar ${avatarId}`);
toastr.warning('You cannot delete the avatar you are currently using', 'Warning');
toastr.warning(t`You cannot delete the avatar you are currently using`, t`Warning`);
return;
}
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.');
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.`);
if (!confirm) {
console.debug('User cancelled deleting avatar');
@ -701,12 +702,12 @@ async function deleteUserAvatar(e) {
delete power_user.persona_descriptions[avatarId];
if (avatarId === power_user.default_persona) {
toastr.warning('The default persona was deleted. You will need to set a new default persona.', 'Default persona deleted');
toastr.warning(t`The default persona was deleted. You will need to set a new default persona.`, t`Default persona deleted`);
power_user.default_persona = null;
}
if (avatarId === chat_metadata['persona']) {
toastr.warning('The locked persona was deleted. You will need to set a new persona for this chat.', 'Persona deleted');
toastr.warning(t`The locked persona was deleted. You will need to set a new persona for this chat.`, t`Persona deleted`);
delete chat_metadata['persona'];
await saveMetadata();
}
@ -807,14 +808,14 @@ async function setDefaultPersona(e) {
if (power_user.personas[avatarId] === undefined) {
console.warn(`No persona name found for avatar ${avatarId}`);
toastr.warning('You must bind a name to this persona before you can set it as the default.', 'Persona name not set');
toastr.warning(t`You must bind a name to this persona before you can set it as the default.`, t`Persona name not set`);
return;
}
const personaName = power_user.personas[avatarId];
if (avatarId === currentDefault) {
const confirm = await Popup.show.confirm('Are you sure you want to remove the default persona?', personaName);
const confirm = await Popup.show.confirm(t`Are you sure you want to remove the default persona?`, personaName);
if (!confirm) {
console.debug('User cancelled removing default persona');
@ -823,11 +824,11 @@ async function setDefaultPersona(e) {
console.log(`Removing default persona ${avatarId}`);
if (power_user.persona_show_notifications) {
toastr.info('This persona will no longer be used by default when you open a new chat.', 'Default persona removed');
toastr.info(t`This persona will no longer be used by default when you open a new chat.`, t`Default persona removed`);
}
delete power_user.default_persona;
} else {
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.');
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.`);
if (!confirm) {
console.debug('User cancelled setting default persona');
@ -836,7 +837,7 @@ async function setDefaultPersona(e) {
power_user.default_persona = avatarId;
if (power_user.persona_show_notifications) {
toastr.success('This persona will be used by default when you open a new chat.', `Default persona set to ${personaName}`);
toastr.success(t`This persona will be used by default when you open a new chat.`, t`Default persona set to ${personaName}`);
}
}
@ -918,13 +919,13 @@ async function onPersonasRestoreInput(e) {
const data = await parseJsonFile(file);
if (!data) {
toastr.warning('Invalid file selected', 'Persona Management');
toastr.warning(t`Invalid file selected`, t`Persona Management`);
console.debug('Invalid file selected');
return;
}
if (!data.personas || !data.persona_descriptions || typeof data.personas !== 'object' || typeof data.persona_descriptions !== 'object') {
toastr.warning('Invalid file format', 'Persona Management');
toastr.warning(t`Invalid file format`, t`Persona Management`);
console.debug('Invalid file selected');
return;
}
@ -972,10 +973,10 @@ async function onPersonasRestoreInput(e) {
}
if (warnings.length) {
toastr.success('Personas restored with warnings. Check console for details.');
toastr.success(t`Personas restored with warnings. Check console for details.`);
console.warn(`PERSONA RESTORE REPORT\n====================\n${warnings.join('\n')}`);
} else {
toastr.success('Personas restored successfully.');
toastr.success(t`Personas restored successfully.`);
}
await getUserAvatars();
@ -985,7 +986,7 @@ async function onPersonasRestoreInput(e) {
}
async function syncUserNameToPersona() {
const confirmation = await Popup.show.confirm('Are you sure?', `All user-sent messages in this chat will be attributed to ${name1}.`);
const confirmation = await Popup.show.confirm(t`Are you sure?`, t`All user-sent messages in this chat will be attributed to ${name1}.`);
if (!confirmation) {
return;
@ -1021,7 +1022,7 @@ async function duplicatePersona(avatarId) {
return;
}
const confirm = await Popup.show.confirm('Are you sure you want to duplicate this persona?', personaName);
const confirm = await Popup.show.confirm(t`Are you sure you want to duplicate this persona?`, personaName);
if (!confirm) {
console.debug('User cancelled duplicating persona');

View File

@ -728,9 +728,9 @@ async function importTags(character, { importSetting = null } = {}) {
const added = addTagsToEntity(tagsToImport, character.avatar);
if (added) {
toastr.success(`Imported tags:<br />${tagsToImport.map(x => x.name).join(', ')}`, 'Importing Tags', { escapeHtml: false });
toastr.success(t`Imported tags:` + `<br />${tagsToImport.map(x => x.name).join(', ')}`, t`Importing Tags`, { escapeHtml: false });
} else {
toastr.error(`Couldn't import tags:<br />${tagsToImport.map(x => x.name).join(', ')}`, 'Importing Tags', { escapeHtml: false });
toastr.error(t`Couldn't import tags:` + `<br />${tagsToImport.map(x => x.name).join(', ')}`, t`Importing Tags`, { escapeHtml: false });
}
return added;
@ -1299,40 +1299,7 @@ export function createTagInput(inputSelector, listSelector, tagListOptions = {})
async function onViewTagsListClick() {
const html = $(document.createElement('div'));
html.attr('id', 'tag_view_list');
html.append(`
<div class="title_restorable alignItemsBaseline">
<h3>Tag Management</h3>
<div class="flex-container alignItemsBaseline">
<div class="menu_button menu_button_icon tag_view_backup" title="Save your tags to a file">
<i class="fa-solid fa-file-export"></i>
<span data-i18n="Backup">Backup</span>
</div>
<div class="menu_button menu_button_icon tag_view_restore" title="Restore tags from a file">
<i class="fa-solid fa-file-import"></i>
<span data-i18n="Restore">Restore</span>
</div>
<div class="menu_button menu_button_icon tag_view_create" title="Create a new tag">
<i class="fa-solid fa-plus"></i>
<span data-i18n="Create">Create</span>
</div>
<input type="file" id="tag_view_restore_input" hidden accept=".json">
</div>
</div>
<div class="justifyLeft m-b-1">
<small>
Drag handle to reorder. Click name to rename. Click color to change display.<br>
${(power_user.bogus_folders ? 'Click on the folder icon to use this tag as a folder.<br>' : '')}
<label class="checkbox flex-container alignitemscenter flexNoGap m-t-1" for="auto_sort_tags">
<input type="checkbox" id="auto_sort_tags" name="auto_sort_tags" ${power_user.auto_sort_tags ? ' checked' : ''} />
<span data-i18n="Use alphabetical sorting">
Use alphabetical sorting
<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."
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.">
</div>
</span>
</label>
</small>
</div>`);
html.append(await renderTemplateAsync('tagManagement', {bogus_folders: power_user.bogus_folders, auto_sort_tags: power_user.auto_sort_tags}));
const tagContainer = $('<div class="tag_view_list_tags ui-sortable"></div>');
html.append(tagContainer);

View File

@ -9,8 +9,8 @@
<input type="radio" id="forbid_media_override_global" name="forbid_media_override" />
<span>
<span data-i18n="Use global setting">Use global setting</span>
<b class="forbid_media_global_state_forbidden">(forbidden)</b>
<b class="forbid_media_global_state_allowed">(allowed)</b>
<b data-i18n="forbid_media_global_state_forbidden" class="forbid_media_global_state_forbidden">(forbidden)</b>
<b data-i18n="forbid_media_global_state_allowed" class="forbid_media_global_state_allowed">(allowed)</b>
</span>
</label>
<label class="checkbox_label" for="forbid_media_override_forbidden">

View File

@ -0,0 +1,33 @@
<div class="title_restorable alignItemsBaseline">
<h3 data-i18n="Tag Management">Tag Management</h3>
<div class="flex-container alignItemsBaseline">
<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">
<i class="fa-solid fa-file-export"></i>
<span data-i18n="Backup">Backup</span>
</div>
<div class="menu_button menu_button_icon tag_view_restore" data-i18n="[title]Restore tags from a file" title="Restore tags from a file">
<i class="fa-solid fa-file-import"></i>
<span data-i18n="Restore">Restore</span>
</div>
<div class="menu_button menu_button_icon tag_view_create" data-i18n="[title]Create a new tag" title="Create a new tag">
<i class="fa-solid fa-plus"></i>
<span data-i18n="Create">Create</span>
</div>
<input type="file" id="tag_view_restore_input" hidden accept=".json">
</div>
</div>
<div class="justifyLeft m-b-1">
<small>
<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>
{{#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}}
<label class="checkbox flex-container alignitemscenter flexNoGap m-t-1" for="auto_sort_tags">
<input type="checkbox" id="auto_sort_tags" name="auto_sort_tags" {{#if auto_sort_tags}} checked{{/if}} />
<span data-i18n="Use alphabetical sorting">
Use alphabetical sorting
</span>
<div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]tags_sorting_desc"
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.">
</div>
</label>
</small>
</div>

View File

@ -33,7 +33,7 @@
<i class="fa-solid fa-image-portrait"></i>
<span data-i18n="Sample characters">Sample characters</span>
</button>
<span data-i18n="or_welcome">or</span>
<span data-i18n="or;or_welcome">or</span>
<button class="external_import_button menu_button menu_button_icon inline-flex">
<i class="fa-solid fa-cloud-arrow-down"></i>
<span data-i18n="Import Characters">Import characters</span>