diff --git a/public/locales/ru-ru.json b/public/locales/ru-ru.json
index 530108758..716fadf98 100644
--- a/public/locales/ru-ru.json
+++ b/public/locales/ru-ru.json
@@ -38,7 +38,7 @@
"LLaMA / Mistral / Yi models only": "Только для моделей LLaMA / Mistral / Yi. Перед этим обязательно выберите подходящий токенизатор.\nПоследовательности, которых не должно быть на выходе.\nОдна на строку. Текст или [идентификаторы токенов].\nМногие токены имеют пробел впереди. Используйте счетчик токенов, если не уверены.",
"Example: some text [42, 69, 1337]": "Пример:\nкакой-то текст\n[42, 69, 1337]",
"Classifier Free Guidance. More helpful tip coming soon": "Classifier Free Guidance. Чуть позже опишем более подробно",
- "Scale": "Масштаб",
+ "Scale": "Scale",
"GBNF Grammar": "Грамматика GBNF",
"Usage Stats": "Статистика исп.",
"Click for stats!": "Нажмите для получения статистики!",
@@ -97,7 +97,7 @@
"Sequences you don't want to appear in the output. One per line.": "Строки, которых не должно быть в выходном тексте. По одной на строчку.",
"AI Module": "Модуль ИИ",
"Changes the style of the generated text.": "Изменяет стиль создаваемого текста.",
- "Used if CFG Scale is unset globally, per chat or character": "Используется, если масштаб CFG не установлен глобально, для каждого чата или персонажа.",
+ "Used if CFG Scale is unset globally, per chat or character": "Используется, если CFG Scale не установлен глобально, для каждого чата или персонажа.",
"Inserts jailbreak as a last system message.": "Вставлять JailBreak последним системным сообщением.",
"This tells the AI to ignore its usual content restrictions.": "Сообщает AI о необходимости игнорировать стандартные ограничения контента.",
"NSFW Encouraged": "Поощрять NSFW",
@@ -262,7 +262,7 @@
"Auto-Continue": "Авто-продолжение",
"Collapse Consecutive Newlines": "Сворачивать последовательные новые строки",
"Allow for Chat Completion APIs": "Разрешить для API Chat Completion",
- "Target length (tokens)": "Целевая длина (токены)",
+ "Target length (tokens)": "Целевая длина (в токенах)",
"Keep Example Messages in Prompt": "Сохранять примеры сообщений в промпте",
"Remove Empty New Lines from Output": "Удалять пустые строчки из вывода",
"Disabled for all models": "Выключено для всех моделей",
@@ -300,11 +300,11 @@
"Chat Style": "Стиль чата",
"Default": "По умолчанию",
"Bubbles": "Пузыри",
- "No Blur Effect": "Отключить эффект размытия",
- "No Text Shadows": "Отключить тень от текста",
+ "No Blur Effect": "Отключить размытие",
+ "No Text Shadows": "Отключить тень текста",
"Waifu Mode": "Рeжим Вайфу",
"Message Timer": "Таймер сообщений",
- "Model Icon": "Показать значки модели",
+ "Model Icon": "Значки моделей",
"# of messages (0 = disabled)": "# сообщений (0 = отключено)",
"Advanced Character Search": "Расширенный поиск по персонажам",
"Allow {{char}}: in bot messages": "Показывать {{char}}: в ответах",
@@ -314,7 +314,7 @@
"Lorebook Import Dialog": "Показывать окно импорта лорбука",
"MUI Preset": "Пресет MUI:",
"If set in the advanced character definitions, this field will be displayed in the characters list.": "Если это поле задано в расширенных параметрах персонажа, оно будет отображаться в списке персонажей.",
- "Relaxed API URLS": "Смягченные URL-адреса API",
+ "Relaxed API URLS": "Смягчённые адреса API",
"Custom CSS": "Пользовательский CSS",
"Default (oobabooga)": "По умолчанию (oobabooga)",
"Mancer Model": "Модель Mancer",
@@ -381,7 +381,7 @@
"text": "текст",
"Delete": "Удалить",
"Cancel": "Отменить",
- "Advanced Defininitions": "Продвинутое описание",
+ "Advanced Defininitions": "Расширенное описание",
"Personality summary": "Сводка по личности",
"A brief description of the personality": "Краткое описание личности",
"Scenario": "Сценарий",
@@ -431,7 +431,7 @@
"JSON": "JSON",
"presets": "Пресеты",
"Message Sound": "Звук сообщения",
- "Author's Note": "Пометки автора",
+ "Author's Note": "Заметки автора",
"Send Jailbreak": "Отправлять джейлбрейк",
"Replace empty message": "Заменять пустые сообщения",
"Send this text instead of nothing when the text box is empty.": "Этот текст будет отправлен в случае отсутствия текста на отправку.",
@@ -475,7 +475,7 @@
"--- Pick to Edit ---": "--- Выберите для редактирования ---",
"or": "или",
"New": "Новый",
- "Priority": "Приритет",
+ "Priority": "Приоритет",
"Custom": "Пользовательский",
"Title A-Z": "Название от A до Z",
"Title Z-A": "Название от Z до A",
@@ -528,7 +528,7 @@
"UI Border": "Границы UI",
"Chat Style:": "Стиль чата",
"Chat Width (PC)": "Ширина чата (для ПК)",
- "Chat Timestamps": "Временные метки в чате",
+ "Chat Timestamps": "Метки времени в чате",
"Tags as Folders": "Теги как папки",
"Chat Truncation": "Усечение чата",
"(0 = unlimited)": "(0 = неограниченное)",
@@ -559,8 +559,8 @@
"Disables animations and transitions": "Отключение анимаций и переходов.",
"removes blur from window backgrounds": "Убрать размытие с фона окон, чтобы ускорить рендеринг.",
"Remove text shadow effect": "Удаление эффекта тени от текста.",
- "Reduce chat height, and put a static sprite behind the chat window": "Уменьшитm высоту чата и поместить статичный спрайт за окном чата.",
- "Always show the full list of the Message Actions context items for chat messages, instead of hiding them behind '...'": "Всегда показывать полный список контекстных элементов 'Действия с сообщением' для сообщений чата, а не прятать их за '...'.",
+ "Reduce chat height, and put a static sprite behind the chat window": "Уменьшить высоту чата и поместить статичный спрайт за окном чата.",
+ "Always show the full list of the Message Actions context items for chat messages, instead of hiding them behind '...'": "Всегда показывать полный список действий с сообщением, а не прятать их за '...'.",
"Alternative UI for numeric sampling parameters with fewer steps": "Альтернативный пользовательский интерфейс для числовых параметров выборки с меньшим количеством шагов.",
"Entirely unrestrict all numeric sampling parameters": "Полностью разграничить все числовые параметры выборки.",
"Time the AI's message generation, and show the duration in the chat log": "Время генерации сообщений ИИ и его показ в журнале чата.",
@@ -600,7 +600,7 @@
"Enable the auto-swipe function. Settings in this section only have an effect when auto-swipe is enabled": "Включить авто-свайп. Настройки в этом разделе действуют только при включенном авто-свайпе.",
"If the generated message is shorter than this, trigger an auto-swipe": "Если сгенерированное сообщение короче этого значения, срабатывает авто-свайп.",
"Reload and redraw the currently open chat": "Перезагрузить и перерисовать открытый в данный момент чат.",
- "Auto-Expand Message Actions": "Развернуть контекстные элементы",
+ "Auto-Expand Message Actions": "Развернуть действия",
"Not Connected": "Не подключено",
"Persona Management": "Управление персоной",
"Persona Description": "Описание персоны",
@@ -629,16 +629,15 @@
"Most chats": "Больше всего чатов",
"Least chats": "Меньше всего чатов",
"Back": "Назад",
- "Prompt Overrides (For OpenAI/Claude/Scale APIs, Window/OpenRouter, and Instruct mode)": "Перезапись промпта (Для OpenAI/Claude/Scale API, Window/OpenRouter, и режима Instruct)",
- "Insert {{original}} into either box to include the respective default prompt from system settings.": "Введите {{original}} в любое поле, чтобы использовать соответствующий промпт из системных настроек",
+ "Prompt Overrides": "Индивидуальный промпт",
+ "(For OpenAI/Claude/Scale APIs, Window/OpenRouter, and Instruct Mode)": "(для API OpenAI/Claude/Scale, Window/OpenRouter, а также режима Instruct)",
+ "Insert {{original}} into either box to include the respective default prompt from system settings.": "Введите {{original}} в любое поле, чтобы вставить соответствующий промпт из системных настроек",
"Main Prompt": "Основной промпт",
"Jailbreak": "Джейлбрейк",
- "Creator's Metadata (Not sent with the AI prompt)": "Метаданные (не отправляются ИИ)",
"Everything here is optional": "Все поля необязательные",
"Created by": "Автор",
"Character Version": "Версия персонажа",
"Tags to Embed": "Встраиваемые теги",
- "How often the character speaks in group chats!": "Как часто персонаж говорит в групповых чатах",
"Important to set the character's writing style.": "Серьёзно влияет на стиль письма персонажа.",
"ATTENTION!": "ВНИМАНИЕ!",
"Samplers Order": "Порядок сэмплеров",
@@ -655,7 +654,7 @@
"Use 'Unlocked Context' to enable chunked generation.": "Использовать 'Неограниченный контекст' для активации кусочной генерации",
"It extends the context window in exchange for reply generation speed.": "Увеличивает размер контекста в обмен на скорость генерации.",
"Continue": "Продолжить",
- "CFG Scale": "Масштаб CFG",
+ "CFG Scale": "CFG Scale",
"Editing:": "Изменения",
"AI reply prefix": "Префикс для ответа ИИ",
"Custom Stopping Strings": "Стоп-строки",
@@ -671,9 +670,9 @@
"Chat Name (Optional)": "Название чата (необязательно)",
"Filter...": "Фильтры...",
"Search...": "Поиск...",
- "Any contents here will replace the default Main Prompt used for this character. (v2 spec: system_prompt)": "Все содержание этой ячейки будет заменять стандартный Промт",
- "Any contents here will replace the default Jailbreak Prompt used for this character. (v2 spec: post_history_instructions)": "Все содержание этой ячейки будет заменять стандартный Джейлбрейк",
- "(Botmaker's name / Contact Info)": "(Имя автора / Контакты)",
+ "Any contents here will replace the default Main Prompt used for this character. (v2 spec: system_prompt)": "Все содержимое этого поля будет заменять стандартный промпт",
+ "Any contents here will replace the default Jailbreak Prompt used for this character. (v2 spec: post_history_instructions)": "Все содержимое этого поля будет заменять стандартный джейлбрейк",
+ "(Botmaker's name / Contact Info)": "(Имя автора, контакты)",
"(If you want to track character versions)": "Если вы хотите отслеживать версии персонажа",
"(Describe the bot, give use tips, or list the chat models it has been tested on. This will be displayed in the character list.)": "(Описание персонажа, советы по использованию, список моделей, на которых он тестировался. Информация будет отображаться в списке персонажей)",
"(Write a comma-separated list of tags)": "(Список тегов через запятую)",
@@ -713,12 +712,12 @@
"Restore defaul note": "Восстановить стандартную заметку",
"API Connections": "Соединения с API",
"Can help with bad responses by queueing only the approved workers. May slowdown the response time.": "Может помочь с плохими ответами ставя в очередь только подтвержденных работников. Может замедлить время ответа.",
- "Clear your API key": "Очистите свой ключ от API",
+ "Clear your API key": "Стереть ключ от API",
"Refresh models": "Обновить модели",
"Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai": "Получите свой OpenRouter API токен используя OAuth. У вас будет открыта вкладка openrouter.ai",
"Verifies your API connection by sending a short test message. Be aware that you'll be credited for it!": "Проверка работоспособности вашего соединения с API. Знайте, что оно будет отправлено от вашего лица.",
"Create New": "Создать новое",
- "Edit": "Изменить",
+ "Edit": "Редактировать",
"Locked = World Editor will stay open": "Закреплено = Редактор мира останется открытым",
"Entries can activate other entries by mentioning their keywords": "Записи могут активировать другие записи, если в них содержатся ключевые слова",
"Lookup for the entry keys in the context will respect the case": "Большая буква имеет значение при активации ключевого слова",
@@ -847,7 +846,7 @@
"Underlined Text": "Подчёркнутый",
"Token Probabilities": "Вероятности токенов",
"Close chat": "Закрыть чат",
- "Manage chat files": "Управление файлами чата",
+ "Manage chat files": "Управление чатами",
"Import Extension From Git Repo": "Импортировать расширение из Git Repository",
"Install extension": "Установить расширение",
"Manage extensions": "Управление расширениями",
@@ -863,12 +862,12 @@
"When this is off, responses will be displayed all at once when they are complete.": "Если параметр выключен, ответы будут отображаться сразу целиком, и только после полного завершения генерации.",
"Quick Prompts Edit": "Быстрое редактирование промптов",
"Enable OpenAI completion streaming": "Включить стриминг OpenAI",
- "Main": "Главное",
+ "Main": "Основной",
"Utility Prompts": "Служебные промпты",
"Add character names": "Добавить имена персонажей",
"Send names in the message objects. Helps the model to associate messages with characters.": "Отправить имена в объектах сообщений. Помогает модели ассоциировать сообщения с персонажами.",
"Continue prefill": "Префилл для продолжения",
- "Continue sends the last message as assistant role instead of system message with instruction.": "Продолжение отправляет последнее сообщение в роли ассистента, а не системное сообщение с инструкцией.",
+ "Continue sends the last message as assistant role instead of system message with instruction.": "Продолжение отправляет последнее сообщение в роли ассистента, вместо системного сообщения с инструкцией.",
"Squash system messages": "Склеивать сообщения системыы",
"Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "Объединяет последовательные системные сообщения в одно (за исключением примеров диалогов). Может улучшить согласованность для некоторых моделей.",
"Send inline images": "Отправлять встроенные изображения",
@@ -973,12 +972,128 @@
"Most tokens have a leading space.": "У большинства токенов в начале пробел.",
"Prompts": "Промпты",
"Text or token ids": "Текст или [идентификаторы токенов]",
- "World Info Format Template": "Шаблон форматирования информации о мире",
+ "World Info Format Template": "Шаблон оформления информации о мире",
"Wraps activated World Info entries before inserting into the prompt.": "Дополняет информацию об активном на данный момент мире перед её отправкой в промпт.",
"Doesn't work? Try adding": "Не работает? Попробуйте добавить в конце",
"at the end!": "!",
"Authorize": "Авторизоваться",
"No persona description": "[Нет описания]",
"Not connected to API!": "Нет соединения с API!",
- "Type a message, or /? for help": "Введите сообщение, или /? для получения справки по командам"
+ "Type a message, or /? for help": "Введите сообщение, или /? для получения справки по командам",
+ "Welcome to SillyTavern!": "Добро пожаловать в SillyTavern!",
+ "Won't be shared with the character card on export.": "Не попадут в карточку персонажа при экспорте.",
+ "Web-search": "Веб-поиск",
+ "Persona Name:": "Имя персоны:",
+ "User first message": "Первое сообщение пользователя",
+ "extension_token_counter": "Токенов:",
+ "Character's Note": "Заметка о персонаже",
+ "(Text to be inserted in-chat @ designated depth and role)": "Этот текст будет вставлен в чат на заданную глубину и с определённой ролью",
+ "@ Depth": "Глубина",
+ "Role": "Роль",
+ "System": "Система",
+ "User": "Пользователь",
+ "Assistant": "Ассистент",
+ "How often the character speaks in": "Как часто персонаж говорит в",
+ "group chats!": "групповых чатах!",
+ "Creator's Metadata": "Метаданные",
+ "(Not sent with the AI Prompt)": "(не отправляются ИИ)",
+ "New Chat": "Новый чат",
+ "Import Chat": "Импорт чата",
+ "Chat Lore": "Лор чата",
+ "Chat Lorebook for": "Лорбук для чата",
+ "A selected World Info will be bound to this chat.": "Выбранный мир будет привязан к этому чату. При генерации ответа ИИ он будет совмещён с записями из глобального лорбука и лорбука персонажа.",
+ "Missing key": "❌ Ключа нет",
+ "Key saved": "✔️ Ключ сохранён",
+ "Use the appropriate tokenizer for Jurassic models, which is more efficient than GPT's.": "Использовать токенайзер для моделей Jurassic, эффективнее GPT-токенайзера",
+ "Use system prompt (Gemini 1.5 pro+ only)": "Использовать системный промпт (только для Gemini 1.5 pro и выше)",
+ "Experimental feature. May not work for all backends.": "Экспериментальная возможность, на некоторых бэкендах может не работать.",
+ "Avatar Hover Magnification": "Зум аватарки по наведению",
+ "Enable magnification for zoomed avatar display.": "Добавляет возможность приближать увеличенную версию аватарки.",
+ "Unique to this chat": "Только для текущего чата",
+ "Checkpoints inherit the Note from their parent, and can be changed individually after that.": "Чекпоинты наследуют заметки от родительского чата, но впоследствие их всегда можно изменить.",
+ "Include in World Info Scanning": "Учитывать при сканировании Информации о мире",
+ "Before Main Prompt / Story String": "Перед основным промптом / строкой истории",
+ "After Main Prompt / Story String": "После основного промпта / строки истории",
+ "In-chat @ Depth": "Встав. на глуб.",
+ "as": "роль:",
+ "Insertion Frequency": "Частота вставки",
+ "(0 = Disable, 1 = Always)": "(0 = никогда, 1 = всегда)",
+ "User inputs until next insertion:": "Ваших сообщений до след. вставки:",
+ "Character Author's Note (Private)": "Заметки автора персонажа (личные)",
+ "Will be automatically added as the author's note for this character. Will be used in groups, but can't be modified when a group chat is open.": "Автоматически применятся к этому персонажу в качестве заметок автора. Будут использоваться в группах, но при активном групповом чате к редактированию недоступны.",
+ "Use character author's note": "Использовать заметки автора персонажа",
+ "Replace Author's Note": "Вместо заметок автора",
+ "Top of Author's Note": "Сверху от заметок автора",
+ "Bottom of Author's Note": "Снизу от заметок автора",
+ "Default Author's Note": "Стандартные заметки автора",
+ "Will be automatically added as the Author's Note for all new chats.": "Будут автоматически добавляться во все новые чаты в качестве Заметок автора",
+ "1 = disabled": "1 = откл.",
+ "write short replies, write replies using past tense": "пиши короткие ответы, пиши в настоящем времени",
+ "Positive Prompt": "Положительный промпт",
+ "Character CFG": "CFG для персонажа",
+ "Will be automatically added as the CFG for this character.": "Автоматически применится к персонажу как его CFG.",
+ "Global CFG": "Глобальный CFG",
+ "Will be used as the default CFG options for every chat unless overridden.": "Будет применяться как стандартный CFG для всех чатов, если не указаны индивидуальные настройки.",
+ "CFG Prompt Cascading": "Совмещение CFG-промптов",
+ "Combine positive/negative prompts from other boxes.": "Комбинировать различные положительные и негативные промпты.",
+ "For example, ticking the chat, global, and character boxes combine all negative prompts into a comma-separated string.": "К примеру, если отметить галочки с чатом, персонажем и глобальной настройкой, то все эти негативы соберутся в одну строку, разделённую запятыми.",
+ "Always Include": "Всегда применять",
+ "Chat Negatives": "Негативы от чата",
+ "Character Negatives": "Негативы от персонажа",
+ "Global Negatives": "Глобальные негативы",
+ "Custom Separator:": "Кастомный разделитель:",
+ "Insertion Depth:": "Глубина вставки:",
+ "Chat CFG": "CFG для чата",
+ "Chat backgrounds generated with the": "Здесь будут появляться фоны, сгенерированные расширением",
+ "extension will appear here.": ".",
+ "Prevent further recursion (this entry will not activate others)": "Пресечь дальнейшую рекурсию (эта запись не будет активировать другие)",
+ "Alert if your world info is greater than the allocated budget.": "Оповещать, если ваш мир выходит за выделенный бюджет.",
+ "Convert to Persona": "Преобразовать в персону",
+ "Link to Source": "Ссылка на источник",
+ "Replace / Update": "Заменить / Обновить",
+ "Smoothing Curve": "Кривая сглаживания",
+ "Message Actions": "Действия с сообщением",
+ "SillyTavern is aimed at advanced users.": "SillyTavern рассчитана на продвинутых пользователей.",
+ "If you're new to this, enable the simplified UI mode below.": "Если вы новичок, советуем включить упрощённый UI.",
+ "Enable simple UI mode": "Включить упрощённый UI",
+ "welcome_message_part_1": "Ознакомьтесь с",
+ "welcome_message_part_2": "официальной документацией",
+ "welcome_message_part_3": ".",
+ "welcome_message_part_4": "Введите",
+ "welcome_message_part_5": "в чате, чтобы получить справку по командам и макросам.",
+ "welcome_message_part_6": "Заходите на наш",
+ "Discord server": "Discord-сервер,",
+ "welcome_message_part_7": "там публикуется много разной полезной информации, в том числе анонсы.",
+ "Before you get started, you must select a persona name.": "Для начала вам следует выбрать имя своей персоны.",
+ "welcome_message_part_8": "Его можно будет изменить в любое время через иконку",
+ "welcome_message_part_9": ".",
+ "UI Language:": "Язык интерфейса:",
+ "Ignore EOS Token": "Игнорировать EOS-токен",
+ "Ignore the EOS Token even if it generates.": "Игнорировать EOS-токен, даже если он сгенерировался.",
+ "Hide Muted Member Sprites": "Скрыть спрайты заглушенных участников",
+ "Group generation handling mode": "Генерировать ответы путём...",
+ "Swap character cards": "Подмены карточки персонажа",
+ "Join character cards (exclude muted)": "Совмещения карточек (кроме заглушенных)",
+ "Join character cards (include muted)": "Совмещения карточек (включая заглушенных)",
+ "Click to allow/forbid the use of external media for this group.": "Нажмите, чтобы разрешить/запретить использование внешних медиа в этой группе.",
+ "Scenario Format Template": "Шаблон оформления сценария",
+ "scenario_format_template_part_1": "Используйте",
+ "scenario_format_template_part_2": "чтобы указать, куда именно вставляется основное содержимое.",
+ "Personality Format Template": "Шаблон оформления характера",
+ "Group Nudge Prompt Template": "Шаблон промпта-подсказки для групп",
+ "Sent at the end of the group chat history to force reply from a specific character.": "Добавляется в конец истории сообщений в групповом чате, чтобы запросить ответ от конкретного персонажа.",
+ "Set at the beginning of the chat history to indicate that a new chat is about to start.": "Добавляется в начале истории сообщений в качестве указания на то, что дальше начнётся новый чат.",
+ "New Group Chat": "Новый групповой чат",
+ "Set at the beginning of the chat history to indicate that a new group chat is about to start.": "Добавляется в начале истории сообщений в качестве указания на то, что дальше начнётся новый групповой чат.",
+ "New Example Chat": "Новый образец чата",
+ "Set at the beginning of Dialogue examples to indicate that a new example chat is about to start.": "Добавляется в начале примеров диалогов в качестве указания на то, что дальше начнётся новый чат-пример.",
+ "Continue nudge": "Подсказка для продолжения",
+ "Set at the end of the chat history when the continue button is pressed.": "Добавляется в конец истории чата, когда отправлен запрос на продолжение текущего сообщения.",
+ "Prompts": "Промпты",
+ "Your Persona": "Ваша персона",
+ "Continue Postfix": "Постфикс для продолжения",
+ "Space": "Пробел",
+ "Newline": "Новая строка",
+ "Double Newline": "Две новые строки",
+ "The next chunk of the continued message will be appended using this as a separator.": "Используется в качестве разделителя между уже имеющимся сообщением и его новым отрывком, при генерации продолжения"
}
diff --git a/public/scripts/i18n.js b/public/scripts/i18n.js
index cf5e0bd9a..def67def4 100644
--- a/public/scripts/i18n.js
+++ b/public/scripts/i18n.js
@@ -1,4 +1,5 @@
import { registerDebugFunction } from './power-user.js';
+import { updateSecretDisplay } from './secrets.js';
const storageKey = 'language';
const overrideLanguage = localStorage.getItem(storageKey);
@@ -12,10 +13,8 @@ const localeData = await getLocaleData(localeFile);
* @returns {Promise
>} Locale data
*/
async function getLocaleData(language) {
- let supportedLang = langs.find(x => x.lang === language);
-
+ let supportedLang = findLang(language);
if (!supportedLang) {
- console.warn(`Unsupported language: ${language}`);
return {};
}
@@ -30,11 +29,24 @@ async function getLocaleData(language) {
return data;
}
+function findLang(language) {
+ var supportedLang = langs.find(x => x.lang === language);
+
+ if (!supportedLang) {
+ console.warn(`Unsupported language: ${language}`);
+ }
+ return supportedLang;
+}
+
async function getMissingTranslations() {
const missingData = [];
- for (const language of langs) {
- const localeData = await getLocaleData(language);
+ // Determine locales to search for untranslated strings
+ const isNotSupported = !findLang(localeFile);
+ const langsToProcess = (isNotSupported || localeFile == 'en') ? langs : [findLang(localeFile)];
+
+ for (const language of langsToProcess) {
+ const localeData = await getLocaleData(language.lang);
$(document).find('[data-i18n]').each(function () {
const keys = $(this).data('i18n').split(';'); // Multi-key entries are ; delimited
for (const key of keys) {
@@ -42,12 +54,12 @@ async function getMissingTranslations() {
if (attributeMatch) { // attribute-tagged key
const localizedValue = localeData?.[attributeMatch[2]];
if (!localizedValue) {
- missingData.push({ key, language, value: $(this).attr(attributeMatch[1]) });
+ missingData.push({ key, language: language.lang, value: $(this).attr(attributeMatch[1]) });
}
} else { // No attribute tag, treat as 'text'
const localizedValue = localeData?.[key];
if (!localizedValue) {
- missingData.push({ key, language, value: $(this).text().trim() });
+ missingData.push({ key, language: language.lang, value: $(this).text().trim() });
}
}
}
@@ -130,6 +142,7 @@ function addLanguagesToDropdown() {
export function initLocales() {
applyLocale();
addLanguagesToDropdown();
+ updateSecretDisplay();
$('#ui_language_select').on('change', async function () {
const language = String($(this).val());
@@ -143,6 +156,6 @@ export function initLocales() {
location.reload();
});
- registerDebugFunction('getMissingTranslations', 'Get missing translations', 'Detects missing localization data and dumps the data into the browser console.', getMissingTranslations);
+ registerDebugFunction('getMissingTranslations', 'Get missing translations', 'Detects missing localization data in the current locale and dumps the data into the browser console. If the current locale is English, searches all other locales.', getMissingTranslations);
registerDebugFunction('applyLocale', 'Apply locale', 'Reapplies the currently selected locale to the page.', applyLocale);
}
diff --git a/public/scripts/secrets.js b/public/scripts/secrets.js
index f6ebd1f2a..00c322353 100644
--- a/public/scripts/secrets.js
+++ b/public/scripts/secrets.js
@@ -62,10 +62,11 @@ async function clearSecret() {
$('#main_api').trigger('change');
}
-function updateSecretDisplay() {
+export function updateSecretDisplay() {
for (const [secret_key, input_selector] of Object.entries(INPUT_MAP)) {
const validSecret = !!secret_state[secret_key];
- const placeholder = validSecret ? '✔️ Key saved' : '❌ Missing key';
+
+ const placeholder = $('#viewSecrets').attr(validSecret ? 'key_saved_text' : 'missing_key_text');
$(input_selector).attr('placeholder', placeholder);
}
}