feat: add full Russian (ru-RU) localization — v0.50.95 (PR #713)
Full Russian locale — 389/389 English keys, Slavic plural forms, native Cyrillic. Rebased from PR #605 with rebase artifacts fixed. Login page Russian added to api/routes.py. Credits: @DrMaks22 (translation), @renheqiang (PR #605 author). Co-authored-by: DrMaks22 <DrMaks22@users.noreply.github.com> Co-authored-by: renheqiang <renheqiang@users.noreply.github.com>
This commit is contained in:
475
static/i18n.js
475
static/i18n.js
@@ -450,6 +450,11 @@ const LOCALES = {
|
||||
profile_use: 'Use',
|
||||
profile_switch_title: 'Switch to this profile',
|
||||
profile_delete_title: 'Delete this profile',
|
||||
profile_default_label: '(default)',
|
||||
profile_name_placeholder: 'Profile name (lowercase, a-z 0-9 hyphens)',
|
||||
profile_clone_label: 'Clone config from active profile',
|
||||
profile_base_url_placeholder: 'Base URL (optional, e.g. http://localhost:11434)',
|
||||
profile_api_key_placeholder: 'API key (optional)',
|
||||
manage_profiles: 'Manage profiles',
|
||||
profiles_load_failed: 'Failed to load profiles',
|
||||
profiles_busy_switch: 'Cannot switch profiles while agent is running',
|
||||
@@ -473,6 +478,465 @@ const LOCALES = {
|
||||
bg_error_multi: (count) => `${count} sessions have encountered an error`,
|
||||
},
|
||||
|
||||
ru: {
|
||||
_lang: 'ru',
|
||||
_label: 'Русский',
|
||||
_speech: 'ru-RU',
|
||||
cancelling: 'Отменяю…',
|
||||
cancel_failed: 'Не удалось отменить: ',
|
||||
mic_denied: 'Доступ к микрофону запрещён. Проверьте разрешения браузера.',
|
||||
mic_no_speech: 'Речь не распознана. Попробуйте ещё раз.',
|
||||
mic_network: 'Распознавание речи недоступно.',
|
||||
mic_error: 'Ошибка ввода речи: ',
|
||||
session_imported: 'Сеанс импортирован',
|
||||
import_failed: 'Не удалось импортировать: ',
|
||||
import_invalid_json: 'Неверный JSON',
|
||||
image_pasted: 'Изображение вставлено: ',
|
||||
edit_message: 'Редактировать сообщение',
|
||||
regenerate: 'Сгенерировать ответ заново',
|
||||
copy: 'Копировать',
|
||||
copied: 'Скопировано!',
|
||||
you: 'Вы',
|
||||
thinking: 'Думаю',
|
||||
expand_all: 'Развернуть всё',
|
||||
collapse_all: 'Свернуть всё',
|
||||
edit_failed: 'Не удалось отредактировать: ',
|
||||
regen_failed: 'Не удалось сгенерировать заново: ',
|
||||
reconnect_active: 'Ответ всё ещё генерируется. Обновить, когда будет готово?',
|
||||
reconnect_finished: 'Когда вы уходили, ответ ещё генерировался. Сообщения могли обновиться.',
|
||||
approval_heading: 'Требуется подтверждение',
|
||||
approval_desc_prefix: 'Обнаружена опасная команда',
|
||||
approval_btn_once: 'Разрешить один раз',
|
||||
approval_btn_once_title: 'Разрешить только эту команду (Enter)',
|
||||
approval_btn_session: 'Разрешить на этот сеанс',
|
||||
approval_btn_session_title: 'Разрешить для этого сеанса разговора',
|
||||
approval_btn_always: 'Всегда разрешать',
|
||||
approval_btn_always_title: 'Всегда разрешать команды по этому шаблону',
|
||||
approval_btn_deny: 'Запретить',
|
||||
approval_btn_deny_title: 'Запретить — не выполнять эту команду',
|
||||
approval_responding: 'Отвечаю…',
|
||||
untitled: 'Без названия',
|
||||
n_messages: (n) => `${n} сообщений`,
|
||||
model_unavailable: ' (недоступна)',
|
||||
model_unavailable_title: 'Эта модель больше не входит в ваш текущий список провайдеров',
|
||||
provider_mismatch_warning: (m, p) =>
|
||||
`"${m}" может не работать с вашим настроенным провайдером (${p}). Всё равно отправить или запустите \`hermes model\` в терминале, чтобы переключиться.`,
|
||||
provider_mismatch_label: 'Несовпадение провайдера',
|
||||
model_custom_label: 'Пользовательский ID модели',
|
||||
model_custom_placeholder: 'например, openai/gpt-5.4',
|
||||
cmd_help: 'Показать доступные команды',
|
||||
cmd_clear: 'Очистить сообщения беседы',
|
||||
cmd_compact: 'Сжать контекст беседы',
|
||||
cmd_model: 'Переключить модель (например, /model gpt-4o)',
|
||||
cmd_workspace: 'Переключить рабочее пространство по названию',
|
||||
cmd_new: 'Начать новую сессию чата',
|
||||
cmd_usage: 'Показать или скрыть использование токенов',
|
||||
cmd_theme: 'Переключить тему (dark/light/slate/solarized/monokai/nord/oled)',
|
||||
cmd_personality: 'Переключить личность агента',
|
||||
cmd_skills: 'Показать доступные навыки Hermes',
|
||||
available_commands: 'Доступные команды:',
|
||||
type_slash: 'Введите /, чтобы увидеть команды',
|
||||
conversation_cleared: 'Беседа очищена',
|
||||
model_usage: 'Использование: /model <name>',
|
||||
no_model_match: 'Нет модели, соответствующей "',
|
||||
switched_to: 'Переключено на ',
|
||||
workspace_usage: 'Использование: /workspace <name>',
|
||||
no_workspace_match: 'Нет рабочего пространства, соответствующего "',
|
||||
switched_workspace: 'Переключено на рабочее пространство: ',
|
||||
workspace_switch_failed: 'Не удалось переключить рабочее пространство: ',
|
||||
new_session: 'Новая сессия создана',
|
||||
compressing: 'Запрашиваю сжатие контекста...',
|
||||
token_usage_on: 'Отображение токенов включено',
|
||||
token_usage_off: 'Отображение токенов выключено',
|
||||
theme_usage: 'Использование: /theme ',
|
||||
theme_set: 'Тема: ',
|
||||
no_active_session: 'Нет активной сессии',
|
||||
|
||||
no_personalities: 'Личности не найдены (добавьте их в ~/.hermes/personalities/)',
|
||||
clarify_heading: 'Требуется уточнение',
|
||||
clarify_hint: 'Выберите вариант или введите свой ответ ниже.',
|
||||
clarify_input_placeholder: 'Введите ответ…',
|
||||
clarify_other: 'Другое',
|
||||
clarify_responding: 'Отвечаю…',
|
||||
clarify_send: 'Отправить',
|
||||
cmd_compact_alias: 'Устаревший псевдоним для /compress',
|
||||
cmd_compress: 'Сжать контекст беседы (использование: /compress [тема])',
|
||||
command_label: 'Команда',
|
||||
compress_complete_label: 'Сжатие завершено',
|
||||
compress_failed_label: 'Ошибка сжатия',
|
||||
compress_running_label: 'Сжатие…',
|
||||
context_compaction_label: 'Сжатие контекста',
|
||||
focus_label: 'Фокус',
|
||||
model_search_no_results: 'Модели не найдены',
|
||||
model_search_placeholder: 'Поиск моделей…',
|
||||
reference_only_label: 'Только справка',
|
||||
settings_label_skin: 'Скин',
|
||||
workspace_empty_dir: 'Это рабочее пространство пусто.',
|
||||
workspace_empty_no_path: 'Рабочее пространство не выбрано. Настройте его в Настройки → Рабочее пространство.',
|
||||
available_personalities: 'Доступные личности:',
|
||||
personality_switch_hint: '\n\nИспользуйте `/personality <name>` для переключения или `/personality none` для сброса.',
|
||||
personalities_load_failed: 'Не удалось загрузить личности',
|
||||
personality_cleared: 'Личность очищена',
|
||||
personality_set: 'Личность: ',
|
||||
failed_colon: 'Не удалось: ',
|
||||
no_workspace: 'Нет рабочего пространства',
|
||||
dialog_confirm_title: 'Подтвердить действие',
|
||||
dialog_prompt_title: 'Введите значение',
|
||||
dialog_confirm_btn: 'Подтвердить',
|
||||
unsaved_confirm: 'У вас есть несохранённые изменения в предпросмотре. Отменить и перейти дальше?',
|
||||
discard: 'Отменить',
|
||||
save: 'Сохранить',
|
||||
edit: 'Редактировать',
|
||||
clear: 'Очистить',
|
||||
create: 'Создать',
|
||||
remove: 'Удалить',
|
||||
save_title: 'Сохранить изменения',
|
||||
edit_title: 'Редактировать этот файл',
|
||||
saved: 'Сохранено',
|
||||
save_failed: 'Не удалось сохранить: ',
|
||||
image_load_failed: 'Не удалось загрузить изображение',
|
||||
file_open_failed: 'Не удалось открыть файл',
|
||||
downloading: (name) => `Скачиваю ${name}…`,
|
||||
double_click_rename: 'Дважды щёлкните, чтобы переименовать',
|
||||
renamed_to: 'Переименовано в ',
|
||||
rename_failed: 'Не удалось переименовать: ',
|
||||
delete_title: 'Удалить',
|
||||
delete_confirm: (name) => `Удалить ${name}?`,
|
||||
deleted: 'Удалено ',
|
||||
delete_failed: 'Не удалось удалить: ',
|
||||
new_file_prompt: 'Имя нового файла (например, notes.md):',
|
||||
project_name_prompt: 'Имя проекта:',
|
||||
created: 'Создано ',
|
||||
create_failed: 'Не удалось создать: ',
|
||||
new_folder_prompt: 'Имя новой папки:',
|
||||
folder_created: 'Папка создана ',
|
||||
folder_create_failed: 'Не удалось создать папку: ',
|
||||
remove_title: 'Удаление',
|
||||
empty_dir: '(пусто)',
|
||||
upload_failed: 'Не удалось загрузить: ',
|
||||
all_uploads_failed: (n) => `Не удалось загрузить все ${n} файлов`,
|
||||
settings_title: 'Настройки',
|
||||
settings_save_btn: 'Сохранить настройки',
|
||||
settings_label_model: 'Модель по умолчанию',
|
||||
settings_label_send_key: 'Клавиша отправки',
|
||||
settings_label_theme: 'Тема',
|
||||
settings_label_language: 'Язык',
|
||||
settings_label_token_usage: 'Показывать использование токенов',
|
||||
settings_label_bubble_layout: 'Раскладка пузырьков чата',
|
||||
settings_label_cli_sessions: 'Показывать сеансы агента',
|
||||
settings_label_sync_insights: 'Синхронизировать с Insights',
|
||||
settings_label_check_updates: 'Проверять обновления',
|
||||
settings_label_bot_name: 'Имя помощника',
|
||||
settings_label_password: 'Пароль доступа',
|
||||
settings_saved: 'Настройки сохранены',
|
||||
settings_save_failed: 'Не удалось сохранить: ',
|
||||
settings_load_failed: 'Не удалось загрузить настройки: ',
|
||||
settings_saved_pw: 'Настройки сохранены (пароль задан — теперь требуется вход)',
|
||||
settings_saved_pw_updated: 'Настройки сохранены (пароль обновлён)',
|
||||
login_title: 'Вход',
|
||||
login_subtitle: 'Введите пароль, чтобы продолжить',
|
||||
login_placeholder: 'Пароль',
|
||||
login_btn: 'Войти',
|
||||
login_invalid_pw: 'Неверный пароль',
|
||||
login_conn_failed: 'Не удалось подключиться',
|
||||
tab_chat: 'Чат',
|
||||
tab_tasks: 'Задачи',
|
||||
tab_skills: 'Навыки',
|
||||
tab_memory: 'Память',
|
||||
tab_workspaces: 'Рабочие пространства',
|
||||
tab_profiles: 'Профили',
|
||||
tab_todos: 'Список дел',
|
||||
new_conversation: 'Новая беседа',
|
||||
filter_conversations: 'Фильтр бесед...',
|
||||
session_time_unknown: 'Неизвестно',
|
||||
session_time_just_now: 'только что',
|
||||
session_time_minutes_ago: (n) => {
|
||||
const mod10 = n % 10;
|
||||
const mod100 = n % 100;
|
||||
const word = mod10 === 1 && mod100 !== 11
|
||||
? 'минута'
|
||||
: (mod10 >= 2 && mod10 <= 4 && (mod100 < 10 || mod100 >= 20)
|
||||
? 'минуты'
|
||||
: 'минут');
|
||||
return `${n} ${word} назад`;
|
||||
},
|
||||
session_time_hours_ago: (n) => {
|
||||
const mod10 = n % 10;
|
||||
const mod100 = n % 100;
|
||||
const word = mod10 === 1 && mod100 !== 11
|
||||
? 'час'
|
||||
: (mod10 >= 2 && mod10 <= 4 && (mod100 < 10 || mod100 >= 20)
|
||||
? 'часа'
|
||||
: 'часов');
|
||||
return `${n} ${word} назад`;
|
||||
},
|
||||
session_time_days_ago: (n) => {
|
||||
const mod10 = n % 10;
|
||||
const mod100 = n % 100;
|
||||
const word = mod10 === 1 && mod100 !== 11
|
||||
? 'день'
|
||||
: (mod10 >= 2 && mod10 <= 4 && (mod100 < 10 || mod100 >= 20)
|
||||
? 'дня'
|
||||
: 'дней');
|
||||
return `${n} ${word} назад`;
|
||||
},
|
||||
session_time_last_week: 'на прошлой неделе',
|
||||
session_time_bucket_today: 'Сегодня',
|
||||
session_time_bucket_yesterday: 'Вчера',
|
||||
session_time_bucket_this_week: 'На этой неделе',
|
||||
session_time_bucket_last_week: 'На прошлой неделе',
|
||||
session_time_bucket_older: 'Ранее',
|
||||
scheduled_jobs: 'Запланированные задания',
|
||||
new_job: 'Новое задание',
|
||||
loading: 'Загрузка...',
|
||||
search_skills: 'Поиск навыков...',
|
||||
new_skill: 'Новый навык',
|
||||
personal_memory: 'Личная память',
|
||||
current_task_list: 'Текущий список задач',
|
||||
workspace_desc: 'Добавляйте рабочие пространства и переключайтесь между ними в своих сеансах.',
|
||||
new_profile: 'Новый профиль',
|
||||
transcript: 'Транскрипт',
|
||||
download_transcript: 'Скачать как Markdown',
|
||||
import: 'Импорт',
|
||||
settings_label_sound: 'Звук уведомления',
|
||||
settings_desc_sound: 'Проигрывать звук, когда помощник завершает ответ.',
|
||||
settings_label_notifications: 'Уведомления браузера',
|
||||
settings_desc_notifications: 'Показывать системное уведомление, когда ответ готов, а вкладка находится в фоне.',
|
||||
settings_desc_token_usage: 'Показывает количество входных и выходных токенов под каждым ответом помощника. Также переключается через /usage.',
|
||||
settings_desc_bubble_layout: 'Выравнивает сообщения пользователя справа, а ответы помощника слева. Выключено по умолчанию, чтобы блоки кода и вывод инструментов занимали всю ширину.',
|
||||
settings_desc_cli_sessions: 'Объединяет сеансы из Hermes CLI (state.db) в список сеансов. Нажмите на CLI-сеанс, чтобы импортировать его и продолжить разговор.',
|
||||
settings_desc_sync_insights: 'Синхронизирует использование токенов WebUI в state.db, чтобы Hermes /insights включал данные браузерных сеансов. Выключено по умолчанию.',
|
||||
settings_desc_check_updates: 'Показывает баннер, когда доступны более новые версии WebUI или Agent. Периодически выполняет git fetch в фоне.',
|
||||
settings_desc_bot_name: 'Отображаемое имя помощника во всём интерфейсе. По умолчанию Hermes.',
|
||||
settings_desc_password: 'Введите новый пароль, чтобы задать или изменить его. Оставьте пустым, чтобы сохранить текущую настройку.',
|
||||
password_placeholder: 'Введите новый пароль…',
|
||||
disable_auth: 'Отключить авторизацию',
|
||||
sign_out: 'Выйти',
|
||||
cancel: 'Отмена',
|
||||
create_job: 'Создать задание',
|
||||
save_skill: 'Сохранить навык',
|
||||
editing: 'Редактирование',
|
||||
empty_title: 'Чем я могу помочь?',
|
||||
empty_subtitle: 'Спрашивайте что угодно, запускайте команды, изучайте файлы или управляйте запланированными задачами.',
|
||||
suggest_files: 'Какие файлы есть в этом рабочем пространстве?',
|
||||
suggest_schedule: 'Что у меня сегодня в расписании?',
|
||||
suggest_plan: 'Помоги спланировать небольшой проект.',
|
||||
onboarding_badge: 'ПЕРВЫЙ ЗАПУСК',
|
||||
onboarding_title: 'Добро пожаловать в Hermes Web UI',
|
||||
onboarding_lead: 'Краткая пошаговая настройка проверит Hermes, сохранит рабочую конфигурацию провайдера, выберет рабочее пространство и модель и при желании защитит приложение паролем.',
|
||||
onboarding_back: 'Назад',
|
||||
onboarding_continue: 'Продолжить',
|
||||
onboarding_skip: 'Пропустить настройку',
|
||||
onboarding_skipped: 'Настройка пропущена — используется существующая конфигурация.',
|
||||
onboarding_open: 'Открыть Hermes',
|
||||
onboarding_step_system_title: 'Проверка системы',
|
||||
onboarding_step_system_desc: 'Проверить Hermes Agent и видимость конфигурации.',
|
||||
onboarding_step_setup_title: 'Настройка провайдера',
|
||||
onboarding_step_setup_desc: 'Сохранить минимальную рабочую конфигурацию провайдера Hermes.',
|
||||
onboarding_step_workspace_title: 'Рабочее пространство и модель',
|
||||
onboarding_step_workspace_desc: 'Выбрать значения по умолчанию для новых сеансов и чатов.',
|
||||
onboarding_step_password_title: 'Необязательный пароль',
|
||||
onboarding_step_password_desc: 'Защитить Web UI перед тем, как делиться им.',
|
||||
onboarding_step_finish_title: 'Готово',
|
||||
onboarding_step_finish_desc: 'Проверьте настройки и войдите в приложение.',
|
||||
onboarding_notice_system_ready: 'Hermes Agent, похоже, доступен из Web UI.',
|
||||
onboarding_notice_system_unavailable: 'Hermes Agent ещё не полностью доступен. Bootstrap может установить его, но для настройки провайдера всё ещё может понадобиться терминал.',
|
||||
onboarding_check_agent: 'Hermes Agent',
|
||||
onboarding_check_agent_ready: 'Обнаружен и доступен для импорта',
|
||||
onboarding_check_agent_missing: 'Отсутствует или доступен только частично',
|
||||
onboarding_check_password: 'Пароль',
|
||||
onboarding_check_password_enabled: 'Уже включён',
|
||||
onboarding_check_password_disabled: 'Пока не включён',
|
||||
onboarding_check_provider: 'Конфигурация провайдера',
|
||||
onboarding_check_provider_ready: 'Готова к чату',
|
||||
onboarding_check_provider_partial: 'Сохранена, но не завершена',
|
||||
onboarding_check_provider_pending: 'Требует проверки',
|
||||
onboarding_config_file: 'Файл конфигурации:',
|
||||
onboarding_env_file: 'Файл .env:',
|
||||
onboarding_unknown: 'Неизвестно',
|
||||
onboarding_current_provider: 'Текущая конфигурация:',
|
||||
onboarding_missing_imports: 'Отсутствующие импорты:',
|
||||
onboarding_notice_setup_required: 'Выберите здесь простой путь настройки провайдера. Продвинутые OAuth-сценарии пока остаются в Hermes CLI.',
|
||||
onboarding_notice_setup_already_ready: 'Уже обнаружена рабочая конфигурация провайдера Hermes. Вы можете оставить её или заменить здесь.',
|
||||
onboarding_oauth_provider_ready_title: 'Провайдер уже авторизован',
|
||||
onboarding_oauth_provider_ready_body: 'Этот экземпляр настроен на использование OAuth-провайдера (<strong>{provider}</strong>), настроенного через Hermes CLI. API-ключ здесь не нужен — нажмите «Продолжить», чтобы завершить настройку.',
|
||||
onboarding_oauth_provider_not_ready_title: 'OAuth-провайдер ещё не авторизован',
|
||||
onboarding_oauth_provider_not_ready_body: 'Этот экземпляр настроен на использование <strong>{provider}</strong>, который работает через OAuth, а не через API-ключ. Запустите <code>hermes auth</code> или <code>hermes model</code> в терминале, чтобы пройти авторизацию, затем обновите Web UI.',
|
||||
onboarding_oauth_switch_hint: 'Или выберите ниже другой провайдер, чтобы перейти на настройку с ключом API:',
|
||||
onboarding_notice_workspace: 'Эти значения используют те же API настроек, что и обычное приложение.',
|
||||
onboarding_workspace_label: 'Рабочее пространство',
|
||||
onboarding_workspace_or_path: 'Или укажите путь к рабочему пространству',
|
||||
onboarding_workspace_placeholder: '/home/you/workspace',
|
||||
onboarding_provider_label: 'Режим настройки',
|
||||
onboarding_quick_setup_badge: 'Быстрая настройка',
|
||||
onboarding_api_key_label: 'Ключ API',
|
||||
onboarding_api_key_placeholder: 'Оставьте пустым, чтобы сохранить уже сохранённый ключ',
|
||||
onboarding_api_key_help_prefix: 'Сохраняется как секрет в вашем файле `.env` Hermes с помощью',
|
||||
onboarding_base_url_label: 'Базовый URL',
|
||||
onboarding_base_url_placeholder: 'https://your-endpoint.example/v1',
|
||||
onboarding_base_url_help: 'Используйте это для OpenAI-compatible маршрутизаторов, self-hosted серверов, LiteLLM, Ollama, LM Studio, vLLM и похожих endpoint-ов.',
|
||||
onboarding_model_label: 'Модель по умолчанию',
|
||||
onboarding_workspace_help: 'Выберите модель, которую Hermes должен использовать для новых чатов после завершения настройки.',
|
||||
onboarding_custom_model_placeholder: 'имя_вашей_модели',
|
||||
onboarding_custom_model_help: 'Для собственных endpoint-ов укажите точный ID модели, который ожидает ваш сервер.',
|
||||
onboarding_notice_password_enabled: 'Пароль уже настроен. Вводите новый только если хотите заменить текущий.',
|
||||
onboarding_notice_password_recommended: 'Необязательно, но рекомендуется, если вы собираетесь открывать UI не только на localhost.',
|
||||
onboarding_password_label: 'Пароль (необязательно)',
|
||||
onboarding_password_placeholder: 'Оставьте пустым, чтобы пропустить',
|
||||
onboarding_password_help: 'Пароли сохраняются через существующий API настроек и хэшируются на сервере.',
|
||||
onboarding_notice_finish: 'Позже вы сможете снова открыть настройки и изменить любое из этих значений.',
|
||||
onboarding_not_set: 'Не задано',
|
||||
onboarding_password_will_enable: 'Будет включён',
|
||||
onboarding_password_will_replace: 'Будет заменён текущий пароль',
|
||||
onboarding_password_keep_existing: 'Оставить текущий пароль',
|
||||
onboarding_password_remains_disabled: 'Останется отключённым',
|
||||
onboarding_password_skipped: 'Пропустить пока',
|
||||
onboarding_finish_help: 'После завершения в настройках сохранится <code>onboarding_completed</code>, и вы попадёте в обычное приложение.',
|
||||
onboarding_error_choose_workspace: 'Выберите рабочее пространство перед продолжением.',
|
||||
onboarding_error_choose_model: 'Выберите модель перед продолжением.',
|
||||
onboarding_error_provider_required: 'Выберите режим настройки перед продолжением.',
|
||||
onboarding_error_base_url_required: 'Для собственных endpoint-ов требуется базовый URL.',
|
||||
onboarding_error_workspace_required: 'Рабочее пространство обязательно.',
|
||||
onboarding_error_model_required: 'Модель обязательна.',
|
||||
onboarding_complete: 'Первичная настройка завершена',
|
||||
error_prefix: 'Ошибка: ',
|
||||
not_available: 'н/д',
|
||||
never: 'никогда',
|
||||
add: 'Добавить',
|
||||
add_failed: 'Не удалось добавить: ',
|
||||
remove_failed: 'Не удалось удалить: ',
|
||||
switch_failed: 'Не удалось переключить: ',
|
||||
name_required: 'Требуется имя',
|
||||
content_required: 'Требуется содержимое',
|
||||
view: 'Просмотр',
|
||||
dismiss: 'Скрыть',
|
||||
disable: 'Отключить',
|
||||
cron_no_jobs: 'Запланированные задания не найдены.',
|
||||
cron_status_off: 'неактивно',
|
||||
cron_status_paused: 'на паузе',
|
||||
cron_status_error: 'ошибка',
|
||||
cron_status_active: 'активно',
|
||||
cron_next: 'Следующий',
|
||||
cron_last: 'Последний',
|
||||
cron_run_now: 'Запустить сейчас',
|
||||
cron_pause: 'Пауза',
|
||||
cron_resume: 'Возобновить',
|
||||
cron_job_name_placeholder: 'Имя задания',
|
||||
cron_schedule_placeholder: 'Расписание',
|
||||
cron_prompt_placeholder: 'Промпт',
|
||||
cron_last_output: 'Последний вывод',
|
||||
cron_all_runs: 'Все запуски',
|
||||
cron_hide_runs: 'Скрыть запуски',
|
||||
cron_no_runs_yet: '(пока запусков нет)',
|
||||
cron_schedule_required_example: 'Требуется расписание (например, "0 9 * * *" или "every 1h")',
|
||||
cron_schedule_required: 'Требуется расписание',
|
||||
cron_prompt_required: 'Требуется промпт',
|
||||
cron_job_created: 'Задание создано',
|
||||
cron_job_triggered: 'Задание запущено',
|
||||
cron_job_paused: 'Задание поставлено на паузу',
|
||||
cron_job_resumed: 'Задание возобновлено',
|
||||
cron_job_updated: 'Задание обновлено',
|
||||
cron_delete_confirm_title: 'Удалить cron-задание',
|
||||
cron_delete_confirm_message: 'Это действие нельзя отменить.',
|
||||
cron_job_deleted: 'Задание удалено',
|
||||
cron_completion_status: (name, status) => `Cron-задание «${name}» — ${status}`,
|
||||
status_failed: 'неудачно',
|
||||
status_completed: 'завершено',
|
||||
todos_no_active: 'В этой сессии нет активного списка задач.',
|
||||
clear_conversation_title: 'Очистить беседу',
|
||||
clear_conversation_message: 'Очистить все сообщения? Это действие нельзя отменить.',
|
||||
clear_failed: 'Не удалось очистить: ',
|
||||
skills_no_match: 'Подходящих навыков не найдено.',
|
||||
linked_files: 'Связанные файлы',
|
||||
skill_load_failed: 'Не удалось загрузить навык: ',
|
||||
skill_file_load_failed: 'Не удалось загрузить файл: ',
|
||||
skill_name_required: 'Требуется имя навыка',
|
||||
skill_updated: 'Навык обновлён',
|
||||
skill_created: 'Навык создан',
|
||||
memory_notes_label: 'память (заметки)',
|
||||
memory_saved: 'Память сохранена',
|
||||
my_notes: 'Мои заметки',
|
||||
user_profile: 'Пользовательский профиль',
|
||||
no_notes_yet: 'Пока нет заметок.',
|
||||
no_profile_yet: 'Пока нет профиля.',
|
||||
workspace_choose_path: 'Выберите путь к рабочему пространству',
|
||||
workspace_choose_path_meta: 'Добавьте проверенный путь и переключите эту беседу',
|
||||
workspace_manage: 'Управление рабочими пространствами',
|
||||
workspace_manage_meta: 'Открыть панель Spaces',
|
||||
workspace_use_title: 'Использовать в текущем сеансе',
|
||||
workspace_use: 'Использовать',
|
||||
workspace_add_path_placeholder: 'Добавьте путь к рабочему пространству (например, /Users/you/project)',
|
||||
workspace_paths_validated_hint: 'Перед сохранением пути проверяются на существование.',
|
||||
workspace_added: 'Рабочее пространство добавлено',
|
||||
workspace_remove_confirm_title: 'Удалить рабочее пространство',
|
||||
workspace_remove_confirm_message: (path) => `Удалить «${path}»?`,
|
||||
workspace_removed: 'Рабочее пространство удалено',
|
||||
workspace_switch_prompt_title: 'Переключить рабочее пространство',
|
||||
workspace_switch_prompt_message: 'Введите абсолютный путь к рабочему пространству, чтобы добавить его и переключить эту беседу.',
|
||||
workspace_switch_prompt_confirm: 'Переключить',
|
||||
workspace_switch_prompt_placeholder: '/Users/you/project',
|
||||
workspace_not_added: 'Рабочее пространство не добавлено',
|
||||
workspace_already_saved: 'Рабочее пространство уже сохранено — выберите его из списка',
|
||||
workspace_busy_switch: 'Нельзя переключать рабочее пространство, пока агент работает',
|
||||
discard_file_edits_title: 'Отменить изменения файлов?',
|
||||
discard_file_edits_message: 'При переключении рабочих пространств несохранённые изменения в предпросмотре будут потеряны.',
|
||||
workspace_switched_to: (name) => `Переключено на ${name}`,
|
||||
profiles_no_profiles: 'Профили не найдены.',
|
||||
profile_api_keys_configured: 'API-ключи настроены',
|
||||
profile_gateway_running: 'Gateway запущен',
|
||||
profile_gateway_stopped: 'Gateway остановлен',
|
||||
profile_active: 'АКТИВЕН',
|
||||
profile_no_configuration: 'Нет конфигурации',
|
||||
profile_skill_count: (count) => {
|
||||
const mod10 = count % 10;
|
||||
const mod100 = count % 100;
|
||||
const word = mod10 === 1 && mod100 !== 11
|
||||
? 'навык'
|
||||
: (mod10 >= 2 && mod10 <= 4 && (mod100 < 10 || mod100 >= 20)
|
||||
? 'навыка'
|
||||
: 'навыков');
|
||||
return `${count} ${word}`;
|
||||
},
|
||||
profile_use: 'Использовать',
|
||||
profile_switch_title: 'Переключиться на этот профиль',
|
||||
profile_delete_title: 'Удалить этот профиль',
|
||||
profile_default_label: '(по умолчанию)',
|
||||
profile_name_placeholder: 'Название профиля (строчные буквы, a-z, 0-9, дефисы)',
|
||||
profile_clone_label: 'Скопировать конфигурацию из активного профиля',
|
||||
profile_base_url_placeholder: 'Базовый URL (необязательно, например http://localhost:11434)',
|
||||
profile_api_key_placeholder: 'API-ключ (необязательно)',
|
||||
manage_profiles: 'Управление профилями',
|
||||
profiles_load_failed: 'Не удалось загрузить профили',
|
||||
profiles_busy_switch: 'Нельзя переключать профили, пока агент работает',
|
||||
profile_switched_new_conversation: (name) => `Переключено на профиль: ${name} — начата новая беседа`,
|
||||
profile_switched: (name) => `Переключено на профиль: ${name}`,
|
||||
profile_name_rule: 'Только строчные буквы, цифры, дефисы и подчёркивания',
|
||||
profile_base_url_rule: 'Базовый URL должен начинаться с http:// или https://',
|
||||
profile_created: (name) => `Профиль создан: ${name}`,
|
||||
profile_delete_confirm_title: (name) => `Удалить профиль «${name}»?`,
|
||||
profile_delete_confirm_message: 'Это удалит всю конфигурацию, навыки, память и сеансы этого профиля.',
|
||||
profile_deleted: (name) => `Профиль удалён: ${name}`,
|
||||
active_conversation_none: 'Активная беседа не выбрана.',
|
||||
active_conversation_meta: (title, count) => {
|
||||
const mod10 = count % 10;
|
||||
const mod100 = count % 100;
|
||||
const word = mod10 === 1 && mod100 !== 11
|
||||
? 'сообщение'
|
||||
: (mod10 >= 2 && mod10 <= 4 && (mod100 < 10 || mod100 >= 20)
|
||||
? 'сообщения'
|
||||
: 'сообщений');
|
||||
return `${title} · ${count} ${word}`;
|
||||
},
|
||||
settings_unsaved_changes: 'У вас есть несохранённые изменения.',
|
||||
sign_out_failed: 'Не удалось выйти: ',
|
||||
disable_auth_confirm_title: 'Отключить защиту паролем',
|
||||
disable_auth_confirm_message: 'Любой сможет получить доступ к этому экземпляру.',
|
||||
auth_disabled: 'Авторизация отключена — защита паролем снята',
|
||||
disable_auth_failed: 'Не удалось отключить авторизацию: ',
|
||||
bg_error_single: (title) => `В "${title}" возникла ошибка`,
|
||||
bg_error_multi: (count) => `${count} сеансов столкнулись с ошибкой`,
|
||||
},
|
||||
|
||||
es: {
|
||||
_lang: 'es',
|
||||
_label: 'Español',
|
||||
@@ -534,6 +998,7 @@ const LOCALES = {
|
||||
cmd_clear: 'Borrar los mensajes de la conversación',
|
||||
cmd_compress: 'Comprimir manualmente el contexto de la conversación (uso: /compress [tema])',
|
||||
cmd_compact_alias: 'Alias antiguo de /compress',
|
||||
cmd_compact: 'Comprimir contexto de la conversación',
|
||||
cmd_model: 'Cambiar de modelo (p. ej. /model gpt-4o)',
|
||||
cmd_workspace: 'Cambiar de espacio de trabajo por nombre',
|
||||
cmd_new: 'Iniciar una nueva sesión de chat',
|
||||
@@ -869,6 +1334,11 @@ const LOCALES = {
|
||||
profile_use: 'Use',
|
||||
profile_switch_title: 'Switch to this profile',
|
||||
profile_delete_title: 'Eliminar este perfil',
|
||||
profile_default_label: '(predeterminado)',
|
||||
profile_name_placeholder: 'Nombre del perfil (minúsculas, a-z, 0-9, guiones)',
|
||||
profile_clone_label: 'Clonar configuración del perfil activo',
|
||||
profile_base_url_placeholder: 'URL base (opcional, p. ej. http://localhost:11434)',
|
||||
profile_api_key_placeholder: 'Clave API (opcional)',
|
||||
manage_profiles: 'Manage profiles',
|
||||
profiles_load_failed: 'Failed to load profiles',
|
||||
profiles_busy_switch: 'Cannot switch profiles while agent is running',
|
||||
@@ -1508,6 +1978,11 @@ const LOCALES = {
|
||||
profile_use: '使用',
|
||||
profile_switch_title: '切换到此配置档',
|
||||
profile_delete_title: '删除此配置档',
|
||||
profile_default_label: '(默认)',
|
||||
profile_name_placeholder: '配置档名称(小写字母、a-z、0-9、连字符)',
|
||||
profile_clone_label: '复制当前配置档的配置',
|
||||
profile_base_url_placeholder: 'Base URL(可选,例如 http://localhost:11434)',
|
||||
profile_api_key_placeholder: 'API 密钥(可选)',
|
||||
manage_profiles: '管理配置档',
|
||||
profiles_load_failed: '加载配置档失败',
|
||||
profiles_busy_switch: 'Agent 运行中,无法切换配置档',
|
||||
|
||||
@@ -127,19 +127,19 @@
|
||||
</div>
|
||||
<!-- Profile create form (hidden by default) -->
|
||||
<div id="profileCreateForm" style="display:none;padding:8px 12px;border-bottom:1px solid var(--border);flex-shrink:0">
|
||||
<input id="profileFormName" placeholder="Profile name (lowercase, a-z 0-9 hyphens)" style="width:100%;background:rgba(255,255,255,.05);border:1px solid var(--border2);border-radius:6px;color:var(--text);padding:5px 8px;font-size:12px;outline:none;margin-bottom:6px;box-sizing:border-box">
|
||||
<input id="profileFormName" data-i18n-placeholder="profile_name_placeholder" placeholder="Profile name (lowercase, a-z 0-9 hyphens)" style="width:100%;background:rgba(255,255,255,.05);border:1px solid var(--border2);border-radius:6px;color:var(--text);padding:5px 8px;font-size:12px;outline:none;margin-bottom:6px;box-sizing:border-box">
|
||||
<label style="display:flex;align-items:center;gap:6px;font-size:11px;color:var(--muted);margin-bottom:8px;cursor:pointer">
|
||||
<input type="checkbox" id="profileFormClone" style="accent-color:var(--accent)"> Clone config from active profile
|
||||
<input type="checkbox" id="profileFormClone" style="accent-color:var(--accent)"> <span data-i18n="profile_clone_label">Clone config from active profile</span>
|
||||
</label>
|
||||
<input id="profileFormBaseUrl" placeholder="Base URL (optional, e.g. http://localhost:11434)" style="width:100%;background:rgba(255,255,255,.05);border:1px solid var(--border2);border-radius:6px;color:var(--text);padding:5px 8px;font-size:12px;outline:none;margin-bottom:6px;box-sizing:border-box">
|
||||
<input id="profileFormApiKey" type="password" placeholder="API key (optional)" style="width:100%;background:rgba(255,255,255,.05);border:1px solid var(--border2);border-radius:6px;color:var(--text);padding:5px 8px;font-size:12px;outline:none;margin-bottom:6px;box-sizing:border-box">
|
||||
<input id="profileFormBaseUrl" data-i18n-placeholder="profile_base_url_placeholder" placeholder="Base URL (optional, e.g. http://localhost:11434)" style="width:100%;background:rgba(255,255,255,.05);border:1px solid var(--border2);border-radius:6px;color:var(--text);padding:5px 8px;font-size:12px;outline:none;margin-bottom:6px;box-sizing:border-box">
|
||||
<input id="profileFormApiKey" type="password" data-i18n-placeholder="profile_api_key_placeholder" placeholder="API key (optional)" style="width:100%;background:rgba(255,255,255,.05);border:1px solid var(--border2);border-radius:6px;color:var(--text);padding:5px 8px;font-size:12px;outline:none;margin-bottom:6px;box-sizing:border-box">
|
||||
<div style="display:flex;gap:6px">
|
||||
<button class="cron-btn run" style="flex:1" onclick="submitProfileCreate()">Create</button>
|
||||
<button class="cron-btn" style="flex:1" onclick="toggleProfileForm()">Cancel</button>
|
||||
<button class="cron-btn run" style="flex:1" onclick="submitProfileCreate()" data-i18n="create">Create</button>
|
||||
<button class="cron-btn" style="flex:1" onclick="toggleProfileForm()" data-i18n="cancel">Cancel</button>
|
||||
</div>
|
||||
<div id="profileFormError" style="font-size:11px;color:var(--accent);margin-top:6px;display:none"></div>
|
||||
</div>
|
||||
<div style="flex:1;overflow-y:auto;padding:0 12px 12px" id="profilesPanel"><div style="color:var(--muted);font-size:12px">Loading...</div></div>
|
||||
<div style="flex:1;overflow-y:auto;padding:0 12px 12px" id="profilesPanel"><div style="color:var(--muted);font-size:12px" data-i18n="loading">Loading...</div></div>
|
||||
</div>
|
||||
<div class="sidebar-bottom">
|
||||
<button class="hermes-launch-btn" id="btnHermesPanel" onclick="toggleSettings()" title="Open Hermes control center">
|
||||
|
||||
@@ -819,10 +819,11 @@ async function loadProfilesPanel() {
|
||||
: `<span class="profile-opt-badge stopped" title="${esc(t('profile_gateway_stopped'))}"></span>`;
|
||||
const isActive = p.name === data.active;
|
||||
const activeBadge = isActive ? `<span style="color:var(--link);font-size:10px;font-weight:600;margin-left:6px">${esc(t('profile_active'))}</span>` : '';
|
||||
const defaultBadge = p.is_default ? ` <span style="opacity:.5">${esc(t('profile_default_label'))}</span>` : '';
|
||||
card.innerHTML = `
|
||||
<div class="profile-card-header">
|
||||
<div style="min-width:0;flex:1">
|
||||
<div class="profile-card-name${isActive ? ' is-active' : ''}">${gwDot}${esc(p.name)}${p.is_default ? ' <span style="opacity:.5">(default)</span>' : ''}${activeBadge}</div>
|
||||
<div class="profile-card-name${isActive ? ' is-active' : ''}">${gwDot}${esc(p.name)}${defaultBadge}${activeBadge}</div>
|
||||
${meta.length ? `<div class="profile-card-meta">${esc(meta.join(' \u00b7 '))}</div>` : `<div class="profile-card-meta">${esc(t('profile_no_configuration'))}</div>`}
|
||||
</div>
|
||||
<div class="profile-card-actions">
|
||||
@@ -833,7 +834,7 @@ async function loadProfilesPanel() {
|
||||
panel.appendChild(card);
|
||||
}
|
||||
} catch (e) {
|
||||
panel.innerHTML = `<div style="color:var(--accent);font-size:12px;padding:12px">Error: ${esc(e.message)}</div>`;
|
||||
panel.innerHTML = `<div style="color:var(--accent);font-size:12px;padding:12px">${esc(t('error_prefix'))}${esc(e.message)}</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -851,7 +852,8 @@ function renderProfileDropdown(data) {
|
||||
if (p.skill_count) meta.push(t('profile_skill_count', p.skill_count));
|
||||
const gwDot = `<span class="profile-opt-badge ${p.gateway_running ? 'running' : 'stopped'}"></span>`;
|
||||
const checkmark = p.name === active ? ' <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="var(--link)" stroke-width="3" style="vertical-align:-1px"><polyline points="20 6 9 17 4 12"/></svg>' : '';
|
||||
opt.innerHTML = `<div class="profile-opt-name">${gwDot}${esc(p.name)}${p.is_default ? ' <span style="opacity:.5;font-weight:400">(default)</span>' : ''}${checkmark}</div>` +
|
||||
const defaultBadge = p.is_default ? ` <span style="opacity:.5;font-weight:400">${esc(t('profile_default_label'))}</span>` : '';
|
||||
opt.innerHTML = `<div class="profile-opt-name">${gwDot}${esc(p.name)}${defaultBadge}${checkmark}</div>` +
|
||||
(meta.length ? `<div class="profile-opt-meta">${esc(meta.join(' \u00b7 '))}</div>` : '');
|
||||
opt.onclick = async () => {
|
||||
closeProfileDropdown();
|
||||
|
||||
@@ -212,7 +212,7 @@ function _openSessionActionMenu(session, anchorEl){
|
||||
menu.className='session-action-menu open';
|
||||
menu.appendChild(_buildSessionAction(
|
||||
session.pinned?'Unpin conversation':'Pin conversation',
|
||||
session.pinned?'Remove from the pinned section':'Keep this conversation at the top',
|
||||
session.pinned?'Remove from pinned':'Keep this conversation at the top',
|
||||
session.pinned?ICONS.pin:ICONS.unpin,
|
||||
async()=>{
|
||||
closeSessionActionMenu();
|
||||
@@ -228,7 +228,7 @@ function _openSessionActionMenu(session, anchorEl){
|
||||
));
|
||||
menu.appendChild(_buildSessionAction(
|
||||
'Move to project',
|
||||
session.project_id?'Change which project this conversation belongs to':'Assign this conversation to a project',
|
||||
session.project_id?'Change the project for this conversation':'Assign a project to this conversation',
|
||||
ICONS.folder,
|
||||
async()=>{
|
||||
closeSessionActionMenu();
|
||||
|
||||
Reference in New Issue
Block a user