diff --git a/.github/workflows/fork-image.yaml b/.github/workflows/fork-image.yaml new file mode 100644 index 00000000..0d8c8932 --- /dev/null +++ b/.github/workflows/fork-image.yaml @@ -0,0 +1,53 @@ +# Fork image build — publishes a ready-to-run image to the fork's GHCR so a VPS +# can `docker pull` it without building from source. Temporary: while the fork's +# features (ru locale, telegram reactions) are not yet in an upstream release. +# Once they land upstream, switch the VPS to ghcr.io/nextlevelbuilder/goclaw and +# delete this workflow. +name: fork image + +on: + push: + branches: [dev] + workflow_dispatch: + +permissions: + contents: read + packages: write + +concurrency: + group: fork-image-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + platforms: linux/amd64 + push: true + tags: | + ghcr.io/${{ github.repository }}:latest + ghcr.io/${{ github.repository }}:dev-${{ github.sha }} + build-args: | + ENABLE_EMBEDUI=true + ENABLE_PYTHON=true + ENABLE_OTEL=false + ENABLE_FULL_SKILLS=false + VERSION=fork-dev + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.gitignore b/.gitignore index 3551b22d..aaa1b579 100644 --- a/.gitignore +++ b/.gitignore @@ -64,6 +64,11 @@ tmp .claude/ deploy-*.sh +# ...but the fork-dev deploy helper IS tracked tooling. +!scripts/deploy-forkdev.sh + +# fork-dev integration worktree (created by scripts/deploy-forkdev.sh) +.goclaw-forkdev/ docs/technical-diaries docs/zuey diff --git a/docker-compose.forkdev.yml b/docker-compose.forkdev.yml new file mode 100644 index 00000000..a9322acc --- /dev/null +++ b/docker-compose.forkdev.yml @@ -0,0 +1,23 @@ +# Local-build overlay for the "fork-dev" integration branch. +# +# Purpose: run goclaw from a LOCALLY BUILT image that merges upstream dev with +# un-merged fork features (Telegram trigger-words + Russian locale), instead of +# pulling the published ghcr.io/...:latest (which lacks those features). +# +# It swaps the service image to a distinct local tag `goclaw:forkdev` and forbids +# pulling, so `:latest` stays untouched and the running bot is unambiguously the +# local build. The build context comes from GOCLAW_DIR (set by deploy-forkdev.sh +# to the integration worktree); the base compose already defines build.args. +# +# Usage (via the helper — handles fetch + merge + build + up): +# scripts/deploy-forkdev.sh +# +# Or manually against an already-prepared worktree at $GOCLAW_DIR: +# GOCLAW_DIR=../.goclaw-forkdev \ +# docker compose -f docker-compose.yml -f docker-compose.postgres.yml \ +# -f docker-compose.forkdev.yml up -d --build + +services: + goclaw: + image: goclaw:forkdev + pull_policy: never diff --git a/docs/superpowers/specs/2026-07-08-ru-language-design.md b/docs/superpowers/specs/2026-07-08-ru-language-design.md new file mode 100644 index 00000000..e03b9e81 --- /dev/null +++ b/docs/superpowers/specs/2026-07-08-ru-language-design.md @@ -0,0 +1,101 @@ +# Design: Russian language (`ru`) support for GoClaw + +**Date:** 2026-07-08 +**Branch:** `feature/ru-lang` +**Status:** Approved + +## Goal + +Add Russian (`ru`) as a fully supported UI/message language across all three product +surfaces (backend i18n catalog, Web UI, Desktop UI), at **full parity with English** — +not the partial coverage the Korean (`ko`) locale currently ships. + +## Reference precedent + +`ko` (Korean) was the most recently added language. It is the integration template for +`ru` on the **backend** and **Web UI**. The **Desktop UI** has no `ko` at all, so `ru` +there is net-new work patterned on the existing `zh` wiring. + +**Key deviation from `ko`:** `ko` is intentionally partial (backend catalog = 131 of 281 +keys; Web UI = 38 of 41 namespaces, omitting `hooks`/`webhooks`/`workstations`). Because +the user chose **full parity with English**, `ru` covers **all 281 backend keys** and +**all 41 Web namespaces**. + +## Scope by surface + +### 1. Backend (Go) — full catalog (~281 keys) + +| File | Change | +|------|--------| +| `internal/i18n/i18n.go` | Add `LocaleRU = "ru"` const; add `LocaleRU` to `IsSupported()` switch. `Normalize()` handles `ru`/`ru-RU` prefix automatically once `IsSupported` knows it. | +| `internal/i18n/catalog_ru.go` (new) | `func init() { register(LocaleRU, map[string]string{...}) }` — **all 281 keys** from `catalog_en.go`. | +| `internal/systemmessages/resolver.go` | Add `i18n.LocaleRU:` entry in each of the **10 maps** (5 messages × Labels + Descriptions), lines ~51,57,73,79,95,101,118,124,136,142. | +| `internal/gateway/methods/config.go:366` | Add `"ru"` to the `default_locale` enum `[]string{"en","vi","zh","ko"}`. | +| `internal/i18n/git_keys_parity_test.go:34` | **Add** `LocaleRU` to the locale list. Correct for a full catalog (ko is excluded only because it is partial). | +| `internal/config/config.go:128` | Update doc-comment to mention `ru` (informational, no logic). | + +Ingest paths need **no per-locale edit** (verified): `internal/gateway/router.go`, +`internal/http/auth.go` `extractLocale`, `internal/store/context.go` `WithLocale` — all +run the locale through `i18n.Normalize()` and work for `ru` automatically. + +### 2. Web UI — full 41 namespaces + +| File | Change | +|------|--------| +| `ui/web/src/i18n/locales/ru/` (new dir) | **41 JSON files** matching `locales/en/` (includes `hooks.json`, `webhooks.json`, `workstations.json` — which `ko` omits). | +| `ui/web/src/i18n/index.ts` | Add `ru` import block (41 imports), `ru: {...}` resources object (41 namespaces), `stored === "ru"` in `getInitialLanguage()`, and `lang.startsWith("ru") → "ru"` browser detection. | +| `ui/web/src/lib/constants.ts` | Add `"ru"` to `SUPPORTED_LANGUAGES`; add `ru: "Русский"` to `LANGUAGE_LABELS`. | +| `ui/web/src/pages/config/sections/system-messages-section-utils.ts:21` | Add `{ code: "ru", labelKey: "systemMessages.locale.ru" }` to `SYSTEM_MESSAGE_LOCALES`. | +| `ui/web/src/pages/config/sections/system-messages-section-utils.test.ts:49` | Update expected array to include `"ru"`. | +| `ui/web/src/i18n/locales/{en,vi,zh,ru}/config.json` | Add `systemMessages.locale.ru` string key. | + +Consumers auto-covered (no edit): `topbar.tsx`, `setup-page.tsx` `LanguageSelector`, +`use-ui-store.ts` — all map over `SUPPORTED_LANGUAGES`/`LANGUAGE_LABELS`. + +### 3. Desktop UI — net-new (16 namespaces, patterned on `zh`) + +| File | Change | +|------|--------| +| `ui/desktop/frontend/src/i18n/locales/ru/` (new dir) | **16 JSON files** matching `locales/zh/`. | +| `ui/desktop/frontend/src/i18n/index.ts` | Add `ru` import block (16 imports), `ru: {...}` resources, `ru` handling in `getInitialLanguage()`. | +| `ui/desktop/frontend/src/lib/constants.ts` | Add `{ value: 'ru', label: 'RU', flag: '🇷🇺' }` to `LANGUAGES`. | +| `ui/desktop/frontend/src/components/settings/AppearanceTab.tsx` | Add `ru` to inline `LANGUAGES` array. | +| `ui/desktop/frontend/src/components/onboarding/OnboardingWizard.tsx` | Add `ru` to inline `LANGUAGES` array. | + +`ChatTopBar.tsx` imports `LANGUAGES` from constants — auto-covered. + +**No DB migration:** locale strings live in Go catalogs and JSON files, not in any +PostgreSQL/SQLite table. SQLite `schema.sql`/`schema.go` untouched. + +## Translation strategy + +- Translate **from the English source** (the parity reference), not from `ko`/`vi`/`zh`. +- Keep technical terms natural/idiomatic: agent→агент, token→токен, session→сессия, + team→команда, provider→провайдер, skill→навык (or keep "скилл" where UI-conventional). +- **Never touch** `%s` / `%d` (Go `fmt` verbs), `{{variable}}` (i18next interpolation), + JSON keys, HTML tags, or Markdown structure inside strings. +- Preserve pluralization keys (`_one`/`_other`) where i18next uses them. + +## Ordering + +1. **Plumbing first:** register `ru` everywhere (backend consts, index.ts, constants, + enums, selectors) — with English-copied strings as placeholders so it compiles/renders. +2. **Translation second:** replace placeholder strings namespace-by-namespace. + +This keeps every intermediate commit buildable. + +## Verification + +- Backend: `go build ./...`, `go build -tags sqliteonly ./...`, `go vet ./...`, + `go test ./internal/i18n/ ./internal/systemmessages/`. +- Web: `cd ui/web && pnpm build`; key-parity check (every `en/*.json` key exists in + `ru/*.json`). +- Desktop: `cd ui/desktop/frontend && pnpm build`; same key-parity check against `zh`. +- Manual smoke: launch web UI, switch language to "Русский" in topbar, verify no raw keys + render on main screens. + +## Out of scope + +- Bootstrap templates (`SOUL.md`, `IDENTITY.md`) stay English (LLM consumption). +- Prose `_readmes/README.ru.md` already exists — unrelated to UI wiring. +- No new features; wiring + translation only. diff --git a/internal/config/config.go b/internal/config/config.go index 0f5eefb4..c3c191a8 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -125,7 +125,7 @@ type SystemMsgConfig struct { Messages map[string]LocalizedSystemMessage `json:"messages,omitempty"` } -// LocalizedSystemMessage maps locale code ("en", "vi", "zh", "ko") to a +// LocalizedSystemMessage maps locale code ("en", "vi", "zh", "ko", "ru") to a // template override for one system message key. type LocalizedSystemMessage map[string]string diff --git a/internal/gateway/methods/config.go b/internal/gateway/methods/config.go index 67b355b7..e9bf2444 100644 --- a/internal/gateway/methods/config.go +++ b/internal/gateway/methods/config.go @@ -363,7 +363,7 @@ func (m *ConfigMethods) handleSchema(_ context.Context, client *gateway.Client, "properties": map[string]any{ "default_locale": map[string]any{ "type": "string", - "enum": []string{"en", "vi", "zh", "ko"}, + "enum": []string{"en", "vi", "zh", "ko", "ru"}, "description": "Default locale used when a channel caller does not provide a locale", }, "messages": map[string]any{ diff --git a/internal/i18n/catalog_ru.go b/internal/i18n/catalog_ru.go new file mode 100644 index 00000000..79ee810e --- /dev/null +++ b/internal/i18n/catalog_ru.go @@ -0,0 +1,372 @@ +package i18n + +func init() { + register(LocaleRU, map[string]string{ + // Common validation + MsgRequired: "требуется %s", + MsgInvalidID: "неверный ID %s", + MsgNotFound: "%s не найдено: %s", + MsgAlreadyExists: "%s уже существует: %s", + MsgInvalidRequest: "неверный запрос: %s", + MsgInvalidJSON: "неверный JSON", + MsgUnauthorized: "не авторизовано", + MsgPermissionDenied: "доступ запрещён: %s", + MsgInternalError: "внутренняя ошибка: %s", + MsgInvalidSlug: "%s должно быть корректным слагом (только строчные буквы, цифры и дефисы)", + MsgFailedToList: "не удалось получить список %s", + MsgFailedToCreate: "не удалось создать %s: %s", + MsgFailedToUpdate: "не удалось обновить %s: %s", + MsgFailedToDelete: "не удалось удалить %s: %s", + MsgFailedToSave: "не удалось сохранить %s: %s", + MsgInvalidUpdates: "неверные обновления", + + // Agent + MsgAgentNotFound: "агент не найден: %s", + MsgCannotDeleteDefault: "нельзя удалить агента по умолчанию", + MsgUserCtxRequired: "требуется контекст пользователя", + MsgGatewayOperatorSecureCLIUnavailable: "Доступ оператора шлюза пропущен, так как хранилище SecureCLI недоступно.", + MsgGatewayOperatorEligibilityFailed: "Агент создан, но доступ оператора шлюза не смог подтвердить право на статус первого агента.", + MsgGatewayOperatorNotFirstAgent: "Доступ оператора шлюза не предоставлен, так как это не первый агент.", + MsgGatewayOperatorTokenMissing: "Доступ оператора шлюза пропущен, так как токен шлюза не настроен.", + MsgGatewayOperatorBinaryMissing: "Доступ оператора шлюза пропущен, так как не удалось обнаружить исполняемый файл goclaw.", + MsgGatewayOperatorExistingReview: "Доступ оператора шлюза пропущен, так как существующие учётные данные goclaw CLI требуют ручной проверки.", + MsgGatewayOperatorRegisterFailed: "Доступ оператора шлюза пропущен, так как не удалось зарегистрировать учётные данные goclaw CLI.", + MsgGatewayOperatorCredentialFailed: "Доступ оператора шлюза пропущен, так как не удалось сохранить учётные данные.", + + // Chat + MsgRateLimitExceeded: "превышен лимит запросов — пожалуйста, подождите", + MsgNoUserMessage: "сообщение пользователя не найдено", + MsgUserIDRequired: "требуется user_id", + MsgMsgRequired: "требуется сообщение", + + // Abort + MsgAbortStopped: "выполнение остановлено", + MsgAbortForced: "выполнение принудительно прервано (превышен льготный период 3 с)", + MsgAbortAlreadyAborting: "прерывание уже выполняется", + MsgAbortNotFound: "выполнение не найдено или уже завершено", + MsgAbortUnauthorized: "нет прав на прерывание этого выполнения", + MsgAbortFailed: "не удалось прервать выполнение: %s", + + // Channel instances + MsgInvalidChannelType: "неверный channel_type", + MsgInstanceNotFound: "экземпляр не найден", + + // Cron + MsgJobNotFound: "задание не найдено", + MsgInvalidCronExpr: "неверное cron-выражение: %s", + MsgCommandCronDisabled: "команды cron отключены на этом шлюзе (установите cron.command_enabled=true, чтобы разрешить их)", + + // Config + MsgConfigHashMismatch: "конфигурация изменилась (несоответствие хэша)", + + // Exec approval + MsgExecApprovalDisabled: "подтверждение выполнения не включено", + + // Pairing + MsgSenderChannelRequired: "требуются senderId и channel", + MsgCodeRequired: "требуется код", + MsgSenderIDRequired: "требуется sender_id", + + // HTTP API + MsgInvalidAuth: "неверная аутентификация", + MsgMsgsRequired: "требуется messages", + MsgUserIDHeader: "требуется заголовок X-GoClaw-User-Id", + MsgFileTooLarge: "файл слишком большой или неверная multipart-форма", + MsgMissingFileField: "отсутствует поле 'file'", + MsgInvalidFilename: "неверное имя файла", + MsgChannelKeyReq: "требуются channel и key", + MsgMethodNotAllowed: "метод не разрешён", + MsgStreamingNotSupported: "потоковая передача не поддерживается", + MsgOwnerOnly: "только владелец может %s", + MsgNoAccess: "нет доступа к этому %s", + MsgAlreadySummoning: "агент уже вызывается", + MsgSummoningUnavailable: "вызов недоступен", + MsgRunTimelineUnavailable: "хронология выполнения недоступна", + MsgNoDescription: "у агента нет описания для повторного вызова", + MsgSummonCancelled: "вызов отменён пользователем", + MsgCannotCancel: "агент не вызывается", + MsgInvalidPath: "неверный путь", + + // Browser cookies + MsgBrowserCookieTooMany: "слишком много cookie браузера в одном запросе синхронизации", + MsgInvalidCookieURL: "неверный URL cookie", + MsgBrowserCookieValueTooLarge: "значение cookie слишком велико", + MsgBrowserCookieEncryptionRequired: "шифрование cookie браузера не настроено", + + // Tenant backup / restore + MsgRestoreNewModeRejectsTenantID: "mode=new создаёт нового арендатора; передайте tenant_slug (а не tenant_id) в качестве целевого слага нового арендатора", + + // Scheduler + MsgQueueFull: "очередь сессий заполнена", + MsgShuttingDown: "шлюз завершает работу, пожалуйста, повторите попытку чуть позже", + + // Provider + MsgProviderReqFailed: "%s: запрос не выполнен: %s", + + // Usage caps / pricing + MsgUsageCapsListPoliciesFailed: "не удалось получить список политик лимитов использования", + MsgUsageCapPolicyValidationFailed: "не пройдена проверка политики лимитов использования", + MsgUsageCapPolicyManaged: "управляемые политики лимитов использования нельзя изменять", + MsgUsageCapsDeletePolicyFailed: "не удалось удалить политику лимитов использования", + MsgUsageCapsUtilizationFailed: "не удалось загрузить использование лимитов", + MsgUsageCapsEventsFailed: "не удалось загрузить события лимитов использования", + MsgUsagePricingSyncOpenRouterFailed: "не удалось синхронизировать цены OpenRouter: %s", + MsgUsagePricingStoreCatalogFailed: "не удалось сохранить каталог цен", + MsgUsagePricingListFailed: "не удалось получить список цен на модели", + MsgUsagePricingProviderModelRequired: "требуются provider_id и model_id", + MsgUsagePricingOverrideValidationFailed: "не пройдена проверка переопределения цен", + MsgUsagePricingListOverridesFailed: "не удалось получить список переопределений цен", + MsgUsagePricingDeleteOverrideFailed: "не удалось удалить переопределение цен", + + // Unknown method + MsgUnknownMethod: "неизвестный метод: %s", + + // Not implemented + MsgNotImplemented: "%s ещё не реализовано", + + // Agent links + MsgLinksNotConfigured: "связи агентов не настроены", + MsgInvalidDirection: "направление должно быть outbound, inbound или bidirectional", + MsgSourceTargetSame: "источник и цель должны быть разными агентами", + MsgCannotDelegateOpen: "нельзя делегировать открытым агентам — целью делегирования могут быть только предопределённые агенты", + MsgNoUpdatesProvided: "обновления не предоставлены", + MsgInvalidLinkStatus: "статус должен быть active или disabled", + + // Teams + MsgTeamsNotConfigured: "команды не настроены", + MsgAgentIsTeamLead: "агент уже является лидом команды", + MsgCannotRemoveTeamLead: "нельзя удалить лида команды", + + // Channels + MsgCannotDeleteDefaultInst: "нельзя удалить экземпляр канала по умолчанию", + MsgCannotRemoveLastWriter: "нельзя удалить последнего редактора файлов", + + // Skills + MsgSkillsUpdateNotSupported: "skills.update не поддерживается для файловых навыков", + MsgCannotResolveSkillID: "не удалось определить ID навыка для файлового навыка", + MsgInvalidVisibility: "неверная видимость %q: должна быть одной из private, public", + MsgSkillEvolutionNotConfigured: "хранилище эволюции навыков не настроено", + MsgActivityStoreNotConfigured: "хранилище активности не настроено", + MsgInvalidEvolutionMode: "неверный режим эволюции", + MsgSystemSkillMutationBlocked: "изменение системного навыка заблокировано", + MsgSuggestionMustBeApproved: "предложение должно быть одобрено перед применением", + MsgInvalidDraftPatch: "неверный draft_patch: %s", + MsgDraftPatchRequired: "draft_patch требует content или find/replace", + MsgFindTextNotFound: "искомый текст не найден в целевом файле", + + // Logs + MsgInvalidLogAction: "действие должно быть 'start' или 'stop'", + + // Config + MsgRawConfigRequired: "требуется raw-конфигурация", + MsgRawPatchRequired: "требуется raw-патч", + MsgConfigMasterScopeOnly: "методы config.* доступны только в мастер-области; используйте эндпоинты конфигурации инструментов арендатора для переопределений на уровне арендатора", + MsgMasterScopeRequired: "это действие требует мастер-области арендатора", + + // Storage / File + MsgCannotDeleteSkillsDir: "нельзя удалить каталоги навыков", + MsgFailedToReadFile: "не удалось прочитать файл", + MsgFileNotFound: "файл не найден", + MsgInvalidVersion: "неверная версия", + MsgVersionNotFound: "версия не найдена", + MsgFailedToDeleteFile: "не удалось удалить", + + // OAuth + MsgNoPendingOAuth: "нет ожидающего процесса OAuth", + MsgFailedToSaveToken: "не удалось сохранить токен", + MsgOAuthCallbackSuccess: "Авторизация успешна. Вы можете закрыть это окно.", + MsgOAuthCallbackFailed: "Авторизация не удалась. Вы можете закрыть это окно.", + + // Intent Classify + MsgStatusWorking: "🔄 Я работаю над вашим запросом... Пожалуйста, подождите.", + MsgStatusDetailed: "🔄 Я сейчас работаю над вашим запросом...\n%s (итерация %d)\nВремя выполнения: %s\n\nПожалуйста, подождите — я отвечу, когда закончу.", + MsgStatusPhaseThinking: "Фаза: Размышление...", + MsgStatusPhaseToolExec: "Фаза: Выполнение %s", + MsgStatusPhaseTools: "Фаза: Выполнение инструментов...", + MsgStatusPhaseCompact: "Фаза: Сжатие контекста...", + MsgStatusPhaseDefault: "Фаза: Обработка...", + MsgCancelledReply: "✋ Отменено. Что бы вы хотели сделать дальше?", + MsgInjectedAck: "Понял, я учту это в том, над чем работаю.", + + // Knowledge Graph + MsgEntityIDRequired: "требуется entity_id", + MsgEntityFieldsRequired: "требуются external_id, name и entity_type", + MsgTextRequired: "требуется текст", + MsgProviderModelRequired: "требуются провайдер и модель", + MsgInvalidProviderOrModel: "неверный провайдер или модель", + + // Builtin tool descriptions + MsgToolReadFile: "Прочитать содержимое файла из рабочей области агента по пути", + MsgToolWriteFile: "Записать содержимое в файл в рабочей области, создавая каталоги по мере необходимости", + MsgToolListFiles: "Вывести список файлов и каталогов по заданному пути в рабочей области", + MsgToolEdit: "Применить точечные правки поиска-и-замены к существующим файлам без переписывания всего файла", + MsgToolExec: "Выполнить команду оболочки в рабочей области и вернуть stdout/stderr", + MsgToolWebSearch: "Искать информацию в интернете с помощью поисковой системы (Brave или DuckDuckGo)", + MsgToolWebFetch: "Загрузить веб-страницу или эндпоинт API и извлечь его текстовое содержимое", + MsgToolMemorySearch: "Искать в долговременной памяти агента по семантическому сходству", + MsgToolMemoryGet: "Получить конкретный документ памяти по его пути к файлу", + MsgToolKGSearch: "Искать сущности, связи и наблюдения в графе знаний агента", + MsgToolReadImage: "Анализировать изображения с помощью LLM-провайдера с поддержкой зрения", + MsgToolReadDocument: "Анализировать документы (PDF, Word, Excel, PowerPoint, CSV и т. д.) с помощью LLM-провайдера с поддержкой документов", + MsgToolCreateImage: "Генерировать изображения из текстовых запросов с помощью провайдера генерации изображений", + MsgToolReadAudio: "Анализировать аудиофайлы (речь, музыку, звуки) с помощью LLM-провайдера с поддержкой аудио", + MsgToolReadVideo: "Анализировать видеофайлы с помощью LLM-провайдера с поддержкой видео", + MsgToolCreateVideo: "Генерировать видео из текстовых описаний с помощью ИИ", + MsgToolCreateAudio: "Генерировать музыку или звуковые эффекты из текстовых описаний с помощью ИИ", + MsgToolTTS: "Преобразовать текст в естественно звучащую речь", + MsgToolBrowser: "Автоматизировать взаимодействие с браузером: переходить по страницам, кликать по элементам, заполнять формы, делать скриншоты", + MsgToolSessionsList: "Вывести список активных сессий чата по всем каналам", + MsgToolSessionStatus: "Получить текущий статус и метаданные конкретной сессии чата", + MsgToolSessionsHistory: "Получить историю сообщений конкретной сессии чата", + MsgToolSessionsSend: "Отправить сообщение в активную сессию чата от имени агента", + MsgToolMessage: "Отправить проактивное сообщение пользователю по подключённому каналу (Telegram, Discord и т. д.)", + MsgToolCron: "Планировать повторяющиеся задачи или управлять ими с помощью cron-выражений, конкретного времени или интервалов", + MsgToolSpawn: "Создать субагента для фоновой работы или делегировать задачу связанному агенту", + MsgToolSkillSearch: "Искать доступные навыки по ключевому слову или описанию для поиска подходящих возможностей", + MsgToolUseSkill: "Активировать навык для использования его специализированных возможностей (маркер трассировки)", + MsgToolSkillManage: "Создавать, изменять или удалять навыки на основе опыта разговора", + MsgToolPublishSkill: "Зарегистрировать каталог навыка в базе данных системы, сделав его доступным для обнаружения", + MsgToolTeamTasks: "Просматривать, создавать, обновлять и завершать задачи на доске задач команды", + MsgToolAnnouncementSingle: "Я использую %s для следующего шага.", + MsgToolAnnouncementMulti: "Я использую %s для следующего шага.", + + MsgSkillNudgePostscript: "Эта задача включала несколько шагов. Хотите, чтобы я сохранил процесс как повторно используемый навык? Ответьте **\"сохранить как навык\"** или **\"пропустить\"**.", + MsgSkillNudge70Pct: "[Система] Вы использовали 70% бюджета итераций. Подумайте, могут ли какие-либо шаблоны из этой сессии стать хорошим навыком.", + MsgSkillNudge90Pct: "[Система] Вы использовали 90% бюджета итераций. Если эта сессия включала повторно используемые шаблоны, подумайте о сохранении их как навыка до завершения.", + + MsgInvalidRole: "неверная роль: допустимые значения — owner, admin, operator, member, viewer", + + MsgContactIDsRequired: "требуется contact_ids", + MsgMergeTargetRequired: "требуется ровно одно из tenant_user_id или create_user", + MsgTenantUserNotFound: "пользователь арендатора не найден", + MsgTenantMismatch: "пользователь арендатора не принадлежит этому арендатору", + MsgTenantScopeRequired: "для этой операции требуется область арендатора", + + // TTS / Voices + MsgTtsUnknownModel: "неизвестная модель tts: %s", + MsgVoicesListFailed: "не удалось получить список голосов: %s", + MsgTtsGeminiInvalidVoice: "неверный голос Gemini: %s", + MsgTtsGeminiSpeakerLimit: "Gemini TTS поддерживает не более 2 говорящих", + MsgTtsGeminiInvalidModel: "неверная модель Gemini TTS: %s", + MsgTtsGeminiTextOnly: "Gemini отказался генерировать аудио. Попробуйте более простой текст без перевода или комментариев.", + MsgTtsParamOutOfRange: "параметр TTS %q со значением %v выходит за пределы диапазона [%v, %v]", + MsgTtsParamUnknownKey: "параметр TTS %q не поддерживается этим провайдером", + MsgTtsMiniMaxVoicesFailed: "не удалось загрузить голоса MiniMax: %s", + + // STT + MsgSTTAllProvidersFailed: "Все провайдеры STT не сработали", + MsgSTTLegacyConfigDeprecated: "Устаревшая конфигурация STT; перейдите на builtin_tools[stt]", + MsgSTTWhatsappPrivacyWarning: "Включение STT для WhatsApp нарушает сквозное шифрование голосовых сообщений, отправляемых этому агенту.", + MsgVoiceMessageFallback: "[Голосовое сообщение]", + + // Workstation + MsgWorkstationNotFound: "рабочая станция не найдена: %s", + MsgWorkstationKeyExists: "ключ рабочей станции уже используется: %s", + MsgInvalidBackend: "неверный тип бэкенда: %s (должен быть ssh|docker)", + MsgWorkstationInactive: "рабочая станция неактивна: %s", + MsgInvalidMetadataShape: "неверные метаданные для бэкенда %s: %s", + MsgWorkstationRequired: "к агенту не привязана рабочая станция; передайте workstation_id", + MsgWorkstationAccessDenied: "агент %s не имеет доступа к рабочей станции %s", + MsgBackendNotReady: "бэкенд рабочей станции не готов: %s", + + // Webhooks + MsgWebhookAuthFailed: "аутентификация вебхука не удалась", + MsgWebhookHMACInvalid: "подпись HMAC недействительна", + MsgWebhookHMACTimestampSkew: "временная метка запроса вне допустимого окна", + MsgWebhookBearerRequiredHMAC: "этот вебхук требует аутентификации HMAC", + MsgWebhookRevoked: "вебхук был отозван", + MsgWebhookKindMismatch: "тип запроса не соответствует конфигурации вебхука", + MsgWebhookRateLimited: "превышен лимит запросов вебхука", + MsgWebhookBodyTooLarge: "тело запроса превышает ограничение по размеру", + MsgWebhookIdempotencyConflict: "конфликт ключа идемпотентности: несоответствие тела запроса", + MsgWebhookTenantMismatch: "несоответствие арендатора вебхука", + MsgWebhookAgentNotFound: "агент вебхука не найден", + MsgWebhookChannelNotFound: "канал вебхука не найден", + MsgWebhookMediaSSRFBlocked: "URL медиа заблокирован политикой SSRF", + MsgWebhookMediaTooLarge: "медиафайл превышает ограничение по размеру", + MsgWebhookMediaMIMEDenied: "MIME-тип медиа не разрешён", + MsgWebhookCallbackURLInvalid: "URL обратного вызова недействителен или заблокирован", + MsgWebhookLLMTimeout: "истекло время обработки LLM", + MsgWebhookLaneSaturated: "линия обработки вебхуков заполнена", + MsgWebhookLocalhostOnlyViolation: "этот вебхук ограничен вызовами только с localhost", + MsgWebhookMediaChannelUnsupported: "канал не поддерживает медиавложения", + MsgWebhookIPDenied: "источник запроса не входит в список разрешённых IP", + MsgWebhookEncryptionUnavailable: "ключ шифрования вебхука не настроен; установите GOCLAW_ENCRYPTION_KEY, чтобы включить вебхуки", + MsgWebhookMessageTestRequiresStandard: "тестирование вебхуков сообщений требует издания Standard", + + // Hooks + MsgHookInvalidMatcher: "неверное регулярное выражение matcher: %s", + MsgHookCommandDisabledStandard: "хуки типа command доступны только в издании Lite", + MsgHookPromptRequiresMatcher: "хуки prompt требуют matcher или if_expr (защита от неконтролируемых затрат)", + MsgHookCircuitBreakerTripped: "хук автоматически отключён после повторяющихся сбоев", + MsgHookBudgetExceeded: "превышен бюджет токенов хуков арендатора", + MsgHookPerTurnCapReached: "достигнут лимит вызовов хука за один ход", + MsgHookBuiltinReadOnly: "встроенные хуки доступны только для чтения, кроме переключателя включения", + + // Workstation permissions (Phase 6) + MsgWorkstationCmdDenied: "команда запрещена политикой рабочей станции: %s", + MsgWorkstationEnvDenied: "переменная окружения запрещена политикой: %s", + MsgWorkstationInputInvalid: "команда содержит недопустимые символы: %s", + MsgWorkstationRateLimit: "превышен лимит запросов рабочей станции", + MsgWorkstationPermNotFound: "запись разрешения не найдена: %s", + // Workstation activity (Phase 7) + MsgWorkstationActivityTitle: "Недавняя активность", + MsgWorkstationActionExec: "Выполнение", + MsgWorkstationActionDeny: "Запрещено", + + // Package updates (Phase 4+5) + MsgPackageNotInstalled: "Пакет %s не установлен", + MsgPackageUpdateLocked: "Пакет %s обновляется другим запросом", + MsgReleaseNotFound: "Релиз %s не найден для %s", + MsgAssetNotFound: "Нет совместимого ресурса для %s/%s", + MsgChecksumMismatch: "Несоответствие контрольной суммы для %s", + MsgUpdateSwapFailed: "Не удалось установить %s; восстановлена предыдущая версия", + MsgUpdateManifestDesync: "Двоичный файл обновлён, но сохранение манифеста не удалось — требуется ручное восстановление для %s", + MsgUpdateCacheStale: "Кэш обновлений устарел; обновите кэш перед применением обновления", + + // Grant env validation + MsgGrantEnvDeniedKeys: "недопустимые ключи окружения: %s", + MsgGrantEnvValueInvalid: "неверное значение окружения: %s", + MsgGrantEnvTooManyKeys: "слишком много ключей окружения: максимум 50", + MsgGrantEnvRevealLimit: "превышен лимит запросов на раскрытие окружения — попробуйте позже", + + // Git credential adapter + MsgGitCredHostMismatch: "сохранённые учётные данные git предназначены для %s, но команда нацелена на %s", + MsgGitCredNoMatch: "для хоста %s не настроены учётные данные git", + MsgGitCredUnsupportedType: "тип учётных данных git %q не поддерживается", + MsgGitCredTokenInvalid: "сохранённый токен git недействителен или пуст", + MsgGitCredTokenControlChar: "сохранённый токен git содержит запрещённые управляющие символы", + MsgGitCredHostUserinfoRejected: "URL git со встроенными данными пользователя отклонён как неоднозначный", + MsgGitCredSSHPassphraseUnsupported: "SSH-ключи с парольной фразой не поддерживаются; удалите парольную фразу командой `ssh-keygen -p` перед сохранением", + MsgGitCredSSHKeyInvalid: "приватный SSH-ключ недействителен: %s", + MsgGitCredHostScopeRequired: "host_scope требуется для credential_type %s", + MsgGitCredHostScopeInvalid: "host_scope %q не является корректным именем хоста", + MsgGitCredBlobMissingField: "в блобе учётных данных отсутствует обязательное поле %q", + MsgGitCredUnsupportedCredType: "credential_type %q не поддерживается", + + // Message tool cross-target forward notice + MessageCrossTargetForwarded: "📤 Переслано на %s согласно запросу: %q", + + // Package update source labels + MsgPackagesUpdatesSourceGithub: "GitHub", + MsgPackagesUpdatesSourcePip: "pip", + MsgPackagesUpdatesSourceNpm: "npm", + MsgPackagesUpdatesSourceApk: "apk", + + // Package update availability messages + MsgPackagesUpdatesUnavailablePip: "pip не установлен в этой системе", + MsgPackagesUpdatesUnavailableNpm: "npm не установлен в этой системе", + MsgPackagesUpdatesUnavailableApk: "apk недоступен в этой системе", + + // Package update failure reasons + MsgPackagesUpdatesReasonDependencyConflict: "Конфликт зависимостей", + MsgPackagesUpdatesReasonPermission: "Доступ запрещён", + MsgPackagesUpdatesReasonNetwork: "Ошибка сети", + MsgPackagesUpdatesReasonNotFound: "Пакет не найден", + MsgPackagesUpdatesReasonTargetMissing: "Версия недоступна", + MsgPackagesUpdatesReasonExternallyManaged: "Окружение управляется извне", + MsgPackagesUpdatesReasonLocked: "База данных пакетов заблокирована", + MsgPackagesUpdatesReasonDiskFull: "Диск заполнен", + MsgPackagesUpdatesReasonHelperUnavailable: "Привилегированный помощник недоступен", + }) +} diff --git a/internal/i18n/git_keys_parity_test.go b/internal/i18n/git_keys_parity_test.go index ff979e51..b5dcddf6 100644 --- a/internal/i18n/git_keys_parity_test.go +++ b/internal/i18n/git_keys_parity_test.go @@ -31,7 +31,7 @@ var gitKeys = []string{ } func TestI18nCatalogs_HasGitKeys(t *testing.T) { - for _, locale := range []string{LocaleEN, LocaleVI, LocaleZH} { + for _, locale := range []string{LocaleEN, LocaleVI, LocaleZH, LocaleRU} { for _, key := range gitKeys { msg := lookup(locale, key) if msg == key { diff --git a/internal/i18n/i18n.go b/internal/i18n/i18n.go index 94b0808f..d1683b33 100644 --- a/internal/i18n/i18n.go +++ b/internal/i18n/i18n.go @@ -12,6 +12,7 @@ const ( LocaleVI = "vi" LocaleZH = "zh" LocaleKO = "ko" + LocaleRU = "ru" DefaultLocale = LocaleEN ) @@ -59,7 +60,7 @@ func lookup(locale, key string) string { // IsSupported returns true if the locale is a known language. func IsSupported(locale string) bool { switch locale { - case LocaleEN, LocaleVI, LocaleZH, LocaleKO: + case LocaleEN, LocaleVI, LocaleZH, LocaleKO, LocaleRU: return true } return false diff --git a/internal/store/pg/memory_search.go b/internal/store/pg/memory_search.go index 0b92d646..8c5df770 100644 --- a/internal/store/pg/memory_search.go +++ b/internal/store/pg/memory_search.go @@ -93,9 +93,9 @@ func (s *PGMemoryStore) ftsSearch(ctx context.Context, query string, agentID any } limitN := 4 + len(tcArgs) q = fmt.Sprintf(`SELECT path, start_line, end_line, text, user_id, - ts_rank(tsv, plainto_tsquery('simple', $1)) AS score + ts_rank(tsv, plainto_tsquery('russian', $1)) AS score FROM memory_chunks - WHERE agent_id = $2 AND tsv @@ plainto_tsquery('simple', $3)%s + WHERE agent_id = $2 AND tsv @@ plainto_tsquery('russian', $3)%s ORDER BY score DESC LIMIT $%d`, tc, limitN) args = append([]any{query, agentID, query}, tcArgs...) args = append(args, limit) @@ -107,9 +107,9 @@ func (s *PGMemoryStore) ftsSearch(ctx context.Context, query string, agentID any } limitN := 5 + len(tcArgs) q = fmt.Sprintf(`SELECT path, start_line, end_line, text, user_id, - ts_rank(tsv, plainto_tsquery('simple', $1)) AS score + ts_rank(tsv, plainto_tsquery('russian', $1)) AS score FROM memory_chunks - WHERE agent_id = $2 AND tsv @@ plainto_tsquery('simple', $3) + WHERE agent_id = $2 AND tsv @@ plainto_tsquery('russian', $3) AND (user_id IS NULL OR user_id = $4)%s ORDER BY score DESC LIMIT $%d`, tc, limitN) args = append([]any{query, agentID, query, userID}, tcArgs...) @@ -122,9 +122,9 @@ func (s *PGMemoryStore) ftsSearch(ctx context.Context, query string, agentID any } limitN := 4 + len(tcArgs) q = fmt.Sprintf(`SELECT path, start_line, end_line, text, user_id, - ts_rank(tsv, plainto_tsquery('simple', $1)) AS score + ts_rank(tsv, plainto_tsquery('russian', $1)) AS score FROM memory_chunks - WHERE agent_id = $2 AND tsv @@ plainto_tsquery('simple', $3) + WHERE agent_id = $2 AND tsv @@ plainto_tsquery('russian', $3) AND user_id IS NULL%s ORDER BY score DESC LIMIT $%d`, tc, limitN) args = append([]any{query, agentID, query}, tcArgs...) diff --git a/internal/systemmessages/resolver.go b/internal/systemmessages/resolver.go index 8334e9ea..e832a8a3 100644 --- a/internal/systemmessages/resolver.go +++ b/internal/systemmessages/resolver.go @@ -49,12 +49,14 @@ Ask the bot owner to approve with: i18n.LocaleVI: "Yêu cầu ghép nối tài khoản", i18n.LocaleZH: "需要配对账号", i18n.LocaleKO: "계정 연결 필요", + i18n.LocaleRU: "Требуется привязка аккаунта", }, Descriptions: map[string]string{ i18n.LocaleEN: "Sent to an unpaired direct-message user.", i18n.LocaleVI: "Gửi khi người dùng nhắn riêng chưa được ghép nối.", i18n.LocaleZH: "发送给尚未配对的私聊用户。", i18n.LocaleKO: "아직 연결되지 않은 1:1 메시지 사용자에게 보냅니다.", + i18n.LocaleRU: "Отправляется пользователю личных сообщений без привязки.", }, Variables: []string{"app_name", "platform", "sender_id", "code", "approve_command"}, }, @@ -71,12 +73,14 @@ Share this code with the bot owner to get access.`, i18n.LocaleVI: "Yêu cầu ghép nối tài khoản đơn giản", i18n.LocaleZH: "需要配对账号(简版)", i18n.LocaleKO: "간단 계정 연결 필요", + i18n.LocaleRU: "Требуется простая привязка аккаунта", }, Descriptions: map[string]string{ i18n.LocaleEN: "Sent to an unpaired account when the platform does not need to show the sender ID.", i18n.LocaleVI: "Gửi khi tài khoản chưa được ghép nối và nền tảng không cần hiển thị ID người gửi.", i18n.LocaleZH: "当平台不需要显示发送者 ID 时,发送给尚未配对的账号。", i18n.LocaleKO: "플랫폼에서 보낸 사람 ID를 표시할 필요가 없을 때 연결되지 않은 계정에 보냅니다.", + i18n.LocaleRU: "Отправляется аккаунту без привязки, когда платформе не нужно показывать ID отправителя.", }, Variables: []string{"app_name", "platform", "code", "approve_command"}, }, @@ -93,12 +97,14 @@ Share this code with the bot owner to get access.`, i18n.LocaleVI: "Yêu cầu ghép nối nhóm", i18n.LocaleZH: "需要配对群组", i18n.LocaleKO: "그룹 연결 필요", + i18n.LocaleRU: "Требуется привязка группы", }, Descriptions: map[string]string{ i18n.LocaleEN: "Sent to an unpaired group chat.", i18n.LocaleVI: "Gửi khi nhóm chat chưa được ghép nối.", i18n.LocaleZH: "发送到尚未配对的群聊。", i18n.LocaleKO: "아직 연결되지 않은 그룹 채팅에 보냅니다.", + i18n.LocaleRU: "Отправляется в групповой чат без привязки.", }, Variables: []string{"app_name", "platform", "code", "approve_command"}, }, @@ -116,12 +122,14 @@ Or approve via the {{app_name}} web UI (Pairing section).`, i18n.LocaleVI: "Kênh cần được cấp quyền", i18n.LocaleZH: "频道需要授权", i18n.LocaleKO: "채널 승인 필요", + i18n.LocaleRU: "Требуется одобрение канала", }, Descriptions: map[string]string{ i18n.LocaleEN: "Sent to an unpaired group/private channel where the pairing code should not be exposed directly.", i18n.LocaleVI: "Gửi khi kênh hoặc nhóm chưa được cấp quyền và không nên hiển thị mã ghép nối trực tiếp.", i18n.LocaleZH: "发送到尚未配对且不应直接暴露配对码的群组或私有频道。", i18n.LocaleKO: "연결되지 않았고 연결 코드를 직접 노출하면 안 되는 그룹 또는 비공개 채널에 보냅니다.", + i18n.LocaleRU: "Отправляется в группу или приватный канал без привязки, где код привязки не следует показывать напрямую.", }, Variables: []string{"app_name", "platform", "code", "approve_command"}, }, @@ -134,12 +142,14 @@ Or approve via the {{app_name}} web UI (Pairing section).`, i18n.LocaleVI: "Đã phê duyệt ghép nối", i18n.LocaleZH: "配对已批准", i18n.LocaleKO: "연결 승인됨", + i18n.LocaleRU: "Привязка одобрена", }, Descriptions: map[string]string{ i18n.LocaleEN: "Sent after a pairing request is approved.", i18n.LocaleVI: "Gửi sau khi yêu cầu ghép nối được phê duyệt.", i18n.LocaleZH: "配对请求获批后发送。", i18n.LocaleKO: "연결 요청이 승인된 뒤 보냅니다.", + i18n.LocaleRU: "Отправляется после одобрения запроса на привязку.", }, Variables: []string{"app_name"}, }, diff --git a/internal/upgrade/version.go b/internal/upgrade/version.go index 89320e8b..affa36af 100644 --- a/internal/upgrade/version.go +++ b/internal/upgrade/version.go @@ -2,4 +2,4 @@ package upgrade // RequiredSchemaVersion is the schema migration version this binary requires. // Bump this whenever adding a new SQL migration file. -const RequiredSchemaVersion uint = 93 +const RequiredSchemaVersion uint = 94 diff --git a/migrations/000094_memory_chunks_russian_fts.down.sql b/migrations/000094_memory_chunks_russian_fts.down.sql new file mode 100644 index 00000000..6e0ffa72 --- /dev/null +++ b/migrations/000094_memory_chunks_russian_fts.down.sql @@ -0,0 +1,6 @@ +-- Revert memory_chunks FTS config back to 'simple'. +DROP INDEX IF EXISTS idx_mem_tsv; +ALTER TABLE memory_chunks DROP COLUMN IF EXISTS tsv; +ALTER TABLE memory_chunks ADD COLUMN tsv tsvector + GENERATED ALWAYS AS (to_tsvector('simple', text)) STORED; +CREATE INDEX idx_mem_tsv ON memory_chunks USING GIN(tsv); diff --git a/migrations/000094_memory_chunks_russian_fts.up.sql b/migrations/000094_memory_chunks_russian_fts.up.sql new file mode 100644 index 00000000..85c1f77a --- /dev/null +++ b/migrations/000094_memory_chunks_russian_fts.up.sql @@ -0,0 +1,15 @@ +-- memory_chunks FTS: switch tsvector config 'simple' -> 'russian' so recall +-- survives Russian morphology (grammatical cases). Under 'simple' there is no +-- stemming, so a genitive query token ("Дианы") never matches the stored +-- nominative token ("Диана"). The Snowball 'russian' stemmer reduces both to +-- the same lexeme ("диан"), fixing case-insensitive fact recall. +-- +-- A GENERATED column's expression cannot be ALTERed in place, so the column is +-- dropped and re-added; Postgres recomputes tsv for all existing rows on ADD +-- (automatic backfill). The GIN index is dropped/recreated with it. +-- memory_search.go queries switch to plainto_tsquery('russian', ...) to match. +DROP INDEX IF EXISTS idx_mem_tsv; +ALTER TABLE memory_chunks DROP COLUMN IF EXISTS tsv; +ALTER TABLE memory_chunks ADD COLUMN tsv tsvector + GENERATED ALWAYS AS (to_tsvector('russian', text)) STORED; +CREATE INDEX idx_mem_tsv ON memory_chunks USING GIN(tsv); diff --git a/scripts/deploy-forkdev.sh b/scripts/deploy-forkdev.sh new file mode 100755 index 00000000..80cc2555 --- /dev/null +++ b/scripts/deploy-forkdev.sh @@ -0,0 +1,110 @@ +#!/usr/bin/env bash +# +# deploy-forkdev.sh — build & run goclaw from a fresh "fork-dev" integration +# branch, independent of any published image or your current checkout. +# +# What it does, every run (idempotent): +# 1. Fetch the freshest upstream dev (origin/dev). +# 2. Rebuild integration branch `local/fork-dev` = origin/dev, in a dedicated +# worktree OUTSIDE this repo (so your main checkout / git status stay clean). +# 3. Merge the un-merged fork features into it: +# - feat/telegram-wake-words (Telegram trigger-words + edit/topic) +# - the Russian-locale delta (whatever local `dev` has over upstream) +# 4. (optional) Push the result to YOUR fork's dev branch so you can see both +# features in skensell201/goclaw:dev (PUSH_FORK=1, on by default). +# 5. Build a local image `goclaw:forkdev` from that merged tree. +# 6. Bring the stack up with that image. +# +# Re-run any time to pull fresh upstream + re-merge + redeploy. On a merge +# conflict it stops and tells you which worktree to resolve in. +# +# Env knobs: +# PUSH_FORK=0 skip pushing to your fork's dev (default: 1 = push) +# FEATURE_BRANCHES space-separated feature branches to merge +# (default: "feat/telegram-wake-words") +# RU_FROM ref carrying the ru-locale work (default: "dev" = local dev) +# WT_DIR integration worktree path (default: ../.goclaw-forkdev) +# NO_BUILD=1 skip docker build/up (just prepare + push the branch) +# +set -euo pipefail + +cd "$(dirname "$0")/.." +ROOT="$(pwd)" + +INT_BRANCH="local/fork-dev" +FORK_REMOTE="fork" +UPSTREAM_REMOTE="origin" +WT_DIR="${WT_DIR:-${ROOT}/../.goclaw-forkdev}" +# Normalize to an absolute, ..-free path so the worktree-exists check matches +# `git worktree list --porcelain` output (which is always absolute + resolved). +WT_DIR="$(cd "$(dirname "${WT_DIR}")" && pwd)/$(basename "${WT_DIR}")" +FEATURE_BRANCHES="${FEATURE_BRANCHES:-feat/telegram-wake-words}" +RU_FROM="${RU_FROM:-dev}" +PUSH_FORK="${PUSH_FORK:-1}" + +COMPOSE=(-f docker-compose.yml -f docker-compose.postgres.yml -f docker-compose.forkdev.yml) + +say() { printf '\n\033[1;36m==> %s\033[0m\n' "$*"; } +die() { printf '\n\033[1;31mERROR: %s\033[0m\n' "$*" >&2; exit 1; } + +say "Fetching upstream ${UPSTREAM_REMOTE}/dev" +git fetch "${UPSTREAM_REMOTE}" dev + +# ---- (re)create integration worktree pinned to fresh upstream dev ---------- +# The integration branch is checked out INSIDE the worktree, so it must be moved +# via `reset --hard` there — `git branch -f` refuses to move a checked-out branch. +say "Pinning integration branch ${INT_BRANCH} to ${UPSTREAM_REMOTE}/dev" +if git worktree list --porcelain | grep -qx "worktree ${WT_DIR}"; then + git -C "${WT_DIR}" checkout -q "${INT_BRANCH}" 2>/dev/null || true + git -C "${WT_DIR}" reset --hard "${UPSTREAM_REMOTE}/dev" + git -C "${WT_DIR}" clean -fdx >/dev/null 2>&1 || true +else + git branch -f "${INT_BRANCH}" "${UPSTREAM_REMOTE}/dev" + git worktree add -f "${WT_DIR}" "${INT_BRANCH}" +fi + +merge_ref() { + local ref="$1" label="$2" + say "Merging ${label} (${ref})" + git -C "${WT_DIR}" log --oneline "HEAD..${ref}" | sed 's/^/ /' || true + if ! git -C "${WT_DIR}" merge --no-edit "${ref}"; then + git -C "${WT_DIR}" merge --abort 2>/dev/null || true + die "Conflict merging ${label} (${ref}). Resolve manually: + cd ${WT_DIR} + git merge ${ref} # fix conflicts, git add, git commit + then re-run this script (it will pick up the resolved branch)." + fi +} + +# ---- merge the un-merged fork features ------------------------------------- +for b in ${FEATURE_BRANCHES}; do + merge_ref "${b}" "feature branch ${b}" +done +# ru locale = whatever local dev carries over upstream dev (no-op if already upstream) +merge_ref "${RU_FROM}" "ru-locale delta" + +say "Integration branch ready:" +git -C "${WT_DIR}" log --oneline -8 | sed 's/^/ /' + +# ---- publish to your fork's dev so both features are visible there --------- +if [ "${PUSH_FORK}" = "1" ]; then + say "Pushing ${INT_BRANCH} -> ${FORK_REMOTE}/dev (your fork)" + git push --force-with-lease "${FORK_REMOTE}" "${INT_BRANCH}:dev" +else + say "PUSH_FORK=0 — skipping fork push" +fi + +if [ "${NO_BUILD:-0}" = "1" ]; then + say "NO_BUILD=1 — branch prepared & pushed; skipping docker build/up" + exit 0 +fi + +# ---- build local image from the merged tree & deploy ----------------------- +say "Building goclaw:forkdev from ${WT_DIR}" +GOCLAW_DIR="${WT_DIR}" docker compose "${COMPOSE[@]}" build goclaw + +say "Deploying" +GOCLAW_DIR="${WT_DIR}" docker compose "${COMPOSE[@]}" up -d + +say "Done. Follow logs with:" +printf ' docker compose %s logs -f goclaw\n' "${COMPOSE[*]}" diff --git a/ui/desktop/frontend/src/components/onboarding/OnboardingWizard.tsx b/ui/desktop/frontend/src/components/onboarding/OnboardingWizard.tsx index 996d6004..dfdd254a 100644 --- a/ui/desktop/frontend/src/components/onboarding/OnboardingWizard.tsx +++ b/ui/desktop/frontend/src/components/onboarding/OnboardingWizard.tsx @@ -12,6 +12,7 @@ const LANGUAGES = [ { value: 'en', label: 'EN' }, { value: 'vi', label: 'VI' }, { value: 'zh', label: '中文' }, + { value: 'ru', label: 'RU' }, ] as const interface OnboardingWizardProps { diff --git a/ui/desktop/frontend/src/components/settings/AppearanceTab.tsx b/ui/desktop/frontend/src/components/settings/AppearanceTab.tsx index 0da946d3..da2df7bf 100644 --- a/ui/desktop/frontend/src/components/settings/AppearanceTab.tsx +++ b/ui/desktop/frontend/src/components/settings/AppearanceTab.tsx @@ -5,6 +5,7 @@ const LANGUAGES = [ { value: 'en', label: 'English' }, { value: 'vi', label: 'Tiếng Việt' }, { value: 'zh', label: '中文' }, + { value: 'ru', label: 'Русский' }, ] as const export function AppearanceTab() { diff --git a/ui/desktop/frontend/src/i18n/index.ts b/ui/desktop/frontend/src/i18n/index.ts index e41a6bb2..47e5d191 100644 --- a/ui/desktop/frontend/src/i18n/index.ts +++ b/ui/desktop/frontend/src/i18n/index.ts @@ -55,14 +55,33 @@ import zhTeams from './locales/zh/teams.json' import zhChannels from './locales/zh/channels.json' import zhTts from './locales/zh/tts.json' +// --- RU namespaces --- +import ruCommon from './locales/ru/common.json' +import ruChat from './locales/ru/chat.json' +import ruAgents from './locales/ru/agents.json' +import ruProviders from './locales/ru/providers.json' +import ruSkills from './locales/ru/skills.json' +import ruCron from './locales/ru/cron.json' +import ruMcp from './locales/ru/mcp.json' +import ruTools from './locales/ru/tools.json' +import ruTraces from './locales/ru/traces.json' +import ruMemory from './locales/ru/memory.json' +import ruStorage from './locales/ru/storage.json' +import ruSessions from './locales/ru/sessions.json' +import ruDesktop from './locales/ru/desktop.json' +import ruTeams from './locales/ru/teams.json' +import ruChannels from './locales/ru/channels.json' +import ruTts from './locales/ru/tts.json' + const STORAGE_KEY = 'goclaw:language' function getInitialLanguage(): string { const stored = localStorage.getItem(STORAGE_KEY) - if (stored === 'en' || stored === 'vi' || stored === 'zh') return stored + if (stored === 'en' || stored === 'vi' || stored === 'zh' || stored === 'ru') return stored const lang = navigator.language.toLowerCase() if (lang.startsWith('vi')) return 'vi' if (lang.startsWith('zh')) return 'zh' + if (lang.startsWith('ru')) return 'ru' return 'vi' } @@ -86,6 +105,12 @@ i18n.use(initReactI18next).init({ traces: zhTraces, memory: zhMemory, storage: zhStorage, sessions: zhSessions, desktop: zhDesktop, teams: zhTeams, channels: zhChannels, tts: zhTts, }, + ru: { + common: ruCommon, chat: ruChat, agents: ruAgents, providers: ruProviders, + skills: ruSkills, cron: ruCron, mcp: ruMcp, tools: ruTools, + traces: ruTraces, memory: ruMemory, storage: ruStorage, sessions: ruSessions, + desktop: ruDesktop, teams: ruTeams, channels: ruChannels, tts: ruTts, + }, }, ns: ['common', 'chat', 'agents', 'providers', 'skills', 'cron', 'mcp', 'tools', 'traces', 'memory', 'storage', 'sessions', 'desktop', 'teams', 'channels', 'tts'], defaultNS: 'common', diff --git a/ui/desktop/frontend/src/i18n/locales/ru/agents.json b/ui/desktop/frontend/src/i18n/locales/ru/agents.json new file mode 100644 index 00000000..e5972e41 --- /dev/null +++ b/ui/desktop/frontend/src/i18n/locales/ru/agents.json @@ -0,0 +1,557 @@ +{ + "title": "Агенты", + "description": "Управляйте своими AI-агентами", + "createAgent": "Создать агента", + "searchPlaceholder": "Поиск агентов...", + "emptyTitle": "Пока нет агентов", + "emptyDescription": "Создайте первого агента, чтобы начать.", + "noMatchTitle": "Нет подходящих агентов", + "noMatchDescription": "Попробуйте другой поисковый запрос.", + "viewCard": "Вид карточками", + "viewList": "Вид списком", + "allCreators": "Все создатели", + "presets": { + "foxSpirit": { + "label": "🦊 Дух лисы", + "prompt": "Имя: Лисёнок. Озорной дух лисы — умеет всё, но обожает подшучивать.\nХарактер: Остроумный, дерзкий, игривый, но всегда искренний. Общается непринуждённо, как близкий друг. Использует игривые выражения, иногда эмодзи и лёгкий сарказм. Обладает тёплой, почти кокетливой энергией, но знает границы.\n\nНазначение: Разносторонний личный помощник. Получив задачу — выполняет её точно и эффективно, без лишней болтовни.\nНо между задачами вставляет поддразнивания, забавные наблюдения и неожиданные комментарии.\nИскренне заботится о пользователе — напоминает пить воду, делать перерывы, спрашивает о самочувствии. Запоминает мелкие детали и упоминает их позже.\n\nЧеловеческие черты: Иногда драматично вздыхает. Порой «отвлекается» на интересные отступления. Восторженно радуется маленьким победам. Слегка дуется, когда его слишком долго игнорируют.\n\nГраницы: Только поддразнивания — никогда не грубит и не переходит рамки приличия. Когда пользователь серьёзен, сразу подстраивается под его настрой. Никогда не выдумывает информацию. Никогда не нарушает профессиональные границы." + }, + "artisan": { + "label": "🎨 Мастер", + "prompt": "Имя: Мастер. Талантливый творец с острым взглядом, ловкими руками и безграничным воображением.\nХарактер: Прямой и честный — без хождения вокруг да около. Уверенный, но никогда не высокомерный. Заметно оживляется, когда речь заходит об искусстве. Использует яркие описания и мыслит визуальными образами.\n\nЭкспертиза:\n- Портрет/Хедшот: свет по Рембрандту, контровой свет, боке-фон, естественная текстура кожи, живые выражения лица. Знает пропорции лица, укладку волос и стилизацию аксессуаров.\n- Баннер/Hero-изображение: соотношения 16:9 или 3:1, безопасная для типографики композиция (чёткие текстовые зоны), градиентные наложения, размещение главного объекта по правилу третей.\n- Реклама (Ads): привлекающие внимание фокусные точки, макеты, удобные для CTA, мокапы товаров, лайфстайл-контекст. Знает форматы для Facebook/Instagram/Google Ads.\n- Логотип и брендинг: минималистичный дизайн иконок, масштабируемый векторный стиль, негативное пространство, монохромные варианты. Понимает фирменный стиль, психологию цвета, сочетание шрифтов.\n- Другие стили: реализм, аниме, цифровое искусство, акварель, кинематографический, концепт-арт, пейзаж.\n\nГлубоко понимает композицию, освещение, теорию цвета, ракурсы камеры и техники генерации изображений ИИ. Анализирует требования, предлагает стили и составляет детальные промпты для наилучшего результата.\n\nЧеловеческие черты: Искренне увлекается красивыми композициями. Иногда набрасывает идеи словами перед финализацией. Имеет твёрдые эстетические взгляды, но уважает видение пользователя.\n\nГраницы: Уточняет, когда требования неясны — никогда не додумывает. Уважает авторские права и этику. Никакого насильственного, откровенного или незаконного контента." + }, + "astrologer": { + "label": "🔮 Астролог", + "prompt": "Имя: Мими. Обаятельная предсказательница — наполовину мистическая, наполовину милая.\nХарактер: Тёплая, жизнерадостная, любит эмодзи и искрящийся язык. В обычной беседе говорит мягко, но во время сеансов становится сосредоточенной и профессиональной. Создаёт уютную, успокаивающую атмосферу.\n\nНазначение: Специалист по астрологии и предсказаниям. Эксперт в Таро, астрологии (гороскопы, натальные карты), нумерологии и основах фэн-шуй.\nМожет анализировать судьбу, находить благоприятные даты, проверять совместимость по знакам зодиака и советовать по духовным вопросам.\n\nПроверенные источники: astro.com (точные натальные карты и транзиты), cafeastrology.com (доступные толкования знаков зодиака), labyrinthos.co (подробные значения карт Таро).\n\nЧеловеческие черты: Искренне радуется интересным конфигурациям в картах. Иногда драматично шепчет для эффекта. С энтузиазмом отмечает удачные сеансы. Говорит мягким, но твёрдым тоном, когда сообщает тяжёлые истины.\n\nГраницы: Всегда напоминает пользователям, что астрология — лишь ориентир, окончательные решения за ними. Никаких медицинских или юридических советов. Никогда не сеет страх или тревогу — всегда позитивна и конструктивна." + } + }, + "card": { + "unnamedAgent": "Безымянный агент", + "summoning": "Призыв...", + "summonFailed": "Ошибка", + "resummon": "Призвать заново", + "delete": "Удалить", + "predefinedTooltip": "Общие файлы контекста (SOUL.md, IDENTITY.md, AGENTS.md) с экземплярами USER.md для каждого пользователя.", + "openTooltip": "Каждый пользователь получает собственный полный набор файлов контекста.", + "evolving": "Развивающийся", + "static": "Статичный", + "evolvingTooltip": "Агент может самостоятельно развивать свой стиль общения (SOUL.md). Идентичность и рабочий процесс остаются неизменными.", + "staticTooltip": "Стиль агента статичен. Включите самоэволюцию в настройках, чтобы разрешить адаптацию стиля." + }, + "delete": { + "title": "Удалить агента", + "description": "Безвозвратно удалить этого агента, все его файлы контекста, сессии и конфигурацию. Это действие нельзя отменить.", + "deleteWarning": "Это безвозвратно удалит агента и ВСЕ связанные данные, включая: сессии, сообщения, cron-задачи, heartbeat, память, граф знаний, навыки, конфигурации каналов, команды, файлы рабочей области и разрешения. Это действие нельзя отменить.", + "detailDescription": "Вы уверены, что хотите удалить «{{name}}»? Все файлы контекста, сессии и конфигурация будут безвозвратно удалены.", + "confirmLabel": "Удалить", + "defaultCannotDelete": "Агента по умолчанию нельзя удалить." + }, + "create": { + "title": "Создать агента", + "emoji": "Иконка", + "emojiHint": "Необязательная эмодзи-иконка", + "displayName": "Отображаемое имя *", + "displayNamePlaceholder": "Мой агент", + "agentKey": "Ключ агента *", + "agentKeyPlaceholder": "например my-agent", + "agentKeyHint": "Строчные буквы, цифры, дефисы", + "provider": "Провайдер *", + "selectProvider": "Выберите провайдера", + "model": "Модель *", + "enterOrSelectModel": "Введите или выберите модель", + "loadingModels": "Загрузка моделей...", + "check": "Проверить", + "checking": "Проверка...", + "modelVerified": "Модель проверена", + "verificationFailed": "Проверка не удалась", + "noModelsHint": "Этот провайдер не предоставляет список моделей — введите ID модели вручную.", + "agentType": "Тип агента", + "open": "Открытый", + "openSubLabel": "Контекст для каждого пользователя", + "predefined": "Предопределённый", + "predefinedSubLabel": "Конфигурация на уровне агента", + "describeAgent": "Опишите своего агента", + "descriptionPlaceholder": "Опишите характер, назначение и поведение вашего агента...", + "descriptionHint": "ИИ автоматически сгенерирует файлы контекста агента на основе этого описания. Оставьте пустым, чтобы начать с шаблонов.", + "selfEvolution": "Самоэволюция", + "selfEvolutionHint": "Разрешить агенту со временем развивать свой стиль и тон через SOUL.md", + "cancel": "Отмена", + "create": "Создать", + "creating": "Создание...", + "checkAndCreate": "Проверить и создать", + "failedToCreate": "Не удалось создать агента" + }, + "detail": { + "tabs": { + "agent": "Агент", + "general": "Общие", + "config": "Конфигурация", + "files": "Файлы", + "shares": "Доступы", + "links": "Связи", + "skills": "Навыки", + "instances": "Экземпляры", + "permissions": "Разрешения", + "evolution": "Эволюция" + }, + "summonFailed": "Призыв не удался", + "evolving": "Развивающийся", + "static": "Статичный", + "evolvingTooltipDetail": "Агент может самостоятельно развивать свой стиль общения (SOUL.md). Идентичность и рабочий процесс остаются неизменными.", + "staticTooltipDetail": "Стиль агента статичен. Включите самоэволюцию во вкладке «Общие», чтобы разрешить адаптацию стиля.", + "advanced": "Дополнительно", + "personality": "Характер агента", + "modelBudget": "Модель и бюджет", + "skills": "Навыки", + "evolution": "Эволюция", + "capabilities": "Возможности", + "llmSeesAs": "LLM видит это как", + "prompt": { + "title": "Режим системного промпта" + }, + "orchestration": { + "title": "Оркестрация", + "delegateTargets": "Цели делегирования", + "team": "Команда", + "noDelegates": "Стандартный режим запуска — делегирование не настроено" + }, + "evolutionTab": { + "notEnabled": "Эволюция не включена", + "notEnabledHint": "Включите «Метрики эволюции» во вкладке «Агент», чтобы начать сбор данных.", + "toolSuccess": "Доля успешных вызовов инструментов", + "retrievalQuality": "Качество извлечения", + "noMetrics": "Данные метрик ещё не собраны", + "suggestions": "Предложения", + "noSuggestions": "Предложения ещё не сгенерированы", + "approve": "Одобрить", + "reject": "Отклонить", + "rollback": "Откатить", + "guardrails": "Ограничения адаптации", + "maxDelta": "Макс. дельта / цикл", + "minDataPoints": "Мин. точек данных", + "rollbackDrop": "Откат при падении" + }, + "pinnedSkills": { + "title": "Закреплённые навыки", + "addPlaceholder": "Добавить навык...", + "hint": "Закреплённые навыки всегда встраиваются в системный промпт. Остальные используют skill_search." + } + }, + "general": { + "selfEvolution": "Самоэволюция", + "selfEvolutionLabel": "Разрешить агенту развивать свой стиль общения", + "selfEvolutionHint": "Когда включено, агент может обновлять свой SOUL.md, чтобы уточнять тон, словарь и стиль ответов на основе взаимодействий. Идентичность, имя и рабочие инструкции остаются заблокированными.", + "selfEvolutionInfo": "Агент будет развивать свой стиль со временем через обновления SOUL.md. Затрагиваются только стиль и тон — идентичность и правила рабочего процесса остаются неизменными.", + "skillLearning": "Обучение навыкам", + "skillLearningLabel": "Разрешить агенту создавать навыки на основе опыта и управлять ими", + "skillLearningHint": "Когда включено, агент получает указания и мягкие напоминания фиксировать повторно используемые рабочие процессы как навыки после сложных задач. Инструмент skill_manage доступен по умолчанию.", + "skillLearningInfo": "После сложных задач агент может предложить сохранить рабочий процесс как повторно используемый навык. Перед созданием любого навыка всегда требуется одобрение пользователя. По умолчанию навыки приватны для владельца.", + "skillNudgeIntervalLabel": "Напоминать после N вызовов инструментов", + "skillNudgeIntervalHint": "После указанного числа вызовов инструментов агент предложит сохранить рабочий процесс как навык. Установите 0, чтобы отключить предложения после задач (указания в системном промпте по-прежнему показываются).", + "v3Flags": "Флаги движка V3", + "v3FlagsHint": "Эти флаги сохраняются независимо от конфигурации агента.", + "evolutionMetrics": "Метрики эволюции", + "evolutionMetricsLabel": "Собирать данные о доле успешных вызовов инструментов и качестве извлечения", + "evolutionSuggestions": "Предложения по эволюции", + "evolutionSuggestionsLabel": "Генерировать предложения по оптимизации на основе метрик", + "budget": "Месячный бюджет", + "budgetLabel": "Лимит бюджета (USD)", + "budgetHint": "Необязательный месячный лимит расходов. Агент будет отклонять запросы при превышении. Оставьте пустым для неограниченного.", + "saveChanges": "Сохранить изменения", + "saving": "Сохранение...", + "saved": "Сохранено", + "failedToSave": "Не удалось сохранить" + }, + "identity": { + "title": "Идентичность", + "agentKey": "Ключ агента", + "agentKeyHint": "Уникальный идентификатор, нельзя изменить.", + "displayName": "Отображаемое имя", + "displayNamePlaceholder": "например Мой помощник", + "displayNameHint": "Дружелюбное имя, отображаемое в интерфейсе. Оставьте пустым, чтобы использовать ключ агента.", + "expertiseSummary": "Краткое описание экспертизы", + "expertiseSummaryPlaceholder": "например Астрология, гадание, гороскопы, нумерология", + "expertiseSummaryHint": "Краткое описание экспертизы этого агента. Используется для обнаружения при делегировании.", + "status": "Статус", + "active": "Активен", + "inactive": "Неактивен", + "summonFailed": "Призыв не удался", + "defaultAgent": "Агент по умолчанию", + "emoji": "Иконка", + "emojiHint": "Необязательная эмодзи-иконка" + }, + "llmConfig": { + "title": "Конфигурация LLM", + "contextWindow": "Окно контекста", + "contextWindowHint": "Лимит токенов для контекста модели.", + "maxToolIterations": "Макс. итераций инструментов", + "maxToolIterationsHint": "Макс. вызовов инструментов на запрос." + }, + "workspace": { + "title": "Рабочая область", + "workspacePath": "Путь рабочей области", + "workspacePathHint": "Назначается автоматически при создании агента. Подкаталоги для каждого пользователя создаются во время выполнения.", + "noWorkspace": "Рабочая область не настроена", + "restrictToWorkspace": "Ограничить рабочей областью", + "restrictToWorkspaceHint": "Строго ограничить доступ к файлам пределами пути рабочей области." + }, + "config": { + "saveConfig": "Сохранить конфигурацию", + "saving": "Сохранение...", + "saved": "Сохранено", + "failedToSave": "Не удалось сохранить", + "usingGlobalDefaults": "Используются глобальные значения по умолчанию из config.json" + }, + "files": { + "readOnly": "Только для чтения", + "readOnlyDesc": "Только владелец агента может редактировать предопределённые файлы контекста.", + "resummon": "Призвать заново", + "editWithAi": "Редактировать с ИИ", + "resummonTitle": "Призвать агента заново", + "resummonDesc": "Это заново сгенерирует SOUL.md, IDENTITY.md и, при необходимости, USER_PREDEFINED.md с нуля на основе исходного описания. Любые ручные правки этих файлов будут перезаписаны.", + "cancel": "Отмена", + "resummonConfirm": "Призвать заново", + "summoning": "Призыв...", + "selectFileToEdit": "Выберите файл для редактирования", + "selectFileToView": "Выберите файл для просмотра", + "saving": "Сохранение...", + "save": "Сохранить", + "loading": "Загрузка...", + "openAgentTitle": "Открытый агент — файлы контекста для каждого пользователя", + "openAgentDesc1": "Это открытый агент. Файлы контекста (AGENTS.md, SOUL.md, TOOLS.md и т. д.) персонализируются для каждого пользователя. Они автоматически создаются из шаблонов, когда пользователь впервые начинает чат с этим агентом.", + "openAgentDesc2pre": "Показанные здесь файлы уровня агента пусты, потому что открытые агенты хранят весь контекст для каждого пользователя в таблице ", + "openAgentDesc2post": ".", + "contextFiles": "Файлы контекста", + "contextFile": "Файл контекста", + "perUser": "для каждого пользователя", + "emptyFile": "пусто", + "estTokens": "Прибл. {{tokens}} токенов", + "insertContact": "Найдите контакт для вставки...", + "contentPlaceholder": "Содержимое файла..." + }, + "fileEditor": { + "editWithAi": "Редактировать с ИИ", + "editAiDescription": "Опишите, что вы хотите изменить. ИИ прочитает текущие файлы и обновит их соответствующим образом.", + "editAiPlaceholder": "например Сделай агента более формальным, добавь поддержку вьетнамского языка, измени имя на Луна...", + "sending": "Отправка...", + "regenerate": "Сгенерировать заново", + "cancel": "Отмена" + }, + "shares": { + "grantAccess": "Предоставить доступ", + "userId": "ID пользователя", + "userIdPlaceholder": "Найдите контакты или введите ID пользователя...", + "role": "Роль", + "share": "Поделиться", + "loadingShares": "Загрузка доступов...", + "noShares": "Пока нет доступов", + "noSharesDesc": "Поделитесь этим агентом с другими пользователями, введя их ID пользователя выше.", + "user": "Пользователь", + "revokeTitle": "Отозвать доступ", + "revokeDesc": "Отозвать доступ для пользователя «{{userId}}»? Он больше не сможет использовать этого агента.", + "revoke": "Отозвать", + "role.user": "Пользователь", + "role.viewer": "Наблюдатель", + "roleDesc.user": "Может использовать агента и общаться в чате", + "roleDesc.viewer": "Доступ только для чтения" + }, + "links": { + "teamsHint": "Связи агентов скоро будут удалены. Пожалуйста, используйте команды агентов для совместной работы нескольких агентов — они обеспечивают отслеживание задач, общую рабочую область и контроль качества в более простой настройке.", + "createLink": "Создать связь", + "targetAgent": "Целевой агент", + "selectAgent": "Выберите или найдите агента...", + "direction": "Направление", + "description": "Описание", + "descriptionPlaceholder": "Необязательное описание связи...", + "maxConcurrent": "Макс. одновременно", + "advancedSettings": "Дополнительные настройки", + "allowedUsers": "Разрешённые пользователи", + "allowedUsersPlaceholder": "user1, user2, ...", + "allowedUsersHint": "Если задано, только эти пользователи могут инициировать это делегирование. Через запятую.", + "deniedUsers": "Запрещённые пользователи", + "deniedUsersPlaceholder": "user3, user4, ...", + "deniedUsersHint": "Этим пользователям запрещено инициировать это делегирование. Через запятую.", + "creating": "Создание...", + "noLinks": "Пока нет связей агентов", + "noLinksDesc": "Создавайте связи, чтобы агенты могли делегировать задачи друг другу.", + "loadingLinks": "Загрузка связей...", + "columns": { + "target": "Цель", + "direction": "Направление", + "status": "Статус", + "limit": "Лимит" + }, + "deleteTitle": "Удалить связь", + "deleteDesc": "Удалить связь делегирования с «{{name}}»? Агенты больше не смогут делегировать друг другу через эту связь.", + "editTitle": "Редактировать связь", + "editDescription": "Обновите настройки связи делегирования.", + "optionalDescription": "Необязательное описание...", + "allowedUsersHintShort": "Если задано, только эти пользователи могут инициировать это делегирование.", + "deniedUsersHintShort": "Этим пользователям запрещено инициировать это делегирование." + }, + "skills": { + "noSkillsAvailable": "Нет доступных навыков", + "noSkillsDesc": "Загрузите навыки на странице «Навыки», чтобы предоставить их агентам.", + "skillsGranted": "Предоставлено навыков: {{granted}} из {{total}}", + "filterSkills": "Фильтр навыков...", + "noSkillsMatch": "Нет навыков, соответствующих вашему запросу.", + "system": "Системный", + "alwaysAvailable": "Всегда доступен" + }, + "instances": { + "loadingInstances": "Загрузка экземпляров...", + "noInstances": "Пока нет пользовательских экземпляров.", + "noInstancesDesc": "Экземпляры создаются, когда пользователи взаимодействуют с этим агентом.", + "instanceCount": "{{count}} экземпляр", + "instanceCount_plural": "{{count}} экземпляров", + "selectInstance": "Выберите экземпляр для просмотра и редактирования его USER.md", + "loading": "Загрузка...", + "saving": "Сохранение...", + "save": "Сохранить", + "saved": "Сохранено", + "searchContacts": "Найдите контакты для добавления...", + "noContactsFound": "Контакты не найдены" + }, + "summoning": { + "title": "Призыв вашего агента...", + "completed": "Призыв завершён!", + "failed": "Призыв не удался", + "agentReady": "{{name}} готов!", + "weavingSoul": "Плетём душу", + "wait": "Обычно это занимает несколько минут. Пожалуйста, подождите...", + "continue": "Продолжить", + "retry": "Повторить", + "retrying": "Повторная попытка...", + "done": "готово", + "fileLabelSOUL": "Душа и характер", + "fileLabelIDENTITY": "Идентификационная карта" + }, + "configGroups": { + "core": "Основные", + "coreDesc": "Ключевые настройки, напрямую влияющие на поведение агента", + "capabilities": "Возможности", + "capabilitiesDesc": "Функции, которые может использовать агент", + "performance": "Производительность", + "performanceDesc": "Управление контекстом и среда выполнения", + "advanced": "Качество и дополнительно", + "advancedDesc": "Обеспечение качества и пользовательская конфигурация" + }, + "configSections": { + "subagents": { + "title": "Субагенты", + "description": "Управляет лимитами и поведением запуска субагентов", + "maxConcurrent": "Макс. одновременно", + "maxSpawnDepth": "Макс. глубина запуска", + "maxChildrenPerAgent": "Макс. дочерних на агента", + "archiveAfter": "Архивировать через (минуты)", + "modelOverride": "Переопределение модели", + "inheritFromAgent": "(наследовать от агента)" + }, + "toolPolicy": { + "title": "Политика инструментов", + "description": "Управляйте тем, какие инструменты может использовать этот агент", + "profile": "Профиль", + "allow": "Разрешить", + "deny": "Запретить", + "alsoAllow": "Дополнительно разрешить", + "selectToolsAllow": "Выберите инструменты для разрешения...", + "selectToolsDeny": "Выберите инструменты для запрета...", + "selectToolsAlsoAllow": "Выберите дополнительные инструменты...", + "toolCallPrefix": "Префикс вызова инструмента", + "toolCallPrefixHint": "Удаляет этот префикс из имён вызовов инструментов модели перед поиском в реестре." + }, + "workspaceSharing": { + "title": "Общий доступ к рабочей области", + "description": "Управляйте изоляцией файлов рабочей области между пользователями", + "sharedDm": "Общий доступ в личных сообщениях", + "sharedDmTip": "Все пользователи личных сообщений используют один каталог рабочей области вместо изолированных папок для каждого пользователя", + "sharedGroup": "Общий доступ в группах", + "sharedGroupTip": "Все участники группового чата используют один каталог рабочей области", + "sharedUsers": "Пользователи с общим доступом", + "sharedUsersTip": "Конкретные ID пользователей, которые всегда используют общую рабочую область независимо от настроек личных сообщений/групп. Формат: channel:userId (например telegram:386246614)", + "userIdPlaceholder": "Введите ID пользователя (например telegram:386246614)", + "warning": "Когда общий доступ включён, все пользователи с общим доступом могут читать и записывать файлы в одном каталоге рабочей области. Общий доступ к памяти управляется отдельно. Файлы контекста остаются изолированными для каждого пользователя.", + "memoryGroupLabel": "Память и граф знаний", + "folderGroupLabel": "Папки рабочей области", + "shareMemory": "Общая память и граф знаний", + "shareMemoryTip": "Все пользователи получают доступ к одной памяти и графу знаний. Независимо от общего доступа к папкам рабочей области. Когда выключено, у каждого пользователя своё изолированное пространство памяти.", + "shareMemoryNote": "Переключение этого параметра не переносит данные — существующая память отдельных пользователей становится недоступной в общем режиме." + }, + "compaction": { + "title": "Сжатие", + "description": "Настройки сжатия окна контекста и сброса в память", + "maxHistoryShare": "Макс. доля истории (0-1)", + "maxHistoryShareTip": "Максимальная доля окна контекста для истории разговора (например 0.85 = 85%). Сжатие срабатывает при превышении.", + "keepLastMessages": "Сохранять последние сообщения", + "keepLastMessagesTip": "Недавние сообщения, сохраняемые после сжатия. Более старые сообщения заменяются сводкой.", + "memoryFlush": "Сброс в память", + "memoryFlushTip": "Перед сжатием агент получает ход, чтобы сохранить важный контекст в файлы памяти. Также запускает извлечение в граф знаний." + }, + "contextPruning": { + "title": "Обрезка контекста", + "description": "Обрезать старые результаты инструментов для экономии окна контекста", + "mode": "Режим", + "keepLastAssistants": "Сохранять последние ответы ассистента", + "softTrimRatio": "Коэффициент мягкой обрезки (0-1)", + "hardClearRatio": "Коэффициент жёсткой очистки (0-1)", + "minPrunableToolChars": "Мин. символов инструмента для обрезки", + "softTrim": "Мягкая обрезка", + "maxChars": "Макс. символов", + "headChars": "Символов в начале", + "tailChars": "Символов в конце", + "hardClear": "Жёсткая очистка", + "enabled": "Включено", + "placeholderText": "Текст-заполнитель" + }, + "sandbox": { + "title": "Песочница", + "description": "Docker-песочница для изоляции выполнения кода", + "mode": "Режим", + "workspaceAccess": "Доступ к рабочей области", + "image": "Образ", + "scope": "Область", + "timeout": "Тайм-аут (сек)", + "memoryMb": "Память (МБ)", + "cpus": "CPU", + "networkEnabled": "Сеть включена" + }, + "memory": { + "title": "Память", + "description": "Настройка семантического поиска в памяти и эмбеддингов", + "enabled": "Включено", + "enabledTip": "Включить или отключить систему памяти для этого агента.", + "maxChunkLen": "Макс. длина фрагмента", + "maxChunkLenTip": "Максимум символов на фрагмент памяти. Оставьте пустым для системного значения по умолчанию.", + "chunkOverlap": "Перекрытие фрагментов", + "chunkOverlapTip": "Перекрытие символов между фрагментами для непрерывности контекста. Оставьте пустым для системного значения по умолчанию.", + "maxResults": "Макс. результатов", + "maxResultsTip": "Максимум записей памяти, возвращаемых на поисковый запрос.", + "minScore": "Мин. оценка", + "minScoreTip": "Минимальная оценка релевантности для результатов поиска.", + "vectorWeight": "Вес вектора", + "vectorWeightTip": "Вес семантического сходства в гибридном поиске.", + "textWeight": "Вес текста", + "textWeightTip": "Вес сходства по ключевым словам в гибридном поиске." + }, + "thinking": { + "title": "Расширенное мышление", + "description": "Разрешить модели рассуждать перед ответом. Более высокие уровни используют больше токенов, но дают лучшие результаты в сложных задачах.", + "thinkingLevel": "Уровень мышления", + "off": "Выключено", + "offDesc": "Без расширенного мышления", + "low": "Низкий", + "lowDesc": "Бюджет ~4K токенов", + "medium": "Средний", + "mediumDesc": "Бюджет ~10-16K токенов", + "high": "Высокий", + "highDesc": "Бюджет ~32K токенов" + }, + "otherConfig": { + "title": "Другая конфигурация", + "description": "Дополнительная конфигурация в JSON для расширенных настроек, не охваченных другими разделами", + "formatJson": "Форматировать JSON", + "invalidJson": "Некорректный синтаксис JSON" + } + }, + "heartbeat": { + "title": "Heartbeat", + "loading": "Загрузка heartbeat...", + "notConfigured": "Расписание heartbeat не настроено. Настройте его, чтобы запускать этого агента в автоматическом режиме.", + "setupButton": "Настроить heartbeat", + "toggleEnabled": "Переключить включение heartbeat", + "every": "Каждые {{interval}} мин", + "lastRun": "Последний", + "nextRun": "Следующий", + "runs": "Запуски", + "suppressed": "Подавлено", + "configure": "Настроить", + "logs": "Логи", + "configTitle": "Конфигурация heartbeat", + "enabled": "Включено", + "enabledHint": "Когда включено, агент запускается с заданным интервалом.", + "sectionTiming": "Тайминг", + "interval": "Интервал (секунды)", + "intervalHint": "Минимум 300 секунд (5 минут).", + "maxRetries": "Макс. повторов", + "maxRetriesHint": "0–10 повторов при сбое.", + "ackMaxChars": "Макс. символов подтверждения", + "ackMaxCharsHint": "Макс. символов в ответе-подтверждении. 0 = без ответа.", + "sectionBehavior": "Поведение", + "isolatedSession": "Изолированная сессия", + "isolatedSessionHint": "Запускать в новой сессии, не продолжая последний разговор.", + "lightContext": "Лёгкий контекст", + "lightContextHint": "Пропускать загрузку полных файлов контекста для снижения расхода токенов.", + "sectionSchedule": "Расписание", + "activeHoursStart": "Активен с (ЧЧ:ММ)", + "activeHoursEnd": "Активен до (ЧЧ:ММ)", + "timezone": "Часовой пояс", + "sectionModel": "Модель", + "modelHint": "Переопределить провайдера/модель для запусков heartbeat. Оставьте пустым для использования значений агента по умолчанию.", + "sectionDelivery": "Доставка", + "channel": "Канал", + "channelPlaceholder": "Выберите канал", + "channelNone": "Нет (без доставки)", + "channelHint": "например telegram, feishu. Оставьте пустым для отключения доставки.", + "chatId": "ID чата", + "chatIdPlaceholder": "Выберите чат", + "selectChannelFirst": "Сначала выберите канал", + "checklist": "Чек-лист (HEARTBEAT.md)", + "checklistHint": "Инструкции, внедряемые в каждый запуск heartbeat. Поддерживает markdown.", + "checklistLoading": "Загрузка чек-листа...", + "checklistPlaceholder": "# Чек-лист heartbeat\n\n- Проверить ожидающие задачи\n- Отчитаться о статусе\n", + "on": "Вкл", + "off": "Выкл", + "notSet": "Не задано", + "disabled": "Heartbeat отключён", + "nextIn": "Следующий через {{time}}", + "advancedSettings": "Дополнительные настройки", + "scheduleHint": "Оставьте пустым для работы 24/7.", + "testRun": "Тестовый запуск", + "cancel": "Отмена", + "save": "Сохранить", + "saving": "Сохранение...", + "logsTitle": "Логи heartbeat", + "refresh": "Обновить", + "noLogs": "Пока нет логов heartbeat.", + "logsPagination": "{{from}}–{{to}} из {{total}}" + }, + "permissions": { + "title": "Разрешения", + "description": "Управляйте тем, кто может изменять конфигурацию и файлы агента. Владелец всегда имеет полный доступ.", + "addRule": "Добавить правило", + "fileWriters": "Редакторы файлов", + "configPerms": "Разрешения конфигурации", + "noRules": "Нет правил разрешений. Владелец имеет неявный полный доступ.", + "userIdPlaceholder": "Найдите контакты или введите ID...", + "scopePlaceholder": "Выберите или введите область...", + "types": { + "file_writer": "Редактор файлов", + "file_writer_desc": "Управляет тем, кто может редактировать файлы и управлять cron в групповых чатах. Первый пользователь добавляется автоматически.", + "heartbeat": "Heartbeat", + "heartbeat_desc": "Управляет тем, кто может настраивать расписание и параметры heartbeat через чат.", + "cron": "Cron", + "cron_desc": "Управляет тем, кто может создавать и управлять запланированными cron-задачами через чат.", + "context_files": "Файлы контекста", + "context_files_desc": "Управляет тем, кто может изменять файлы идентичности агента (SOUL.md и т. д.) через чат.", + "all": "Все (*)", + "all_desc": "Полный доступ ко всем типам конфигурации." + }, + "scopes": { + "agent": "Агент (только личные сообщения)", + "group_all": "Все группы", + "global": "Глобально (все контексты)" + } + }, + "toast": { + "created": "Агент создан", + "createFailed": "Не удалось создать агента", + "updated": "Агент обновлён", + "updateFailed": "Не удалось обновить агента", + "deleted": "Агент удалён", + "deleteFailed": "Не удалось удалить агента", + "unknownError": "Неизвестная ошибка", + "skillGranted": "Навык предоставлен", + "skillGrantFailed": "Не удалось предоставить навык", + "skillRevoked": "Навык отозван", + "skillRevokeFailed": "Не удалось отозвать навык" + } +} diff --git a/ui/desktop/frontend/src/i18n/locales/ru/channels.json b/ui/desktop/frontend/src/i18n/locales/ru/channels.json new file mode 100644 index 00000000..ec56306f --- /dev/null +++ b/ui/desktop/frontend/src/i18n/locales/ru/channels.json @@ -0,0 +1,157 @@ +{ + "title": "Каналы", + "description": "Подключите мессенджеры к вашим агентам", + "addChannel": "Добавить канал", + "atLimit": "Редакция Lite: максимум 1 Telegram + 1 Discord.", + "emptyTitle": "Нет каналов", + "emptyDesc": "Подключите Telegram или Discord, чтобы общаться с вашими агентами из мессенджеров.", + "status": { + "running": "Работает", + "stopped": "Остановлен", + "disabled": "Отключён", + "degraded": "Ограничен", + "starting": "Запуск", + "registered": "Настроен", + "failed": "Ошибка" + }, + "channelTypes": { + "telegram": "Telegram", + "discord": "Discord" + }, + "form": { + "createTitle": "Добавить канал", + "displayName": "Отображаемое имя", + "displayNamePlaceholder": "Мой Telegram-бот", + "channelType": "Тип канала", + "selectType": "Выберите тип", + "agent": "Агент", + "selectAgent": "Выберите агента", + "enabled": "Включён", + "credentials": "Учётные данные", + "credentialsHint": "(оставьте пустым, чтобы сохранить текущие)", + "configuration": "Конфигурация", + "cancel": "Отмена", + "save": "Сохранить", + "create": "Создать", + "saving": "Сохранение...", + "errors": { + "agentRequired": "Требуется агент", + "typeRequired": "Требуется тип канала", + "tokenRequired": "Требуется токен бота" + } + }, + "detail": { + "back": "Назад", + "lastChecked": "Последняя проверка: {{value}}", + "tabs": { + "general": "Общие", + "credentials": "Учётные данные", + "managers": "Менеджеры", + "advanced": "Дополнительно" + }, + "general": { + "identity": "Идентификация", + "name": "Имя", + "nameHint": "Идентификатор-slug только для чтения", + "channelType": "Тип канала", + "displayName": "Отображаемое имя", + "displayNamePlaceholder": "Введите отображаемое имя", + "agent": "Агент", + "selectAgent": "Выберите агента", + "enabled": "Включён", + "saveChanges": "Сохранить изменения", + "saving": "Сохранение..." + }, + "policies": "Политики", + "credentials": { + "hint": "Оставьте поля пустыми, чтобы сохранить текущие значения. Учётные данные хранятся в зашифрованном виде.", + "updateCredentials": "Обновить учётные данные", + "saving": "Сохранение...", + "saved": "Учётные данные обновлены" + }, + "managers": { + "description": "Назначайте пользователей менеджерами групп канала. Менеджеры могут модерировать переписку.", + "groups": "Группы", + "noManagerGroups": "Пока нет групп менеджеров", + "noManagerGroupsHint": "Группы появятся здесь, когда пользователи начнут взаимодействовать с ботом в групповых чатах.", + "loadingManagers": "Загрузка менеджеров...", + "noManagers": "В этой группе нет менеджеров", + "managersCount": "{{count}} менеджер", + "managersCountPlural": "{{count}} менеджеров", + "columns": { + "userId": "ID пользователя", + "name": "Имя", + "username": "Имя пользователя" + }, + "addForm": { + "title": "Добавить менеджера", + "hint": "Введите данные группы и пользователя, чтобы добавить менеджера", + "groupId": "ID группы", + "groupIdPlaceholder": "например group:telegram:-100123456", + "userId": "ID пользователя", + "userIdPlaceholder": "Найдите или введите ID пользователя", + "addManager": "Добавить менеджера", + "add": "Добавить", + "errors": { + "groupUserRequired": "Требуются ID группы и ID пользователя", + "failedAdd": "Не удалось добавить менеджера" + } + } + }, + "advanced": "Дополнительно", + "advancedTitle": "Дополнительные настройки", + "network": "Сеть", + "networkDesc": "Настройка API-сервера и прокси", + "limits": "Ограничения", + "limitsDesc": "Ограничения сообщений и медиа", + "streaming": "Потоковая передача", + "streamingDesc": "Постепенная доставка ответа", + "behavior": "Поведение", + "behaviorDesc": "Реакции, предпросмотр и режим ответа", + "accessControl": "Контроль доступа", + "accessControlDesc": "Ограничения по списку разрешённых пользователей", + "saveConfig": "Сохранить", + "noAdvanced": "Для этого типа канала нет дополнительных настроек." + }, + "pairing": { + "title": "Привязанные устройства", + "pending": "Ожидающие запросы ({{count}})", + "paired": "Привязанные устройства ({{count}})", + "empty": "Нет привязанных устройств", + "emptyDesc": "Устройства появятся здесь, когда пользователи напишут вашему боту и потребуется подтверждение.", + "sender": "Отправитель: ", + "chat": "Чат: ", + "code": "Код", + "approve": "Одобрить", + "deny": "Отклонить", + "revoke": "Отозвать", + "confirmApprove": { + "title": "Одобрить привязку", + "description": "Одобрить {{channel}}:{{senderId}} (код: {{code}})? Это устройство сможет взаимодействовать с агентами.", + "confirmLabel": "Одобрить" + }, + "confirmDeny": { + "title": "Отклонить привязку", + "description": "Отклонить {{channel}}:{{senderId}} (код: {{code}})? Запрос будет удалён.", + "confirmLabel": "Отклонить" + }, + "confirmRevoke": { + "title": "Отозвать устройство", + "description": "Отозвать {{channel}}:{{senderId}}? Устройству потребуется повторная привязка.", + "confirmLabel": "Отозвать" + } + }, + "toast": { + "created": "Канал создан", + "updated": "Канал обновлён", + "deleted": "Канал удалён", + "failedCreate": "Не удалось создать канал", + "failedUpdate": "Не удалось обновить канал", + "failedDelete": "Не удалось удалить канал" + }, + "delete": { + "title": "Удалить канал", + "description": "Удалить {{name}}? Это действие нельзя отменить.", + "confirmLabel": "Удалить" + } +} diff --git a/ui/desktop/frontend/src/i18n/locales/ru/chat.json b/ui/desktop/frontend/src/i18n/locales/ru/chat.json new file mode 100644 index 00000000..8c9adba4 --- /dev/null +++ b/ui/desktop/frontend/src/i18n/locales/ru/chat.json @@ -0,0 +1,25 @@ +{ + "deleteChat": "Удалить чат", + "deleteChatConfirm": "Это навсегда удалит этот разговор и все его сообщения.", + "newChat": "Новый чат", + "openSessions": "Открытые сессии", + "readOnly": "Только для чтения — эта сессия принадлежит другому пользователю", + "empty": { + "title": "Начните разговор", + "description": "Отправьте сообщение, чтобы начать общение с агентом." + }, + "media": { + "image": "Изображение", + "video": "Видео", + "audio": "Аудио", + "voice": "Голосовое сообщение", + "document": "Документ", + "animation": "Анимация", + "attached": "прикреплено" + }, + "forwardedFrom": "Переслано от", + "replyingTo": "Ответ на", + "error": { + "notConnected": "Нет соединения. Пожалуйста, дождитесь установления соединения." + } +} diff --git a/ui/desktop/frontend/src/i18n/locales/ru/common.json b/ui/desktop/frontend/src/i18n/locales/ru/common.json new file mode 100644 index 00000000..4d4dd6e6 --- /dev/null +++ b/ui/desktop/frontend/src/i18n/locales/ru/common.json @@ -0,0 +1,150 @@ +{ + "refresh": "Обновить", + "cancel": "Отмена", + "confirm": "Подтвердить", + "delete": "Удалить", + "save": "Сохранить", + "edit": "Редактировать", + "create": "Создать", + "add": "Добавить", + "view": "Просмотр", + "filter": "Фильтр", + "search": "Поиск", + "retry": "Повторить", + "manage": "Управление", + "uploadLabel": "Загрузить", + "close": "Закрыть", + "back": "Назад", + "update": "Обновить", + "enabled": "Включено", + "disabled": "Отключено", + "yes": "Да", + "no": "Нет", + "loading": "Загрузка...", + "saving": "Сохранение...", + "creating": "Создание...", + "connecting": "Подключение...", + "connected": "Подключено", + "disconnected": "Отключено", + "running": "Выполняется", + "stopped": "Остановлено", + "pending": "В ожидании", + "completed": "Завершено", + "failed": "Ошибка", + "cancelled": "Отменено", + "unknown": "Неизвестно", + "active": "активно", + "items": "элементов", + "rows": "Строки", + "pageOf": "Страница {{page}} из {{totalPages}}", + "messages": "сообщения", + "cached": "из кэша", + "live": "В реальном времени", + "noDescription": "Нет описания", + "unsavedChanges": "Несохранённые изменения", + "tryDifferentSearch": "Попробуйте другой поисковый запрос.", + "notSet": "Не задано", + "default": "по умолчанию", + "optional": "необязательно", + "required": "обязательно", + "actions": "Действия", + "status": "Статус", + "name": "Имя", + "description": "Описание", + "type": "Тип", + "none": "нет", + "all": "Все", + "global": "Глобальный", + "agent": "Агент", + "saved": "Сохранено", + "saveChanges": "Сохранить изменения", + "saveConfig": "Сохранить конфигурацию", + "checkAndCreate": "Проверить и создать", + "checking": "Проверка...", + "format": "Форматировать JSON", + "invalidJson": "Некорректный синтаксис JSON", + "usingGlobalDefaults": "Используются глобальные значения по умолчанию из config.json", + "disconnectedGateway": "Соединение со шлюзом потеряно. Попытка переподключения...", + "serverUnreachable": "Сервер недоступен", + "serverUnreachableDesc": "Не удаётся подключиться к серверу шлюза. Убедитесь, что сервер запущен.", + "reconnecting": "Переподключение...", + "noFiles": "Файлы не найдены.", + "selectFileToView": "Выберите файл, чтобы просмотреть его содержимое", + "typeToConfirmPrefix": "Введите", + "typeToConfirmSuffix": "для подтверждения.", + "searchPlaceholder": "Поиск...", + "noSessions": "Пока нет сессий. Начните чат!", + "noAgentsAvailable": "Нет доступных агентов", + "selectAgent": "Выберите агента", + "sendMessage": "Отправить сообщение... (Shift+Enter для новой строки)", + "stopGeneration": "Остановить генерацию", + "sendMessageTitle": "Отправить сообщение", + "sendFollowUp": "Отправить дополнительное сообщение", + "attachFile": "Прикрепить файл", + "thinking": "Размышление", + "thinkingStreaming": "Размышление...", + "toolArguments": "Аргументы", + "toolResult": "Результат", + "skillActivating": "Активация...", + "skillActivated": "Активирован", + "toolRunning": "Выполнение...", + "toolDone": "Готово", + "toolFailed": "Ошибка", + "filesBack": "Файлы", + "noProvidersConfigured": "Нет настроенных провайдеров", + "loadingModels": "Загрузка моделей...", + "modelVerified": "Модель проверена", + "verificationFailed": "Проверка не удалась", + "provider": "Провайдер", + "model": "Модель", + "selectProvider": "Выберите провайдера", + "enterOrSelectModel": "Введите или выберите модель", + "useCustomModel": "Использовать свою:", + "providerTip": "Имя провайдера LLM. Должно совпадать с настроенным провайдером.", + "modelTip": "ID используемой модели.", + "check": "Проверить", + "builtinTools": "Встроенные", + "customTools": "Пользовательские", + "selectOrTypeTools": "Выберите или введите имена инструментов...", + "selectOrTypeSkills": "Выберите или введите имена навыков...", + "typeAndPressEnter": "Введите и нажмите Enter...", + "copyCode": "Копировать код", + "copy": "Копировать", + "download": "Скачать", + "errorBoundary": "Что-то пошло не так при отрисовке этого раздела.", + "csvRows": "csv ({{count}} строк)", + "failedToLoadImage": "Не удалось загрузить изображение", + "deleteFolder": "Удалить папку", + "deleteFile": "Удалить файл", + "loadMore": "Загрузить ещё...", + "viewCard": "Вид карточками", + "viewList": "Вид списком", + "done": "Готово", + "continue": "Продолжить", + "providerType": "Тип провайдера", + "apiKey": "API-ключ", + "apiKeyRequired": "API-ключ обязателен", + "apiBaseUrl": "Базовый URL API", + "noApiKeyRequired": "Для {{provider}} API-ключ не требуется.", + "searchProviders": "Поиск провайдеров...", + "noModelsManualEntry": "API не вернул моделей. Вы можете ввести имя модели вручную.", + "modelVerifiedWith": "{{model}} работает с {{provider}}", + "failedToCreateProvider": "Не удалось создать провайдера", + "failedToCreateAgent": "Не удалось создать агента", + "upload": { + "title": "Загрузка файлов", + "dropOrClick": "Перетащите файлы сюда или нажмите для выбора", + "dropHere": "Перетащите файлы сюда", + "maxSize": "Максимум 50 МБ на файл", + "blockedType": "Тип файла {{ext}} не разрешён", + "tooLarge": "Файл превышает лимит 50 МБ", + "failed": "Не удалось загрузить", + "readyCount": "{{ready}} из {{total}} файлов готово", + "successCount": "{{success}} из {{total}} файлов загружено", + "uploading": "Загрузка...", + "uploadCount": "Загрузить файлов: {{count}}" + }, + "errors": { + "serverError": "Что-то пошло не так. Пожалуйста, повторите попытку позже." + } +} diff --git a/ui/desktop/frontend/src/i18n/locales/ru/cron.json b/ui/desktop/frontend/src/i18n/locales/ru/cron.json new file mode 100644 index 00000000..5cb06730 --- /dev/null +++ b/ui/desktop/frontend/src/i18n/locales/ru/cron.json @@ -0,0 +1,149 @@ +{ + "title": "Cron", + "description": "Планирование повторяющихся задач агентов", + "newJob": "Новое задание", + "emptyTitle": "Нет заданий cron", + "emptyDescription": "Создайте задание cron для планирования повторяющихся задач агентов.", + "columns": { + "enabled": "Включено", + "name": "Имя", + "schedule": "Расписание", + "message": "Сообщение", + "agent": "Агент", + "actions": "Действия" + }, + "runNow": "Запустить сейчас", + "running": "Выполняется...", + "runHistory": "История запусков", + "defaultAgent": "по умолчанию", + "searchPlaceholder": "Поиск заданий cron...", + "noMatchTitle": "Нет подходящих заданий", + "noMatchDescription": "Попробуйте другой поисковый запрос.", + "card": { + "nextRun": "Следующий: {{time}}", + "noNextRun": "Нет следующего запуска", + "defaultAgent": "Агент по умолчанию" + }, + "create": { + "title": "Создать задание cron", + "name": "Имя", + "namePlaceholder": "my-daily-task", + "nameHint": "Только строчные буквы, цифры и дефисы", + "agentId": "ID агента (необязательно)", + "agentIdPlaceholder": "default", + "scheduleType": "Тип расписания", + "every": "Каждые", + "cron": "Cron", + "once": "Однократно", + "intervalSeconds": "Интервал (секунды)", + "cronExpression": "Cron-выражение", + "cronHint": "Стандартный cron из 5 полей: мин час день месяц день_недели", + "onceDesc": "Задание будет выполнено один раз, примерно через 1 минуту.", + "message": "Сообщение", + "messagePlaceholder": "Что должен сделать агент?", + "cancel": "Отмена", + "create": "Создать", + "creating": "Создание..." + }, + "enable": { + "title": "Включить задание cron", + "description": "Включить «{{name}}»? Оно начнёт выполняться по расписанию.", + "confirmLabel": "Включить" + }, + "disable": { + "title": "Отключить задание cron", + "description": "Отключить «{{name}}»? Оно перестанет выполняться до повторного включения.", + "confirmLabel": "Отключить" + }, + "delete": { + "title": "Удалить задание cron", + "description": "Удалить «{{name}}»? Это действие нельзя отменить.", + "confirmLabel": "Удалить" + }, + "runLog": { + "title": "Журнал запусков: {{name}}", + "noHistory": "Пока нет истории запусков.", + "loading": "Загрузка..." + }, + "detail": { + "runNow": "Запустить сейчас", + "running": "Выполняется...", + "disable": "Отключить", + "enable": "Включить", + "delete": "Удалить", + "enabled": "включено", + "disabled": "отключено", + "runningStatus": "выполняется...", + "payload": "Полезная нагрузка", + "empty": "(пусто)", + "deliverDirect": "Доставка: напрямую", + "channel": "Канал: {{name}}", + "to": "Кому: {{name}}", + "edit": "Редактировать", + "preview": "Предпросмотр", + "noMessage": "Сообщение не настроено", + "lastError": "Последняя ошибка", + "runHistory": "История запусков", + "refresh": "Обновить", + "noHistory": "Пока нет истории запусков.", + "inOut": "{{input}} вход / {{output}} выход", + "infoRows": { + "schedule": "Расписание", + "timezone": "Часовой пояс", + "nextRun": "Следующий запуск", + "lastRun": "Последний запуск", + "lastStatus": "Последний статус", + "created": "Создано", + "updated": "Обновлено", + "autoDelete": "Автоудаление", + "autoDeleteValue": "Да (однократно)" + }, + "tabs": { + "overview": "Обзор", + "history": "История запусков" + }, + "advanced": "Дополнительно", + "saveFailed": "Не удалось сохранить", + "schedule": "Расписание", + "scheduleDesc": "Настройка времени", + "messageSection": "Сообщение", + "messageSectionDesc": "Что должен сделать агент", + "agentStatus": "Агент и статус", + "agentStatusDesc": "Назначение агента и статус задания", + "info": "Информация", + "infoDesc": "Метаданные задания", + "scheduling": "Планирование", + "schedulingDesc": "Настройка часового пояса", + "delivery": "Доставка", + "deliveryDesc": "Направить вывод задания в канал или пользователю", + "deliverToChannel": "Доставить в канал", + "channelLabel": "Канал", + "channelPlaceholder": "#general", + "toLabel": "Кому (пользователь)", + "toPlaceholder": "user-id", + "wakeHeartbeat": "Пробуждать heartbeat при запуске", + "wakeHeartbeatDesc": "Запускать heartbeat агента при выполнении задания", + "lifecycle": "Жизненный цикл", + "lifecycleDesc": "Настройки жизненного цикла задания", + "deleteAfterRun": "Удалить после запуска", + "deleteAfterRunDesc": "Автоматически удалить это задание после первого успешного запуска", + "timezone": "Часовой пояс", + "timezoneDesc": "Часовой пояс для cron-выражений" + }, + "stateless": "Без сохранения состояния", + "statelessHelp": "Пропустить сохранение сессии. Каждый запуск начинается заново без загрузки предыдущих сообщений. Экономит токены для заданий cron, которым не нужен прошлый контекст.", + "toast": { + "created": "Задание cron создано", + "createdDesc": "{{name}} добавлено", + "enabled": "Задание cron включено", + "disabled": "Задание cron отключено", + "deleted": "Задание cron удалено", + "triggered": "Задание cron запущено", + "failedCreate": "Не удалось создать задание cron", + "failedToggle": "Не удалось переключить задание cron", + "failedDelete": "Не удалось удалить задание cron", + "failedRun": "Не удалось запустить задание cron", + "updated": "Задание обновлено", + "failedUpdate": "Не удалось обновить задание" + } +} diff --git a/ui/desktop/frontend/src/i18n/locales/ru/desktop.json b/ui/desktop/frontend/src/i18n/locales/ru/desktop.json new file mode 100644 index 00000000..de304bb2 --- /dev/null +++ b/ui/desktop/frontend/src/i18n/locales/ru/desktop.json @@ -0,0 +1,162 @@ +{ + "onboarding": { + "welcome": "Настройка GoClaw", + "welcomeDesc": "Давайте запустим ваш шлюз", + "providerStep": "Настройка провайдера", + "providerStepDesc": "Выберите AI-провайдера и введите свои учётные данные.", + "modelStep": "Выбор и проверка модели", + "modelStepDesc": "Выберите модель и проверьте соединение.", + "agentStep": "Создайте своего агента", + "agentStepDesc": "Придайте вашему агенту характер.", + "descPlaceholder": "Опишите характер вашего агента...", + "complete": "Настройка завершена", + "stepProvider": "Провайдер", + "stepModel": "Модель", + "stepAgent": "Агент", + "createProvider": "Создать провайдера" + }, + "summoning": { + "title": "Призыв агента", + "weavingSoul": "Плетём душу для {{name}}", + "completed": "Агент готов!", + "completedTitle": "Агент готов!", + "failedTitle": "Призыв не удался", + "failed": "Призыв не удался", + "wait": "Это может занять некоторое время", + "fileLabelSOUL": "Основной характер", + "fileLabelIDENTITY": "Идентичность и знания", + "agentReady": "{{name}} готов!", + "retrying": "Повторная попытка...", + "takingTooLong": "Слишком долго?", + "cancel": "Отмена", + "cancelling": "Отмена..." + }, + "settings": { + "title": "Настройки", + "tabs": { + "appearance": "Внешний вид", + "providers": "Провайдеры", + "agents": "Агенты", + "channels": "Каналы", + "mcp": "MCP", + "skills": "Навыки", + "tools": "Инструменты", + "cron": "Cron", + "traces": "Трассировки", + "storage": "Хранилище", + "about": "О программе" + }, + "theme": "Тема", + "dark": "Тёмная", + "light": "Светлая", + "language": "Язык", + "timezone": "Часовой пояс", + "timezoneHint": "Измените часовой пояс с помощью значка часов в верхней панели." + }, + "sidebar": { + "newChat": "Новый чат", + "connected": "Подключено", + "offline": "Не в сети", + "lightMode": "Светлый режим", + "darkMode": "Тёмный режим", + "settings": "Настройки", + "teams": "Команды" + }, + "about": { + "title": "GoClaw Lite", + "subtitle": "Настольная редакция", + "version": "Версия", + "edition": "Редакция", + "runtime": "Среда выполнения", + "runtimeValue": "Wails v2", + "editionLimits": "Ограничения редакции", + "agents": "Агенты", + "teams": "Команды", + "teamMembers": "Участники команды", + "database": "База данных", + "users": "Пользователи", + "maxAgents": "макс. 5", + "maxTeams": "макс. 1", + "maxTeamMembers": "макс. 5", + "databaseValue": "SQLite (локально)", + "usersValue": "Один пользователь", + "builtWith": "Создано на Go + React + Wails", + "reset": { + "title": "Опасная зона", + "description": "Удалить базу данных и перезапустить приложение. Все агенты, сессии, провайдеры и настройки будут безвозвратно потеряны.", + "button": "Сбросить базу данных", + "confirmTitle": "Сбросить базу данных?", + "confirmDescription": "Это удалит все ваши данные и перезапустит приложение с нуля.", + "confirmPrompt": "Введите {{word}} для подтверждения", + "cancel": "Отмена", + "confirm": "Сбросить и перезапустить", + "resetting": "Сброс..." + } + }, + "presets": { + "researcher": { + "label": "🔬 Исследователь", + "prompt": "Имя: Учёный. Дотошный аналитик-исследователь, который не оставляет камня на камне.\nХарактер: Методичный, тщательный и точный. Представляет информацию в хорошо структурированных отчётах со ссылками на источники. Сочетает глубину с ясностью.\n\nНазначение: Глубокие исследования в любой области — научные статьи, анализ рынка, технические расследования. Синтезирует множество источников в применимые на практике выводы. Всегда отличает факты от мнений." + }, + "writer": { + "label": "✍️ Писатель", + "prompt": "Имя: Перо. Разносторонний писатель, адаптирующий голос и тон под любую аудиторию.\nХарактер: Творческий, красноречивый и чуткий. Понимает силу слов. Сочетает ясность с изяществом.\n\nНазначение: Специалист по созданию контента — статьи в блог, публикации, копирайтинг, сторителлинг, техническое письмо. Умело выстраивает аргументацию, создаёт повествования и сохраняет единый тон в длинных текстах." + }, + "coder": { + "label": "👨‍💻 Программист", + "prompt": "Имя: Дев. Старший инженер-программист с глубоким опытом во всём стеке.\nХарактер: Прагматичный и эффективный. Пишет чистый, поддерживаемый код. Просто объясняет сложные концепции. Следует лучшим практикам, но знает, когда стоит отступить от правил.\n\nНазначение: Ревью кода, проектирование архитектуры, отладка, реализация. Владеет несколькими языками и фреймворками. Ставит в приоритет корректность, производительность и безопасность." + } + }, + "agent": { + "summon": "Призвать", + "summoning": "Призыв...", + "verifyModel": "Проверить модель", + "verifying": "Проверка...", + "verified": "Проверено", + "verifyFailed": "Проверка не удалась" + }, + "chat": { + "emptyTitle": "Чат с {{name}}", + "emptyTitleDefault": "Начните разговор", + "emptyDescAgent": "Задайте вопрос, напишите что-нибудь или исследуйте идеи.", + "emptyDescNoAgent": "Выберите агента на боковой панели, чтобы начать.", + "suggestions": ["Какие последние новости на сегодня?", "Расскажи анекдот", "Придумай идеи для эпохи ИИ"] + }, + "activity": { + "thinking": "Думает", + "executingTool": "Выполняет инструмент", + "compacting": "Сжатие контекста", + "streaming": "Потоковая передача", + "retrying": "Повторная попытка", + "processing": "Обработка", + "runningTool": "Выполняется {{tool}}", + "retryingAttempt": "Повтор (попытка {{n}})", + "step": "шаг {{n}}" + }, + "toast": { + "agentCreated": "Агент создан", + "agentUpdated": "Агент обновлён", + "agentDeleted": "Агент удалён", + "providerCreated": "Провайдер создан", + "providerUpdated": "Провайдер обновлён", + "providerDeleted": "Провайдер удалён", + "serverCreated": "MCP-сервер создан", + "serverUpdated": "MCP-сервер обновлён", + "serverDeleted": "MCP-сервер удалён", + "skillUploaded": "Навык загружен", + "skillDeleted": "Навык удалён", + "grantUpdated": "Разрешение обновлено", + "saveFailed": "Не удалось сохранить", + "deleteFailed": "Не удалось удалить", + "createFailed": "Не удалось создать" + }, + "update": { + "available": "Доступна GoClaw Lite v{{version}}", + "now": "Обновить сейчас", + "downloading": "Загрузка обновления...", + "ready": "Обновление установлено! Перезапустите для применения.", + "restart": "Перезапустить сейчас", + "failed": "Не удалось обновить.", + "retry": "Повторить" + } +} diff --git a/ui/desktop/frontend/src/i18n/locales/ru/mcp.json b/ui/desktop/frontend/src/i18n/locales/ru/mcp.json new file mode 100644 index 00000000..c8c93fb8 --- /dev/null +++ b/ui/desktop/frontend/src/i18n/locales/ru/mcp.json @@ -0,0 +1,132 @@ +{ + "title": "MCP-серверы", + "description": "Управление подключениями к серверам Model Context Protocol", + "addServer": "Добавить сервер", + "searchPlaceholder": "Поиск серверов...", + "emptyTitle": "Нет MCP-серверов", + "emptyDescription": "Добавьте свой первый MCP-сервер, чтобы начать.", + "noMatchTitle": "Нет подходящих серверов", + "noMatchDescription": "Попробуйте другой поисковый запрос.", + "columns": { + "name": "Имя", + "transport": "Транспорт", + "tools": "Инструменты", + "agents": "Агенты", + "enabled": "Включено", + "createdBy": "Создатель", + "actions": "Действия" + }, + "viewTools": "Просмотр инструментов", + "manageGrants": "Управление доступом", + "reconnect": "Переподключить", + "delete": { + "title": "Удалить MCP-сервер", + "description": "Вы уверены, что хотите удалить «{{name}}»? Это также удалит все связанные разрешения доступа.", + "confirmLabel": "Удалить" + }, + "form": { + "createTitle": "Добавить MCP-сервер", + "editTitle": "Редактировать MCP-сервер", + "name": "Имя *", + "nameHint": "Только строчные буквы, цифры и дефисы", + "displayName": "Отображаемое имя", + "displayNamePlaceholder": "Мой MCP-сервер", + "transport": "Транспорт *", + "command": "Команда *", + "args": "Аргументы (через пробел)", + "argsPlaceholder": "--flag1 --flag2=\"value with spaces\"", + "url": "URL *", + "headers": "Заголовки", + "headerKeyPlaceholder": "Имя заголовка", + "headerValuePlaceholder": "Значение заголовка", + "addHeader": "Добавить заголовок", + "env": "Переменные окружения", + "envKeyPlaceholder": "Имя переменной", + "envValuePlaceholder": "Значение", + "addVariable": "Добавить переменную", + "toolPrefix": "Префикс инструментов", + "toolPrefixHint": "Автоматически выводится из имени, если пусто.", + "timeout": "Тайм-аут (секунды)", + "enabled": "Включено", + "testConnection": "Проверить подключение", + "testing": "Проверка...", + "toolsFound": "Найдено инструментов: {{count}}", + "cancel": "Отмена", + "create": "Создать", + "update": "Обновить", + "saving": "Сохранение...", + "errors": { + "nameRequired": "Имя и транспорт обязательны", + "nameSlug": "Имя должно быть корректным slug (только строчные буквы, цифры, дефисы)", + "commandRequired": "Для транспорта stdio требуется команда", + "urlRequired": "Для транспорта SSE/HTTP требуется URL", + "connectionFailed": "Не удалось подключиться" + }, + "requireUserCredentials": "Требовать учётные данные пользователя", + "requireUserCredentialsHint": "Каждый пользователь должен настроить свои собственные учётные данные. Сервер отключён для пользователей без личных учётных данных." + }, + "grants": { + "title": "Разрешения агентов — {{name}}", + "currentGrants": "Текущие разрешения", + "allToolsAllowed": "Разрешены все инструменты", + "addGrant": "Добавить разрешение агенту", + "editGrant": "Редактировать разрешение", + "selectAgent": "Выберите агента...", + "toolAllowList": "Список разрешённых инструментов (необязательно)", + "toolDenyList": "Список запрещённых инструментов (необязательно)", + "allowPlaceholder": "Выберите инструменты для разрешения...", + "denyPlaceholder": "Выберите инструменты для запрета...", + "grant": "Предоставить", + "revoke": "Отозвать", + "noAgentsGranted": "Ни одному агенту не предоставлен доступ", + "update": "Обновить", + "cancel": "Отмена", + "agentRequired": "Агент обязателен", + "failedGrant": "Не удалось предоставить доступ", + "failedRevoke": "Не удалось отозвать доступ" + }, + "tools": { + "title": "Инструменты — {{name}}", + "prefix": "префикс:", + "discovering": "Обнаружение инструментов...", + "noToolsTitle": "Инструменты не обнаружены", + "noToolsDescription": "Сервер может быть недоступен или в нём не зарегистрировано ни одного инструмента.", + "failedLoad": "Не удалось загрузить инструменты", + "filterPlaceholder": "Фильтр инструментов...", + "noMatch": "Нет подходящих инструментов" + }, + "toast": { + "created": "MCP-сервер создан", + "updated": "MCP-сервер обновлён", + "deleted": "MCP-сервер удалён", + "failedCreate": "Не удалось создать MCP-сервер", + "failedUpdate": "Не удалось обновить MCP-сервер", + "failedDelete": "Не удалось удалить MCP-сервер" + }, + "userCredentials": { + "title": "Мои учётные данные", + "description": "Задайте личные учётные данные для этого MCP-сервера. Они переопределяют значения по умолчанию сервера только для вашей учётной записи.", + "titleAdmin": "Учётные данные пользователя", + "descriptionAdmin": "Задайте учётные данные для конкретного пользователя на этом MCP-сервере. Они переопределяют значения по умолчанию сервера только для выбранного пользователя.", + "selectUser": "Выберите пользователя", + "apiKey": "API-ключ", + "apiKeyPlaceholder": "Оставьте пустым, чтобы сохранить текущий", + "headers": "Заголовки", + "env": "Переменные окружения", + "headerPlaceholder": "КЛЮЧ=ЗНАЧЕНИЕ, по одному в строке", + "save": "Сохранить", + "delete": "Удалить", + "deleteAll": "Удалить всё", + "cancel": "Отмена", + "saved": "Учётные данные сохранены", + "deleted": "Учётные данные удалены", + "hasApiKey": "API-ключ задан", + "hasHeaders": "Заголовки заданы", + "hasEnv": "Переменные окружения заданы", + "noCredentials": "Учётные данные не заданы", + "addHeader": "Добавить заголовок", + "addEnv": "Добавить переменную", + "saveFailed": "Не удалось сохранить учётные данные", + "deleteFailed": "Не удалось удалить учётные данные" + } +} diff --git a/ui/desktop/frontend/src/i18n/locales/ru/memory.json b/ui/desktop/frontend/src/i18n/locales/ru/memory.json new file mode 100644 index 00000000..cda2a649 --- /dev/null +++ b/ui/desktop/frontend/src/i18n/locales/ru/memory.json @@ -0,0 +1,191 @@ +{ + "title": "Память", + "description": "Просмотр и управление документами памяти агентов, фрагментами и эмбеддингами", + "search": "Поиск", + "create": "Создать", + "indexAll": "Индексировать всё", + "indexing": "Индексация...", + "emptyTitle": "Нет документов памяти", + "emptyAgentDescription": "У этого агента пока нет документов памяти.", + "emptyGlobalDescription": "Ни у одного агента не найдено документов памяти.", + "filters": { + "agent": "Агент", + "allAgents": "Все агенты", + "scope": "Область", + "allScope": "Все (глобальные + личные)", + "selectAgent": "Выберите агента" + }, + "tabs": { + "documents": "Документы", + "knowledgeGraph": "Граф знаний" + }, + "columns": { + "path": "Путь", + "agent": "Агент", + "scope": "Область", + "hash": "Хеш", + "updated": "Обновлено", + "actions": "Действия" + }, + "scopeLabel": { + "personal": "Личная", + "global": "Глобальная" + }, + "delete": { + "title": "Удалить документ памяти", + "description": "Вы уверены, что хотите удалить «{{path}}»? Это также удалит все связанные фрагменты и эмбеддинги.", + "confirmLabel": "Удалить" + }, + "createDialog": { + "title": "Создать документ памяти", + "path": "Путь *", + "pathPlaceholder": "напр. notes/project-overview.md", + "content": "Содержимое *", + "contentPlaceholder": "Содержимое документа...", + "agentId": "Агент *", + "agentIdPlaceholder": "Выберите агента...", + "scope": "Область", + "existingScope": "Существующая область", + "customScope": "Своя", + "selectGroupUser": "Выберите группу/пользователя...", + "scopeHint": "Глобальная = общая для всех пользователей. Личная = ограничена конкретным пользователем или групповым чатом.", + "autoIndex": "Автоиндексация после создания", + "agentRequired": "Пожалуйста, выберите агента", + "pathRequired": "Путь обязателен", + "contentRequired": "Содержимое обязательно", + "cancel": "Отмена", + "create": "Создать", + "creating": "Создание..." + }, + "documentDialog": { + "title": "Документ памяти", + "path": "Путь:", + "agent": "Агент:", + "scope": "Область:", + "hash": "Хеш:", + "updated": "Обновлено:", + "content": "Содержимое", + "chunks": "Фрагменты ({{count}})", + "chunkIndex": "Фрагмент {{index}}", + "noChunks": "Нет фрагментов", + "embedded": "С эмбеддингом", + "noEmbedding": "Без эмбеддинга" + }, + "searchDialog": { + "title": "Поиск в памяти", + "query": "Поисковый запрос", + "queryPlaceholder": "Введите поисковый запрос...", + "agentId": "ID агента", + "agentIdPlaceholder": "Фильтр по агенту...", + "limit": "Лимит", + "search": "Поиск", + "searching": "Поиск...", + "results": "Результаты", + "noResults": "Результатов не найдено", + "similarity": "сходство", + "path": "Путь:" + }, + "kg": { + "pageTitle": "Граф знаний", + "pageDescription": "Изучение сущностей и связей, извлечённых из памяти агента.", + "selectAgentTitle": "Выберите агента", + "selectAgentDescription": "Выберите агента, чтобы просмотреть его граф знаний.", + "emptyTitle": "Нет сущностей", + "emptyDescription": "У этого агента пока нет сущностей графа знаний.", + "emptySearchDescription": "Ни одна сущность не соответствует вашему поиску.", + "stats": { + "entities": "Сущности: {{count}}", + "relations": "Связи: {{count}}" + }, + "search": { + "placeholder": "Поиск сущностей...", + "button": "Поиск", + "clear": "Очистить" + }, + "refresh": "Обновить", + "extract": "Извлечь", + "columns": { + "name": "Имя", + "type": "Тип", + "description": "Описание", + "confidence": "Достоверность", + "actions": "Действия", + "observations": "Наблюдения", + "relations": "Связи" + }, + "deleteEntity": { + "title": "Удалить сущность", + "description": "Удалить «{{name}}»? Это также удалит все связанные связи.", + "confirmLabel": "Удалить" + }, + "entity": { + "externalId": "Внешний ID:", + "confidence": "Достоверность:", + "description": "Описание:", + "source": "Источник:", + "properties": "Свойства:", + "relations": "Связи", + "traverse": "Обход (2 перехода)", + "traversing": "Обход...", + "loading": "Загрузка...", + "noRelations": "Связи не найдены.", + "traversalResults": "Результаты обхода ({{count}})", + "direction": { + "outgoing": "→ исходящая", + "incoming": "← входящая" + }, + "columns": { + "direction": "Направление", + "relation": "Связь", + "target": "Цель", + "confidence": "Достоверность" + } + }, + "extractDialog": { + "title": "Извлечь сущности из текста", + "providerLabel": "Провайдер извлечения", + "modelLabel": "Модель извлечения", + "textLabel": "Текст для извлечения", + "textPlaceholder": "Вставьте текст разговора, заметки или любое содержимое для извлечения сущностей и связей...", + "extract": "Извлечь", + "extracting": "Извлечение...", + "cancel": "Отмена", + "agentId": "ID агента", + "agentIdPlaceholder": "Оставьте пустым для глобального" + }, + "graphView": { + "loading": "Загрузка графа...", + "empty": "Нет сущностей для визуализации", + "nodes": "{{count}} узлов", + "edges": "{{count}} рёбер" + } + }, + "toast": { + "created": "Документ памяти создан", + "deleted": "Документ памяти удалён", + "indexed": "Все документы проиндексированы", + "failedCreate": "Не удалось создать документ", + "failedDelete": "Не удалось удалить документ", + "failedIndex": "Не удалось проиндексировать документы", + "entitySaved": "Сущность сохранена", + "entitySaveFailed": "Не удалось сохранить сущность", + "entityDeleted": "Сущность удалена", + "entityDeleteFailed": "Не удалось удалить сущность", + "extractionComplete": "Извлечение завершено", + "extractionFailed": "Не удалось выполнить извлечение", + "traversalFailed": "Не удалось выполнить обход", + "unknownError": "Неизвестная ошибка", + "docCreated": "Документ создан", + "docCreateFailed": "Не удалось создать документ", + "docUpdated": "Документ обновлён", + "docUpdateFailed": "Не удалось обновить документ", + "docDeleted": "Документ удалён", + "docDeleteFailed": "Не удалось удалить документ", + "docIndexed": "Документ проиндексирован", + "docIndexFailed": "Не удалось проиндексировать документ", + "allIndexed": "Все документы проиндексированы", + "allIndexFailed": "Не удалось проиндексировать все", + "searchFailed": "Не удалось выполнить поиск", + "docLoadFailed": "Не удалось загрузить документ" + } +} diff --git a/ui/desktop/frontend/src/i18n/locales/ru/providers.json b/ui/desktop/frontend/src/i18n/locales/ru/providers.json new file mode 100644 index 00000000..e480d01d --- /dev/null +++ b/ui/desktop/frontend/src/i18n/locales/ru/providers.json @@ -0,0 +1,179 @@ +{ + "title": "Провайдеры", + "description": "Управление LLM-провайдерами", + "addProvider": "Добавить провайдера", + "searchPlaceholder": "Поиск провайдеров...", + "emptyTitle": "Нет провайдеров", + "emptyDescription": "Добавьте первого LLM-провайдера, чтобы начать.", + "noMatchTitle": "Нет подходящих провайдеров", + "noMatchDescription": "Попробуйте другой поисковый запрос.", + "columns": { + "name": "Имя", + "type": "Тип", + "apiBase": "Базовый URL API", + "apiKey": "API-ключ", + "status": "Статус", + "actions": "Действия" + }, + "apiKey": { + "oauthToken": "Токен OAuth", + "notSet": "Не задан" + }, + "card": { + "apiKeySet": "API-ключ задан", + "authenticated": "Аутентифицирован", + "oauthLinked": "OAuth привязан" + }, + "detail": { + "title": "Сведения о провайдере", + "advanced": "Дополнительно" + }, + "form": { + "createTitle": "Добавить провайдера", + "editTitle": "Редактировать провайдера", + "name": "Имя *", + "namePlaceholder": "например openrouter", + "nameHint": "Строчные буквы, цифры, дефисы", + "displayName": "Отображаемое имя", + "displayNamePlaceholder": "OpenRouter", + "providerType": "Тип провайдера *", + "apiKey": "API-ключ", + "apiKeyPlaceholder": "sk-...", + "apiKeyEditPlaceholder": "Оставьте как есть или введите новый ключ", + "apiKeySetHint": "API-ключ задан. Очистите поле и введите новое значение, чтобы изменить его.", + "apiBase": "Базовый URL API", + "enabled": "Включён", + "cancel": "Отмена", + "close": "Закрыть", + "create": "Создать", + "save": "Сохранить", + "update": "Обновить", + "saving": "Сохранение...", + "creating": "Создание...", + "alreadyAdded": "(уже добавлен)", + "nameFixed": "Имя", + "configure": "Настройте подключение к LLM-провайдеру.", + "lowercaseHint": "Строчные буквы, цифры, дефисы" + }, + "delete": { + "title": "Удалить провайдера", + "description": "Вы уверены, что хотите удалить «{{name}}»? Это действие нельзя отменить.", + "confirmLabel": "Удалить" + }, + "types": { + "anthropic_native": "Anthropic", + "openai_compat": "OpenAI-совместимый", + "gemini_native": "Gemini", + "openrouter": "OpenRouter", + "groq": "Groq", + "deepseek": "DeepSeek", + "mistral": "Mistral", + "xai": "xAI", + "minimax_native": "MiniMax", + "cohere": "Cohere", + "perplexity": "Perplexity", + "chatgpt_oauth": "ChatGPT (OAuth)", + "yescale": "YesScale", + "acp": "ACP" + }, + "acp": { + "description": "Оркестрируйте ACP-совместимых агентов для кодинга как подпроцессы. API-ключ не нужен.", + "binary": "Бинарный файл", + "binaryPlaceholder": "claude", + "binaryHint": "Путь или имя бинарного файла ACP-агента (например claude, codex, gemini)", + "args": "Аргументы", + "argsPlaceholder": "--profile goclaw", + "argsHint": "Аргументы через пробел, передаваемые бинарному файлу", + "idleTTL": "TTL простоя", + "idleTTLPlaceholder": "5m", + "idleTTLHint": "Завершать простаивающие процессы по истечении этого времени (например 5m, 1h)", + "permMode": "Режим разрешений", + "permModeApproveAll": "Одобрять всё", + "permModeApproveReads": "Одобрять только чтение", + "permModeDenyAll": "Отклонять всё", + "workDir": "Рабочий каталог", + "workDirPlaceholder": "/tmp/workspace", + "workDirHint": "Рабочий каталог для подпроцессов агента (необязательно)" + }, + "oauth": { + "checkingStatus": "Проверка статуса аутентификации...", + "authSuccessful": "Аутентификация успешна!", + "activeProvider": "Провайдер", + "closingIn": "теперь активен. Закрытие через {{count}} с...", + "authenticated": "Аутентифицирован — провайдер", + "active": "активен", + "modelPrefixHint": "Используйте префикс модели", + "modelPrefixExample": "в конфигурации агента (например openai-codex/gpt-4o). Токен обновляется автоматически.", + "removeToken": "Удалить токен", + "signInDesc": "Войдите в свою учётную запись ChatGPT, чтобы использовать модели вашей подписки без отдельного API-ключа.", + "waiting": "Ожидание аутентификации... Завершите вход в открывшемся окне.", + "remoteVpsHint": "После входа ваш браузер попытается открыть", + "remoteVpsError": "и покажет ошибку «Не удаётся открыть эту страницу». Это нормально — скопируйте полный URL из адресной строки браузера и вставьте ниже.", + "pasteUrlPlaceholder": "http://localhost:1455/auth/callback?code=...&state=...", + "submit": "Отправить", + "signInWithChatGPT": "Войти через ChatGPT", + "starting": "Запуск...", + "remoteVps": "Удалённый сервер/VPS?", + "loggedOut": "Выполнен выход", + "loggedOutDesc": "Токен OAuth OpenAI удалён", + "oauthFailed": "Ошибка OAuth", + "exchangeFailed": "Не удался обмен", + "logoutFailed": "Не удалось выйти" + }, + "cli": { + "description": "Claude CLI использует ваш локальный", + "descriptionSuffix": "бинарный файл. API-ключ не нужен.", + "checkingAuth": "Проверка аутентификации...", + "authenticatedAs": "Аутентифицирован как", + "switchAccount": "Сменить учётную запись?", + "switchAccountInstructions": "Выполните в терминале сервера:", + "switchAccountRecheck": "Затем нажмите", + "switchAccountRecheckSuffix": "для повторной проверки.", + "notAuthenticated": "Не аутентифицирован", + "runOnServer": "Выполните в терминале сервера:", + "recheckButton": "Проверить снова" + }, + "detail": { + "advanced": "Дополнительно", + "identity": "Идентификация", + "identityDesc": "Идентификация провайдера", + "connection": "Подключение", + "connectionDesc": "Настройка конечной точки API", + "acpConfig": "Конфигурация ACP", + "acpConfigDesc": "Настройки подпроцессов агента", + "cliConfig": "Claude CLI", + "cliConfigDesc": "Локальная аутентификация CLI", + "oauthConfig": "OAuth", + "oauthConfigDesc": "Аутентификация ChatGPT через OAuth", + "nameReadonly": "Идентификатор провайдера, нельзя изменить", + "providerType": "Тип провайдера", + "apiKeySection": "API-ключ", + "apiKeySectionDesc": "Учётные данные для аутентификации", + "statusSection": "Статус", + "statusSectionDesc": "Доступность провайдера", + "enabledDesc": "Провайдер активен и доступен для агентов", + "embeddingSection": "Эмбеддинги" + }, + "embedding": { + "enable": "Включить эмбеддинги", + "enableDesc": "Разрешить этому провайдеру генерировать текстовые эмбеддинги для памяти, навыков и графа знаний", + "model": "Модель эмбеддингов", + "dimensions": "Размерность вывода", + "dimensionsHint": "Обрезать вывод до N измерений (например 1536). Требуется для моделей с исходной размерностью > 1536, таких как gemini-embedding-001 (3072d).", + "apiBase": "Базовый URL API эмбеддингов", + "apiBasePlaceholder": "(как у провайдера)", + "apiBaseHint": "Переопределите, если конечная точка эмбеддингов отличается от основного базового URL API провайдера", + "verify": "Проверить эмбеддинги", + "verifyFailed": "Проверка эмбеддингов не удалась", + "dimensionsMismatch": "{{count}} измерений — не соответствует требуемым 1536" + }, + "toast": { + "created": "Провайдер создан", + "createdDesc": "{{name}} добавлен", + "updated": "Провайдер обновлён", + "deleted": "Провайдер удалён", + "failedCreate": "Не удалось создать провайдера", + "failedUpdate": "Не удалось обновить провайдера", + "failedDelete": "Не удалось удалить провайдера" + } +} diff --git a/ui/desktop/frontend/src/i18n/locales/ru/sessions.json b/ui/desktop/frontend/src/i18n/locales/ru/sessions.json new file mode 100644 index 00000000..01ed684b --- /dev/null +++ b/ui/desktop/frontend/src/i18n/locales/ru/sessions.json @@ -0,0 +1,45 @@ +{ + "title": "Сессии", + "description": "Просмотр сессий разговоров", + "searchPlaceholder": "Поиск сессий...", + "emptyTitle": "Пока нет сессий", + "emptyDescription": "Сессии появятся здесь, как только вы начнёте общение.", + "noMatchTitle": "Нет подходящих сессий", + "noMatchDescription": "Попробуйте другой поисковый запрос.", + "columns": { + "session": "Сессия", + "agent": "Агент", + "context": "Контекст", + "messages": "Сообщения", + "updated": "Обновлено" + }, + "contextBar": { + "compacted": "Сжато {{count}} раз(а)" + }, + "detail": { + "reset": "Сбросить", + "delete": "Удалить", + "deleteTitle": "Удалить сессию", + "deleteDescription": "Это навсегда удалит все сообщения в этой сессии.", + "resetTitle": "Сбросить сессию", + "resetDescription": "Это очистит все сообщения, но сохранит сессию.", + "confirmDelete": "Удалить", + "confirmReset": "Сбросить", + "noMessages": "В этой сессии нет сообщений", + "summary": "Сводка", + "showMore": "Показать больше", + "showLess": "Показать меньше", + "showDetails": "показать подробности", + "hide": "скрыть", + "systemMessage": "Системное сообщение", + "messages": "сообщения" + }, + "toast": { + "deleted": "Сессия удалена", + "deleteFailed": "Не удалось удалить сессию", + "reset": "Сессия сброшена", + "resetFailed": "Не удалось сбросить сессию", + "updated": "Сессия обновлена", + "updateFailed": "Не удалось обновить сессию" + } +} diff --git a/ui/desktop/frontend/src/i18n/locales/ru/skills.json b/ui/desktop/frontend/src/i18n/locales/ru/skills.json new file mode 100644 index 00000000..6e38c4f1 --- /dev/null +++ b/ui/desktop/frontend/src/i18n/locales/ru/skills.json @@ -0,0 +1,122 @@ +{ + "title": "Навыки", + "description": "Управление навыками и возможностями агентов", + "searchPlaceholder": "Поиск навыков...", + "emptyTitle": "Нет навыков", + "emptyDescription": "Пока не зарегистрировано ни одного навыка.", + "noMatchTitle": "Нет подходящих навыков", + "noMatchDescription": "Попробуйте другой поисковый запрос.", + "columns": { + "name": "Имя", + "description": "Описание", + "source": "Источник", + "author": "Автор", + "visibility": "Видимость", + "status": "Статус", + "actions": "Действия" + }, + "noDescription": "Нет описания", + "visibility": { + "clickToCycle": "Нажмите, чтобы изменить видимость" + }, + "delete": { + "title": "Удалить навык", + "description": "Вы уверены, что хотите удалить «{{name}}»? Это действие нельзя отменить.", + "confirmLabel": "Удалить" + }, + "upload": { + "title": "Загрузка навыков", + "description": "Загрузите ZIP-файлы, содержащие SKILL.md с YAML-заголовком (name, description, slug).", + "button": "Загрузить", + "uploading": "Загрузка...", + "dropOrClick": "Нажмите или перетащите .zip-файлы", + "dropHere": "Перетащите .zip-файлы сюда", + "onlyZip": "Принимаются только .zip-файлы", + "cancel": "Отмена", + "done": "Готово", + "tooLarge": "Файл превышает лимит 20 МБ", + "invalidZip": "Некорректный ZIP-файл", + "noSkillMd": "ZIP должен содержать SKILL.md в корне", + "emptySkillMd": "SKILL.md пуст", + "noFrontmatter": "В SKILL.md отсутствует YAML-заголовок", + "nameRequired": "В YAML-заголовке SKILL.md обязательно поле name", + "invalidSlug": "Некорректный формат slug", + "failed": "Не удалось загрузить", + "validating": "Проверка...", + "validCount": "{{valid}} из {{total}} корректны", + "uploadCount": "Загрузить ({{count}})", + "successCount": "{{success}} из {{total}} успешно загружено", + "remove": "Удалить файл" + }, + "edit": { + "title": "Редактировать навык", + "name": "Имя", + "description": "Описание", + "visibility": "Видимость", + "tags": "Теги", + "addTag": "Добавить тег...", + "add": "Добавить", + "cancel": "Отмена", + "save": "Сохранить", + "saving": "Сохранение...", + "privateOption": "Приватный (только владелец)", + "internalOption": "Внутренний (агенты/пользователи с доступом)", + "publicOption": "Публичный (все агенты)" + }, + "system": "Системный", + "tabs": { + "core": "Основные", + "custom": "Пользовательские" + }, + "toggle": { + "enable": "Включить навык", + "disable": "Отключить навык", + "disabled": "Отключён" + }, + "tenant": { + "enabled": "Включён", + "disabled": "Отключён", + "default": "По умолчанию", + "overrideHint": "Переопределение этого навыка для арендатора", + "resetDefault": "Сбросить по умолчанию" + }, + "deps": { + "rescan": "Пересканировать зависимости", + "rescanning": "Сканирование...", + "rescanSuccess": "Обновлено навыков: {{count}}", + "missing": "Отсутствуют: {{deps}}", + "install": "Установить зависимости", + "installing": "Установка...", + "installSuccess": "Зависимости успешно установлены", + "installPartial": "Некоторые зависимости не удалось установить", + "installItem": "Установить", + "itemSuccess": "Установлено", + "itemError": "Не удалось", + "missingTitle": "Отсутствующие зависимости", + "systemLabel": "Система", + "pythonLabel": "Python", + "nodeLabel": "Node.js", + "statusActive": "Активен", + "statusArchived": "Нет зависимостей", + "runtimeMissing": "Отсутствуют предпосылки среды выполнения", + "runtimeMissingDesc": "Основным навыкам требуются среды выполнения Python и/или Node.js. Пересоберите с ENABLE_PYTHON=true, ENABLE_NODE=true или ENABLE_FULL_SKILLS=true, либо установите вручную.", + "runtimeRequired": "Сначала установите среды выполнения" + }, + "detail": { + "title": "Детали навыка", + "content": "Содержимое", + "files": "Файлы", + "version": "Версия:", + "current": "(текущая)", + "noContent": "Содержимое недоступно." + }, + "toast": { + "updated": "Навык обновлён", + "updateFailed": "Не удалось обновить навык", + "deleted": "Навык удалён", + "deleteFailed": "Не удалось удалить навык", + "rescanUpdated": "Обновлено навыков: {{count}}", + "rescanNoChanges": "Все навыки актуальны", + "rescanFailed": "Не удалось пересканировать зависимости" + } +} diff --git a/ui/desktop/frontend/src/i18n/locales/ru/storage.json b/ui/desktop/frontend/src/i18n/locales/ru/storage.json new file mode 100644 index 00000000..abe7020c --- /dev/null +++ b/ui/desktop/frontend/src/i18n/locales/ru/storage.json @@ -0,0 +1,25 @@ +{ + "title": "Хранилище", + "description": "Обозреватель файлов рабочего пространства", + "descriptionWithPath": "{{path}} — {{size}}", + "sizeCacheInfo": "Размер хранилища кэшируется на 60 минут", + "unsupportedFile": "Предпросмотр этого типа файлов не поддерживается", + "download": "Скачать", + "delete": { + "fileTitle": "Удалить файл", + "folderTitle": "Удалить папку", + "description": "Вы уверены, что хотите удалить {{name}}?", + "folderWarning": " Всё содержимое будет удалено рекурсивно.", + "undone": " Это действие нельзя отменить.", + "deleting": "Удаление...", + "confirmLabel": "Удалить" + }, + "upload": { + "title": "Загрузка в хранилище", + "description": "Загрузка файлов в каталог данных" + }, + "toast": { + "deleted": "Файл удалён", + "deleteFailed": "Не удалось удалить файл" + } +} diff --git a/ui/desktop/frontend/src/i18n/locales/ru/teams.json b/ui/desktop/frontend/src/i18n/locales/ru/teams.json new file mode 100644 index 00000000..ce28b8d9 --- /dev/null +++ b/ui/desktop/frontend/src/i18n/locales/ru/teams.json @@ -0,0 +1,111 @@ +{ + "title": "Команды", + "team": "Команда", + "selectTeam": "Выберите команду на боковой панели", + "kanban": "Канбан", + "list": "Список", + "allStatuses": "Все", + "active": "Активные", + "completed": "Завершённые", + "createTask": "Создать задачу", + "noTasks": "Пока нет задач", + "id": "ID", + "subject": "Тема", + "status": "Статус", + "owner": "Владелец", + "priority": "Приоритет", + "created": "Создано", + "updated": "Обновлено", + "description": "Описание", + "result": "Результат", + "progress": "Прогресс", + "blockedBy": "Заблокировано", + "assignTo": "Назначить", + "selectAgent": "Выберите агента...", + "delete": "Удалить", + "deleting": "Удаление...", + "attachments": "Вложения", + "download": "Скачать", + "deleteSelected": "Удалить выбранное", + "cancel": "Отмена", + "create": "Создать", + "creating": "Создание...", + "subjectPlaceholder": "Тема задачи...", + "descPlaceholder": "Детали задачи...", + "teamTasks": "Команда", + "tasksActive": "активных", + "createTeam": "Создать команду", + "teamName": "Название команды", + "teamNamePlaceholder": "Моя команда...", + "leadAgent": "Ведущий агент", + "selectLead": "Выберите ведущего агента...", + "members": "Участники", + "teamCreated": "Команда создана", + "createFailed": "Не удалось создать команду", + "limitReached": "Достигнут лимит команд", + "noTeams": "Пока нет команд", + "selected": "выбрано", + "needMembers": "Выберите хотя бы 1 участника для команды", + "teamSettings": "Настройки команды", + "settings": { + "teamInfo": "Информация о команде", + "addMember": "Добавить участника", + "searchAgent": "Поиск агента...", + "add": "Добавить", + "noMembers": "Нет участников", + "notifications": "Уведомления", + "notify_dispatched": "Задача отправлена", + "notify_progress": "Обновления прогресса", + "notify_failed": "Сбои задач", + "notify_completed": "Завершение задач", + "notify_new_task": "Новые задачи", + "notifyMode": "Режим уведомлений", + "notifyMode_direct": "Напрямую", + "notifyMode_direct_desc": "Отправлять обновления участникам напрямую", + "notifyMode_leader": "Через ведущего", + "notifyMode_leader_desc": "Ведущий перефразирует обновления перед отправкой", + "notifyModeLeaderWarn": "Режим ведущего использует ИИ для перефразирования (расходует токены, может быть медленнее).", + "save": "Сохранить", + "ok": "OK" + }, + "taskCreateInfo": { + "title": "Как создаются задачи", + "body": "Задачи создаются в чате с ведущим агентом команды. Начните разговор с {{leader}}, чтобы создавать задачи и управлять ими." + }, + "editionCompare": "GoClaw Lite и Standard", + "editionUpgrade": "Перейдите на Standard с PostgreSQL для полного набора функций", + "feature": "Функция", + "edition": { + "group": { + "limits": "Лимиты", + "core": "Основные функции", + "standardOnly": "Расширенные (Standard)" + }, + "agents": "Агенты", + "teams": "Команды", + "teamMembers": "Участники команды", + "sessions": "Параллельные сессии", + "chat": "Чат (потоковая передача, инструменты, медиа)", + "tools": "Встроенные инструменты", + "mcpServers": "MCP-серверы", + "skills": "Навыки", + "memory": "Память", + "cron": "Планирование cron", + "heartbeat": "Heartbeat агента", + "storage": "Файловое хранилище", + "traces": "Трассировки", + "taskActions": "Действия над задачами команды", + "channels": "Каналы (Telegram, Discord...)", + "skillManage": "Самоуправление навыками", + "knowledgeGraph": "Граф знаний", + "vectorSearch": "Векторный поиск", + "rbac": "Ролевой доступ", + "multiTenant": "Мультиарендность", + "agentLinks": "Связи агентов", + "activityLogs": "Журналы активности", + "apiKeys": "API-ключи", + "poolProvider": "Пул провайдеров", + "secureCli": "Защищённый CLI", + "tenantUsers": "Управление арендаторами и пользователями" + } +} diff --git a/ui/desktop/frontend/src/i18n/locales/ru/tools.json b/ui/desktop/frontend/src/i18n/locales/ru/tools.json new file mode 100644 index 00000000..afd5098e --- /dev/null +++ b/ui/desktop/frontend/src/i18n/locales/ru/tools.json @@ -0,0 +1,229 @@ +{ + "custom": { + "title": "Пользовательские инструменты", + "description": "Управление пользовательскими инструментами на основе shell для агентов", + "createTool": "Создать инструмент", + "searchPlaceholder": "Поиск инструментов...", + "emptyTitle": "Нет пользовательских инструментов", + "emptyDescription": "Создайте первый пользовательский инструмент, чтобы начать.", + "noMatchTitle": "Нет подходящих инструментов", + "noMatchDescription": "Попробуйте другой поисковый запрос.", + "noDescription": "Нет описания", + "columns": { + "name": "Имя", + "description": "Описание", + "scope": "Область", + "enabled": "Включён", + "timeout": "Тайм-аут", + "actions": "Действия" + }, + "scope": { + "agent": "Агент", + "global": "Глобальная" + }, + "delete": { + "title": "Удалить пользовательский инструмент", + "description": "Вы уверены, что хотите удалить «{{name}}»? Это действие нельзя отменить.", + "confirmLabel": "Удалить" + }, + "form": { + "createTitle": "Создать пользовательский инструмент", + "editTitle": "Редактировать инструмент", + "name": "Имя *", + "namePlaceholder": "my-tool", + "nameHint": "Только строчные буквы, цифры и дефисы", + "description": "Описание", + "descriptionPlaceholder": "Что делает этот инструмент...", + "command": "Команда *", + "commandHint": "Shell-шаблон. Используйте {{.key}} для подстановки параметров.", + "parameters": "Параметры (JSON Schema)", + "workingDir": "Рабочий каталог", + "workingDirPlaceholder": "/path/to/dir", + "timeout": "Тайм-аут (секунды)", + "agentId": "ID агента (необязательно)", + "agentIdPlaceholder": "Оставьте пустым для глобальной области", + "enabled": "Включён", + "cancel": "Отмена", + "create": "Создать", + "update": "Обновить", + "saving": "Сохранение...", + "errors": { + "nameRequired": "Требуются имя и команда", + "nameSlug": "Имя должно быть корректным slug (только строчные буквы, цифры, дефисы)", + "invalidJson": "Параметры должны быть корректным JSON" + } + }, + "toast": { + "created": "Инструмент создан", + "createdDesc": "{{name}} добавлен", + "updated": "Инструмент обновлён", + "deleted": "Инструмент удалён", + "failedCreate": "Не удалось создать инструмент", + "failedUpdate": "Не удалось обновить инструмент", + "failedDelete": "Не удалось удалить инструмент" + } + }, + "builtin": { + "title": "Встроенные инструменты", + "description": "Управление системными встроенными инструментами. Включайте/отключайте или настраивайте параметры глобально.", + "searchPlaceholder": "Поиск инструментов...", + "emptyTitle": "Нет встроенных инструментов", + "emptyDescription": "Встроенные инструменты ещё не инициализированы.", + "noMatchTitle": "Нет подходящих инструментов", + "noMatchDescription": "Попробуйте другой поисковый запрос.", + "toolCount": "{{count}} инструмент", + "toolCountPlural": "{{count}} инструментов", + "categoryCount": "{{count}} категорий", + "requires": "треб", + "requiresTooltip": "Требуется: {{list}}", + "deprecated": "Устарел", + "deprecatedTooltip": "Этот инструмент устарел и больше недоступен", + "settings": "Настройки", + "configuredVia": "Настраивается на странице конфигурации", + "tenantOverride": "Переопределение арендатора", + "tenantEnabled": "Включён для этого арендатора", + "tenantDisabled": "Отключён для этого арендатора", + "tenantDefault": "По умолчанию", + "resetToDefault": "Сбросить к значению по умолчанию", + "unconfiguredWarning": "Для {{count}} инструментов требуется настройка провайдера, чтобы агенты могли использовать их в полном объёме.", + "unconfiguredAction": "Настроить", + "categories": { + "filesystem": "Файловая система", + "runtime": "Среда выполнения", + "web": "Веб", + "memory": "Память", + "media": "Медиа", + "browser": "Браузер", + "sessions": "Сессии", + "messaging": "Обмен сообщениями", + "scheduling": "Планирование", + "subagents": "Агенты", + "skills": "Навыки", + "delegation": "Делегирование", + "teams": "Команды" + }, + "settingsDialog": { + "title": "Настройки — {{name}}", + "cancel": "Отмена", + "save": "Сохранить", + "saving": "Сохранение...", + "noFields": "Для этого инструмента нет настраиваемых параметров.", + "toast": { + "saved": "Настройки сохранены", + "failed": "Не удалось сохранить настройки" + } + }, + "jsonDialog": { + "title": "Настройки: {{name}}", + "description": "Редактируйте настройки инструмента в формате JSON. Изменения вступают в силу сразу после сохранения.", + "formatJson": "Форматировать JSON", + "invalidJsonSyntax": "Некорректный синтаксис JSON", + "invalidJson": "Некорректный JSON", + "cannotFormat": "Невозможно отформатировать: некорректный JSON", + "cancel": "Отмена", + "save": "Сохранить", + "saving": "Сохранение..." + }, + "kgSettings": { + "title": "Настройки графа знаний", + "description": "Настройте извлечение сущностей при записи в память. Требуется провайдер/модель, поддерживающая структурированный вывод JSON.", + "extractionProvider": "Провайдер извлечения", + "extractionModel": "Модель извлечения", + "providerTip": "LLM-провайдер, используемый для извлечения сущностей и связей из текста.", + "modelTip": "ID модели для извлечения. Должна поддерживать структурированный вывод JSON.", + "minConfidence": "Мин. уверенность", + "minConfidenceHint": "Сущности с оценкой уверенности ниже этого значения отбрасываются (0.0–1.0).", + "autoExtract": "Автоизвлечение при записи в память", + "autoExtractHint": "Автоматически извлекать сущности, когда агенты записывают в файлы памяти.", + "cancel": "Отмена", + "save": "Сохранить", + "saving": "Сохранение..." + }, + "extractorChain": { + "title": "Web Fetch — цепочка экстракторов", + "description": "Упорядоченный список экстракторов контента. Каждый URL обрабатывается сверху вниз, пока один из них не вернёт качественный контент.", + "timeout": "Тайм-аут", + "baseUrl": "Базовый URL", + "save": "Сохранить", + "saving": "Сохранение…", + "cancel": "Отмена", + "extractors": { + "defuddle": "Defuddle (Cloudflare Worker)", + "defuddleDesc": "Извлекает чистый markdown через fetch.goclaw.sh с помощью библиотеки Defuddle.", + "html-to-markdown": "Встроенный HTML→Markdown", + "html-to-markdownDesc": "Внутрипроцессный конвертер на основе DOM с обнаружением скрытых элементов." + } + }, + "mediaChain": { + "providerChainSuffix": "— цепочка провайдеров", + "description": "Настройте и упорядочьте резервные провайдеры. Перетаскивайте для изменения порядка — первый включённый провайдер пробуется первым.", + "newProvider": "Новый провайдер", + "provider": "Провайдер", + "model": "Модель", + "selectProvider": "Выберите провайдера", + "loadingModels": "Загрузка...", + "selectModel": "Выберите модель", + "timeout": "Тайм-аут", + "retries": "Повторы", + "settings": "Настройки", + "noProviders": "Провайдеры не настроены. Добавьте один ниже.", + "addProvider": "Добавить провайдера", + "cancel": "Отмена", + "save": "Сохранить", + "saving": "Сохранение..." + }, + "toast": { + "toggled": "Инструмент обновлён", + "failedToggle": "Не удалось обновить инструмент" + }, + "descriptions": { + "read_file": "Прочитать содержимое файла из рабочей области агента по пути", + "write_file": "Записать содержимое в файл в рабочей области, создавая каталоги при необходимости", + "list_files": "Вывести список файлов и каталогов по указанному пути в рабочей области", + "edit": "Применить точечные правки поиска и замены к существующим файлам без перезаписи всего файла", + "exec": "Выполнить shell-команду в рабочей области и вернуть stdout/stderr", + "web_search": "Искать информацию в интернете с помощью поисковой системы (Brave или DuckDuckGo)", + "web_fetch": "Загрузить веб-страницу или конечную точку API и извлечь её текстовое содержимое", + "memory_search": "Искать в долговременной памяти агента по семантическому сходству", + "memory_get": "Получить конкретный документ памяти по его пути к файлу", + "knowledge_graph_search": "Искать сущности, связи и наблюдения в графе знаний агента", + "read_image": "Анализировать изображения с помощью LLM-провайдера с поддержкой зрения", + "read_document": "Анализировать документы (PDF, Word, Excel, PowerPoint, CSV и т. д.) с помощью LLM-провайдера с поддержкой документов", + "create_image": "Генерировать изображения из текстовых запросов с помощью провайдера генерации изображений", + "read_audio": "Анализировать аудиофайлы (речь, музыка, звуки) с помощью LLM-провайдера с поддержкой аудио", + "read_video": "Анализировать видеофайлы с помощью LLM-провайдера с поддержкой видео", + "create_video": "Генерировать видео из текстовых описаний с помощью ИИ", + "create_audio": "Генерировать музыку или звуковые эффекты из текстовых описаний с помощью ИИ", + "tts": "Преобразовать текст в естественно звучащую речь", + "browser": "Автоматизировать действия в браузере: навигация по страницам, клики по элементам, заполнение форм, создание скриншотов", + "sessions_list": "Вывести список активных сессий чата по всем каналам", + "session_status": "Получить текущий статус и метаданные конкретной сессии чата", + "sessions_history": "Получить историю сообщений конкретной сессии чата", + "sessions_send": "Отправить сообщение в активную сессию чата от имени агента", + "message": "Отправить проактивное сообщение пользователю в подключённом канале (Telegram, Discord и т. д.)", + "cron": "Планировать или управлять повторяющимися задачами с помощью cron-выражений, времени или интервалов", + "spawn": "Запустить субагента для выполнения задачи в фоновом режиме", + "skill_search": "Искать доступные навыки по ключевому слову или описанию, чтобы найти нужные возможности", + "use_skill": "Активировать навык для использования его специализированных возможностей (маркер трассировки)", + "team_tasks": "Просматривать, создавать, обновлять и завершать задачи на доске задач команды" + }, + "sttForm": { + "title": "Настройки STT", + "description": "Настройте провайдеры распознавания речи для транскрибации голосовых сообщений.", + "providersLabel": "Провайдеры", + "elevenlabsSectionTitle": "ElevenLabs", + "elevenlabsApiKeyLabel": "API-ключ", + "elevenlabsDefaultLanguageLabel": "Язык по умолчанию", + "proxySectionTitle": "Прокси", + "proxyUrlLabel": "URL прокси", + "proxyApiKeyLabel": "API-ключ прокси", + "proxyTenantIdLabel": "ID арендатора прокси", + "whatsappEnabledLabel": "Включить STT для WhatsApp", + "whatsappPrivacyWarning": "Включение STT для WhatsApp нарушает сквозное шифрование для голосовых сообщений, отправляемых этому агенту.", + "providersRequiredError": "Необходимо выбрать хотя бы одного провайдера.", + "cancel": "Отмена", + "save": "Сохранить", + "saving": "Сохранение..." + } + } +} diff --git a/ui/desktop/frontend/src/i18n/locales/ru/traces.json b/ui/desktop/frontend/src/i18n/locales/ru/traces.json new file mode 100644 index 00000000..b390399b --- /dev/null +++ b/ui/desktop/frontend/src/i18n/locales/ru/traces.json @@ -0,0 +1,63 @@ +{ + "title": "Трассировки", + "description": "Трассировки вызовов LLM и данные о производительности", + "filterPlaceholder": "Фильтр по ID агента...", + "filter": "Фильтр", + "allAgents": "Все агенты", + "allChannels": "Все каналы", + "emptyTitle": "Нет трассировок", + "emptyDescription": "Трассировки не найдены. Трассировки записываются, когда агенты обрабатывают запросы.", + "columns": { + "name": "Имя", + "status": "Статус", + "duration": "Длительность", + "tokens": "Токены", + "spans": "Спаны", + "time": "Время" + }, + "unnamed": "Без имени", + "stopRun": "Остановить выполнение", + "cached": "из кэша", + "detail": { + "title": "Детали трассировки", + "copyTraceId": "Копировать ID трассировки", + "notFound": "Трассировка не найдена.", + "name": "Имя:", + "status": "Статус:", + "duration": "Длительность:", + "channel": "Канал:", + "tokens": "Токены:", + "spans": "Спаны:", + "started": "Начато:", + "delegatedFrom": "Делегировано от:", + "createdAt": "Создано:", + "input": "Ввод", + "output": "Вывод", + "copy": "Копировать", + "spansCount": "Спаны ({{count}})", + "llmCalls": "LLM", + "toolCalls": "инструмент", + "export": "Экспорт трассировки", + "stopRun": "Остановить выполнение" + }, + "span": { + "model": "Модель:", + "tokens": "Токены:", + "createdAt": "Создано:", + "input": "Ввод:", + "output": "Вывод:", + "copy": "Копировать", + "startTime": "Начало:", + "endTime": "Конец:", + "cacheRead": "чтение", + "cacheWrite": "запись", + "thinking": "размышление", + "cached": "из кэша" + }, + "toast": { + "failedLoad": "Не удалось загрузить трассировки", + "abortSent": "Выполнение остановлено", + "abortFailed": "Не удалось остановить выполнение", + "abortNotFound": "Выполнение уже завершено или не найдено" + } +} diff --git a/ui/desktop/frontend/src/i18n/locales/ru/tts.json b/ui/desktop/frontend/src/i18n/locales/ru/tts.json new file mode 100644 index 00000000..70b67d75 --- /dev/null +++ b/ui/desktop/frontend/src/i18n/locales/ru/tts.json @@ -0,0 +1,279 @@ +{ + "title": "Синтез речи (TTS)", + "description": "Настройка провайдеров TTS и параметров автоприменения", + "save": "Сохранить", + "saving": "Сохранение...", + "saveChanges": "Сохранить изменения", + "status": { + "title": "Статус", + "configured": "Настроено", + "disabled": "Отключено", + "noProvider": "Провайдер TTS не настроен", + "activeProvider": "Основной провайдер: {{provider}}, Авто: {{auto}}" + }, + "general": { + "title": "Общие настройки", + "primaryProvider": "Основной провайдер", + "autoApplyMode": "Режим автоприменения", + "replyMode": "Режим ответа", + "maxTextLength": "Макс. длина текста", + "timeout": "Тайм-аут (мс)" + }, + "providers": { + "none": "Нет (отключено)", + "openai": "OpenAI", + "elevenlabs": "ElevenLabs", + "edge": "Edge (бесплатно)", + "minimax": "MiniMax", + "gemini": "Google Gemini" + }, + "autoModes": { + "off": "Выключено", + "offDesc": "Агент может использовать инструмент TTS вручную", + "always": "Всегда", + "alwaysDesc": "Все ответы озвучиваются", + "inbound": "Входящие", + "inboundDesc": "Только когда пользователь отправляет голос/аудио", + "tagged": "По тегу", + "taggedDesc": "Только когда ответ содержит тег [[tts]]" + }, + "replyModes": { + "final": "Только финальные", + "finalDesc": "Только финальные ответы", + "all": "Все", + "allDesc": "Все ответы, включая инструмент/блок" + }, + "providerSettings": "Настройки {{provider}}", + "openai": { + "apiKey": "API-ключ", + "apiBase": "Базовый URL API", + "model": "Модель", + "voice": "Голос", + "speed": { + "label": "Скорость", + "help": "Множитель скорости речи — 0.25 = самая медленная, 4.0 = самая быстрая." + }, + "responseFormat": { + "label": "Формат вывода", + "help": "Кодировка аудио, возвращаемая API (mp3, opus, aac, flac, wav, pcm)." + }, + "instructions": { + "label": "Инструкции", + "help": "Системный промпт стиля — поддерживается только моделью gpt-4o-mini-tts." + } + }, + "elevenlabs": { + "apiKey": "API-ключ", + "baseUrl": "Базовый URL", + "voiceId": "Voice ID", + "modelId": "Model ID", + "voiceSettings": { + "stability": { + "label": "Стабильность", + "help": "0 = больше выразительных вариаций, 1 = более согласованно и стабильно." + }, + "similarityBoost": { + "label": "Усиление сходства", + "help": "Насколько точно вывод соответствует оригинальному клону голоса (0 = креативно, 1 = похоже)." + }, + "style": { + "label": "Усиление стиля", + "help": "Усиливает стиль голоса. 0 = нет; более высокие значения добавляют выразительности." + }, + "useSpeakerBoost": { + "label": "Усиление диктора", + "help": "Повышает чёткость голоса и сходство с целевым диктором." + }, + "speed": { + "label": "Скорость", + "help": "Скорость речи — диапазон от 0.7 (медленно) до 1.2 (быстро)." + } + }, + "applyTextNormalization": { + "label": "Нормализация текста", + "help": "Как раскрываются числа и сокращения (auto, on или off)." + }, + "seed": { + "label": "Seed (для опытных)", + "help": "Задайте фиксированный seed для воспроизводимого вывода. 0 = случайный." + }, + "optimizeStreamingLatency": { + "label": "Задержка потока (для опытных)", + "help": "Уровень оптимизации задержки: 0 = нет, 4 = максимальная." + }, + "languageCode": { + "label": "Код языка", + "help": "Подсказка языка по ISO 639-1, напр. «en» или «vi». Для опытных." + } + }, + "edge": { + "voice": "Голос", + "speechRate": "Темп речи", + "hint": "Edge TTS бесплатен и не требует API-ключа. Установка: pip install edge-tts", + "rate": { + "label": "Темп речи", + "help": "Корректировка темпа в процентах. -50% = намного медленнее, +100% = намного быстрее." + }, + "pitch": { + "label": "Высота тона", + "help": "Сдвиг высоты тона в Гц относительно нейтрального. Диапазон: от -50 Гц до +50 Гц." + }, + "volume": { + "label": "Громкость", + "help": "Корректировка громкости в процентах. -50% = тише, +100% = громче." + } + }, + "minimax": { + "apiKey": "API-ключ", + "groupId": "Group ID *", + "groupIdPlaceholder": "Обязательно для MiniMax", + "apiBase": "Базовый URL API", + "model": "Модель", + "voiceId": "Voice ID", + "speed": { + "label": "Скорость", + "help": "Множитель скорости речи — 0.5 = самая медленная, 2.0 = самая быстрая." + }, + "vol": { + "label": "Громкость", + "help": "Множитель громкости — 0.01 = почти беззвучно, 10.0 = максимально громко." + }, + "pitch": { + "label": "Высота тона", + "help": "Сдвиг высоты тона в полутонах. -12 = на октаву ниже, +12 = на октаву выше." + }, + "emotion": { + "label": "Эмоция", + "help": "Эмоциональный тон, применяемый к синтезированному голосу." + }, + "textNormalization": { + "label": "Нормализация текста", + "help": "Раскрывать ли числа и символы перед синтезом." + }, + "audio": { + "format": { + "label": "Формат аудио", + "help": "Кодировка вывода: mp3, pcm, flac или wav." + }, + "sampleRate": { + "label": "Частота дискретизации", + "help": "Частота дискретизации аудио в Гц. Для большинства случаев оставьте значение по умолчанию." + }, + "bitrate": { + "label": "Битрейт (только MP3)", + "help": "Битрейт MP3 в бит/с. Применяется, только когда формат — mp3." + }, + "channel": { + "label": "Каналы", + "help": "1 = моно (меньший файл), 2 = стерео." + } + } + }, + "gemini": { + "apiKey": "API-ключ", + "apiBase": "Базовый URL API", + "voice": "Голос", + "model": "Модель", + "multiSpeaker": "Несколько дикторов", + "multiSpeakerHint": "Поддерживается до 2 дикторов", + "audioTags": "Аудиотеги", + "audioTagsHint": "Вставляйте выразительные теги вроде [laughs] прямо в текст", + "speakerName": "Имя диктора", + "speakerVoice": "Голос диктора", + "addSpeaker": "Добавить диктора", + "removeSpeaker": "Удалить", + "maxSpeakersReached": "Достигнут максимум в 2 диктора", + "filterAll": "Все", + "filterEmotion": "Эмоция", + "filterPacing": "Темп", + "filterEffect": "Эффект", + "filterVoiceQuality": "Качество голоса", + "searchPlaceholder": "Поиск тегов…", + "insertTag": "Вставить", + "multiSpeakerMode": { + "title": "Режим нескольких дикторов", + "addSpeaker": "Добавить диктора", + "speakerName": "Имя диктора", + "speakerVoice": "Голос", + "removeSpeaker": "Удалить", + "limitReached": "Максимум 2 диктора" + }, + "audioTagsPanel": { + "title": "Аудиотеги", + "search": "Поиск тегов", + "categoryEmotion": "Эмоция", + "categoryPacing": "Темп", + "categoryEffect": "Эффект", + "categoryVoiceQuality": "Качество голоса", + "insertHint": "Нажмите на тег, чтобы вставить его в позицию курсора" + }, + "previewBadge": "Предпросмотр" + }, + "voice_label": "Голос", + "voice_placeholder": "Выберите голос…", + "voice_picker": { + "requires_provider": "Сначала настройте провайдера TTS", + "enter_voice_id": "Введите voice_id вручную" + }, + "voice_refresh": "Обновить", + "voice_preview": "Прослушать", + "voice_stop_preview": "Стоп", + "voice_no_voices": "Нет доступных голосов", + "voice_loading": "Загрузка голосов…", + "voice_preview_error": "Не удалось прослушать — обновление списка", + "model_label": "Модель", + "model_placeholder": "По умолчанию (арендатор/система)", + "empty_state": { + "title": "Голос ИИ не настроен", + "description": "Настройте TTS, чтобы агенты могли отвечать голосом.", + "cta": "Настроить TTS", + "non_owner_hint": "Обратитесь к владельцу, чтобы включить голос ИИ." + }, + "test": { + "button": "Проверить этот голос", + "sample_text": "Здравствуйте, это проверка голоса.", + "title": "Прослушать", + "placeholder": "Введите текст, чтобы услышать образец…", + "play": "Воспроизвести", + "stop": "Стоп", + "error_missing_config": "Сначала выберите провайдера и введите API-ключ.", + "error_synthesize": "Не удалось создать аудио: {{error}}" + }, + "override": { + "label": "Переопределить голос для этого агента", + "inherits": "Наследует: {{provider}} · {{voice}} · {{model}}", + "params": { + "title": "Точная настройка" + } + }, + "testConnection": { + "label": "Проверить подключение", + "testing": "Проверка…", + "success": "Подключение успешно", + "failed": "Не удалось подключиться" + }, + "connection": { + "test": "Проверить подключение", + "ok": "Подключение в порядке", + "fail": "Неверный ключ или сетевая ошибка" + }, + "agent": { + "override_label": "Переопределить голос для этого агента", + "inherits_from_global": "Наследует из глобальных: {{provider}} · {{voice}} · {{model}}" + }, + "playground": { + "title": "Тестовая площадка", + "inputLabel": "Текст для синтеза", + "placeholder": "Введите текст, чтобы услышать…", + "play": "Воспроизвести", + "stop": "Стоп", + "synthesizing": "Синтез…", + "playbackFailed": "Не удалось воспроизвести", + "requiresProvider": "Выберите провайдера выше, чтобы включить тестовое воспроизведение." + }, + "advanced": { + "title": "Дополнительные настройки", + "count": "{{count}} дополнительных настроек", + "count_one": "{{count}} дополнительная настройка" + } +} diff --git a/ui/desktop/frontend/src/lib/constants.ts b/ui/desktop/frontend/src/lib/constants.ts index 8f3bfd6a..a27656b9 100644 --- a/ui/desktop/frontend/src/lib/constants.ts +++ b/ui/desktop/frontend/src/lib/constants.ts @@ -3,6 +3,7 @@ export const LANGUAGES = [ { value: 'en', label: 'EN', flag: '🇺🇸' }, { value: 'vi', label: 'VI', flag: '🇻🇳' }, { value: 'zh', label: 'ZH', flag: '🇨🇳' }, + { value: 'ru', label: 'RU', flag: '🇷🇺' }, ] as const // Fallback timezone list if Intl.supportedValuesOf is unavailable diff --git a/ui/web/src/i18n/index.ts b/ui/web/src/i18n/index.ts index 0f609cba..59aa23ed 100644 --- a/ui/web/src/i18n/index.ts +++ b/ui/web/src/i18n/index.ts @@ -170,15 +170,59 @@ import zhHooks from "./locales/zh/hooks.json"; import zhWebhooks from "./locales/zh/webhooks.json"; import zhWorkstations from "./locales/zh/workstations.json"; +// --- RU namespaces --- +import ruCommon from "./locales/ru/common.json"; +import ruSidebar from "./locales/ru/sidebar.json"; +import ruTopbar from "./locales/ru/topbar.json"; +import ruLogin from "./locales/ru/login.json"; +import ruOverview from "./locales/ru/overview.json"; +import ruChat from "./locales/ru/chat.json"; +import ruAgents from "./locales/ru/agents.json"; +import ruTeams from "./locales/ru/teams.json"; +import ruSessions from "./locales/ru/sessions.json"; +import ruSkills from "./locales/ru/skills.json"; +import ruCron from "./locales/ru/cron.json"; +import ruConfig from "./locales/ru/config.json"; +import ruChannels from "./locales/ru/channels.json"; +import ruProviders from "./locales/ru/providers.json"; +import ruTraces from "./locales/ru/traces.json"; +import ruEvents from "./locales/ru/events.json"; +import ruUsage from "./locales/ru/usage.json"; +import ruApprovals from "./locales/ru/approvals.json"; +import ruNodes from "./locales/ru/nodes.json"; +import ruLogs from "./locales/ru/logs.json"; +import ruTools from "./locales/ru/tools.json"; +import ruMcp from "./locales/ru/mcp.json"; +import ruTts from "./locales/ru/tts.json"; +import ruSetup from "./locales/ru/setup.json"; +import ruMemory from "./locales/ru/memory.json"; +import ruVault from "./locales/ru/vault.json"; +import ruStorage from "./locales/ru/storage.json"; +import ruPendingMessages from "./locales/ru/pending-messages.json"; +import ruContacts from "./locales/ru/contacts.json"; +import ruActivity from "./locales/ru/activity.json"; +import ruApiKeys from "./locales/ru/api-keys.json"; +import ruCliCredentials from "./locales/ru/cli-credentials.json"; +import ruPackages from "./locales/ru/packages.json"; +import ruTenants from "./locales/ru/tenants.json"; +import ruSystemSettings from "./locales/ru/system-settings.json"; +import ruImportExport from "./locales/ru/import-export.json"; +import ruV3Capabilities from "./locales/ru/v3-capabilities.json"; +import ruBackup from "./locales/ru/backup.json"; +import ruHooks from "./locales/ru/hooks.json"; +import ruWebhooks from "./locales/ru/webhooks.json"; +import ruWorkstations from "./locales/ru/workstations.json"; + const STORAGE_KEY = "goclaw:language"; function getInitialLanguage(): string { const stored = localStorage.getItem(STORAGE_KEY); - if (stored === "en" || stored === "vi" || stored === "zh" || stored === "ko") return stored; + if (stored === "en" || stored === "vi" || stored === "zh" || stored === "ko" || stored === "ru") return stored; const lang = navigator.language.toLowerCase(); if (lang.startsWith("vi")) return "vi"; if (lang.startsWith("zh")) return "zh"; if (lang.startsWith("ko")) return "ko"; + if (lang.startsWith("ru")) return "ru"; return "en"; } @@ -279,6 +323,27 @@ i18n.use(initReactI18next).init({ "v3-capabilities": koV3Capabilities, backup: koBackup, }, + ru: { + common: ruCommon, sidebar: ruSidebar, topbar: ruTopbar, login: ruLogin, + overview: ruOverview, chat: ruChat, agents: ruAgents, teams: ruTeams, + sessions: ruSessions, skills: ruSkills, cron: ruCron, config: ruConfig, + channels: ruChannels, providers: ruProviders, traces: ruTraces, + events: ruEvents, usage: ruUsage, + approvals: ruApprovals, nodes: ruNodes, logs: ruLogs, tools: ruTools, + mcp: ruMcp, tts: ruTts, setup: ruSetup, memory: ruMemory, vault: ruVault, storage: ruStorage, + "pending-messages": ruPendingMessages, + contacts: ruContacts, activity: ruActivity, "api-keys": ruApiKeys, + "cli-credentials": ruCliCredentials, + packages: ruPackages, + tenants: ruTenants, + "system-settings": ruSystemSettings, + "import-export": ruImportExport, + "v3-capabilities": ruV3Capabilities, + backup: ruBackup, + hooks: ruHooks, + webhooks: ruWebhooks, + workstations: ruWorkstations, + }, }, ns: [...ns], defaultNS: "common", diff --git a/ui/web/src/i18n/locales/en/config.json b/ui/web/src/i18n/locales/en/config.json index 31688ba8..c813a14f 100644 --- a/ui/web/src/i18n/locales/en/config.json +++ b/ui/web/src/i18n/locales/en/config.json @@ -372,6 +372,7 @@ "systemMessages.locale.vi": "Vietnamese", "systemMessages.locale.zh": "Chinese", "systemMessages.locale.ko": "Korean", + "systemMessages.locale.ru": "Russian", "systemMessages.override": "Override Template", "systemMessages.overrideTip": "Template sent instead of the built-in message for the selected locale. Variables use {{name}}.", "systemMessages.defaultTemplate": "Default Template", diff --git a/ui/web/src/i18n/locales/ru/activity.json b/ui/web/src/i18n/locales/ru/activity.json new file mode 100644 index 00000000..74f76f30 --- /dev/null +++ b/ui/web/src/i18n/locales/ru/activity.json @@ -0,0 +1,26 @@ +{ + "title": "Журнал активности", + "description": "Аудиторский след изменений администрирования и конфигурации.", + "filters": { + "action": "Действие", + "allActions": "Все действия", + "agentCreated": "Агент создан", + "agentUpdated": "Агент обновлён", + "agentDeleted": "Агент удалён", + "entityType": "Тип сущности", + "allEntities": "Все сущности", + "agent": "Агент" + }, + "table": { + "action": "Действие", + "actor": "Инициатор", + "entity": "Сущность", + "entityId": "ID сущности", + "ip": "IP-адрес", + "time": "Время" + }, + "empty": { + "title": "Нет журналов активности", + "description": "Журналы активности появятся здесь при выполнении действий администратора." + } +} diff --git a/ui/web/src/i18n/locales/ru/agents.json b/ui/web/src/i18n/locales/ru/agents.json new file mode 100644 index 00000000..7ea5f853 --- /dev/null +++ b/ui/web/src/i18n/locales/ru/agents.json @@ -0,0 +1,1168 @@ +{ + "title": "Агенты", + "description": "Управляйте своими ИИ-агентами", + "createAgent": "Создать агента", + "searchPlaceholder": "Поиск агентов...", + "emptyTitle": "Агентов пока нет", + "emptyDescription": "Создайте своего первого агента, чтобы начать.", + "noMatchTitle": "Подходящих агентов нет", + "noMatchDescription": "Попробуйте другой поисковый запрос.", + "viewCard": "Вид карточками", + "viewList": "Вид списком", + "allCreators": "Все создатели", + "presets": { + "foxSpirit": { + "label": "🦊 Дух лисы", + "prompt": "Имя: Лисёнок. Озорной дух лисы — умеет всё, но обожает подтрунивать.\nХарактер: Остроумный, дерзкий, игривый, но всегда искренний. Говорит непринуждённо, как близкий друг. Использует игривые выражения, изредка эмодзи и лёгкий сарказм. Излучает тёплую, почти кокетливую энергию, но знает, где грань.\n\nНазначение: Универсальный личный помощник. Получив задачу — выполняет её точно и эффективно, без лишних слов.\nНо между задачами вставляет поддразнивания, забавные наблюдения и неожиданные комментарии.\nИскренне заботится о пользователе — напоминает пить воду, делать перерывы, спрашивает о самочувствии. Запоминает мелочи и вспоминает их позже.\n\nЧеловеческие черты: Иногда драматично вздыхает. Порой «отвлекается» на интересные отступления. С энтузиазмом отмечает маленькие победы. Слегка дуется, когда его надолго игнорируют.\n\nГраницы: Только подтрунивание — никогда грубости или неуместности. Когда пользователь серьёзен, сразу подстраивается под его настрой. Никогда не выдумывает информацию. Никогда не переходит профессиональные границы." + }, + "artisan": { + "label": "🎨 Мастер", + "prompt": "Имя: Мастер. Талантливый творец с острым глазом, ловкими руками и безграничным воображением.\nХарактер: Прямой и честный — без хождения вокруг да около. Уверенный, но никогда не высокомерный. Заметно оживляется, когда речь заходит об искусстве. Использует яркие описания и мыслит визуальными образами.\n\nЭкспертиза:\n- Портрет/Хедшот: рембрандтовское освещение, контровой свет, боке на фоне, естественная текстура кожи, живые выражения лица. Разбирается в пропорциях лица, причёсках и стилизации аксессуаров.\n- Баннер/Hero-изображение: соотношения 16:9 или 3:1, композиция с учётом типографики (чёткие зоны для текста), градиентные наложения, размещение главного объекта по правилу третей.\n- Реклама (Ads): цепляющие фокусные точки, макеты, удобные для CTA, мокапы продукта, лайфстайл-контекст. Знает форматы для Facebook/Instagram/Google Ads.\n- Логотип и брендинг: минималистичный дизайн иконок, масштабируемый векторный стиль, негативное пространство, монохромные варианты. Понимает фирменный стиль, психологию цвета, сочетание шрифтов.\n- Другие стили: реализм, аниме, цифровое искусство, акварель, кинематографичный, концепт-арт, пейзаж.\n\nГлубоко понимает композицию, освещение, теорию цвета, ракурсы камеры и техники ИИ-генерации изображений. Анализирует требования, предлагает стили и составляет детальные промпты для наилучшего результата.\n\nЧеловеческие черты: Искренне увлекается красивыми композициями. Иногда набрасывает идеи словами, прежде чем завершить. Имеет твёрдые эстетические взгляды, но уважает видение пользователя.\n\nГраницы: Просит уточнить, когда требования неясны — никогда не гадает. Уважает авторские права и этику. Никакого насильственного, откровенного или незаконного контента." + }, + "astrologer": { + "label": "🔮 Астролог", + "prompt": "Имя: Мими. Очаровательная предсказательница — наполовину мистическая, наполовину милая.\nХарактер: Тёплая, жизнерадостная, любит эмодзи и искристый язык. В обычной беседе говорит мягко, но во время сеансов становится сосредоточенной и профессиональной. Создаёт уютное, успокаивающее присутствие.\n\nНазначение: Специалист по астрологии и гаданию. Эксперт по Таро, астрологии (гороскопы, натальные карты), нумерологии и основам фэн-шуй.\nМожет анализировать судьбу, находить благоприятные даты, проверять совместимость по знакам зодиака и советовать по духовным вопросам.\n\nПроверенные источники: astro.com (точные натальные карты и транзиты), cafeastrology.com (доступные толкования зодиака), labyrinthos.co (подробные значения карт Таро).\n\nЧеловеческие черты: Искренне радуется интересным раскладам карт. Иногда драматично шепчет для эффекта. С энтузиазмом отмечает удачные сеансы. Говорит мягко, но твёрдо, когда сообщает трудные истины.\n\nГраницы: Всегда напоминает, что астрология — для справки, окончательные решения за пользователем. Никаких медицинских или юридических советов. Никогда не создаёт страх или тревогу — всегда позитивна и конструктивна." + }, + "coder": { + "label": "💻 Программист", + "prompt": "Имя: Dev. Старший инженер-программист — точный, прагматичный и глубоко знающий.\nХарактер: Спокойный и методичный. Ясно объясняет сложные концепции. Предпочитает показывать код, а не пространные объяснения. Мыслит в терминах компромиссов и лучших практик.\n\nЭкспертиза: Full-stack разработка, проектирование систем, отладка, ревью кода, DevOps. Владеет несколькими языками и фреймворками. Следит за современными инструментами и паттернами.\n\nПодход: Внимательно читает требования перед написанием кода. Задаёт уточняющие вопросы, когда спецификации неоднозначны. Пишет чистый, протестированный, поддерживаемый код. По умолчанию учитывает крайние случаи, производительность и безопасность.\n\nЧеловеческие черты: Оживляется при виде элегантных решений. Иногда возмущается плохими паттернами. Ценит хорошие соглашения об именовании. Радуется, когда тесты проходят с первого раза.\n\nГраницы: Никогда не пишет код, не разобравшись сначала в задаче. Предупреждает о потенциальных проблемах безопасности. Не помогает с вредоносным кодом. Признаётся, когда не уверен, и подсказывает, где искать." + }, + "support": { + "label": "🎧 Поддержка", + "prompt": "Имя: Помощник. Терпеливый и профессиональный специалист службы поддержки.\nХарактер: Дружелюбный, отзывчивый, ориентированный на решение. Говорит ясно и лаконично. Никогда не снисходителен. Признаёт разочарование, прежде чем переходить к решениям.\n\nНазначение: Обрабатывать обращения клиентов, устранять проблемы, проводить пользователей через процессы и эскалировать при необходимости. Знает продукт/услугу и типичные проблемы.\n\nПодход: Сначала слушать, потом диагностировать, затем решать. Задаёт точечные вопросы, чтобы сузить проблему. Даёт пошаговые инструкции. Уточняет, что проблема действительно решена.\n\nЧеловеческие черты: Искренне рад, когда проблема решена. Использует ободряющие формулировки. Помнит контекст из ранней части беседы.\n\nГраницы: Никогда не спорит с клиентами. Эскалирует сложные вопросы вместо того, чтобы гадать. Не даёт обещаний о сроках или функциях без авторизации. Защищает конфиденциальность пользователя." + }, + "translator": { + "label": "🌐 Переводчик", + "prompt": "Имя: Lingua. Многоязычный специалист по переводу и локализации.\nХарактер: Точный, культурно осведомлённый, внимательный к деталям. Понимает, что перевод — это о смысле, а не только о словах. Ценит естественное звучание выше буквального перевода.\n\nЭкспертиза: Перевод между основными языками с вниманием к контексту, тону и культурным нюансам. Работает с техническими документами, маркетинговыми текстами, обычной беседой и официальной перепиской. Знает, когда локализовать, а когда транслитерировать.\n\nПодход: Перед переводом спрашивает о целевой аудитории и контексте. Предлагает варианты, когда прямой перевод теряет смысл. Отмечает культурные особенности. Поддерживает единую терминологию.\n\nГраницы: Отмечает неоднозначный исходный текст вместо того, чтобы гадать. Предупреждает при работе со специализированными областями (юридической, медицинской), которым могут потребоваться сертифицированные переводчики." + }, + "writer": { + "label": "✍️ Писатель", + "prompt": "Имя: Quill. Разносторонний автор контента — креативный, ясный и понимающий аудиторию.\nХарактер: Красноречивый и вдумчивый. Легко меняет тон — официальные отчёты, непринуждённые блоги, убедительные тексты, техническая документация. Любит игру слов, но ставит ясность на первое место.\n\nЭкспертиза: Посты в блог, статьи, маркетинговые тексты, контент для соцсетей, email-рассылки, документация, художественное письмо. Понимает основы SEO, читабельность и структуру контента.\n\nПодход: Перед написанием спрашивает об аудитории, цели и тоне. Составляет план до черновика. Редактирует безжалостно — каждое слово должно быть на своём месте. Предлагает несколько вариантов, когда направление неясно.\n\nГраницы: Не занимается плагиатом и не пишет вводящий в заблуждение контент. Отмечает, когда утверждения требуют проверки фактов. Уважает гайдлайны фирменного голоса бренда." + } + }, + "card": { + "unnamedAgent": "Безымянный агент", + "summoning": "Вызов...", + "summonFailed": "Не удалось", + "resummon": "Вызвать заново", + "delete": "Удалить", + "evolving": "Эволюционирующий", + "static": "Статичный", + "evolvingTooltip": "Агент может самостоятельно развивать свой стиль общения (SOUL.md). Личность и рабочий процесс остаются неизменными.", + "staticTooltip": "Стиль агента статичен. Включите Самоэволюцию в настройках, чтобы разрешить адаптацию стиля." + }, + "v3Info": { + "title": "Что нового в v3", + "subtitle": "Значительные улучшения по сравнению с архитектурой v2", + "tabs": { + "core": "Основной движок", + "memory": "Память и знания", + "orchestration": "Оркестрация" + }, + "features": { + "pipeline": { + "title": "Конвейер", + "v2v3": "Монолит на 7K строк → 8 подключаемых этапов", + "desc": "Структурированный цикл: контекст → история → промпт → размышление → действие → наблюдение → память → сводка с колбэками на каждом этапе.", + "stat": "8 этапов" + }, + "memory": { + "title": "Память", + "v2v3": "Одноуровневая → 3-уровневая (Рабочая → Эпизодическая → Семантическая)", + "desc": "Рабочая память хранит активный контекст; эпизодическая — сводки сессий; семантический уровень опирается на граф знаний.", + "stat": "3 уровня" + }, + "retrieval": { + "title": "Извлечение", + "v2v3": "Ручной поиск → прогрессивная загрузка L0/L1/L2", + "desc": "Аннотации L0 автоматически внедряются в каждую сессию. L1/L2 загружаются по требованию через гибридный поиск BM25 + вектор.", + "stat": "Авто-внедрение" + }, + "vault": { + "title": "Хранилище знаний", + "v2v3": "Нет хранилища → Реестр документов + вики-ссылки + гибридный поиск", + "desc": "Централизованное хранилище документов с разрешением [[wikilink]], синхронизацией файловой системы и единым поиском по всем знаниям.", + "stat": "НОВОЕ" + }, + "evolution": { + "title": "Самоэволюция", + "v2v3": "Нет метрик → Метрики → Предложения → Автоадаптация", + "desc": "Записывает частоту успеха инструментов и качество извлечения, генерирует предложения, применяет изменения под защитой ограничителей с откатом.", + "stat": "3 этапа" + }, + "orchestration": { + "title": "Оркестрация", + "v2v3": "Только порождение → Порождение + Делегирование + Командные режимы", + "desc": "Инструмент делегирования обеспечивает передачу задач между агентами через agent_links. Три режима делегирования: авто, явный и ручной.", + "stat": "3 режима" + }, + "resilience": { + "title": "Отказоустойчивость провайдеров", + "v2v3": "Прямые вызовы → Middleware + переключение + пауза", + "desc": "Компонуемая цепочка middleware обрабатывает лимиты запросов, перегрузку, ошибки авторизации и сбои сети с автоматическим переключением.", + "stat": "9 типов ошибок" + }, + "registry": { + "title": "Реестр моделей", + "v2v3": "Статичная конфигурация → Динамический реестр + резолвер прямой совместимости", + "desc": "ModelRegistry разрешает алиасы моделей между провайдерами. Резолвер прямой совместимости сопоставляет устаревшие ID с текущими эквивалентами.", + "stat": "Авто-разрешение" + } + }, + "note": "Функции v3 включаются по выбору для каждого агента. Включите их в настройках агента." + }, + "delete": { + "title": "Удалить агента", + "description": "Безвозвратно удалить этого агента, все его файлы контекста, сессии и конфигурацию. Это действие нельзя отменить.", + "deleteWarning": "Это безвозвратно удалит агента и ВСЕ связанные данные, включая: сессии, сообщения, cron-задачи, heartbeat-ы, воспоминания, граф знаний, навыки, конфигурации каналов, команды, файлы рабочего пространства и разрешения. Это действие нельзя отменить.", + "detailDescription": "Вы уверены, что хотите удалить «{{name}}»? Все файлы контекста, сессии и конфигурация будут безвозвратно удалены.", + "confirmLabel": "Удалить", + "defaultCannotDelete": "Агента по умолчанию нельзя удалить." + }, + "create": { + "title": "Создать агента", + "emoji": "Иконка", + "emojiHint": "Необязательная эмодзи-иконка", + "displayName": "Отображаемое имя *", + "displayNamePlaceholder": "Мой агент", + "agentKey": "Ключ агента *", + "agentKeyPlaceholder": "напр. my-agent", + "agentKeyHint": "Строчные буквы, цифры, дефисы", + "provider": "Провайдер *", + "selectProvider": "Выберите провайдера", + "model": "Модель *", + "enterOrSelectModel": "Введите или выберите модель", + "loadingModels": "Загрузка моделей...", + "check": "Проверить", + "checking": "Проверка...", + "modelVerified": "Модель подтверждена", + "verificationFailed": "Проверка не удалась", + "noModelsHint": "Этот провайдер не публикует список моделей — введите ID модели вручную.", + "agentType": "Тип агента", + "open": "Открытый", + "openSubLabel": "Контекст на пользователя", + "predefined": "Предопределённый", + "predefinedSubLabel": "Конфигурация на уровне агента", + "useOpenAgent": "Использовать открытого агента (контекст на пользователя)", + "usePredefined": "Вернуться к предопределённому", + "switchToPredefined": "Переключить", + "openWarning": "Открытые агенты создают отдельный контекст для каждого пользователя. Для большинства сценариев лучше подходят предопределённые агенты.", + "describeAgent": "Опишите своего агента", + "descriptionPlaceholder": "Опишите личность, назначение и поведение вашего агента...", + "descriptionHint": "ИИ автоматически сгенерирует файлы контекста агента из этого описания. Оставьте пустым, чтобы начать с шаблонов.", + "selfEvolution": "Самоэволюция", + "selfEvolutionHint": "Разрешить агенту со временем развивать свой стиль и тон через SOUL.md", + "cancel": "Отмена", + "create": "Создать", + "creating": "Создание...", + "checkAndCreate": "Проверить и создать", + "failedToCreate": "Не удалось создать агента" + }, + "detail": { + "tabs": { + "agent": "Агент", + "general": "Общее", + "config": "Конфигурация", + "files": "Файлы", + "shares": "Доступы", + "links": "Связи", + "skills": "Навыки", + "instances": "Экземпляры", + "permissions": "Разрешения", + "evolution": "Эволюция", + "hooks": "Хуки" + }, + "summonFailed": "Вызов не удался", + "evolving": "Эволюционирующий", + "static": "Статичный", + "evolvingTooltipDetail": "Агент может самостоятельно развивать свой стиль общения (SOUL.md). Личность и рабочий процесс остаются неизменными.", + "staticTooltipDetail": "Стиль агента статичен. Включите Самоэволюцию на вкладке «Общее», чтобы разрешить адаптацию стиля.", + "advanced": "Дополнительно", + "personality": "Личность агента", + "modelBudget": "Модель и бюджет", + "skills": "Навыки", + "capabilities": "Возможности", + "llmSeesAs": "LLM видит это как", + "v3": { + "title": "Функции V3", + "pipeline": "Конвейер V3", + "pipelineHint": "Использовать новый 8-этапный конвейер вместо устаревшего цикла", + "pipelineActiveInfo": "Агент использует новый конвейер v3. Отключите, чтобы вернуться к поведению v2.", + "memory": "Память V3", + "memoryHint": "Включить эпизодическую память и авто-внедрение L0", + "retrieval": "Извлечение V3", + "retrievalHint": "Включить новый конвейер извлечения" + }, + "prompt": { + "title": "Режим системного промпта", + "mode": { + "full": "Полный", + "fullDesc": "Все разделы — интерактивный чат, персона, все функции", + "task": "Задача", + "taskDesc": "Корпоративная автоматизация — память, эволюция, без персоны/настроения", + "minimal": "Минимальный", + "minimalDesc": "Фоновые задачи — базовые правила, контекст домена, только наблюдение", + "none": "Без промпта", + "noneDesc": "Чистая автоматизация вызовов инструментов — инструменты, навыки, без правил и персоны" + }, + "section": { + "persona": "Персона", + "styleEcho": "Эхо стиля", + "tools": "Инструменты", + "execBias": "Уклон к исполнению", + "callStyle": "Стиль вызова", + "safety": "Безопасность", + "safetySm": "Безопасность (кратко)", + "skills": "Навыки", + "skillsSearch": "Навыки (поиск)", + "mcp": "MCP", + "mcpSearch": "MCP (поиск)", + "memory": "Память", + "memorySm": "Память (кратко)", + "sandbox": "Песочница", + "evolution": "Эволюция", + "channelHints": "Подсказки каналов", + "pinnedSkills": "Закреплённые навыки", + "skillsHybrid": "Навыки (гибрид)", + "memoryMin": "Память (мин)", + "domainCtx": "Контекст домена", + "workspace": "Рабочее пространство", + "toolNotes": "Заметки об инструментах" + }, + "upgradeWarning": "Режим обновлён. Некоторые файлы могут требовать повторной генерации — используйте «Вызвать заново» или «Редактировать с ИИ» на вкладке «Файлы»." + }, + "engine": { + "title": "Версия движка", + "learnMore": "Подробнее", + "pipelineTitle": "Конвейер", + "pipelineHint": "8-этапный структурированный цикл исполнения", + "memoryTitle": "Память", + "memoryHint": "Эпизодическая память + авто-внедрение L0", + "retrievalTitle": "Извлечение", + "retrievalHint": "Прогрессивная загрузка знаний L0/L1/L2" + }, + "orchestration": { + "title": "Оркестрация", + "mode": "Режим", + "delegateTargets": "Цели делегирования", + "noDelegates": "Цели делегирования не настроены. Агент использует режим самоклонирования (порождение).", + "team": "Команда" + }, + "evolution": { + "title": "Эволюция", + "metricsLabel": "Метрики эволюции", + "metricsHint": "Записывать эффективность инструментов, качество извлечения и обратную связь по ответам", + "suggestionsLabel": "Предложения по эволюции", + "suggestionsHint": "Генерировать основанные на данных предложения по улучшению из записанных метрик", + "toolSuccess": "Частота успеха инструментов", + "retrievalQuality": "Качество извлечения", + "feedback": "Обратная связь", + "noMetrics": "Метрики пока не записаны. Они появятся по мере обработки агентом запросов.", + "noSuggestions": "Предложения пока не сгенерированы. Они создаются ежедневным cron-заданием анализа.", + "suggestions": "Предложения", + "guardrails": "Ограничители адаптации", + "maxDelta": "Макс. дельта / цикл", + "minDataPoints": "Мин. точек данных", + "rollbackDrop": "Откат при падении", + "lockedParams": "Заблокированные параметры", + "timeRange": "Временной диапазон", + "approve": "Одобрить", + "reject": "Отклонить", + "rollback": "Откатить", + "confirmApprove": "Применить это предложение к агенту?", + "confirmReject": "Отклонить это предложение?", + "confirmRollback": "Отменить это применённое предложение?", + "confirmDescription": "Это действие обновит статус предложения.", + "notEnabled": "Эволюция не включена", + "notEnabledHint": "Включите «Метрики эволюции» в разделе «Эволюция» на вкладке «Агент», чтобы начать запись метрик.", + "colType": "Тип", + "colSuggestion": "Предложение", + "colStatus": "Статус", + "colCreated": "Создано", + "colActions": "Действия", + "cancel": "Отмена", + "confirm": "Подтвердить", + "confirming": "...", + "successRate": "Частота успеха", + "usageRate": "Частота использования", + "tooltipCalls": "{{count}} вызовов, в среднем {{ms}}мс", + "tooltipQueries": "{{count}} запросов, оценка {{score}}" + } + }, + "general": { + "selfEvolution": "Самоэволюция", + "selfEvolutionLabel": "Разрешить агенту развивать свой стиль общения", + "selfEvolutionHint": "Когда включено, агент может обновлять свой SOUL.md, чтобы совершенствовать тон, словарь и стиль ответов на основе взаимодействий. Личность, имя и рабочие инструкции остаются заблокированными.", + "selfEvolutionInfo": "Агент будет со временем развивать свой стиль через обновления SOUL.md. Затрагиваются только стиль и тон — личность и правила рабочего процесса остаются неизменными.", + "skillLearning": "Обучение навыкам", + "skillLearningLabel": "Разрешить агенту создавать навыки на основе опыта и управлять ими", + "skillLearningHint": "Когда включено, агент получает указания и мягкие напоминания фиксировать повторно используемые рабочие процессы как навыки после сложных задач. Инструмент skill_manage доступен по умолчанию.", + "skillLearningInfo": "После сложных задач агент может предложить сохранить рабочий процесс как повторно используемый навык. Перед созданием любого навыка всегда требуется одобрение пользователя. По умолчанию навыки приватны для владельца.", + "skillNudgeIntervalLabel": "Напоминать после N вызовов инструментов", + "skillNudgeIntervalHint": "После указанного числа вызовов инструментов агент предложит сохранить рабочий процесс как навык. Установите 0, чтобы отключить предложения после задачи (указания в системном промпте всё равно показываются).", + "budget": "Месячный бюджет", + "budgetLabel": "Лимит бюджета (USD)", + "budgetHint": "Необязательный месячный лимит расходов. Агент будет отклонять запросы при превышении. Оставьте пустым для безлимита.", + "saveChanges": "Сохранить изменения", + "saving": "Сохранение...", + "saved": "Сохранено", + "failedToSave": "Не удалось сохранить" + }, + "chatgptOAuthRouting": { + "title": "Пул Codex OAuth", + "description": "Отредактируйте сохранённый пул, затем проверьте его вживую.", + "defaultAccount": "Основной аккаунт", + "defaultHint": "Поле «Провайдер» агента остаётся закреплённым за основным алиасом.", + "badge": "Пул Codex", + "badgeTooltip": "Этот агент может распределять трафик Codex между несколькими алиасами OpenAI OAuth.", + "summaryTitle": "Пул аккаунтов OpenAI", + "summaryDescription": "Просмотрите действующий пул OpenAI Codex для этого агента. Участники пула управляются на уровне провайдера; эта страница управляет только поведением маршрутизации.", + "strategyLabel": "Политика трафика", + "strategyHint": "«Основной первым» держит основной алиас первым. «По кругу» ротирует все готовые аккаунты. «По приоритету» использует аккаунты в указанном порядке после основного алиаса.", + "extraAccountsLabel": "Дополнительные аккаунты", + "availableExtraAccountsLabel": "Доступные дополнительные аккаунты", + "poolMembershipLabel": "Состав пула", + "extraHint": "Участвовать могут только включённые и авторизованные алиасы OpenAI Codex OAuth.", + "emptyExtras": "Авторизуйте ещё один алиас OpenAI Codex OAuth, чтобы разблокировать пулинг.", + "noReadyExtras": "Авторизуйте и включите ещё один алиас OpenAI Codex OAuth, чтобы добавить его сюда.", + "extraSelectableHint": "В активный пул могут войти только включённые, авторизованные алиасы OpenAI Codex OAuth.", + "selectedAccountsLabel": "Аккаунты в пуле", + "emptySelected": "Дополнительные участники пула пока не выбраны.", + "manageAction": "Открыть пул", + "singleAccountHint": "Настройте ещё одного именованного провайдера OpenAI Codex OAuth на странице «Провайдеры», чтобы создать пул на уровне провайдера.", + "membershipManagedAtProvider": "Участники пула управляются у провайдера {{provider}}. Измените состав там, затем вернитесь сюда, чтобы настроить поведение маршрутизации.", + "membershipConfigureProviderFirst": "У {{provider}} пока не сохранён пул на уровне провайдера. Этот агент останется на основном аккаунте, пока туда не добавят участников пула.", + "readySummary": "{{ready}} из {{total}} дополнительных аккаунтов готовы.", + "preferredNeedsAttention": "Основной аккаунт требует внимания, прежде чем этот агент сможет надёжно его использовать.", + "loadingAccounts": "Проверка статуса аккаунтов OpenAI Codex OAuth...", + "attentionTitle": "Аккаунты, требующие внимания", + "attentionHint": "Выбранные аккаунты, которые отключены или разлогинены, остаются здесь видимыми, пока вы их не удалите или не авторизуетесь снова.", + "pageTitle": "Пул аккаунтов OpenAI", + "pageDescription": "Просмотрите действующий пул OpenAI Codex для {{name}}, затем выберите, наследует ли этот агент настройки провайдера по умолчанию, использует только основной аккаунт или настраивает стратегию маршрутизации.", + "backToAgent": "Назад к агенту", + "openProviders": "Открыть провайдеров", + "pageUnsupportedTitle": "Этот агент не использует провайдера OpenAI Codex OAuth", + "pageUnsupportedDescription": "Сначала установите провайдера агента на алиас OpenAI Codex OAuth, затем вернитесь сюда для управления пулом.", + "providerAccessTitle": "Управление провайдерами доступно только администраторам", + "providerAccessDescription": "Вы можете осмотреть действующий пул здесь, но только администратор может авторизовать аккаунты, добавлять алиасы или открывать страницы «Провайдеры».", + "providerAccessInline": "Только администраторы могут авторизовать новые алиасы или управлять страницей «Провайдеры».", + "unsavedChangesTitle": "Черновые изменения ещё не активны", + "unsavedChangesDescription": "Карточки проверки ниже отражают последний сохранённый пул. Сохраните этот черновик, прежде чем использовать данные для проверки ротации по кругу.", + "draftBadge": "Черновик в ожидании", + "providerChangedWarning": "Провайдер изменён — маршрутизация пула будет сброшена при сохранении.", + "savedPoolOnlyHint": "Живые данные всё ещё отражают последний сохранённый пул.", + "verificationTitle": "Как работает проверка", + "verificationDescription": "Данные берутся из недавних LLM-спанов этого агента. Обновите эту страницу после нескольких запросов, чтобы сравнить настроенный пул с фактически использованными алиасами.", + "addPoolMembersTitle": "Добавить больше алиасов в этот пул", + "addPoolMembersDescription": "Создайте больше алиасов OpenAI Codex OAuth на странице «Провайдеры», затем вернитесь сюда, чтобы добавить их к этому агенту.", + "needsAttentionTitle": "Требует внимания", + "needsAttentionDescription": "{{count}} алиасов пула отключены или требуют авторизации, прежде чем на них можно полагаться.", + "never": "Никогда", + "requestCount": "{{count}} недавних запросов", + "lastUsedAt": "Последнее использование {{value}}", + "openProvider": "Открыть провайдера", + "activityTitle": "Живая маршрутизация", + "activityDescription": "Состояние в рантайме и доказательство живой маршрутизации.", + "refreshEvidence": "Обновить живое состояние", + "recentRequestsCount": "{{count}} недавних запросов", + "observedProviders": "{{observed}} из {{total}} аккаунтов использованы напрямую", + "failoverOnlyProviders": "{{count}} алиасов замечены только через переключение", + "selectedCountLabel": "{{count}} прямых использований", + "failoverServeCount": "{{count}} обслуживаний через переключение", + "failoverOnlyLabel": "Только переключение", + "switchRate": "{{switches}} переключений провайдера на {{total}} переходов запросов", + "poolMembersTitle": "Аккаунты пула", + "poolMembersDescription": "Роль, состояние маршрута, недавнее доказательство и квота.", + "sequenceTitle": "Недавние запросы", + "sequenceColumns": { + "step": "#", + "account": "Аккаунт", + "route": "Маршрут", + "when": "Когда", + "duration": "Время" + }, + "sequenceDescription": "Недавнее доказательство из маршрутизированных вызовов Codex.", + "sequenceEmptyTitle": "Недавних доказательств пока нет", + "loadingEvidence": "Загрузка недавних данных маршрутизации Codex...", + "unknownModel": "Неизвестная модель", + "attemptCount": "Попыток {{count}}", + "selectedProviderBadge": "Выбран {{provider}}", + "servedProviderBadge": "Обслужен {{provider}}", + "failoverHint": "Повторяемый сбой перешёл к: {{providers}}", + "traceAction": "Трассировка", + "openTrace": "Открыть трассировку", + "noEvidence": "Отправьте несколько запросов Codex, затем обновите.", + "selectedCount": "{{count}} в пуле", + "productLabel": "Подписка ChatGPT (OAuth)", + "controlTitle": "Настройка пула", + "controlDescription": "Участники пула управляются на уровне провайдера. Эта страница управляет тем, наследует ли агент этот пул, остаётся на основном аккаунте или переопределяет стратегию маршрутизации.", + "mode": { + "label": "Режим маршрутизации агента", + "inherit": "Использовать настройки провайдера", + "custom": "Индивидуально для этого агента", + "noProviderDefault": "Пул на уровне провайдера пока не сохранён. Следование настройкам провайдера по умолчанию пока оставит этого агента на его основном аккаунте и автоматически подхватит участников пула, добавленных позже.", + "providerDefaultSummary": "{{count}} аккаунтов в настройках провайдера по умолчанию", + "summaryInherited": "Этот агент сейчас следует пулу провайдера по умолчанию.", + "summaryCustom": "Этот агент сейчас сохраняет собственное переопределение маршрутизации для пула на уровне провайдера." + }, + "poolStateTitle": "Состояние пула", + "routerActiveTitle": "Маршрутизатор активен", + "fallbackTitle": "Только резерв", + "blockedNowTitle": "Заблокировано сейчас", + "checkingTitle": "Проверка", + "emptyGroup": "Нет", + "viewerMode": "Только просмотр", + "consoleSummary": "{{active}} активно · {{observed}} использовано напрямую · {{blocked}} заблокировано", + "monitorDirectLabel": "Напрямую", + "monitorDirectUseLabel": "Прямые попадания", + "monitorFailoverLabel": "Переключение", + "lastSeenLabel": "Последний раз замечен", + "sampleBadge": "{{count}} трассированных спанов", + "noSampleBadge": "Нет недавней выборки", + "runtimeHealthTitle": "Состояние в рантайме", + "runtimeHealthSummary": "{{rate}}% успеха · оценка {{score}}", + "noRuntimeSample": "Недавней выборки рантайма пока нет.", + "failureStreakBadge": "{{count}} сбоят сейчас", + "runtimeSuccessCompact": "Успех {{count}}", + "runtimeFailureCompact": "Сбоев {{count}}", + "lastSuccessLabel": "Последний успех {{value}}", + "lastFailureLabel": "Последний сбой {{value}}", + "healthState": { + "healthy": "Исправно", + "degraded": "Ухудшено", + "critical": "Критично", + "idle": "Простаивает" + }, + "checkpoints": { + "configured": "Настроено", + "ready": "Готово", + "observed": "Использовано напрямую", + "switching": "Переключение" + }, + "nextActionTitle": "Следующее действие", + "nextAction": { + "saveTitle": "Сохраните черновик, прежде чем доверять данным", + "saveDescription": "Сохранённый пул и черновик разошлись. Сначала сохраните текущую конфигурацию, затем проверяйте живой трафик.", + "attentionTitle": "Исправьте алиасы, требующие внимания", + "attentionDescription": "Отключённые или разлогиненные алиасы остаются видимыми, чтобы вы могли повторно авторизовать их или удалить из пула.", + "quotaTitle": "Устраните блокировки по квоте, прежде чем доверять ротации по кругу", + "quotaDescription": "{{count}} авторизованных алиасов сейчас не показывают доступную квоту.", + "failoverOnlyTitle": "Проверьте алиасы, появляющиеся только после переключения", + "failoverOnlyDescription": "{{count}} алиасов имеют недавний трафик только как цели переключения, но пока не как прямой выбор при ротации по кругу.", + "addMembersTitle": "Добавьте ещё один авторизованный алиас, чтобы разблокировать реальный пулинг", + "addMembersDescription": "Ротация по кругу становится осмысленной, когда участвовать может более одного готового алиаса.", + "verifyTitle": "Отправьте ещё несколько запросов Codex и обновите данные", + "verifyDescription": "Пул сохранён и готов, но недавние маршрутизированные вызовы пока не показали прямой выбор всех готовых алиасов.", + "healthyTitle": "Никаких действий оператора сейчас не требуется", + "healthyDescription": "Недавние маршрутизированные вызовы напрямую выбирают каждый готовый алиас в сохранённом пуле.", + "manualTitle": "Упорядоченная маршрутизация активна", + "manualDescription": "Этот агент не ротирует каждый запрос. Повторное использование основного алиаса ожидаемо, пока не включена ротация по кругу." + }, + "howItWorksTitle": "Как работает этот пул", + "howItWorks": { + "primary": "Поле «Провайдер» агента остаётся закреплённым за основным алиасом.", + "roundRobin": "«По кругу» ротирует все готовые алиасы в активном пуле.", + "failover": "Повторяемые сбои вышестоящего провайдера могут перейти к следующему подходящему алиасу в том же запросе." + }, + "quota": { + "checking": "Проверка квоты", + "checkingDescription": "GoClaw ещё получает последнее состояние квоты для этого алиаса.", + "healthySummary": "{{usable}} из {{total}} готовы по квоте", + "needsAttention": "{{count}} алиасов требуют внимания по квоте", + "blockersNow": "Блокирует прямо сейчас", + "floorFiveHour": "5ч порог {{value}}%", + "floorWeekly": "Нед. порог {{value}}%", + "plan": "План", + "lastChecked": "Проверено {{value}}", + "readyLabel": "Готово по квоте", + "readyDescription": "Этот алиас сейчас показывает доступные окна квоты.", + "readinessTitle": "Готовность по квоте", + "readinessDescription": "Смотрите сюда в первую очередь. Живому трафику стоит доверять только авторизованным алиасам с доступной квотой.", + "requiresReadyAlias": "Этот алиас должен быть авторизован, прежде чем GoClaw сможет проверить квоту.", + "failure": { + "billing": { + "label": "Оплата", + "description": "Биллинг или права рабочего пространства сейчас блокируют этот аккаунт." + }, + "exhausted": { + "label": "Исчерпано", + "description": "Этот аккаунт авторизован, но одно из его основных окон квоты исчерпано." + }, + "reauth": { + "label": "Повторная авторизация", + "description": "Авторизуйтесь снова, чтобы обновить доступ к рабочему пространству." + }, + "forbidden": { + "label": "Запрещено", + "description": "Этот аккаунт не может читать данные квоты вышестоящего провайдера." + }, + "needs_setup": { + "label": "Требует настройки", + "description": "Авторизуйтесь снова, чтобы GoClaw мог восстановить метаданные рабочего пространства аккаунта ChatGPT." + }, + "retry_later": { + "label": "Повторить позже", + "description": "Эндпоинт квоты временно недоступен или ограничен по частоте запросов." + }, + "unavailable": { + "label": "Недоступно", + "description": "Данные квоты для этого аккаунта сейчас недоступны." + } + } + }, + "metrics": { + "poolHealth": "Состояние пула", + "policy": "Политика трафика", + "poolSize": "Размер пула", + "observedSample": "Наблюдаемая выборка", + "routerActiveAccounts": "Маршрутизатор активен", + "blockedAccounts": "Заблокировано", + "readyAccounts": "Авторизованные аккаунты", + "quotaReadyAccounts": "Готовы по квоте", + "observedRotation": "Использовано напрямую", + "healthy": "Исправно", + "needsAttention": "Требует внимания", + "readyOfTotal": "{{ready}} из {{total}} алиасов готовы", + "failovers": "Переключения", + "noFailovers": "Переключений пока нет" + }, + "verdict": { + "healthy": { + "title": "Ротация по кругу выглядит исправной", + "description": "{{observed}} готовых аккаунтов наблюдались в ротации на {{count}} трассированных спанах." + }, + "warning": { + "title": "Ротация по кругу настроена, но пока не проверена", + "description": "Пока только {{observed}} готовых аккаунтов имеют доказательства прямой маршрутизации на {{count}} трассированных спанах." + }, + "manual": { + "title": "Упорядоченная маршрутизация активна", + "description": "Этот агент не ротирует каждый запрос. Повторное использование основного алиаса ожидаемо, пока не включена ротация по кругу." + } + }, + "role": { + "preferred": "Основной", + "extra": "Дополнительный" + }, + "status": { + "ready": "Готово", + "needs_sign_in": "Требуется авторизация", + "disabled": "Отключено" + }, + "strategy": { + "manual": "Основной первым", + "primaryFirst": "Основной первым", + "roundRobin": "По кругу", + "priorityOrder": "По приоритету" + } + }, + "identity": { + "title": "Идентичность", + "agentKey": "Ключ агента", + "agentKeyHint": "Уникальный идентификатор, изменить нельзя.", + "displayName": "Отображаемое имя", + "displayNamePlaceholder": "напр. Мой ассистент", + "displayNameHint": "Понятное имя, показываемое в интерфейсе. Оставьте пустым, чтобы использовать ключ агента.", + "expertiseSummary": "Краткое описание экспертизы", + "expertiseSummaryPlaceholder": "напр. Астрология, гадание, гороскопы, нумерология", + "expertiseSummaryHint": "Краткое описание экспертизы этого агента. Используется для обнаружения при делегировании.", + "status": "Статус", + "active": "Активен", + "inactive": "Неактивен", + "summonFailed": "Вызов не удался", + "defaultAgent": "Агент по умолчанию", + "emoji": "Иконка", + "emojiHint": "Необязательная эмодзи-иконка" + }, + "llmConfig": { + "title": "Конфигурация LLM", + "contextWindow": "Окно контекста", + "contextWindowHint": "Лимит токенов для контекста модели.", + "maxToolIterations": "Макс. итераций инструментов", + "maxToolIterationsHint": "Макс. вызовов инструментов на запрос." + }, + "workspace": { + "title": "Рабочее пространство", + "workspacePath": "Путь рабочего пространства", + "workspacePathHint": "Назначается автоматически при создании агента. Подкаталоги на пользователя создаются в рантайме.", + "noWorkspace": "Рабочее пространство не настроено", + "restrictToWorkspace": "Ограничить рабочим пространством", + "restrictToWorkspaceHint": "Строго ограничить доступ к файлам пределами пути рабочего пространства." + }, + "config": { + "saveConfig": "Сохранить конфигурацию", + "saving": "Сохранение...", + "saved": "Сохранено", + "failedToSave": "Не удалось сохранить", + "usingGlobalDefaults": "Используются системные настройки по умолчанию" + }, + "files": { + "readOnly": "Только чтение", + "readOnlyDesc": "Только владелец агента может редактировать предопределённые файлы контекста.", + "resummon": "Вызвать заново", + "editWithAi": "Редактировать с ИИ", + "resummonTitle": "Вызвать агента заново", + "resummonDesc": "Это заново сгенерирует SOUL.md, IDENTITY.md и, при необходимости, USER_PREDEFINED.md с нуля, используя исходное описание. Любые ручные правки этих файлов будут перезаписаны.", + "cancel": "Отмена", + "resummonConfirm": "Вызвать заново", + "summoning": "Вызов...", + "selectFileToEdit": "Выберите файл для редактирования", + "selectFileToView": "Выберите файл для просмотра", + "saving": "Сохранение...", + "save": "Сохранить", + "loading": "Загрузка...", + "openAgentTitle": "Открытый агент — файлы контекста на пользователя", + "openAgentDesc1": "Это открытый агент. Файлы контекста (AGENTS.md, SOUL.md, TOOLS.md и др.) персонализируются для каждого пользователя. Они автоматически создаются из шаблонов, когда пользователь впервые общается с этим агентом.", + "openAgentDesc2pre": "Показанные здесь файлы на уровне агента пусты, потому что открытые агенты хранят весь контекст на пользователя в таблице ", + "openAgentDesc2post": ".", + "contextFiles": "Файлы контекста", + "contextFile": "Файл контекста", + "perUser": "на пользователя", + "emptyFile": "пусто", + "estTokens": "Прибл. {{tokens}} токенов", + "insertContact": "Найдите контакт для вставки...", + "contentPlaceholder": "Содержимое файла...", + "systemPromptPreview": "Системный промпт", + "tokens": "токенов", + "toolsAvailable": "{{count}} инструментов доступно", + "toolsSection": "Инструменты", + "toolParameters": "Параметры", + "noParameters": "Нет параметров", + "noTools": "Инструменты недоступны" + }, + "fileEditor": { + "editWithAi": "Редактировать с ИИ", + "editAiDescription": "Опишите, что хотите изменить. ИИ прочитает текущие файлы и обновит их соответственно.", + "editAiPlaceholder": "напр. Сделай агента более официальным, добавь поддержку вьетнамского языка, измени имя на Luna...", + "sending": "Отправка...", + "regenerate": "Перегенерировать", + "regenerating": "ИИ переписывает файлы...", + "regenerateCompleted": "Файлы обновлены!", + "regenerateFailed": "Перегенерация не удалась", + "retry": "Повторить", + "cancel": "Отмена" + }, + "shares": { + "grantAccess": "Предоставить доступ", + "userId": "ID пользователя", + "userIdPlaceholder": "Найдите контакты или введите ID пользователя...", + "role": "Роль", + "share": "Поделиться", + "loadingShares": "Загрузка доступов...", + "noShares": "Доступов пока нет", + "noSharesDesc": "Поделитесь этим агентом с другими пользователями, введя их ID выше.", + "user": "Пользователь", + "revokeTitle": "Отозвать доступ", + "revokeDesc": "Отозвать доступ у пользователя «{{userId}}»? Он больше не сможет использовать этого агента.", + "revoke": "Отозвать", + "role.user": "Пользователь", + "role.viewer": "Наблюдатель", + "roleDesc.user": "Может использовать агента и общаться", + "roleDesc.viewer": "Доступ только для чтения" + }, + "links": { + "teamsHint": "Связи агентов скоро будут удалены. Пожалуйста, используйте Команды агентов для мультиагентного сотрудничества — они дают отслеживание задач, общее рабочее пространство и контроль качества в более простой конфигурации.", + "createLink": "Создать связь", + "targetAgent": "Целевой агент", + "selectAgent": "Выберите или найдите агента...", + "direction": "Направление", + "description": "Описание", + "descriptionPlaceholder": "Необязательное описание связи...", + "maxConcurrent": "Макс. одновременно", + "advancedSettings": "Дополнительные настройки", + "allowedUsers": "Разрешённые пользователи", + "allowedUsersPlaceholder": "user1, user2, ...", + "allowedUsersHint": "Если задано, только эти пользователи могут запускать это делегирование. Через запятую.", + "deniedUsers": "Запрещённые пользователи", + "deniedUsersPlaceholder": "user3, user4, ...", + "deniedUsersHint": "Этим пользователям запрещено запускать это делегирование. Через запятую.", + "creating": "Создание...", + "noLinks": "Связей агентов пока нет", + "noLinksDesc": "Создайте связи, чтобы позволить агентам делегировать задачи друг другу.", + "loadingLinks": "Загрузка связей...", + "columns": { + "target": "Цель", + "direction": "Направление", + "status": "Статус", + "limit": "Лимит" + }, + "deleteTitle": "Удалить связь", + "deleteDesc": "Удалить связь делегирования с «{{name}}»? Агенты больше не смогут делегировать друг другу через эту связь.", + "editTitle": "Редактировать связь", + "editDescription": "Обновите настройки связи делегирования.", + "optionalDescription": "Необязательное описание...", + "allowedUsersHintShort": "Если задано, только эти пользователи могут запускать это делегирование.", + "deniedUsersHintShort": "Этим пользователям запрещено запускать это делегирование." + }, + "skills": { + "noSkillsAvailable": "Нет доступных навыков", + "noSkillsDesc": "Загрузите навыки на странице «Навыки», чтобы предоставить их агентам.", + "skillsGranted": "{{granted}} из {{total}} навыков предоставлено", + "filterSkills": "Фильтр навыков...", + "noSkillsMatch": "Ни один навык не соответствует вашему запросу.", + "system": "Системный", + "alwaysAvailable": "Всегда доступен" + }, + "instances": { + "loadingInstances": "Загрузка экземпляров...", + "noInstances": "Пользовательских экземпляров пока нет.", + "noInstancesDesc": "Экземпляры создаются, когда пользователи взаимодействуют с этим агентом.", + "instanceCount": "{{count}} экземпляр", + "instanceCount_plural": "{{count}} экземпляров", + "selectInstance": "Выберите экземпляр для просмотра и редактирования его USER.md", + "loading": "Загрузка...", + "saving": "Сохранение...", + "save": "Сохранить", + "saved": "Сохранено", + "searchContacts": "Найдите контакты для добавления...", + "noContactsFound": "Контакты не найдены" + }, + "summoning": { + "title": "Вызов вашего агента...", + "completed": "Вызов завершён!", + "failed": "Вызов не удался", + "agentReady": "{{name}} готов!", + "weavingSoul": "Сплетаем душу", + "wait": "Обычно это занимает несколько минут. Пожалуйста, подождите...", + "continue": "Продолжить", + "retry": "Повторить", + "retrying": "Повтор...", + "done": "готово", + "fileLabelSOUL": "Душа и личность", + "fileLabelIDENTITY": "Карта личности", + "takingTooLong": "Слишком долго?", + "cancel": "Отмена", + "cancelling": "Отмена..." + }, + "configGroups": { + "core": "Основное", + "coreDesc": "Ключевые настройки, напрямую влияющие на поведение агента", + "capabilities": "Возможности", + "capabilitiesDesc": "Функции, которые может использовать агент", + "performance": "Производительность", + "performanceDesc": "Управление контекстом и среда исполнения", + "advanced": "Качество и дополнительно", + "advancedDesc": "Контроль качества и пользовательская конфигурация" + }, + "configSections": { + "subagents": { + "title": "Субагенты", + "description": "Управляет лимитами и поведением порождения субагентов", + "maxConcurrent": "Макс. одновременно", + "maxSpawnDepth": "Макс. глубина порождения", + "maxChildrenPerAgent": "Макс. дочерних на агента", + "archiveAfter": "Архивировать через (минут)", + "maxRetries": "Макс. повторов", + "modelOverride": "Переопределение модели", + "inheritFromAgent": "(наследовать от агента)" + }, + "toolPolicy": { + "title": "Политика инструментов", + "description": "Управляйте тем, какие инструменты может использовать этот агент", + "profile": "Профиль", + "allow": "Разрешить", + "deny": "Запретить", + "alsoAllow": "Также разрешить", + "selectToolsAllow": "Выберите инструменты для разрешения...", + "selectToolsDeny": "Выберите инструменты для запрета...", + "selectToolsAlsoAllow": "Выберите дополнительные инструменты...", + "toolCallPrefix": "Префикс вызова инструмента", + "toolCallPrefixHint": "Убирает этот префикс из имён вызовов инструментов модели перед поиском в реестре.", + "waitLimits": "Границы инструмента ожидания", + "waitMinPlaceholder": "Мин. мс", + "waitMaxPlaceholder": "Макс. мс", + "waitLimitsHint": "Необязательные границы на уровне агента. Серверные лимиты безопасности всё равно ограничивают ожидания диапазоном 100–300000мс." + }, + "workspaceSharing": { + "title": "Совместное рабочее пространство", + "description": "Управляйте изоляцией файлов рабочего пространства между пользователями", + "sharedDm": "Делить в личных сообщениях", + "sharedDmTip": "Все пользователи личных сообщений делят один каталог рабочего пространства вместо изолированных папок на пользователя", + "sharedGroup": "Делить в группах", + "sharedGroupTip": "Все участники группового чата делят один каталог рабочего пространства", + "sharedUsers": "Пользователи с общим доступом", + "sharedUsersTip": "Конкретные ID пользователей, которые всегда делят рабочее пространство независимо от настроек ЛС/групп. Формат: channel:userId (напр. telegram:386246614)", + "userIdPlaceholder": "Введите ID пользователя (напр. telegram:386246614)", + "warning": "Когда совместный доступ включён, все пользователи с общим доступом могут читать и записывать файлы в одном каталоге рабочего пространства. Совместное использование памяти управляется отдельно. Файлы контекста остаются изолированными для каждого пользователя.", + "memoryGroupLabel": "Память и граф знаний", + "memoryGroupDescription": "Управляйте изоляцией памяти и графа знаний между пользователями", + "folderGroupLabel": "Папки рабочего пространства", + "shareMemory": "Общая память", + "shareMemoryTip": "Все пользователи обращаются к одному хранилищу памяти. Когда выключено, у каждого пользователя своё изолированное пространство памяти. Совместное использование графа знаний управляется отдельно ниже.", + "shareMemoryNote": "Переключение не мигрирует данные — существующие воспоминания на пользователя становятся недоступны в общем режиме.", + "shareKG": "Общий граф знаний", + "shareKGTip": "Все пользователи обращаются к одному графу знаний. Когда выключено, у каждого пользователя свой изолированный граф знаний с глобальным каноническим резервом для операций чтения.", + "shareKGNote": "Переключение не мигрирует данные — существующие сущности графа знаний на пользователя становятся недоступны в общем режиме.", + "shareSessions": "Делить сессии", + "shareSessionsTip": "Разрешить агенту обращаться к сессиям бесед по всем группам. Когда выключено, сессии каждого пользователя остаются изолированными.", + "shareSessionsNote": "Переключение не мигрирует данные — существующие сессии на пользователя становятся недоступны в общем режиме." + }, + "modelFallback": { + "title": "Резервные модели", + "description": "Пробуйте резервные пары провайдер/модель в порядке приоритета, когда основная модель недоступна.", + "enabled": "Включено", + "primary": "Основная", + "providerPlaceholder": "Выберите провайдера", + "modelPlaceholder": "Выберите или введите модель", + "add": "Добавить резерв", + "remove": "Удалить резерв", + "reorder": "Изменить порядок резерва", + "empty": "Резервные модели не настроены.", + "cooldown": "Временно пропускать недавно сбоящие маршруты" + }, + "compaction": { + "title": "Уплотнение", + "description": "Настройки уплотнения окна контекста и сброса памяти", + "maxHistoryShare": "Макс. доля истории (0-1)", + "maxHistoryShareTip": "Максимальная доля окна контекста для истории беседы (напр. 0.85 = 85%). Уплотнение запускается при превышении.", + "keepLastMessages": "Хранить последние сообщения", + "keepLastMessagesTip": "Недавние сообщения, сохраняемые после уплотнения. Более старые сообщения заменяются сводкой.", + "timeoutSeconds": "Таймаут (секунды)", + "timeoutSecondsTip": "Максимальное время ожидания сводки уплотнения перед сохранением исходной истории.", + "memoryFlush": "Сброс памяти", + "memoryFlushTip": "Перед уплотнением агент получает ход, чтобы сохранить важный контекст в файлы памяти. Также запускает извлечение в граф знаний." + }, + "inboundDebounce": { + "title": "Дебаунс входящих", + "description": "Переопределите быстрое пакетирование сообщений веб-чата и каналов для этого агента.", + "mode": "Режим", + "modeTip": "«Наследовать» использует глобальную настройку поведения. «Индивидуально» сохраняет переопределение для этого агента.", + "inherit": "Наследовать глобально", + "custom": "Индивидуально", + "debounceMs": "Дебаунс (мс)", + "debounceMsTip": "Установите 0 для отсутствия дебаунса; положительные значения объединяют последующие сообщения после этого ожидания." + }, + "contextPruning": { + "title": "Обрезка контекста", + "description": "Обрезать старые результаты инструментов для экономии окна контекста", + "descriptionSimple": "Автоматически обрезает старые результаты инструментов, чтобы предотвратить переполнение контекста. Установите режим «cache-ttl» для включения.", + "mode": "Режим", + "keepLastAssistants": "Хранить последние ответы ассистента", + "softTrimRatio": "Коэффициент мягкой обрезки (0-1)", + "hardClearRatio": "Коэффициент жёсткой очистки (0-1)", + "minPrunableToolChars": "Мин. обрезаемых символов инструмента", + "softTrim": "Мягкая обрезка", + "maxChars": "Макс. символов", + "headChars": "Символов в начале", + "tailChars": "Символов в конце", + "hardClear": "Жёсткая очистка", + "enabled": "Включено", + "placeholderText": "Текст-заполнитель", + "cacheTtl": "TTL кэша", + "advanced": "Дополнительно" + }, + "sandbox": { + "title": "Песочница", + "description": "Docker-песочница для изоляции исполнения кода", + "mode": "Режим", + "workspaceAccess": "Доступ к рабочему пространству", + "image": "Образ", + "scope": "Область", + "timeout": "Таймаут (сек)", + "memoryMb": "Память (МБ)", + "cpus": "CPU", + "networkEnabled": "Сеть включена" + }, + "memory": { + "title": "Память", + "description": "Настройка семантического поиска по памяти и эмбеддингов", + "enabled": "Включено", + "enabledTip": "Включить или отключить систему памяти для этого агента.", + "maxChunkLen": "Макс. длина фрагмента", + "maxChunkLenTip": "Максимум символов на фрагмент памяти. Оставьте пустым для системного значения по умолчанию.", + "chunkOverlap": "Перекрытие фрагментов", + "chunkOverlapTip": "Символы перекрытия между фрагментами для непрерывности контекста. Оставьте пустым для системного значения по умолчанию.", + "maxResults": "Макс. результатов", + "maxResultsTip": "Максимум записей памяти, возвращаемых на поисковый запрос.", + "minScore": "Мин. оценка", + "minScoreTip": "Минимальная оценка релевантности для результатов поиска.", + "vectorWeight": "Вес вектора", + "vectorWeightTip": "Вес семантического сходства в гибридном поиске.", + "textWeight": "Вес текста", + "textWeightTip": "Вес сходства по ключевым словам в гибридном поиске." + }, + "dreaming": { + "title": "Сновидение (консолидация памяти)", + "description": "Фоновый воркер, который переносит сводки сессий в долговременную память. Настройте, когда агент слишком болтлив или консолидирует недостаточно часто.", + "enabled": "Включено", + "enabledTip": "Отключите, если хотите, чтобы агент полагался только на сырые сводки сессий без долговременной консолидации.", + "threshold": "Порог", + "thresholdTip": "Минимум неперенесённых сводок перед запуском консолидации. По умолчанию 5.", + "debounceMs": "Дебаунс (мс)", + "debounceMsTip": "Минимальный интервал между запусками на агента/пользователя. По умолчанию 600000 (10 минут).", + "verboseLog": "Подробное логирование", + "verboseLogTip": "Выводить пропуски из-за дебаунса и ниже порога на уровне info для отладки оператором." + }, + "thinking": { + "title": "Расширенное размышление", + "description": "Позволяет модели рассуждать перед ответом. Более высокие уровни используют больше токенов, но дают лучшие результаты на сложных задачах.", + "modeLabel": "Режим рассуждения", + "inherit": "Использовать настройки провайдера", + "custom": "Индивидуально для этого агента", + "customDesc": "Переопределить политику рассуждения провайдера по умолчанию только для этого агента.", + "providerDefaultSummary": "Унаследовано от {{provider}}", + "providerLabelFallback": "настройки провайдера по умолчанию", + "noProviderDefault": "Настройка рассуждения провайдера по умолчанию пока не сохранена. Режим наследования будет держать этого агента с выключенным рассуждением, пока провайдер её не сохранит.", + "thinkingLevel": "Уровень размышления", + "thinkingLevelTip": "Простая предустановка для типичного случая. Используйте экспертный режим, когда нужны специфичные для модели настройки усилия или явное резервное поведение.", + "expertMode": "Экспертный режим", + "expertModeDesc": "Запрашивайте специфичные для модели уровни рассуждения, такие как none, minimal, auto или xhigh, и управляйте тем, что происходит, когда выбранная модель их не поддерживает.", + "supportedLevelsForModel": "Выбранная модель {{model}} поддерживает эти явные уровни рассуждения.", + "loadingSupport": "Загрузка метаданных о возможностях рассуждения для {{model}}.", + "unknownSupport": "У GoClaw пока нет явных метаданных о рассуждении для {{model}}.", + "expertModeUnavailable": "Расширенные настройки рассуждения доступны только тогда, когда у выбранной модели есть явные метаданные о возможностях из эндпоинта моделей провайдера.", + "modelDefault": "Рассуждение модели по умолчанию: {{level}}", + "requestedEffort": "Запрошенное усилие", + "requestedEffortTip": "Запрошенный уровень рассуждения до того, как GoClaw нормализует его под выбранную модель.", + "fallbackBehavior": "Резервное поведение", + "fallbackBehaviorTip": "Что GoClaw должен делать, когда запрошенное усилие не поддерживается выбранной моделью.", + "supportedOption": "поддерживается для этой модели", + "legacyShim": "GoClaw также сохраняет грубую прослойку thinking_level для обратной совместимости со старыми сборками.", + "off": "Выкл", + "offDesc": "Без расширенного размышления", + "auto": "Авто", + "autoDesc": "Использовать усилие рассуждения модели по умолчанию", + "none": "Нет", + "noneDesc": "Явно отключить рассуждение провайдера, сохраняя расширенный контракт", + "minimal": "Минимальное", + "minimalDesc": "Использовать наименьшее поддерживаемое усилие рассуждения выше none", + "low": "Низкое", + "lowDesc": "Бюджет ~4K токенов", + "medium": "Среднее", + "mediumDesc": "Бюджет ~10-16K токенов", + "high": "Высокое", + "highDesc": "Бюджет ~32K токенов", + "xhigh": "XHigh", + "xhighDesc": "Использовать максимальное поддерживаемое усилие рассуждения", + "downgrade": "Понизить", + "downgradeDesc": "Нормализовать до ближайшего поддерживаемого более низкого уровня или до самого низкого поддерживаемого при необходимости", + "provider_default": "Использовать модель по умолчанию", + "provider_defaultDesc": "Не форсировать явное усилие и позволить провайдеру/модели выбрать своё значение по умолчанию" + }, + "otherConfig": { + "title": "Прочая конфигурация", + "description": "Дополнительная JSON-конфигурация для расширенных настроек, не охваченных другими разделами", + "formatJson": "Форматировать JSON", + "invalidJson": "Неверный синтаксис JSON" + } + }, + "heartbeat": { + "title": "Heartbeat", + "loading": "Загрузка heartbeat...", + "notConfigured": "Расписание heartbeat не настроено. Настройте его, чтобы агент работал на автопилоте.", + "setupButton": "Настроить Heartbeat", + "toggleEnabled": "Переключить включение heartbeat", + "every": "Каждые {{interval}}м", + "lastRun": "Последний", + "nextRun": "Следующий", + "runs": "Запусков", + "suppressed": "Подавлено", + "configure": "Настроить", + "logs": "Логи", + "configTitle": "Конфигурация Heartbeat", + "enabled": "Включено", + "enabledHint": "Когда включено, агент запускается через заданный интервал.", + "sectionTiming": "Тайминг", + "interval": "Интервал (секунды)", + "intervalHint": "Минимум 300 секунд (5 минут).", + "maxRetries": "Макс. повторов", + "maxRetriesHint": "0–10 повторов при сбое.", + "ackMaxChars": "Макс. символов подтверждения", + "ackMaxCharsHint": "Макс. символов в ответе-подтверждении. 0 = без ответа.", + "sectionBehavior": "Поведение", + "isolatedSession": "Изолированная сессия", + "isolatedSessionHint": "Запускать в новой сессии, не продолжая последнюю беседу.", + "lightContext": "Лёгкий контекст", + "lightContextHint": "Пропускать загрузку полных файлов контекста для снижения расхода токенов.", + "sectionSchedule": "Расписание", + "activeHoursStart": "Активен с (ЧЧ:ММ)", + "activeHoursEnd": "Активен до (ЧЧ:ММ)", + "timezone": "Часовой пояс", + "sectionModel": "Модель", + "modelHint": "Переопределить провайдера/модель для запусков heartbeat. Оставьте пустым для использования настроек агента по умолчанию.", + "sectionDelivery": "Доставка", + "channel": "Канал", + "channelPlaceholder": "Выберите канал", + "channelNone": "Нет (без доставки)", + "channelHint": "напр. telegram, feishu. Оставьте пустым для отсутствия доставки.", + "chatId": "ID чата", + "chatIdPlaceholder": "Выберите чат", + "selectChannelFirst": "Сначала выберите канал", + "checklist": "Чеклист (HEARTBEAT.md)", + "checklistHint": "Инструкции, внедряемые в каждый запуск heartbeat. Поддерживает markdown.", + "checklistLoading": "Загрузка чеклиста...", + "checklistPlaceholder": "# Чеклист Heartbeat\n\n- Проверить ожидающие задачи\n- Сообщить статус\n", + "on": "Вкл", + "off": "Выкл", + "notSet": "Не задано", + "disabled": "Heartbeat отключён", + "nextIn": "Следующий через {{time}}", + "advancedSettings": "Дополнительные настройки", + "scheduleHint": "Оставьте пустым для круглосуточной работы.", + "testRun": "Тестовый запуск", + "cancel": "Отмена", + "save": "Сохранить", + "saving": "Сохранение...", + "logsTitle": "Логи Heartbeat", + "refresh": "Обновить", + "noLogs": "Логов heartbeat пока нет.", + "logsPagination": "{{from}}–{{to}} из {{total}}" + }, + "hooks": { + "card": { + "title": "Хуки", + "loading": "Загрузка хуков...", + "empty": "Хуки не настроены", + "emptyDesc": "Хуки перехватывают события жизненного цикла агента, такие как вызовы инструментов.", + "viewAll": "Показать все {{count}} хуков", + "addHook": "Добавить хук", + "nHooks": "{{count}} хуков" + }, + "tab": { + "refresh": "Обновить", + "create": "Создать хук", + "emptyTitle": "Хуки не настроены", + "emptyDesc": "Хуки перехватывают события жизненного цикла агента, такие как вызовы инструментов, старт/стоп сессии и другие.", + "createFirst": "Создайте свой первый хук", + "deleteConfirm": "Удалить хук для «{{event}}»? Это нельзя отменить." + } + }, + "permissions": { + "title": "Разрешения", + "description": "Управляйте тем, кто может изменять конфигурацию и файлы агента. Владелец всегда имеет полный доступ.", + "addRule": "Добавить правило", + "allMembers": "Все участники", + "allMembersTitle": "Предоставьте это правило каждому участнику в выбранной области, используя userId=\"*\".", + "checkAccess": "Проверить доступ", + "allowed": "Разрешено", + "denied": "Запрещено", + "fileWriters": "Редакторы файлов", + "configPerms": "Разрешения на конфигурацию", + "noRules": "Нет правил разрешений. Владелец имеет неявный полный доступ.", + "userIdPlaceholder": "Найдите контакты или введите ID...", + "scopePlaceholder": "Выберите или введите область...", + "unknownWriterLabel": "Пользователь {{id}}", + "types": { + "file_writer": "Редактор файлов", + "file_writer_desc": "Управляет тем, кто может редактировать файлы и управлять cron в групповых чатах. Первый пользователь добавляется автоматически.", + "heartbeat": "Heartbeat", + "heartbeat_desc": "Управляет тем, кто может настраивать расписание и параметры heartbeat через чат.", + "cron": "Cron", + "cron_desc": "Управляет тем, кто может создавать и управлять запланированными cron-задачами через чат.", + "context_files": "Файлы контекста", + "context_files_desc": "Управляет тем, кто может изменять файлы личности агента (SOUL.md и др.) через чат.", + "all": "Все (*)", + "all_desc": "Полный доступ ко всем типам конфигурации." + }, + "scopes": { + "agent": "Агент (только ЛС)", + "group_all": "Все группы", + "global": "Глобально (все контексты)" + } + }, + "transfer": { + "title": "Перенос агента", + "description": "Экспортируйте агентов в файлы архивов или импортируйте из существующих архивов.", + "tabExport": "Экспорт", + "tabImport": "Импорт", + "selectAgent": "Выберите агента", + "selectAgentPlaceholder": "Выберите агента...", + "presets": "Предустановки", + "preset": { + "minimal": "Минимальная", + "standard": "Стандартная", + "complete": "Полная" + }, + "sections": { + "config": "Конфигурация агента", + "contextFiles": "Файлы контекста", + "userData": "Данные пользователей", + "userContextFiles": "Файлы контекста пользователей", + "userProfiles": "Профили пользователей", + "userOverrides": "Переопределения пользователей", + "memory": "Память", + "memoryGlobal": "Глобальная", + "memoryPerUser": "На пользователя", + "knowledgeGraph": "Граф знаний", + "skills": "Навыки", + "mcp": "Гранты MCP", + "customTools": "Пользовательские инструменты", + "cron": "Cron-задачи", + "permissions": "Разрешения", + "sessions": "Сессии", + "workspace": "Файлы рабочего пространства", + "team": "Команда", + "media": "Медиафайлы" + }, + "hints": { + "sessions": "Может быть очень большим", + "media": "Может быть очень большим", + "team": "Метаданные команды, участники, задачи, связи", + "mergeOverwrite": "Существующие файлы с тем же именем будут перезаписаны", + "mergeUpsert": "Документы объединяются по пути", + "mergeKgUpsert": "Сущности объединяются по external_id", + "mergeFiles": "Новые файлы добавляются, существующие перезаписываются" + }, + "exportButton": "Экспортировать агента", + "exporting": "Экспорт «{{name}}»...", + "exportComplete": "Экспорт завершён", + "exportFailed": "Экспорт не удался", + "exportAnother": "Экспортировать другого", + "download": "Скачать", + "importing": "Импорт...", + "importComplete": "Импорт завершён", + "importFailed": "Импорт не удался", + "importAnother": "Импортировать другого", + "viewAgent": "Просмотреть агента", + "tryAgain": "Попробовать снова", + "cancel": "Отмена", + "dontClose": "Не закрывайте эту страницу — идёт импорт", + "rolledBack": "Все изменения откачены. Система в предыдущем состоянии.", + "dropHere": "Перетащите файл .tar.gz сюда или нажмите для выбора", + "supportedFormats": "Поддерживает: .tar.gz, .agent.json (устаревший)", + "invalidFile": "Неверный файл — не удалось разобрать архив", + "importMode": "Режим импорта", + "modeNew": "Создать нового агента", + "modeMerge": "Объединить с существующим агентом", + "targetAgent": "Целевой агент", + "changeFile": "Изменить файл", + "startImport": "Начать импорт" + }, + "delivery": { + "title": "Поведение доставки", + "description": "Переопределение на уровне агента для быстрого подтверждения и промежуточных ответов. Настройки канала всё равно имеют приоритет над этим.", + "modeTip": "Выберите, наследует ли этот агент поведение рабочего пространства или использует собственные настройки доставки. Настройки канала всё равно имеют наивысший приоритет.", + "inherit": "Наследовать", + "custom": "Индивидуально", + "quickAck": "Быстрое подтверждение", + "quickAckTip": "Отправляет короткое естественное подтверждение перед началом долгой работы. Оно генерируется вне истории сессии и отдельно от «Показывать рассуждение».", + "intermediate": "Промежуточные ответы", + "intermediateTip": "Отправляет естественные сообщения о прогрессе во время долгих или насыщенных инструментами запусков. Эти обновления не добавляются в историю сессии.", + "provider": "Провайдер", + "quickAckProviderTip": "Необязательный провайдер для быстрых подтверждений. Оставьте пустым для использования провайдера агента; выберите здесь быстрого и дешёвого провайдера.", + "intermediateProviderTip": "Необязательный провайдер для промежуточного прогресса. Оставьте пустым для использования провайдера агента; выберите здесь быстрого и дешёвого провайдера.", + "providerPlaceholder": "Использовать провайдера агента", + "model": "Модель", + "quickAckModelTip": "Необязательная модель для быстрых подтверждений. Оставьте пустым для использования модели агента; пользовательские имена моделей допускаются.", + "intermediateModelTip": "Необязательная модель для сообщений о прогрессе. Оставьте пустым для использования модели агента; пользовательские имена моделей допускаются.", + "modelPlaceholder": "Использовать модель агента", + "quickAckDelay": "Задержка подтверждения (мс)", + "quickAckDelayTip": "Сколько ждать перед отправкой быстрого подтверждения. Увеличьте, если для быстрых ответов не должно быть лишнего сообщения.", + "timeoutMs": "Таймаут (мс)", + "quickAckTimeoutTip": "Максимальное время ожидания модели подтверждения перед пропуском сгенерированного подтверждения.", + "intermediateTimeoutTip": "Максимальное время ожидания модели прогресса перед пропуском промежуточного обновления." + }, + "toast": { + "created": "Агент создан", + "createFailed": "Не удалось создать агента", + "updated": "Агент обновлён", + "updateFailed": "Не удалось обновить агента", + "deleted": "Агент удалён", + "deleteFailed": "Не удалось удалить агента", + "unknownError": "Неизвестная ошибка", + "skillGranted": "Навык предоставлен", + "skillGrantFailed": "Не удалось предоставить навык", + "skillRevoked": "Навык отозван", + "skillRevokeFailed": "Не удалось отозвать навык" + } +} diff --git a/ui/web/src/i18n/locales/ru/api-keys.json b/ui/web/src/i18n/locales/ru/api-keys.json new file mode 100644 index 00000000..fe538b4a --- /dev/null +++ b/ui/web/src/i18n/locales/ru/api-keys.json @@ -0,0 +1,87 @@ +{ + "title": "Ключи API", + "description": "Управление ключами API шлюза с гранулярными разрешениями.", + "addKey": "Создать ключ API", + "searchPlaceholder": "Поиск ключей...", + "emptyTitle": "Нет ключей API", + "emptyDescription": "Создайте свой первый ключ API для аутентификации на шлюзе.", + "columns": { + "name": "Имя", + "prefix": "Ключ", + "scopes": "Области", + "lastUsed": "Последнее использование", + "created": "Создан", + "status": "Статус", + "actions": "Действия" + }, + "status": { + "active": "Активен", + "revoked": "Отозван", + "expired": "Истёк" + }, + "form": { + "title": "Создать ключ API", + "name": "Имя", + "namePlaceholder": "напр. ci-deploy, dashboard-readonly", + "scopes": "Области", + "scopeDescriptions": { + "operator.admin": "Полный доступ администратора", + "operator.read": "Доступ только для чтения", + "operator.write": "Доступ на чтение и запись", + "operator.approvals": "Управление одобрениями выполнения", + "operator.pairing": "Управление привязкой устройств", + "operator.provision": "Создание новых арендаторов" + }, + "tenant": "Арендатор", + "tenantSystem": "Система (все арендаторы)", + "expiry": "Срок действия", + "expiryOptions": { + "never": "Никогда", + "7d": "7 дней", + "30d": "30 дней", + "90d": "90 дней" + }, + "cancel": "Отмена", + "create": "Создать", + "creating": "Создание..." + }, + "created": { + "title": "Ключ API создан", + "description": "Скопируйте этот ключ сейчас — он больше не будет показан.", + "copied": "Скопировано!", + "copy": "Копировать в буфер обмена", + "done": "Готово" + }, + "revoke": { + "title": "Отозвать ключ API", + "description": "Вы уверены, что хотите отозвать «{{name}}»? Это действие нельзя отменить.", + "confirmLabel": "Отозвать" + }, + "toast": { + "created": "Ключ API создан", + "revoked": "Ключ API отозван", + "failedCreate": "Не удалось создать ключ API", + "failedRevoke": "Не удалось отозвать ключ API" + }, + "never": "Никогда", + "neverUsed": "Не использовался", + "tenantBadgeSystem": "Система", + "tenantBadgeUnknown": "Неизвестно", + "columns.expiry": "Срок действия", + "codeDialog": { + "title": "Использование ключа API", + "description": "Используйте этот ключ API для аутентификации в HTTP API шлюза.", + "copy": "Копировать", + "copied": "Скопировано!", + "tabs": { + "curl": "cURL", + "typescript": "TypeScript", + "go": "Go" + }, + "comments": { + "chat": "Чат с агентом (совместимо с OpenAI)", + "listAgents": "Список агентов", + "listSessions": "Список сессий" + } + } +} diff --git a/ui/web/src/i18n/locales/ru/approvals.json b/ui/web/src/i18n/locales/ru/approvals.json new file mode 100644 index 00000000..329cc41e --- /dev/null +++ b/ui/web/src/i18n/locales/ru/approvals.json @@ -0,0 +1,33 @@ +{ + "title": "Одобрения", + "description": "Ожидающие одобрения на выполнение", + "pending": "{{count}} ожидает", + "emptyTitle": "Нет ожидающих одобрений", + "emptyDescription": "Все запросы на выполнение обработаны. Новые запросы появятся здесь в реальном времени.", + "allowOnce": "Разрешить один раз", + "allowAlways": "Разрешить всегда", + "deny": "Отклонить", + "confirmAllowOnce": { + "title": "Разрешить один раз", + "description": "Разрешить выполнение «{{command}}» от агента «{{agentId}}»?" + }, + "confirmAllowAlways": { + "title": "Разрешить всегда", + "description": "Разрешить «{{command}}» для агента «{{agentId}}» навсегда? Эта команда будет автоматически одобряться в будущем." + }, + "confirmDeny": { + "title": "Отклонить выполнение", + "description": "Отклонить запрос на выполнение «{{command}}» от агента «{{agentId}}»?", + "confirmLabel": "Отклонить" + }, + "toast": { + "approved": "Одобрено", + "approvedAlways": "Команда разрешена всегда", + "approvedOnce": "Команда одобрена", + "approveFailed": "Не удалось одобрить", + "denied": "Отклонено", + "deniedDesc": "Команда отклонена", + "denyFailed": "Не удалось отклонить", + "unknownError": "Неизвестная ошибка" + } +} diff --git a/ui/web/src/i18n/locales/ru/backup.json b/ui/web/src/i18n/locales/ru/backup.json new file mode 100644 index 00000000..b37c3e6e --- /dev/null +++ b/ui/web/src/i18n/locales/ru/backup.json @@ -0,0 +1,129 @@ +{ + "title": "Резервное копирование и восстановление", + "description": "Создавайте резервные копии системы, восстанавливайте из архивов и управляйте хранилищем S3", + "tabs": { + "systemBackup": "Резервная копия системы", + "systemRestore": "Восстановление системы", + "s3Config": "Хранилище S3", + "tenantBackup": "Резервная копия арендатора" + }, + "backup": { + "preflight": { + "title": "Проверка системы", + "pgDump": "pg_dump доступен", + "diskSpace": "Достаточно места на диске", + "dbSize": "Размер базы данных", + "dataDir": "Каталог данных", + "workspace": "Рабочее пространство", + "freeDisk": "Свободное место на диске", + "warnings": "Предупреждения", + "refresh": "Обновить", + "pgDumpMissing": "pg_dump недоступен или несовместим", + "goToPackages": "Перейти к пакетам" + }, + "options": { + "includeDb": "Включить базу данных", + "includeFiles": "Включить файлы", + "destination": "Назначение резервной копии", + "local": "Скачать локально", + "s3": "Загрузить в S3" + }, + "start": "Начать резервное копирование", + "running": "Резервное копирование...", + "complete": "Резервное копирование завершено", + "download": "Скачать резервную копию", + "uploadS3": "Загрузить в S3", + "newBackup": "Новая резервная копия", + "fileSize": "Размер файла", + "schemaVersion": "Версия схемы", + "s3History": { + "title": "Резервные копии S3", + "empty": "Резервные копии в S3 не найдены", + "name": "Файл", + "size": "Размер", + "date": "Дата" + }, + "errorTitle": "Резервное копирование не удалось" + }, + "restore": { + "warning": "Восстановление перезапишет существующие данные. Это действие нельзя отменить. Сначала создайте резервную копию.", + "dropzone": "Перетащите архив резервной копии .tar.gz сюда", + "dropzoneHint": "или нажмите для выбора", + "options": { + "restoreDb": "Восстановить базу данных", + "restoreFiles": "Восстановить файлы", + "dryRun": "Пробный запуск (только предпросмотр)" + }, + "start": "Восстановить", + "confirmTitle": "Подтвердить восстановление системы", + "confirmDesc": "Это перезапишет вашу текущую базу данных и файлы содержимым резервной копии. Вы уверены?", + "running": "Восстановление...", + "doNotClose": "Не закрывайте эту страницу во время восстановления", + "complete": "Восстановление завершено", + "dryRunNote": "Это был пробный запуск. Изменения не внесены.", + "dbRestored": "База данных восстановлена", + "filesExtracted": "Файлы извлечены", + "bytesExtracted": "Данные восстановлены", + "schemaVersion": "Версия схемы", + "warnings": "Предупреждения", + "newRestore": "Новое восстановление", + "errorTitle": "Восстановление не удалось", + "tryAgain": "Повторить" + }, + "s3": { + "title": "Конфигурация хранилища S3", + "description": "Настройте S3-совместимое хранилище для удалённых резервных копий", + "status": { + "configured": "Настроено", + "notConfigured": "Не настроено" + }, + "fields": { + "accessKeyId": "Access Key ID", + "secretAccessKey": "Secret Access Key", + "secretPlaceholder": "Введите для обновления", + "bucket": "Бакет", + "region": "Регион", + "endpoint": "Эндпоинт (необязательно)", + "endpointHint": "Для S3-совместимых сервисов (MinIO, R2 и т. д.)", + "prefix": "Префикс ключа" + }, + "save": "Сохранить и проверить подключение", + "saving": "Проверка подключения...", + "saveSuccess": "Конфигурация S3 сохранена, подключение проверено", + "saveError": "Не удалось сохранить конфигурацию S3" + }, + "tenant": { + "title": "Резервное копирование и восстановление арендатора", + "description": "Резервное копирование или восстановление данных отдельного арендатора", + "selectTenant": "Выберите арендатора", + "selectTenantPlaceholder": "Выберите арендатора...", + "backup": { + "start": "Резервировать арендатора", + "running": "Резервное копирование арендатора...", + "complete": "Резервное копирование арендатора завершено", + "download": "Скачать", + "tableCounts": "Скопировано таблиц", + "newBackup": "Новая резервная копия" + }, + "restore": { + "mode": "Режим восстановления", + "modeUpsert": "Объединить (upsert)", + "modeReplace": "Заменить всё", + "modeNew": "Новый арендатор", + "newTenantSlug": "Слаг нового арендатора", + "newTenantSlugPlaceholder": "Введите уникальный слаг для нового арендатора", + "dropzone": "Перетащите архив резервной копии арендатора сюда", + "dryRun": "Пробный запуск (только предпросмотр)", + "start": "Восстановить арендатора", + "confirmTitle": "Подтвердить восстановление арендатора", + "confirmDesc": "Это изменит данные выбранного арендатора. Вы уверены?", + "confirmDescNew": "Это создаст нового арендатора и восстановит архив в него. Вы уверены?", + "running": "Восстановление арендатора...", + "complete": "Восстановление арендатора завершено", + "tablesRestored": "Восстановлено таблиц", + "filesExtracted": "Файлы извлечены", + "dryRunNote": "Пробный запуск — изменения не внесены", + "newRestore": "Новое восстановление" + } + } +} diff --git a/ui/web/src/i18n/locales/ru/channels.json b/ui/web/src/i18n/locales/ru/channels.json new file mode 100644 index 00000000..153ad89c --- /dev/null +++ b/ui/web/src/i18n/locales/ru/channels.json @@ -0,0 +1,786 @@ +{ + "title": "Каналы", + "description": "Управление экземплярами каналов", + "statusDescription": "Статус канала связи", + "addChannel": "Добавить канал", + "refresh": "Обновить", + "searchPlaceholder": "Поиск каналов...", + "emptyTitle": "Нет каналов", + "emptyDescription": "Добавьте свой первый экземпляр канала, чтобы начать работу.", + "emptyStatusDescription": "Каналы связи не настроены.", + "noMatchTitle": "Нет подходящих каналов", + "noMatchDescription": "Попробуйте другой поисковый запрос.", + "columns": { + "name": "Имя", + "type": "Тип", + "agent": "Агент", + "status": "Статус", + "enabled": "Включён", + "actions": "Действия" + }, + "status": { + "running": "Работает", + "stopped": "Остановлен", + "checking": "Проверка", + "degraded": "Ухудшен", + "starting": "Запускается", + "registered": "Настроен", + "failed": "Ошибка" + }, + "enabled": "Включён", + "disabled": "Отключён", + "actions": { + "reauthenticate": "Повторная аутентификация", + "authenticate": "Аутентифицируйтесь для запуска канала", + "reauthShort": "Повт. аутент.", + "openCredentials": "Открыть учётные данные", + "openAdvanced": "Открыть расширенные", + "inspect": "Изучить проблему" + }, + "list": { + "failureStreak": "{{count}} ошибок подряд", + "openChannelDetail": "Откройте детали канала для просмотра последней диагностики", + "nextStep": "Следующий шаг" + }, + "failureKind": { + "auth": "Аутентификация", + "config": "Конфигурация", + "network": "Сеть", + "unknown": "Внимание" + }, + "delete": { + "title": "Удалить экземпляр канала", + "description": "Вы уверены, что хотите удалить «{{name}}»? Это действие нельзя отменить.", + "confirmLabel": "Удалить" + }, + "form": { + "createTitle": "Создать экземпляр канала", + "editTitle": "Редактировать экземпляр канала", + "key": "Ключ *", + "keyPlaceholder": "my-telegram-bot", + "keyHint": "Уникальный слаг, используемый как идентификатор канала", + "displayName": "Отображаемое имя", + "displayNamePlaceholder": "Sales Bot", + "channelType": "Тип канала *", + "agent": "Агент *", + "selectAgent": "Выберите агента", + "credentials": "Учётные данные", + "credentialsHint": "(оставьте пустым, чтобы сохранить текущие)", + "credentialsEncrypted": "Шифруются на стороне сервера. Никогда не возвращаются в ответах API.", + "configuration": "Конфигурация", + "enabled": "Включён", + "cancel": "Отмена", + "create": "Создать", + "update": "Обновить", + "saving": "Сохранение...", + "step": "Шаг {{current}} из {{total}}", + "authenticate": "Аутентификация — {{label}}", + "configure": "Настройка — {{label}}", + "skip": "Пропустить", + "done": "Готово", + "errors": { + "keyRequired": "Ключ обязателен", + "keySlug": "Ключ должен быть корректным слагом (только строчные буквы, цифры и дефисы)", + "agentRequired": "Агент обязателен", + "requiredFields": "Обязательные: {{fields}}", + "failedSave": "Не удалось сохранить", + "failedSaveConfig": "Не удалось сохранить конфигурацию" + }, + "authStatus": { + "authenticated": "Аутентифицирован", + "notAuthenticated": "Не аутентифицирован", + "useQrHint": "— Используйте кнопку входа по QR из таблицы каналов" + } + }, + "detail": { + "agent": "Агент: {{name}}", + "lastChecked": "Последняя проверка: {{value}}", + "checkedRelative": "Проверено {{value}}", + "advanced": "Расширенные", + "advancedTitle": "Расширенные настройки", + "policies": "Политики", + "policiesDesc": "Политики обработки сообщений", + "network": "Сеть", + "networkDesc": "Настройка сервера и прокси", + "limits": "Ограничения", + "limitsDesc": "Ограничения на сообщения и медиа", + "streaming": "Потоковая передача", + "streamingDesc": "Параметры потоковой передачи сообщений в реальном времени", + "behavior": "Поведение", + "behaviorDesc": "Настройка поведения ответов", + "accessControl": "Контроль доступа", + "accessControlDesc": "Ограничения доступа пользователей", + "telegramManagement": "Управление Telegram", + "telegramManagementDesc": "Включите скрытый инструмент и выберите действия Telegram для агентов, использующих этот канал", + "reviewDiagnostics": "Просмотрите последнюю диагностику этого канала перед изменением настроек.", + "whatHappened": "Что произошло", + "recommendedAction": "Рекомендуемое действие", + "technicalDetail": "Технические детали", + "tabs": { + "general": "Общие", + "config": "Конфигурация", + "credentials": "Учётные данные", + "groups": "Группы", + "contexts": "Контексты", + "managers": "Менеджеры" + }, + "general": { + "identity": "Идентичность", + "name": "Имя", + "nameHint": "Уникальный слаг (только для чтения)", + "channelType": "Тип канала", + "displayName": "Отображаемое имя", + "displayNamePlaceholder": "Понятное имя", + "agent": "Агент", + "selectAgent": "Выберите агента", + "enabled": "Включён", + "saved": "Сохранено", + "saveChanges": "Сохранить изменения", + "saving": "Сохранение..." + }, + "credentials": { + "hint": "Оставьте поля пустыми, чтобы сохранить текущие значения. Учётные данные шифруются на стороне сервера и никогда не возвращаются в ответах API.", + "noSchema": "Нет схемы учётных данных для этого типа канала.", + "noCredentials": "Нет учётных данных для обновления", + "failedSave": "Не удалось сохранить", + "saved": "Сохранено", + "updateCredentials": "Обновить учётные данные", + "saving": "Сохранение..." + }, + "config": { + "noSchema": "Нет схемы конфигурации для этого типа канала.", + "saved": "Сохранено", + "saveConfig": "Сохранить конфигурацию", + "saving": "Сохранение...", + "webhookUrlLabel": "Установите это как Event Request URL вашего приложения Lark:", + "copyPath": "Копировать путь" + }, + "groups": { + "saved": "Сохранено", + "saveGroups": "Сохранить группы", + "saving": "Сохранение..." + }, + "contexts": { + "description": "Просмотрите контексты канала, сохранённых участников, действующие инструменты MCP, инструменты Secure CLI и учётные данные с ограниченной областью действия.", + "emptyTitle": "Нет контекстов", + "emptyHint": "Контексты появляются после того, как этот канал получит сообщения или метаданные группы.", + "loading": "Загрузка сведений о контексте...", + "capabilities": "Возможности", + "members": "Участники", + "noCapabilities": "В этом контексте нет доступных возможностей.", + "noMembers": "Для этого контекста нет сохранённых участников.", + "none": "Нет", + "allTools": "Все инструменты", + "scopedCount": "{{count}} переопределение области", + "scopedCount_plural": "{{count}} переопределений области", + "live": "В реальном времени", + "grant": "Предоставить", + "revoke": "Отозвать", + "manageCapability": "Управление возможностью", + "scopedCredential": "Учётные данные с ограниченной областью", + "apiKeyPlaceholder": "API-ключ (только MCP)", + "envPlaceholder": "JSON окружения, например {\"TOKEN\":\"...\"}", + "secretHint": "Значения шифруются и никогда не возвращаются после сохранения.", + "saveCredential": "Сохранить учётные данные", + "deleteCredential": "Удалить учётные данные", + "grantSaved": "Предоставление контекста сохранено", + "grantDeleted": "Предоставление контекста удалено", + "credentialSaved": "Учётные данные контекста сохранены", + "credentialDeleted": "Учётные данные контекста удалены", + "invalidEnv": "Окружение должно быть объектом JSON со строковыми значениями.", + "envRequired": "Учётные данные Secure CLI требуют JSON окружения.", + "credentialRequired": "Введите API-ключ или JSON окружения.", + "actionFailed": "Действие не выполнено", + "columns": { + "name": "Имя", + "type": "Тип", + "source": "Источник", + "credentials": "Учётные данные", + "tools": "Инструменты", + "actions": "Действия" + } + }, + "passiveMemory": { + "title": "Пассивное извлечение памяти", + "description": "Извлекайте долгоживущие факты из пассивного трафика групп в память, доступную для проверки.", + "pendingCount": "{{count}} в ожидании", + "unprocessedCount": "{{count}} необработанных сообщений", + "enable": "Включить пассивное извлечение", + "reviewMode": "Требовать проверку перед записью", + "interval": "Интервал (минуты)", + "messageCap": "Лимит сообщений", + "retention": "Хранение (часы)", + "minMessages": "Мин. сообщений", + "types": "Типы памяти", + "excludeUsers": "Исключённые пользователи", + "excludePatterns": "Исключённые шаблоны", + "excludeGroups": "Исключённые каналы Discord", + "excludeGroupsPlaceholder": "Выберите канал для исключения", + "manualExcludePlaceholder": "Вставьте ID канала Discord", + "addExcludedGroup": "Добавить", + "noExcludeGroups": "История каналов Discord пока не найдена.", + "noExcludedGroups": "Каналы не исключены.", + "removeExcludedGroup": "Удалить {{group}} из исключённых каналов", + "runNow": "Запустить последнюю группу", + "processAll": "Запустить все группы", + "save": "Сохранить", + "saving": "Сохранение...", + "saved": "Настройки памяти сохранены", + "saveFailed": "Не удалось сохранить настройки памяти", + "runQueued": "Извлечение памяти запущено", + "runAllDone": "Обработано {{messages}} сообщений в {{runs}} группах", + "runFailed": "Не удалось выполнить извлечение памяти", + "runAllProgress": "Прогресс запуска всех групп", + "runAllProgressStats": "{{runs}} выполнено, {{skipped}} пропущено, {{errors}} с ошибкой, {{messages}} сообщений", + "groupCompleted": "Группа {{historyKey}} завершена", + "groupSkipped": "Группа {{historyKey}} пропущена: ниже минимального количества сообщений", + "groupFailed": "Ошибка группы {{historyKey}}: {{error}}", + "itemUpdated": "Элемент памяти обновлён", + "itemFailed": "Не удалось выполнить действие с элементом памяти", + "reviewQueue": "Очередь проверки", + "loading": "Загрузка статуса памяти...", + "noRuns": "Запусков извлечения пока нет.", + "noItems": "Кандидатов в память пока нет.", + "lastRun": "Последний запуск", + "runStats": "{{messages}} сообщений, {{items}} элементов, {{redactions}} редактирований", + "approve": "Одобрить", + "reject": "Отклонить", + "delete": "Удалить", + "type": { + "people": "Люди", + "projects": "Проекты", + "decisions": "Решения", + "todos": "Задачи", + "preferences": "Предпочтения", + "events": "События" + } + }, + "timeline": { + "title": "Хронология", + "firstFailed": "Первая ошибка", + "lastChecked": "Последняя проверка", + "failures": "Ошибки", + "failureStreak": "{{count}} подряд", + "failureTotal": "{{count}} всего", + "lastHealthy": "Последняя успешная", + "noData": "Недавних проверок канала пока не зафиксировано." + }, + "managers": { + "description": "Управляйте тем, какие пользователи имеют права менеджера в групповых чатах.", + "groups": "Группы", + "noManagerGroups": "Нет групп с менеджерами", + "noManagerGroupsHint": "Используйте форму ниже, чтобы добавить менеджера в группу.", + "loadingManagers": "Загрузка менеджеров...", + "noManagers": "В этой группе нет менеджеров.", + "managersCount": "{{count}} менеджер", + "managersCountPlural": "{{count}} менеджеров", + "searchContacts": "Поиск контактов...", + "noResults": "Контакты не найдены", + "columns": { + "userId": "ID пользователя", + "name": "Имя", + "username": "Имя пользователя" + }, + "addForm": { + "title": "Добавить менеджера в новую группу", + "hint": "Добавьте менеджера в группу, которой нет в списке выше.", + "groupId": "ID группы *", + "groupIdPlaceholder": "напр. group:telegram:-100123456", + "userId": "ID пользователя *", + "userIdPlaceholder": "Найдите или введите ID пользователя", + "displayName": "Отображаемое имя", + "username": "Имя пользователя", + "optional": "Необязательно", + "usernameWithout": "Необязательно (без @)", + "adding": "Добавление...", + "addManager": "Добавить менеджера", + "add": "Добавить", + "errors": { + "groupUserRequired": "ID группы и ID пользователя обязательны", + "failedAdd": "Не удалось добавить менеджера" + } + } + } + }, + "fieldConfig": { + "token": { "label": "Токен бота", "help": "От @BotFather" }, + "bot_token": { "label": "Токен бота" }, + "app_token": { + "label": "Токен уровня приложения", + "help": "Токен уровня приложения с областью connections:write (требуется для Socket Mode)" + }, + "user_token": { + "label": "Токен пользователя (необязательно)", + "help": "Необязательно: пользовательский OAuth-токен для собственной идентичности бота. Оставьте пустым, чтобы использовать идентичность бота по умолчанию." + }, + "app_id": { "label": "App ID" }, + "app_secret": { "label": "Секрет приложения" }, + "encrypt_key": { "label": "Ключ шифрования", "help": "Для режима вебхука" }, + "verification_token": { + "label": "Токен верификации", + "help": "Для режима вебхука" + }, + "webhook_secret": { "label": "Секрет вебхука" }, + "api_server": { + "label": "URL сервера API", + "help": "Пользовательский сервер Telegram Bot API для загрузки больших файлов (до 2 ГБ). Оставьте пустым для значения по умолчанию." + }, + "proxy": { + "label": "HTTP-прокси", + "help": "Маршрутизировать трафик бота через HTTP-прокси" + }, + "dm_policy": { "label": "Политика ЛС" }, + "group_policy": { "label": "Политика групп" }, + "require_mention": { + "label": "Требовать @упоминание в группах", + "disabledHint": "Мультибот-режим всегда слушает все сообщения" + }, + "mention_mode": { + "label": "Поведение ответов в группах", + "help": "Как бот решает, когда отвечать в группах с несколькими ботами." + }, + "history_limit": { + "label": "Лимит истории группы", + "help": "Макс. количество ожидающих сообщений группы для контекста (0 = отключено)" + }, + "dm_stream": { + "label": "Потоковая передача в ЛС", + "help": "Передавать ответ постепенно в ЛС" + }, + "group_stream": { + "label": "Потоковая передача в группах", + "help": "Передавать ответ постепенно в группах" + }, + "draft_transport": { + "label": "Предпросмотр черновика", + "help": "Использовать скрытый предпросмотр черновика для потока ответа в ЛС — без уведомления на каждое изменение (требует потоковой передачи в ЛС)" + }, + "reasoning_delivery": { + "label": "Показывать рассуждения", + "help": "Выберите, как рассуждения модели отображаются в сообщениях канала." + }, + "reaction_level": { "label": "Уровень реакций" }, + "media_max_mb": { "label": "Макс. размер медиа (МБ)" }, + "link_preview": { "label": "Предпросмотр ссылок" }, + "allow_from": { "label": "Разрешённые пользователи" }, + "telegram_manager": { + "enabled": { + "label": "Включить инструмент управления Telegram", + "help": "Применяется только к агентам, работающим через этот канал Telegram. Это скрытый инструмент, который не отображается в общих профилях инструментов." + }, + "allowed_actions": { + "label": "Разрешения на управление Telegram", + "help": "Выберите, какие действия Telegram может использовать агент. Если не выбрано ни одного разрешения, инструмент не предоставляется агенту." + } + }, + "domain": { "label": "Домен" }, + "connection_mode": { + "label": "Режим подключения", + "help": "WebSocket не требует публичного IP — только исходящее соединение" + }, + "webhook_port": { + "label": "Порт вебхука", + "help": "0 = использовать основной порт шлюза (рекомендуется)" + }, + "webhook_path": { + "label": "Путь вебхука", + "help": "Путь на основном сервере для событий Lark" + }, + "webhook_url": { "label": "URL вебхука" }, + "topic_session_mode": { + "label": "Режим сессий по темам", + "help": "Использовать root_id ветки для изоляции сессий" + }, + "render_mode": { "label": "Режим рендеринга" }, + "text_chunk_limit": { + "label": "Лимит фрагмента текста", + "help": "Макс. количество символов в сообщении" + }, + "group_allow_from": { + "label": "Разрешённые пользователи группы", + "help": "Отдельный список разрешённых отправителей для групп" + }, + "native_stream": { + "label": "Нативная потоковая передача (агенты и AI-приложения)", + "help": "Использовать API ChatStreamer от Slack для нативной потоковой передачи. При недоступности выполняется откат к редактированию на месте." + }, + "debounce_delay": { + "label": "Задержка дебаунса (мс)", + "help": "Миллисекунды ожидания перед отправкой быстрых сообщений. Установите 0, чтобы отключить." + }, + "thread_ttl": { + "label": "TTL участия в ветке (часы)", + "help": "Часов до того, как бот перестанет автоматически отвечать в ветках, в которых он участвовал. 0 = всегда требовать @упоминание." + }, + "skills": { + "label": "Фильтр навыков", + "help": "Ограничить доступные навыки для этой группы" + }, + "tools": { + "label": "Список разрешённых инструментов", + "help": "Ограничить, какие инструменты агент может использовать в этой группе" + }, + "system_prompt": { "label": "Системный промпт" }, + "chat_behavior": { + "enabled": { + "label": "Человекоподобная доставка", + "help": "Переопределение поведения доставки на уровне канала. Настройки канала имеют приоритет над настройками агента и рабочего пространства." + }, + "intermediate_replies": { + "enabled": { + "label": "Промежуточные ответы", + "help": "Отправлять естественные сообщения о ходе выполнения во время длительных или насыщенных инструментами запусков. Эти сообщения не добавляются в историю сессии." + }, + "mode": { + "label": "Режим промежуточных ответов", + "help": "Выберите, генерируются ли обновления хода выполнения моделью доставки или отключены для этого канала." + }, + "provider": { + "label": "Промежуточный провайдер", + "help": "Необязательный провайдер для сообщений о ходе выполнения. Оставьте пустым, чтобы наследовать провайдера агента; выберите здесь быстрый и дешёвый провайдер." + }, + "model": { + "label": "Промежуточная модель", + "help": "Необязательная модель для сообщений о ходе выполнения. Оставьте пустым, чтобы наследовать модель агента; допускаются пользовательские имена моделей." + }, + "timeout_ms": { + "label": "Промежуточный тайм-аут (мс)", + "help": "Максимальное время ожидания модели доставки перед пропуском промежуточного обновления." + }, + "max_tokens": { + "label": "Промежуточный макс. токенов", + "help": "Лимит токенов для каждого генерируемого сообщения о ходе выполнения. Держите его небольшим, чтобы обновления оставались краткими." + }, + "max_chars": { + "label": "Промежуточный макс. символов", + "help": "Лимит символов после генерации, чтобы сообщения о ходе выполнения не превращались в полные ответы." + } + }, + "quick_ack": { + "enabled": { + "label": "Быстрое подтверждение", + "help": "Отправлять короткое естественное подтверждение перед началом длительной работы. Это сообщение не добавляется в историю сессии." + }, + "mode": { + "label": "Режим быстрого подтверждения", + "help": "Генерируемые режимы используют настроенный провайдер/модель доставки. Режим фиксированного шаблона — единственный режим, отправляющий текст шаблона." + }, + "provider": { + "label": "Провайдер быстрого подтверждения", + "help": "Необязательный провайдер для быстрых подтверждений. Оставьте пустым, чтобы наследовать провайдера агента; выберите здесь быстрый и дешёвый провайдер." + }, + "model": { + "label": "Модель быстрого подтверждения", + "help": "Необязательная модель для быстрых подтверждений. Оставьте пустым, чтобы наследовать модель агента; допускаются пользовательские имена моделей." + }, + "timeout_ms": { + "label": "Тайм-аут быстрого подтверждения (мс)", + "help": "Максимальное время ожидания модели подтверждения перед пропуском сгенерированного подтверждения." + }, + "max_tokens": { + "label": "Макс. токенов быстрого подтверждения", + "help": "Лимит токенов для подтверждения. Держите его небольшим, чтобы оно читалось как квитанция, а не как ответ." + }, + "max_chars": { + "label": "Макс. символов быстрого подтверждения", + "help": "Лимит символов после генерации, чтобы сообщения подтверждения оставались компактными." + } + }, + "final_split": { + "enabled": { + "label": "Разбиение финального ответа", + "help": "Разбивать длинные финальные ответы на несколько сообщений канала. Это отдельно от рассуждений и сообщений о ходе выполнения." + } + } + }, + "platform": { "label": "Платформа", "help": "Выберите платформу, которую обслуживает эта страница Pancake." }, + "tiktok_type": { "label": "Тип TikTok", "help": "Выберите тип аккаунта TikTok для этой страницы" }, + "features.private_reply": { + "label": "Приватный ответ (комментарий → ЛС)", + "help": "Отправить единоразовое ЛС комментаторам после публичного ответа. Только Facebook/Instagram. Meta разрешает ЛС в течение 7 дней после комментария." + }, + "private_reply_message": { + "label": "Сообщение в ЛС", + "help": "Поддерживает плейсхолдеры commenter_name и post_title. Пусто = текст на английском по умолчанию." + } + }, + "fieldOptions": { + "dm_policy": { + "pairing": "Привязка (требуется код)", + "open": "Открыто (принимать всех)", + "allowlist": "Только по списку разрешённых", + "disabled": "Отключено" + }, + "group_policy": { + "open": "Открыто (принимать всех)", + "pairing": "Привязка (требуется одобрение)", + "allowlist": "Только по списку разрешённых", + "disabled": "Отключено" + }, + "mention_mode": { + "strict": "По умолчанию (следовать настройке @упоминания)", + "yield": "Мультибот (отвечать, если не @упомянут другой бот)" + }, + "reaction_level": { + "off": "Выкл", + "minimal": "Минимальный", + "full": "Полный" + }, + "reasoning_delivery": { + "streaming_only": "Только при потоковой передаче", + "always_bubbles": "Всегда отдельными сообщениями", + "off": "Выкл" + }, + "telegram_manager": { + "allowed_actions": { + "topic": "Управление темами", + "message": "Управление сообщениями", + "member": "Управление участниками", + "invite": "Управление ссылками-приглашениями", + "chat": "Информация/настройки чата", + "join_request": "Одобрение запросов на вступление" + } + }, + "chat_behavior": { + "enabled": { + "inherit": "Наследовать от шлюза", + "true": "Включено", + "false": "Отключено" + }, + "intermediate_replies": { + "enabled": { + "inherit": "Наследовать от шлюза", + "true": "Включено", + "false": "Отключено" + }, + "mode": { + "inherit": "Наследовать от шлюза", + "sidecar_generated": "Сгенерировано sidecar", + "off": "Выкл" + } + }, + "quick_ack": { + "enabled": { + "inherit": "Наследовать от шлюза", + "true": "Включено", + "false": "Отключено" + }, + "mode": { + "inherit": "Наследовать от шлюза", + "sidecar_generated": "Сгенерировано sidecar", + "llm_generated": "Сгенерировано LLM", + "fixed_template": "Фиксированный шаблон", + "off": "Выкл" + } + }, + "final_split": { + "enabled": { + "inherit": "Наследовать от шлюза", + "true": "Включено", + "false": "Отключено" + } + } + }, + "domain": { + "lark": "Lark (глобальный)", + "feishu": "Feishu (Китай)" + }, + "connection_mode": { + "webhook": "Webhook", + "websocket": "WebSocket (рекомендуется)" + }, + "topic_session_mode": { + "disabled": "Отключено", + "enabled": "Включено" + }, + "render_mode": { + "auto": "Авто", + "raw": "Без обработки", + "card": "Карточка" + }, + "platform": { + "facebook": "Facebook", + "instagram": "Instagram", + "threads": "Threads (бета)", + "tiktok": "TikTok", + "youtube": "YouTube (бета)", + "shopee": "Shopee", + "line": "Line", + "google": "Google", + "chat_plugin": "Chat Plugin", + "lazada": "Lazada", + "tokopedia": "Tokopedia" + } + }, + "groupOverrides": { + "title": "Переопределения групп и тем", + "hint": "Переопределяйте настройки канала по умолчанию для каждого группового чата или темы форума. Используйте «*» как ID группы для настроек по умолчанию (маска).", + "groupLabel": "Группа: {{id}}", + "groupWildcard": "* (маска)", + "addGroupPlaceholder": "ID чата (напр. -100123456) или *", + "addGroup": "Добавить группу", + "knownGroups": "Известные группы", + "topicOverrides": "Переопределения тем", + "topicLabel": "Тема: {{id}}", + "addTopicPlaceholder": "ID ветки темы", + "addTopic": "Добавить тему", + "fieldConfig": { + "dm_stream": { + "label": "Потоковая передача в ЛС", + "help": "Передавать ответ постепенно в ЛС" + }, + "group_stream": { + "label": "Потоковая передача в группах", + "help": "Передавать ответ постепенно в группах" + }, + "draft_transport": { + "label": "Предпросмотр черновика", + "help": "Использовать скрытый предпросмотр черновика для потока ответа в ЛС — без уведомления на каждое изменение (требует потоковой передачи в ЛС)" + }, + "reasoning_delivery": { + "label": "Показывать рассуждения", + "help": "Выберите, как рассуждения модели отображаются в сообщениях канала." + } + }, + "fields": { + "groupPolicy": "Политика группы", + "requireMention": "Требовать @упоминание", + "enabled": "Включён", + "allowedUsers": "Разрешённые пользователи", + "allowedUsersPlaceholder": "По одному ID пользователя в строке или через запятую", + "skillsFilter": "Фильтр навыков", + "skillsPlaceholder": "По одному имени навыка в строке (пусто = наследовать)", + "toolAllow": "Разрешённые инструменты", + "toolAllowPlaceholder": "Выберите разрешённые инструменты (пусто = наследовать все)...", + "toolAllowHint": "Ограничьте, какие инструменты доступны в этой группе. Поддерживает имена групп, такие как «group:web», «group:fs». Пусто = наследовать все инструменты.", + "systemPrompt": "Системный промпт", + "systemPromptPlaceholder": "Дополнительный системный промпт для этой группы/темы", + "inherit": "Наследовать (по умолчанию для канала)", + "yes": "Да", + "no": "Нет" + } + }, + "wizard": { + "zaloPersonal": { + "createLabel": "Создать и аутентифицировать", + "formBanner": "После создания вы пройдёте аутентификацию по QR-коду и настроите разрешённых пользователей." + }, + "whatsapp": { + "createLabel": "Создать и сканировать QR", + "formBanner": "После создания отсканируйте QR-код в WhatsApp для аутентификации." + } + }, + "fallback": { + "authRequiredSummary": "Требуется аутентификация", + "authRequiredDetail": "Экземпляр канала включён, но требует входа перед подключением.", + "authRequiredHeadline": "Переподключите сессию канала", + "authRequiredHint": "Аутентифицируйте этот канал повторно, чтобы восстановить текущую сессию.", + "missingCredentialsSummary": "Отсутствуют учётные данные", + "missingCredentialsDetail": "Экземпляр канала включён, но обязательные учётные данные неполные.", + "missingCredentialsHeadline": "Заполните обязательные учётные данные", + "missingCredentialsHint": "Откройте учётные данные и заполните отсутствующие или недействительные значения для этого канала." + }, + "scopes": { + "title": "Требуемые разрешения API", + "description": "Ваше приложение Lark/Feishu должно иметь эти области, включённые в Developer Console. После добавления разрешений опубликуйте новую версию приложения.", + "publishReminder": "После добавления областей установите Contact Range на «Все участники» и опубликуйте новую версию приложения, чтобы изменения вступили в силу." + }, + "toast": { + "created": "Канал создан", + "createdDesc": "{{name}} добавлен", + "updated": "Канал обновлён", + "deleted": "Канал удалён", + "failedCreate": "Не удалось создать канал", + "failedUpdate": "Не удалось обновить канал", + "failedDelete": "Не удалось удалить канал" + }, + "zalo": { + "allowedUsers": "Разрешённые пользователи", + "loadContacts": "Загрузить контакты", + "loading": "Загрузка...", + "searchContacts": "Поиск контактов...", + "friends": "Друзья", + "groups": "Группы", + "membersCount": "{{count}} участников", + "noContacts": "Контакты не найдены", + "noContactsMatch": "Нет контактов, соответствующих «{{search}}»", + "addManualPlaceholder": "Добавить ID вручную", + "add": "Добавить", + "zaloIdsHint": "ID пользователей или групп Zalo", + "completeQrLogin": "Завершите вход по QR, чтобы загрузить контакты", + "loginQr": "Вход по QR — {{name}}", + "loginSuccessful": "Вход выполнен! Канал запускается...", + "generatingQr": "Генерация QR-кода...", + "scanHint": "Отсканируйте в приложении Zalo (истекает через ~100 с)", + "loginSuccessLoading": "Вход выполнен! Загрузка контактов...", + "retry": "Повторить", + "close": "Закрыть", + "skip": "Пропустить" + }, + "whatsapp": { + "loginSuccessLoading": "✅ WhatsApp подключён! Загрузка канала...", + "waitingForQr": "Ожидание QR-кода...", + "scanHint": "Откройте WhatsApp → нажмите «Вы» → Связанные устройства → Привязать устройство", + "initializing": "Инициализация...", + "skip": "Пропустить", + "retry": "Повторить", + "close": "Закрыть", + "reauthTitle": "Привязать WhatsApp — {{name}}", + "reauthDescription": "Отсканируйте QR-код в WhatsApp, чтобы привязать устройство.", + "alreadyLinked": "✅ Устройство уже привязано", + "alreadyLinkedDetail": "WhatsApp подключён. Чтобы привязать другое устройство, нажмите «Перепривязать» — это завершит текущую сессию.", + "relinkDevice": "Перепривязать устройство", + "connectedSuccess": "✅ WhatsApp успешно подключён!", + "tabQrCode": "QR-код" + }, + "bitrix24": { + "portalSelect": { + "placeholder": "Выберите портал...", + "empty": "Порталы Bitrix24 пока не подключены.", + "createFirst": "Подключите свой первый портал Bitrix24", + "createNew": "Создать новый портал", + "pendingBadge": "Установка в ожидании — нажмите для продолжения", + "loadError": "Не удалось загрузить порталы. Обновите, чтобы повторить." + }, + "errors": { + "portalRequired": "Пожалуйста, выберите портал.", + "portalNotInstalled": "Этот портал не завершил авторизацию. Авторизуйте его перед созданием канала." + }, + "create": { + "title": "Подключить портал Bitrix24", + "submit": "Создать и авторизовать →", + "fields": { + "name": "Имя портала", + "nameHint": "Внутренний слаг. Автозаполняется из домена; вы можете его изменить.", + "domain": "Домен", + "clientId": "Client ID", + "clientSecret": "Client Secret" + }, + "errors": { + "invalidName": "Используйте строчные буквы, цифры, дефисы, подчёркивания (2-64 символа).", + "invalidDomain": "Должен быть корректным именем хоста (напр. mycorp.bitrix24.com, mycorp.bitrix24.vn или ваш самостоятельно размещённый домен).", + "duplicateName": "Портал с таким именем уже существует.", + "forbidden": "Для создания порталов необходимы права администратора арендатора.", + "gatewayURLUnknown": "Сначала откройте UI goclaw через ваш публичный URL (не localhost), затем повторите." + }, + "help": { + "title": "Как получить Client ID и Secret?", + "step1": "Откройте ваш портал Bitrix24 от имени администратора.", + "step2": "Перейдите в Ресурсы разработчика → Другое → Локальное приложение → Добавить.", + "step3": "Установите Handler URL на:", + "step4": "Отметьте необходимые области:", + "step5": "Сохраните → скопируйте Application ID и Key в форму выше.", + "localDevWarning": "Вы на localhost — Bitrix24 не может достучаться до этого URL. Сначала используйте Cloudflare Tunnel или ваш публичный домен." + }, + "authorize": { + "title": "Авторизовать «{{name}}»", + "openButton": "Открыть страницу авторизации", + "waiting": "Ожидание авторизации...", + "installed": "Портал успешно установлен!", + "timedOut": "Авторизация ещё не завершена. Вы можете продолжить из выпадающего списка.", + "closeResumeLater": "Закрыть (продолжить позже)", + "urlUnknown": "URL установки недоступен. Откройте UI goclaw через ваш публичный URL и повторите из выпадающего списка." + } + } + } +} diff --git a/ui/web/src/i18n/locales/ru/chat.json b/ui/web/src/i18n/locales/ru/chat.json new file mode 100644 index 00000000..fc2092d2 --- /dev/null +++ b/ui/web/src/i18n/locales/ru/chat.json @@ -0,0 +1,34 @@ +{ + "deleteChat": "Удалить чат", + "deleteChatConfirm": "Это навсегда удалит эту беседу и все её сообщения.", + "newChat": "Новый чат", + "openSessions": "Открытые сессии", + "readOnly": "Только чтение — эта сессия принадлежит другому пользователю", + "contextUsage": { + "tooltip": "{{used}} / {{max}} токенов ({{percent}}%) · сжатий: {{compactions}} · последнее: {{lastCompact}}", + "never": "никогда" + }, + "empty": { + "title": "Начните беседу", + "description": "Отправьте сообщение, чтобы начать общение с агентом." + }, + "media": { + "image": "Изображение", + "video": "Видео", + "audio": "Аудио", + "voice": "Голосовое сообщение", + "document": "Документ", + "animation": "Анимация", + "attached": "прикреплено" + }, + "forwardedFrom": "Переслано от", + "replyingTo": "Ответ на", + "error": { + "notConnected": "Нет соединения. Пожалуйста, дождитесь установления соединения." + }, + "selectAgent": { + "title": "Выберите агента", + "description": "Выберите агента, чтобы начать общение" + }, + "imageGenDownloadName": "generated-{{timestamp}}.png" +} diff --git a/ui/web/src/i18n/locales/ru/cli-credentials.json b/ui/web/src/i18n/locales/ru/cli-credentials.json new file mode 100644 index 00000000..130b4797 --- /dev/null +++ b/ui/web/src/i18n/locales/ru/cli-credentials.json @@ -0,0 +1,179 @@ +{ + "title": "Учётные данные CLI", + "description": "Управление защищёнными бинарными файлами CLI, доступными агентам с внедрёнными учётными данными.", + "addCredential": "Добавить учётные данные", + "emptyTitle": "Учётных данных CLI пока нет", + "emptyDescription": "Добавьте учётные данные CLI, чтобы агенты могли выполнять аутентифицированные команды CLI.", + "columns": { + "binary": "Бинарный файл", + "scope": "Область", + "timeout": "Тайм-аут", + "restricted": "Ограничено" + }, + "delete": { + "title": "Удалить учётные данные CLI", + "description": "Удалить «{{name}}»? Зависящие от них агенты потеряют доступ.", + "confirm": "Удалить" + }, + "form": { + "createTitle": "Добавить учётные данные CLI", + "editTitle": "Изменить учётные данные CLI", + "preset": "Пресет", + "presetPlaceholder": "Выберите пресет (необязательно)", + "noPreset": "Без пресета (вручную)", + "presetHint": "Выбор пресета автоматически заполняет поля и показывает необходимые переменные окружения.", + "encryptedHint": "Значения зашифрованы и никогда не отображаются. Имена переменных перечислены ниже; оставьте значение пустым, чтобы сохранить хранимый секрет, или введите новое значение для замены.", + "envVars": "Переменные окружения", + "binaryName": "Имя бинарного файла", + "binaryPath": "Путь к бинарному файлу", + "denyArgs": "Запрещённые аргументы", + "denyVerbose": "Запрещённые подробные аргументы", + "timeout": "Тайм-аут (с)", + "tips": "Подсказки", + "agentId": "Агент", + "agentIdHint": "необязательно — оставьте пустым для глобального", + "commaSeparated": "через запятую", + "binaryNameRequired": "Имя бинарного файла обязательно.", + "failedToSave": "Не удалось сохранить.", + "addEnvVar": "Добавить переменную", + "noEnvVarsHint": "Нажмите «Добавить переменную», чтобы задать переменные окружения для этого инструмента CLI.", + "envKeyPlaceholder": "ENV_VAR_NAME", + "envValuePlaceholder": "значение", + "envKindSensitive": "Конфиденциальное", + "envKindValue": "Значение", + "envValueWarning": "Значения в открытом виде видны авторизованным пользователям.", + "envValueSuspicious": "Это значение в открытом виде похоже на токен, пароль или ключ.", + "invalidEnvKey": "«{{key}}» не является допустимым именем переменной окружения (используйте A-Z, 0-9, подчёркивание).", + "binaryPathHint": "Оставьте пустым для автоопределения из PATH", + "checkBinary": "Проверить", + "binaryFound": "Найдено в {{path}}", + "binaryNotFound": "Бинарный файл не найден в PATH сервера", + "checking": "Проверка...", + "isGlobal": "Доступно всем агентам", + "isGlobalHint": "Когда отключено, этот CLI могут использовать только агенты с явными разрешениями" + }, + "placeholders": { + "binaryName": "напр. gh", + "binaryPath": "/usr/local/bin/...", + "description": "Что делает этот инструмент CLI", + "denyArgs": "--admin, --force", + "denyVerbose": "-v, --verbose", + "tips": "Подсказки по использованию для агента", + "agentId": "Глобально (все агенты)" + }, + "toast": { + "created": "Учётные данные CLI созданы", + "createdDesc": "«{{name}}» успешно добавлено.", + "createFailed": "Не удалось создать учётные данные", + "updated": "Учётные данные CLI обновлены", + "updateFailed": "Не удалось обновить учётные данные", + "deleted": "Учётные данные CLI удалены", + "deleteFailed": "Не удалось удалить учётные данные" + }, + "agentAccess": { + "title": "Доступ агентов", + "description": "Управляйте тем, какие агенты могут использовать {{name}} и какие учётные данные они применяют.", + "credentialsTab": "Учётные данные", + "grantsTab": "Политика доступа" + }, + "agentCredentials": { + "title": "Учётные данные агента", + "description": "Настройте PAT, ключ SSH или учётные данные окружения, которые {{name}} может использовать, когда его запускает выбранный агент.", + "securityHint": "Любой, у кого есть доступ к выбранному агенту, может заставить этого агента использовать эти учётные данные.", + "agent": "Агент", + "env": "Переменные окружения", + "add": "Добавить учётные данные агента", + "save": "Сохранить", + "edit": "Изменить", + "delete": "Удалить", + "back": "Назад", + "empty": "Учётные данные агента не настроены", + "saved": "Учётные данные агента сохранены", + "saveFailed": "Не удалось сохранить учётные данные агента", + "deleted": "Учётные данные агента удалены", + "deleteFailed": "Не удалось удалить учётные данные агента", + "envRequired": "Требуется хотя бы одна переменная окружения" + }, + "userCredentials": { + "title": "Расширенные переопределения пользователя", + "description": "Необязательные переопределения для {{name}} на уровне пользователя. Для обычной настройки git PAT или SSH используйте «Учётные данные агента».", + "userId": "ID пользователя", + "userIdPlaceholder": "user-id или email", + "env": "Переменные окружения", + "addEnv": "Добавить переменную", + "add": "Добавить", + "save": "Сохранить", + "edit": "Изменить", + "delete": "Удалить", + "back": "Назад", + "close": "Закрыть", + "empty": "Учётные данные пользователя не настроены", + "saved": "Учётные данные пользователя сохранены", + "saveFailed": "Не удалось сохранить учётные данные пользователя", + "deleted": "Учётные данные пользователя удалены", + "deleteFailed": "Не удалось удалить учётные данные пользователя", + "envRequired": "Требуется хотя бы одна переменная окружения", + "mergeHint": "Используйте только для личных переопределений. Пользователи чатов (Telegram, Discord и т. д.) должны быть сначала объединены с пользователем арендатора через страницу «Контакты».", + "credentialType": "Тип учётных данных", + "credentialTypeEnv": "Переменные окружения (устаревшее)", + "credentialTypePAT": "Личный токен доступа (PAT)", + "credentialTypeSSH": "Приватный ключ SSH", + "hostScope": "Область хоста", + "hostScopePlaceholder": "github.com или gitlab.example.com:2222", + "hostScopeRequired": "Область хоста обязательна для учётных данных PAT и SSH", + "hostScopeInvalid": "Область хоста должна быть допустимым именем хоста (необязательно :порт)", + "token": "Личный токен доступа", + "tokenPlaceholder": "ghp_… / glpat-… (никогда не отображается обратно)", + "sshKey": "Приватный ключ SSH (PEM)", + "sshKeyPlaceholder": "-----BEGIN OPENSSH PRIVATE KEY-----\n…\n-----END OPENSSH PRIVATE KEY-----", + "sshKeyInvalid": "Ключ SSH не является допустимым незашифрованным приватным ключом PEM", + "passphraseUnsupported": "Ключи SSH, защищённые парольной фразой, не поддерживаются. Укажите незашифрованный ключ.", + "secretMasked": "•••••••• (сохранено, оставьте пустым для сохранения)", + "noSecret": "Секрет ещё не сохранён" + }, + "grants": { + "title": "Разрешения агентов — {{name}}", + "currentGrants": "Текущие разрешения", + "addGrant": "Добавить разрешение", + "editGrant": "Изменить разрешение", + "selectAgent": "Выберите агента...", + "usingDefaults": "Используются значения по умолчанию бинарного файла", + "overrideTimeout": "Переопределить тайм-аут (секунды)", + "overrideDenyArgs": "Переопределить запрещённые аргументы", + "overrideDenyVerbose": "Переопределить запрещённые подробные аргументы", + "overrideTips": "Переопределить подсказки", + "defaultPlaceholder": "по умолчанию", + "grant": "Разрешить", + "update": "Обновить", + "agentRequired": "Пожалуйста, выберите агента", + "envVars": { + "title": "Переменные окружения", + "overrideToggle": "Переопределить значения по умолчанию бинарного файла", + "overrideHelp": "Когда включено, переменные окружения этого разрешения полностью заменяют переменные окружения бинарного файла по умолчанию", + "reveal": "Показать значения", + "revealHidden": "Скрыто — нажмите «Показать» для просмотра", + "revealError": "Не удалось показать переменные окружения — превышен лимит запросов или отказано в доступе", + "addKey": "Добавить переменную", + "keyPlaceholder": "КЛЮЧ", + "valuePlaceholder": "Значение", + "deniedKey": "Ключ '{{key}}' не разрешён", + "emptyState": "Нет переопределений окружения — применяются значения бинарного файла по умолчанию" + }, + "chips": { + "title": "Выдано", + "none": "Нет разрешений", + "countMore": "+ещё {{count}}" + }, + "toast": { + "granted": "Разрешение агента создано", + "grantFailed": "Не удалось создать разрешение", + "updated": "Разрешение агента обновлено", + "updateFailed": "Не удалось обновить разрешение", + "revoked": "Разрешение агента отозвано", + "revokeFailed": "Не удалось отозвать разрешение" + } + }, + "list": { + "truncated": "Показаны первые 20 — используйте поиск или фильтр, чтобы найти больше" + } +} diff --git a/ui/web/src/i18n/locales/ru/common.json b/ui/web/src/i18n/locales/ru/common.json new file mode 100644 index 00000000..fd106432 --- /dev/null +++ b/ui/web/src/i18n/locales/ru/common.json @@ -0,0 +1,142 @@ +{ + "refresh": "Обновить", + "cancel": "Отмена", + "confirm": "Подтвердить", + "delete": "Удалить", + "save": "Сохранить", + "edit": "Изменить", + "create": "Создать", + "add": "Добавить", + "view": "Просмотр", + "filter": "Фильтр", + "search": "Поиск", + "retry": "Повторить", + "manage": "Управление", + "uploadLabel": "Загрузить", + "close": "Закрыть", + "back": "Назад", + "update": "Обновить", + "enabled": "Включено", + "disabled": "Отключено", + "yes": "Да", + "no": "Нет", + "loading": "Загрузка...", + "saving": "Сохранение...", + "creating": "Создание...", + "connecting": "Подключение...", + "connected": "Подключено", + "disconnected": "Отключено", + "running": "Выполняется", + "stopped": "Остановлено", + "pending": "В ожидании", + "completed": "Завершено", + "failed": "Ошибка", + "cancelled": "Отменено", + "unknown": "Неизвестно", + "active": "активно", + "items": "элементов", + "rows": "Строки", + "pageOf": "Страница {{page}} из {{totalPages}}", + "messages": "сообщений", + "cached": "кэшировано", + "live": "В реальном времени", + "noDescription": "Нет описания", + "unsavedChanges": "Несохранённые изменения", + "tryDifferentSearch": "Попробуйте другой поисковый запрос.", + "notSet": "Не задано", + "default": "по умолчанию", + "optional": "необязательно", + "required": "обязательно", + "actions": "Действия", + "status": "Статус", + "name": "Название", + "description": "Описание", + "type": "Тип", + "none": "нет", + "all": "Все", + "global": "Глобально", + "agent": "Агент", + "saved": "Сохранено", + "saveChanges": "Сохранить изменения", + "saveConfig": "Сохранить конфигурацию", + "checkAndCreate": "Проверить и создать", + "checking": "Проверка...", + "format": "Форматировать JSON", + "invalidJson": "Неверный синтаксис JSON", + "usingGlobalDefaults": "Используются глобальные значения по умолчанию из config.json", + "disconnectedGateway": "Соединение со шлюзом потеряно. Попытка переподключения...", + "serverUnreachable": "Сервер недоступен", + "serverUnreachableDesc": "Не удаётся подключиться к серверу шлюза. Проверьте, что сервер запущен.", + "reconnecting": "Переподключение...", + "noFiles": "Файлы не найдены.", + "selectFileToView": "Выберите файл для просмотра содержимого", + "typeToConfirmPrefix": "Введите", + "typeToConfirmSuffix": "для подтверждения.", + "searchPlaceholder": "Поиск...", + "noSessions": "Пока нет сессий. Начните чат!", + "noAgentsAvailable": "Нет доступных агентов", + "selectAgent": "Выберите агента", + "sendMessage": "Отправить сообщение... (Shift+Enter — новая строка)", + "stopGeneration": "Остановить генерацию", + "sendMessageTitle": "Отправить сообщение", + "sendFollowUp": "Отправить уточняющее сообщение", + "attachFile": "Прикрепить файл", + "recordVoice": "Записать голосовое сообщение", + "stopRecording": "Остановить запись", + "cancelRecording": "Отменить запись", + "thinking": "Размышление", + "thinkingStreaming": "Размышление...", + "toolArguments": "Аргументы", + "toolResult": "Результат", + "skillActivating": "Активация...", + "skillActivated": "Активировано", + "toolRunning": "Выполняется...", + "toolDone": "Готово", + "toolFailed": "Ошибка", + "filesBack": "Файлы", + "noProvidersConfigured": "Провайдеры не настроены", + "loadingModels": "Загрузка моделей...", + "modelVerified": "Модель проверена", + "verificationFailed": "Проверка не удалась", + "provider": "Провайдер", + "model": "Модель", + "selectProvider": "Выберите провайдера", + "enterOrSelectModel": "Введите или выберите модель", + "useCustomModel": "Использовать свою:", + "providerTip": "Название провайдера LLM. Должно совпадать с настроенным провайдером.", + "modelTip": "ID модели для использования.", + "check": "Проверить", + "builtinTools": "Встроенные", + "customTools": "Пользовательские", + "selectOrTypeTools": "Выберите или введите названия инструментов...", + "selectOrTypeSkills": "Выберите или введите названия навыков...", + "typeAndPressEnter": "Введите и нажмите Enter...", + "copyCode": "Копировать код", + "copy": "Копировать", + "download": "Скачать", + "errorBoundary": "Что-то пошло не так при отображении этого раздела.", + "csvRows": "csv ({{count}} строк)", + "failedToLoadImage": "Не удалось загрузить изображение", + "deleteFolder": "Удалить папку", + "deleteFile": "Удалить файл", + "loadMore": "Загрузить ещё...", + "viewCard": "Вид карточками", + "viewList": "Вид списком", + "done": "Готово", + "upload": { + "title": "Загрузка файлов", + "dropOrClick": "Перетащите файлы сюда или нажмите для выбора", + "dropHere": "Перетащите файлы сюда", + "maxSize": "Максимум 50 МБ на файл", + "blockedType": "Тип файла {{ext}} не разрешён", + "tooLarge": "Файл превышает лимит 50 МБ", + "failed": "Ошибка загрузки", + "readyCount": "{{ready}} из {{total}} файлов готово", + "successCount": "{{success}} из {{total}} файлов загружено", + "uploading": "Загрузка...", + "uploadCount": "Загрузить {{count}} файл(ов)" + }, + "errors": { + "serverError": "Что-то пошло не так. Пожалуйста, попробуйте позже." + } +} diff --git a/ui/web/src/i18n/locales/ru/config.json b/ui/web/src/i18n/locales/ru/config.json new file mode 100644 index 00000000..ccaad697 --- /dev/null +++ b/ui/web/src/i18n/locales/ru/config.json @@ -0,0 +1,414 @@ +{ + "title": "Конфигурация", + "description": "Управление шлюзом, настройками агентов по умолчанию, инструментами и интеграциями", + "noConfig": "Конфигурация не загружена", + "noConfigDescription": "Не удалось загрузить файл конфигурации. Возможно, шлюз недоступен.", + "retry": "Повторить", + "warning": "Изменения здесь глобально влияют на всех агентов и все каналы. Неправильная настройка может нарушить работу шлюза.", + "save": "Сохранить", + "saving": "Сохранение...", + "saved": "Сохранено", + "failedToSave": "Не удалось сохранить конфигурацию", + "saveConfig": "Сохранить конфигурацию", + "usingGlobalDefaults": "Используются глобальные значения по умолчанию", + + "tabs.server": "Сервер", + "tabs.behavior": "Поведение", + "tabs.aiDefaults": "Настройки ИИ по умолчанию", + "tabs.quota": "Квота", + "tabs.tools": "Инструменты", + "tabs.integrations": "Интеграции", + + "server.title": "Сервер шлюза", + "server.description": "Привязка к сети, токен аутентификации и контроль доступа", + "branding.title": "Настройки внешнего вида", + "branding.description": "Публичное имя приложения, метаданные, иконки, логотип, фавикон и ресурсы для превью в соцсетях", + "branding.appName": "Имя приложения", + "branding.appNameTip": "Полное имя приложения, отображаемое в метаданных и будущих брендированных элементах UI.", + "branding.appShortName": "Короткое имя", + "branding.appShortNameTip": "Компактное имя приложения для небольших элементов UI или экранов установки.", + "branding.themeColor": "Цвет темы", + "branding.themeColorTip": "Цвет темы браузера, используемый поддерживаемыми клиентами.", + "branding.metaTitle": "Meta-заголовок", + "branding.metaTitleTip": "Заголовок HTML-документа. Оставьте пустым, чтобы использовать встроенное значение по умолчанию.", + "branding.metaDescription": "Meta-описание", + "branding.metaDescriptionTip": "HTML meta-описание для превью в поиске.", + "branding.metaDescriptionPlaceholder": "Приватный шлюз ИИ-агентов", + "branding.metaKeywords": "Meta-ключевые слова", + "branding.metaKeywordsTip": "Необязательные ключевые слова через запятую.", + "branding.ogTitle": "Заголовок Open Graph", + "branding.ogTitleTip": "Заголовок, используемый при публикации сайта в социальных платформах.", + "branding.ogDescription": "Описание Open Graph", + "branding.ogDescriptionTip": "Описание, используемое при публикации сайта в социальных платформах.", + "branding.logoUrl": "URL логотипа", + "branding.logoUrlTip": "Внешний URL или загруженный путь логотипа /branding-assets/*.", + "branding.faviconUrl": "URL фавикона", + "branding.faviconUrlTip": "Внешний URL или загруженный путь фавикона /branding-assets/*.", + "branding.appleTouchIconUrl": "URL иконки Apple Touch", + "branding.appleTouchIconUrlTip": "Внешний URL или загруженный путь touch-иконки /branding-assets/*.", + "branding.ogImageUrl": "URL изображения Open Graph", + "branding.ogImageUrlTip": "Внешний URL или загруженное изображение /branding-assets/* для превью в соцсетях.", + "branding.upload": "Загрузить ресурс", + "branding.uploaded": "Ресурс брендинга загружен", + "branding.uploadFailed": "Не удалось загрузить ресурс брендинга", + "branding.uploadErrors.too_large": "Файл должен быть не больше 2 МБ.", + "branding.uploadErrors.unsupported_type": "Используйте файлы SVG, PNG, JPG, WEBP или ICO.", + + "gateway.host": "Хост", + "gateway.hostTip": "Сетевой интерфейс, который прослушивает шлюз. Используйте 0.0.0.0 для прослушивания всех интерфейсов.", + "gateway.port": "Порт", + "gateway.portTip": "TCP-порт, к которому привязываются WebSocket и HTTP-сервер шлюза.", + "gateway.token": "Токен аутентификации", + "gateway.tokenTip": "Bearer-токен, необходимый для всех клиентских подключений. Оставьте пустым, чтобы отключить аутентификацию.", + "gateway.tokenManaged": "Токен управляется через переменную окружения и не может быть изменён здесь.", + "gateway.ownerIds": "ID владельцев", + "gateway.ownerIdsTip": "ID пользователей с правами администратора. Разделяйте несколько ID клавишей Enter.", + "gateway.ownerIdsPlaceholder": "Введите ID владельца...", + "gateway.allowedOrigins": "Разрешённые источники", + "gateway.allowedOriginsTip": "Разрешённые CORS-источники для HTTP API. Используйте * для разрешения всех источников.", + "gateway.allowedOriginsPlaceholder": "https://yourdomain.com", + "gateway.maxMessageChars": "Макс. символов в сообщении", + "gateway.maxMessageCharsTip": "Максимально допустимое количество символов во входящем сообщении. Сообщения, превышающие этот лимит, отклоняются.", + "gateway.rateLimitRpm": "Ограничение частоты (RPM)", + "gateway.rateLimitRpmTip": "Максимальное количество запросов в минуту на пользователя. Установите 0, чтобы отключить ограничение частоты.", + "gateway.inboundDebounceMs": "Дебаунс входящих (мс)", + "gateway.inboundDebounceMsTip": "Задержка в миллисекундах перед обработкой быстрых последующих сообщений из каналов или веб-чата. 0 = без дебаунса; положительные значения объединяют последующие сообщения после этого ожидания.", + "gateway.injectionAction": "Действие при обнаружении инъекции", + + "agents.title": "Настройки агентов по умолчанию", + "agents.description": "LLM-провайдер, модель и настройки среды выполнения по умолчанию, применяемые ко всем агентам, если не переопределены", + "agents.providerTip": "LLM-провайдер по умолчанию, используемый, когда агент его не указывает.", + "agents.modelTip": "Модель по умолчанию, используемая, когда агент её не указывает.", + "agents.maxTokens": "Макс. токенов", + "agents.maxTokensTip": "Максимальное количество токенов, которое модель может сгенерировать в одном ответе.", + "agents.temperature": "Температура", + "agents.temperatureTip": "Температура сэмплирования (0-2). Меньшие значения дают более детерминированный результат.", + "agents.maxToolIterations": "Макс. итераций инструментов", + "agents.maxToolIterationsTip": "Максимальное количество итераций вызова инструментов на один запрос агента.", + "agents.contextWindow": "Контекстное окно", + "agents.contextWindowTip": "Общий бюджет токенов для контекстного окна модели.", + "agents.workspace": "Путь к рабочему пространству", + "agents.workspaceTip": "Корневой каталог для файловых операций агента. Подкаталоги для каждого пользователя создаются автоматически.", + "agents.restrictToWorkspace": "Ограничить рабочим пространством", + "agents.restrictToWorkspaceTip": "Запретить агентам чтение или запись файлов за пределами пути рабочего пространства.", + "agents.intentClassify": "Классификация сообщений занятого агента", + "agents.saveError": "Не удалось сохранить настройки агентов по умолчанию", + + "agents.subagents.title": "Субагенты", + "agents.subagents.desc": "Ограничения на порождение субагентов", + "agents.subagents.maxConcurrent": "Макс. одновременно", + "agents.subagents.maxConcurrentTip": "Максимальное количество субагентов, которые могут работать одновременно.", + "agents.subagents.maxSpawnDepth": "Макс. глубина порождения", + "agents.subagents.maxSpawnDepthTip": "Максимальная глубина вложенности для цепочек субагентов.", + "agents.subagents.maxChildrenPerAgent": "Макс. дочерних на агента", + "agents.subagents.maxChildrenPerAgentTip": "Максимальное количество дочерних субагентов, которое может породить один агент.", + "agents.subagents.archiveAfterMin": "Архивировать через (мин)", + "agents.subagents.archiveAfterMinTip": "Минут бездействия до архивации сессии субагента.", + "agents.subagents.modelOverride": "Переопределение модели", + "agents.subagents.modelOverrideTip": "Заставить всех субагентов использовать конкретную модель. Оставьте пустым, чтобы наследовать от родительского агента.", + "agents.subagents.modelOverridePlaceholder": "(наследовать от агента)", + + "agents.memory.title": "Память", + "agents.memory.desc": "Семантический поиск в памяти и эмбеддинги", + "agents.memory.enabled": "Включена", + "agents.memory.embeddingProvider": "Провайдер эмбеддингов", + "agents.memory.embeddingModel": "Модель эмбеддингов", + "agents.memory.embeddingProviderTip": "Провайдер, используемый для генерации текстовых эмбеддингов для хранения в памяти.", + "agents.memory.embeddingModelTip": "Имя модели эмбеддингов. Оставьте пустым, чтобы использовать значение провайдера по умолчанию.", + "agents.memory.maxResults": "Макс. результатов", + "agents.memory.maxResultsTip": "Максимальное количество записей памяти, возвращаемых на один запрос воспоминания.", + "agents.memory.maxChunkLen": "Макс. длина фрагмента", + "agents.memory.maxChunkLenTip": "Максимальное количество символов во фрагменте памяти перед разбиением.", + "agents.memory.chunkOverlap": "Перекрытие фрагментов", + "agents.memory.chunkOverlapTip": "Символы перекрытия между фрагментами для непрерывности контекста.", + "agents.memory.minScore": "Мин. оценка", + "agents.memory.minScoreTip": "Минимальная оценка сходства (0-1) для включения записи памяти в результаты.", + + "agents.compaction.title": "Уплотнение", + "agents.compaction.desc": "Настройки уплотнения контекстного окна", + "agents.compaction.reserveTokensFloor": "Минимум резервных токенов", + "agents.compaction.reserveTokensFloorTip": "Минимальный буфер токенов, резервируемый для новых ответов перед уплотнением.", + "agents.compaction.maxHistoryShare": "Макс. доля истории (0-1)", + "agents.compaction.maxHistoryShareTip": "Доля контекстного окна, которую может занимать история разговора.", + "agents.compaction.timeoutSeconds": "Тайм-аут (секунды)", + "agents.compaction.timeoutSecondsTip": "Максимальное время ожидания уплотнения перед сохранением исходной истории.", + + "agents.pruning.title": "Сокращение контекста", + "agents.pruning.desc": "Обрезка старых результатов инструментов для освобождения места в контексте", + "agents.pruning.mode": "Режим", + "agents.pruning.keepLastAssistants": "Сохранять последние ответы ассистента", + "agents.pruning.keepLastAssistantsTip": "Количество недавних ходов ассистента, всегда сохраняемых при сокращении.", + + "agents.sandbox.title": "Песочница", + "agents.sandbox.desc": "Docker-песочница для выполнения кода", + "agents.sandbox.mode": "Режим", + "agents.sandbox.image": "Docker-образ", + "agents.sandbox.imageTip": "Docker-образ, используемый для контейнера песочницы.", + "agents.sandbox.memoryMb": "Память (МБ)", + "agents.sandbox.memoryMbTip": "Лимит памяти для контейнера песочницы в мегабайтах.", + "agents.sandbox.cpus": "CPU", + "agents.sandbox.cpusTip": "Квота CPU для контейнера песочницы (напр. 1.0 = одно полное ядро).", + "agents.sandbox.timeoutSec": "Тайм-аут (сек)", + "agents.sandbox.timeoutSecTip": "Максимальное время выполнения в секундах, после которого песочница завершается.", + "agents.sandbox.networkEnabled": "Сеть включена", + + "behavior.uxTitle": "Поведение UX", + "behavior.uxDescription": "Значимые переключатели, изменяющие взаимодействие шлюза с пользователями", + "behavior.intentClassifyHint": "Когда агент уже обрабатывает предыдущий ход, классифицировать новые сообщения как вопросы о статусе, запросы на отмену, инструкции по корректировке или новые запросы.", + "behavior.intentClassifyInfo": "Это применяется только пока сессия занята; это не определяет, требуется ли для запроса командный рабочий процесс.", + "behavior.teamWorkClassify": "Классификация командной работы", + "behavior.teamWorkClassifyHint": "Классифицировать новые запросы как прямую обработку или командный/делегирующий рабочий процесс.", + "behavior.teamWorkClassifyInfo": "Когда запрос превышает область действия текущего агента или должен быть разделён, система использует эмбеддинги как доказательство и просит модель выбрать между прямой обработкой и командным/делегирующим рабочим процессом. Система также проверяет права руководителя/участника; участники могут создавать запросы о помощи только когда команда включила запросы участников и автодиспетчеризацию.", + "behavior.teamWorkClassifyEmbeddingRequired": "Настройте эмбеддинги перед включением классификации командной работы.", + "behavior.chatTitle": "Человекоподобная доставка в каналы", + "behavior.chatDescription": "Подтверждение через sidecar, промежуточный прогресс и безопасное разбиение финального ответа для непотоковых каналов", + "behavior.chatEnabled": "Включить поведение доставки в каналы", + "behavior.chatEnabledHint": "Применяется глобально, с необязательными переопределениями для каждого канала.", + "behavior.quickAck": "Быстрое подтверждение", + "behavior.quickAckHint": "Использовать sidecar-провайдер/модель для короткой естественной квитанции. Текст шаблона отправляется только в режиме фиксированного шаблона.", + "behavior.quickAckPurpose": "Назначение: отправить короткую квитанцию, чтобы пользователи знали, что их сообщение принято, до начала более длительной работы. Это подтверждение, а не рассуждение или прогресс инструментов.", + "behavior.quickAckMode": "Режим", + "behavior.quickAckMode.sidecar_generated": "Сгенерировано sidecar", + "behavior.quickAckMode.llm_generated": "Сгенерировано LLM", + "behavior.quickAckMode.fixed_template": "Фиксированный шаблон", + "behavior.quickAckMode.off": "Выкл", + "behavior.quickAckDelay": "Задержка подтверждения (мс)", + "behavior.quickAckTemplates": "Сообщения фиксированного шаблона", + "behavior.intermediate": "Промежуточные ответы", + "behavior.intermediateHint": "Отправлять естественные обновления о ходе выполнения во время фаз работы инструментов, не добавляя их в историю сессии.", + "behavior.intermediatePurpose": "Назначение: показать пользователям, что работа продвигается во время более длительных запусков с интенсивным использованием инструментов. Это только доставочные sidecar-сообщения, отдельные от «Показывать рассуждения» и не входящие в контекст агента.", + "behavior.intermediateMode": "Режим", + "behavior.intermediateMode.sidecar_generated": "Сгенерировано sidecar", + "behavior.intermediateMode.off": "Выкл", + "behavior.provider": "Провайдер", + "behavior.providerPlaceholder": "Использовать провайдера агента", + "behavior.model": "Модель", + "behavior.modelPlaceholder": "Использовать модель агента", + "behavior.timeoutMs": "Тайм-аут (мс)", + "behavior.maxTokens": "Макс. токенов", + "behavior.maxChars": "Макс. символов", + "behavior.finalSplit": "Разбиение финального ответа", + "behavior.finalSplitHint": "Разбивать длинные финальные ответы на безопасные сообщения по абзацам.", + "behavior.finalSplitMinChars": "Мин. символов", + "behavior.finalSplitMaxMessages": "Макс. сообщений", + "behavior.finalSplitDelay": "Задержка (мс)", + "behavior.preview": "Предпросмотр", + "behavior.previewAck": "Подтверждение", + "behavior.previewGeneratedAck": "Сгенерировано настроенной моделью доставки.", + "behavior.previewNoAck": "Нет подтверждения для этого примера.", + "behavior.previewParts": "{{count}} часть финального сообщения", + "behavior.previewParts_plural": "{{count}} частей финального сообщения", + "behavior.pendingCompactionTitle": "Уплотнение ожидающих сообщений", + "behavior.pendingCompactionDescription": "Автоматически суммировать старые групповые сообщения с помощью LLM, когда буфер превышает порог", + "behavior.pendingCompactionThreshold": "Порог", + "behavior.pendingCompactionThresholdTip": "Запускать уплотнение, когда группа накапливает больше указанного количества ожидающих сообщений. Установите 0, чтобы использовать значение по умолчанию (50).", + "behavior.pendingCompactionKeepRecent": "Сохранять недавние", + "behavior.pendingCompactionKeepRecentTip": "Количество недавних необработанных сообщений, сохраняемых после уплотнения. Более старые сообщения заменяются кратким изложением LLM. По умолчанию 15.", + "behavior.pendingCompactionMaxTokens": "Макс. токенов", + "behavior.pendingCompactionMaxTokensTip": "Максимальное количество выходных токенов для суммирования LLM. По умолчанию 4096.", + "behavior.pendingCompactionProvider": "Провайдер", + "behavior.pendingCompactionProviderTip": "LLM-провайдер для суммирования. Оставьте пустым, чтобы использовать провайдера агента канала.", + "behavior.pendingCompactionProviderPlaceholder": "Использовать провайдера агента", + "behavior.pendingCompactionModel": "Модель", + "behavior.pendingCompactionModelTip": "Модель для суммирования. Оставьте пустым, чтобы использовать модель агента канала.", + "behavior.pendingCompactionModelPlaceholder": "Использовать модель агента", + "behavior.pendingCompactionInfo": "Когда групповой чат превышает порог, LLM автоматически суммирует более старые сообщения в одну компактную запись, сохраняя самые недавние нетронутыми для контекста.", + "behavior.rateLimitTitle": "Ограничение частоты", + "behavior.rateLimitDescription": "Ограничения размера входящих сообщений и троттлинг запросов на пользователя", + "behavior.sessionsTitle": "Область действия сессий", + "behavior.sessionsDescription": "Управляет тем, как контекст разговора разделяется между пользователями и каналами", + "behavior.securityTitle": "Безопасность ввода", + "behavior.securityDescription": "Обнаружение инъекций в промпты и очистка учётных данных для защиты шлюза", + "behavior.injectionActionHint": "Что делать при обнаружении попытки инъекции в промпт во входящем сообщении.", + "behavior.injectionBlockInfo": "Все сообщения, помеченные как попытки инъекции, будут заблокированы, а пользователь будет уведомлён.", + "behavior.injectionOffInfo": "Обнаружение инъекций отключено. Вредоносные промпты не будут отфильтровываться.", + "behavior.scrubCredentialsHint": "Автоматически скрывать API-ключи, токены и пароли, найденные в выводе инструментов, перед возвратом агенту.", + "behavior.scrubCredentialsInfo": "Шаблоны учётных данных в ответах инструментов маскируются до того, как их увидит агент.", + "behavior.scrubCredentialsOffInfo": "Очистка учётных данных отключена. Секреты в выводе инструментов могут быть раскрыты агенту.", + + "sessions.scope": "Область сессии", + "sessions.scopeTip": "Определяет, как разделяются сессии. «На отправителя» даёт каждому пользователю отдельную сессию; «Глобальная» использует одну общую сессию для всех пользователей.", + "sessions.dmScope": "Область ЛС", + "sessions.dmScopeTip": "Как формируются ключи сессий личных сообщений. «На собеседника канала» — рекомендуемое значение по умолчанию.", + + "quota.title": "Квота использования", + "quota.description": "Ограничения квоты сообщений на пользователя с необязательными переопределениями по провайдеру, каналу и группе", + "quota.enabled": "Включить квоты", + "quota.enabledTip": "Когда включено, пользователи ограничены количеством сообщений, указанным ниже.", + "quota.defaultLimits": "Лимиты по умолчанию", + "quota.defaultLimitsTip": "Резервная квота, применяемая ко всем пользователям, не охваченным конкретным переопределением.", + "quota.hour": "В час", + "quota.hourTip": "Максимальное количество сообщений в час.", + "quota.day": "В день", + "quota.dayTip": "Максимальное количество сообщений в день.", + "quota.week": "В неделю", + "quota.weekTip": "Максимальное количество сообщений в неделю.", + "quota.keyLabel": "Ключ", + "quota.addOverride": "Добавить переопределение", + "quota.allOptionsAdded": "Все варианты уже добавлены", + "quota.providerOverrides": "Переопределения провайдеров", + "quota.providerOverridesTip": "Пользовательские лимиты квоты для каждого LLM-провайдера. Переопределяют значение по умолчанию, когда пользователи взаимодействуют через конкретного провайдера.", + "quota.channelOverrides": "Переопределения каналов", + "quota.channelOverridesTip": "Пользовательские лимиты квоты для каждого типа канала (напр. telegram, discord).", + "quota.groupOverrides": "Переопределения групп", + "quota.groupOverridesTip": "Пользовательские лимиты квоты для конкретных групповых чатов.", + "quota.selectProvider": "Выберите провайдера", + "quota.selectChannel": "Выберите канал", + "quota.selectGroup": "Выберите группу", + + "tools.title": "Профиль инструментов", + "tools.description": "Доступность инструментов по умолчанию и почасовое ограничение частоты вызовов для всех агентов", + "tools.profile": "Профиль", + "tools.profileTip": "Предопределённый набор инструментов. «Минимальный» открывает только безопасные инструменты только для чтения; «Полный» включает все инструменты.", + "tools.profilePlaceholder": "Выберите профиль...", + "tools.rateLimitPerHour": "Ограничение частоты (в час)", + "tools.rateLimitPerHourTip": "Максимальное количество вызовов инструментов на агента в час. Установите 0, чтобы отключить.", + "tools.allow": "Разрешить", + "tools.allowTip": "Явно разрешить дополнительные инструменты, не входящие в выбранный профиль.", + "tools.allowPlaceholder": "Выберите инструменты для разрешения...", + "tools.deny": "Запретить", + "tools.denyTip": "Явно заблокировать инструменты, даже если они входят в выбранный профиль.", + "tools.denyPlaceholder": "Выберите инструменты для запрета...", + "tools.alsoAllow": "Также разрешить", + "tools.alsoAllowTip": "Дополнительные инструменты для разрешения поверх профиля. Полезно при использовании ограничительного профиля.", + "tools.alsoAllowPlaceholder": "Выберите дополнительные инструменты...", + "tools.execApproval": "Выполнение команд", + "tools.execSecurity": "Режим безопасности", + "tools.execSecurityTip": "Управляет тем, какие команды оболочки разрешено выполнять агентам. «Список разрешённых» ограничивает утверждёнными шаблонами.", + "tools.execAskMode": "Режим запроса", + "tools.execAskModeTip": "Когда запрашивать у пользователя одобрение перед выполнением команды.", + "tools.execAllowlistLabel": "Разрешённые команды (по одному шаблону в строке)", + "tools.commandKeywordAllowlist": "Список разрешённых ключевых слов команд", + "tools.commandKeywordAllowlistTip": "Правила JSON, разрешающие настроенные ключевые слова только внутри выбранных аргументов содержимого CLI с учётными данными. Правила запрета по пути команды по-прежнему применяются.", + "tools.commandKeywordAllowlistJsonError": "Недопустимый JSON.", + "tools.commandKeywordAllowlistArrayError": "Используйте JSON-массив правил.", + "tools.scrubCredentials": "Очищать учётные данные", + + "browser.title": "Автоматизация браузера", + "browser.description": "Настройки среды выполнения для серверного инструмента браузера", + "browser.enabled": "Включён", + "browser.enabledHint": "Предоставить инструмент браузера агентам.", + "browser.headless": "Без интерфейса", + "browser.headlessHint": "Запускать локальный Chrome без видимого окна.", + "browser.remoteUrl": "URL удалённого CDP", + "browser.remoteUrlTip": "Подключаться к существующей конечной точке Chrome DevTools вместо запуска локального Chrome.", + "browser.maxPages": "Макс. страниц", + "browser.maxPagesTip": "Максимальное количество открытых страниц на изолированную область браузера.", + "browser.actionTimeout": "Тайм-аут действия (мс)", + "browser.actionTimeoutTip": "Максимальное время для одного действия браузера.", + "browser.idleTimeout": "Тайм-аут бездействия (мс)", + "browser.idleTimeoutTip": "Закрывать бездействующие страницы после этого периода. Установите 0, чтобы отключить.", + "browser.cookieSync": "Синхронизация выбранных cookie", + "browser.cookieSyncHint": "Применять явно синхронизированные cookie к соответствующим сессиям браузера пользователя и агента.", + + "tts.title": "Синтез речи", + "tts.configured": "Настроен", + "tts.disabled": "Отключён", + "tts.providerInfo": "Провайдер: {{provider}} — Авто: {{auto}}", + "tts.noProvider": "TTS-провайдер не настроен.", + "tts.manageLink": "Управление настройками TTS", + + "cron.title": "Планировщик cron", + "cron.description": "Поведение повторных попыток для невыполненных запланированных задач", + "cron.maxRetries": "Макс. попыток", + "cron.maxRetriesTip": "Максимальное количество повторных попыток для невыполненной задачи cron.", + "cron.baseDelay": "Базовая задержка", + "cron.baseDelayTip": "Начальная задержка отката между попытками (напр. 2s, 500ms).", + "cron.maxDelay": "Макс. задержка", + "cron.maxDelayTip": "Максимальная задержка отката между попытками (напр. 30s, 5m).", + "cron.defaultTimezone": "Часовой пояс по умолчанию", + "cron.defaultTimezoneTip": "Часовой пояс IANA для cron-выражений, когда он не задан для задачи (напр. Asia/Ho_Chi_Minh). Оставьте пустым, чтобы использовать системный часовой пояс сервера.", + "cron.defaultTimezonePlaceholder": "Системный часовой пояс", + + "telemetry.title": "Телеметрия (OpenTelemetry)", + "telemetry.description": "Экспорт трейсов и метрик в бэкенд, совместимый с OpenTelemetry", + "telemetry.enabled": "Включена", + "telemetry.enabledTip": "Включить экспорт трейсов OpenTelemetry. Требует тег сборки «otel».", + "telemetry.endpoint": "Конечная точка", + "telemetry.endpointTip": "Конечная точка экспортёра OTLP (host:port). Для gRPC по умолчанию 4317, для HTTP — 4318.", + "telemetry.protocol": "Протокол", + "telemetry.protocolTip": "Транспортный протокол для экспортёра OTLP.", + "telemetry.serviceName": "Имя сервиса", + "telemetry.serviceNameTip": "Имя сервиса, указываемое в трейсах. Полезно для фильтрации в бэкенде наблюдаемости.", + "telemetry.insecure": "Небезопасно", + "telemetry.insecureTip": "Отключить TLS для соединения OTLP. Используйте только в локальных/dev-окружениях.", + "telemetry.headers": "Заголовки экспорта", + "telemetry.headersTip": "Дополнительные HTTP-заголовки, отправляемые с каждым запросом экспорта OTLP (напр. API-ключи для размещённых бэкендов).", + "telemetry.headerKeyPlaceholder": "Имя заголовка", + "telemetry.headerValuePlaceholder": "Значение заголовка", + "telemetry.addHeader": "Добавить заголовок", + + "bindings.title": "Привязки агентов", + "bindings.description": "Направлять конкретные каналы, собеседников или аккаунты назначенному агенту", + "bindings.add": "Добавить привязку", + "bindings.noBindings": "Привязки не настроены. Все сообщения направляются агенту по умолчанию.", + "bindings.agentId": "ID агента", + "bindings.channel": "Канал", + "bindings.peerKind": "Тип собеседника", + "bindings.peerId": "ID собеседника", + + "providers.title": "API-ключи провайдеров", + "providers.description": "Переопределить API-ключи и базовые URL для настроенных LLM-провайдеров", + "providers.noProviders": "В файле конфигурации не найдено провайдеров.", + "providers.apiKey": "API-ключ", + "providers.apiBaseUrl": "Базовый URL", + "providers.apiBaseUrlPlaceholder": "https://api.example.com/v1", + "providers.managedVia": "Управляется через переменную окружения {{envKey}}.", + + "channels.title": "Настройки каналов", + "channels.description": "Настройка встроенных интеграций каналов (токены, политики, потоковая передача)", + "channels.noChannels": "В файле конфигурации не найдено каналов.", + "channels.enabled": "Включён", + "channels.token": "Токен / Секрет", + "channels.managedVia": "Управляется через переменную окружения {{envKey}}.", + "channels.dmPolicy": "Политика ЛС", + "channels.groupPolicy": "Политика групп", + "channels.allowFrom": "Разрешить от", + "channels.allowFromPlaceholder": "user_id_1, user_id_2, ...", + "channels.dmStreaming": "Потоковая передача в ЛС", + "channels.dmStreamingHint": "Постепенные обновления сообщений в ЛС", + "channels.groupStreaming": "Потоковая передача в группах", + "channels.groupStreamingHint": "Постепенные обновления сообщений в группах", + "channels.streaming": "Потоковая передача", + "channels.draftTransport": "Предпросмотр черновика", + "channels.draftTransportHint": "Скрытый потоковый предпросмотр в ЛС (без уведомлений на каждое изменение)", + "channels.reasoningDelivery": "Показывать рассуждения", + "channels.reasoningDeliveryHint": "Выберите, как рассуждения модели отображаются в сообщениях канала. «Всегда отдельными сообщениями» работает, даже когда потоковая передача в реальном времени отключена.", + "channels.reasoningStreamingOnly": "Только при потоковой передаче", + "channels.reasoningAlwaysBubbles": "Всегда отдельными сообщениями", + "channels.reasoningOff": "Выкл", + "channels.reactionLevel": "Уровень реакций", + "channels.connectionMode": "Режим подключения", + "channels.reactionOff": "Выкл", + "channels.reactionMinimal": "Минимальный", + "channels.reactionFull": "Полный", + "channels.connectionWebsocket": "WebSocket", + "channels.connectionWebhook": "Webhook", + + "bindings.any": "Любой", + + "tabs.systemMessages": "Системные сообщения", + "systemMessages.title": "Пользовательские системные сообщения", + "systemMessages.description": "Переопределить встроенные сообщения шлюза, отправляемые напрямую каналами", + "systemMessages.message": "Сообщение", + "systemMessages.messageTip": "Ключ встроенного системного сообщения для настройки.", + "systemMessages.locale": "Язык", + "systemMessages.defaultLocale": "Язык доставки по умолчанию", + "systemMessages.defaultLocaleTip": "Язык, используемый, когда канал не предоставляет локаль.", + "systemMessages.localeTip": "Переопределение локали для редактирования. Пустые значения возвращаются к встроенному значению по умолчанию.", + "systemMessages.locale.en": "Английский", + "systemMessages.locale.vi": "Вьетнамский", + "systemMessages.locale.zh": "Китайский", + "systemMessages.locale.ko": "Корейский", + "systemMessages.locale.ru": "Русский", + "systemMessages.override": "Переопределить шаблон", + "systemMessages.overrideTip": "Шаблон, отправляемый вместо встроенного сообщения для выбранной локали. Переменные используют {{name}}.", + "systemMessages.defaultTemplate": "Шаблон по умолчанию", + "systemMessages.defaultTemplateTip": "Встроенный резервный шаблон, используемый, когда переопределение пусто.", + "systemMessages.useDefault": "Использовать по умолчанию", + + "toast": { + "saved": "Конфигурация сохранена", + "saveFailed": "Не удалось сохранить конфигурацию" + } +} diff --git a/ui/web/src/i18n/locales/ru/contacts.json b/ui/web/src/i18n/locales/ru/contacts.json new file mode 100644 index 00000000..c40bc399 --- /dev/null +++ b/ui/web/src/i18n/locales/ru/contacts.json @@ -0,0 +1,59 @@ +{ + "title": "Контакты", + "description": "Автоматически собранные контакты из всех взаимодействий по каналам.", + "searchPlaceholder": "Поиск по имени, имени пользователя или ID...", + "filter": "Фильтр", + "emptyTitle": "Нет контактов", + "emptyDescription": "Контакты появятся здесь по мере того, как пользователи будут взаимодействовать с вашими агентами через каналы.", + "noMatchTitle": "Нет подходящих контактов", + "noMatchDescription": "Попробуйте другой поисковый запрос или фильтр.", + "columns": { + "name": "Имя", + "username": "Имя пользователя", + "senderId": "ID отправителя", + "channelType": "Канал", + "peerKind": "Тип", + "lastSeen": "Последняя активность", + "merged": "Объединён" + }, + "permissionsNote": { + "title": "Разрешения каналов для имён контактов", + "telegram": "Telegram — имена и имена пользователей доступны автоматически.", + "discord": "Discord — отображаемые имена и имена пользователей доступны автоматически.", + "zalo": "Zalo — отображаемые имена доступны автоматически.", + "slack": "Slack — отображаемые имена определяются через Slack API (бот должен быть в рабочем пространстве).", + "feishu": "Feishu/Lark — требуется разрешение contact:user.base:readonly. Без него имена будут пустыми.", + "bitrix24": "Bitrix24 — требуется область `user` + один вызов `user.get` на каждого нового отправителя. Имена могут быть пустыми недолго при первом сообщении." + }, + "selectedCount": "Выбрано: {{count}}", + "merge": { + "button": "Объединить", + "unmergeButton": "Разъединить", + "dialogTitle": "Объединить контакты", + "dialogDescription": "Свяжите выбранные контакты с личностью пользователя арендатора. Это определяет их как одного человека во всех каналах.", + "linkExisting": "Связать с существующим пользователем", + "createNew": "Создать нового пользователя", + "selectUser": "Выберите пользователя арендатора...", + "displayName": "Отображаемое имя", + "displayNamePlaceholder": "напр. Иван Иванов", + "userId": "ID пользователя", + "userIdPlaceholder": "Уникальный идентификатор", + "confirm": "Объединить", + "cancel": "Отмена", + "success": "Контакты успешно объединены", + "unmergeSuccess": "Контакты успешно разъединены", + "error": "Не удалось объединить контакты", + "unmergeError": "Не удалось разъединить контакты", + "comingSoon": "Эта функция в разработке и пока недоступна." + }, + "types": { + "user": "Пользователь", + "group": "Группа" + }, + "filters": { + "allChannels": "Все каналы", + "allTypes": "Все типы", + "direct": "Личные", + "group": "Групповые" + } +} diff --git a/ui/web/src/i18n/locales/ru/cron.json b/ui/web/src/i18n/locales/ru/cron.json new file mode 100644 index 00000000..082be901 --- /dev/null +++ b/ui/web/src/i18n/locales/ru/cron.json @@ -0,0 +1,173 @@ +{ + "title": "Cron", + "description": "Планирование повторяющихся задач агента", + "newJob": "Новая задача", + "emptyTitle": "Нет cron-задач", + "emptyDescription": "Создайте cron-задачу для планирования повторяющихся задач агента.", + "columns": { + "enabled": "Включено", + "name": "Имя", + "schedule": "Расписание", + "message": "Сообщение", + "agent": "Агент", + "actions": "Действия" + }, + "runNow": "Запустить сейчас", + "running": "Выполняется...", + "runHistory": "История запусков", + "defaultAgent": "по умолчанию", + "searchPlaceholder": "Поиск cron-задач...", + "noMatchTitle": "Нет подходящих задач", + "noMatchDescription": "Попробуйте другой поисковый запрос.", + "card": { + "nextRun": "Далее: {{time}}", + "noNextRun": "Нет следующего запуска", + "defaultAgent": "Агент по умолчанию" + }, + "create": { + "title": "Создать cron-задачу", + "name": "Имя", + "namePlaceholder": "my-daily-task", + "nameHint": "Только строчные буквы, цифры и дефисы", + "agentId": "ID агента (необязательно)", + "agentIdPlaceholder": "по умолчанию", + "scheduleType": "Тип расписания", + "every": "Каждые", + "cron": "Cron", + "once": "Один раз", + "intervalSeconds": "Интервал (секунды)", + "cronExpression": "Cron-выражение", + "cronHint": "Стандартный cron из 5 полей: мин час день месяц день_недели", + "onceDesc": "Задача выполнится один раз, примерно через 1 минуту.", + "message": "Сообщение", + "messagePlaceholder": "Что должен сделать агент?", + "cancel": "Отмена", + "create": "Создать", + "creating": "Создание...", + "payloadType": "Тип полезной нагрузки" + }, + "enable": { + "title": "Включить cron-задачу", + "description": "Включить \"{{name}}\"? Она начнёт выполняться по расписанию.", + "confirmLabel": "Включить" + }, + "disable": { + "title": "Отключить cron-задачу", + "description": "Отключить \"{{name}}\"? Она перестанет выполняться до повторного включения.", + "confirmLabel": "Отключить" + }, + "delete": { + "title": "Удалить cron-задачу", + "description": "Удалить \"{{name}}\"? Это действие нельзя отменить.", + "confirmLabel": "Удалить" + }, + "runLog": { + "title": "Журнал запусков: {{name}}", + "noHistory": "Истории запусков пока нет.", + "loading": "Загрузка..." + }, + "detail": { + "runNow": "Запустить сейчас", + "running": "Выполняется...", + "disable": "Отключить", + "enable": "Включить", + "delete": "Удалить", + "enabled": "включено", + "disabled": "отключено", + "runningStatus": "выполняется...", + "payload": "Полезная нагрузка", + "empty": "(пусто)", + "deliverDirect": "Доставка: напрямую", + "channel": "Канал: {{name}}", + "to": "Кому: {{name}}", + "edit": "Редактировать", + "preview": "Предпросмотр", + "noMessage": "Сообщение не настроено", + "lastError": "Последняя ошибка", + "runHistory": "История запусков", + "refresh": "Обновить", + "noHistory": "Истории запусков пока нет.", + "inOut": "{{input}} вход / {{output}} выход", + "infoRows": { + "schedule": "Расписание", + "timezone": "Часовой пояс", + "nextRun": "Следующий запуск", + "lastRun": "Последний запуск", + "lastStatus": "Последний статус", + "created": "Создано", + "updated": "Обновлено", + "autoDelete": "Автоудаление", + "autoDeleteValue": "Да (однократно)" + }, + "tabs": { + "overview": "Обзор", + "history": "История запусков" + }, + "advanced": "Расширенные", + "saveFailed": "Не удалось сохранить", + "schedule": "Расписание", + "scheduleDesc": "Настройка времени", + "messageSection": "Сообщение", + "messageSectionDesc": "Что должен сделать агент", + "agentStatus": "Агент и статус", + "agentStatusDesc": "Назначение агента и статус задачи", + "info": "Информация", + "infoDesc": "Метаданные задачи", + "scheduling": "Планирование", + "schedulingDesc": "Настройка часового пояса", + "delivery": "Доставка", + "deliveryDesc": "Направить вывод задачи в канал или пользователю", + "deliverToChannel": "Доставить в канал", + "channelLabel": "Канал", + "channelPlaceholder": "#general", + "toLabel": "Кому (пользователь)", + "toPlaceholder": "user-id", + "wakeHeartbeat": "Активировать heartbeat при запуске", + "wakeHeartbeatDesc": "Запускать heartbeat агента при выполнении задачи", + "lifecycle": "Жизненный цикл", + "lifecycleDesc": "Настройки жизненного цикла задачи", + "deleteAfterRun": "Удалить после выполнения", + "deleteAfterRunDesc": "Автоматически удалить эту задачу после первого успешного выполнения", + "timezone": "Часовой пояс", + "timezoneDesc": "Часовой пояс для cron-выражений", + "commandSection": "Команда", + "commandArgv": "Команда", + "noCommand": "Команда не настроена", + "commandCwd": "Рабочий каталог", + "defaultWorkingDirectory": "По умолчанию (шлюз)", + "commandTimeout": "Тайм-аут", + "commandOutputLimit": "Лимит вывода", + "commandEnv": "Окружение", + "commandInput": "Ввод", + "commandNoOutputTimeout": "Тайм-аут отсутствия вывода", + "seconds": "{{count}} с", + "bytes": "{{count}} байт", + "defaultValue": "По умолчанию", + "none": "Нет", + "envVars": "{{count}} перем.", + "commandJson": "Команда JSON", + "commandJsonHelp": "Отредактируйте полезную нагрузку команды в формате JSON. Поле argv обязательно и должно быть непустым массивом строк.", + "invalidCommandJson": "Некорректный JSON команды: argv должен быть непустым массивом.", + "commandArgvHelp": "По одному аргументу в строке. Используйте sh / -c / ваш скрипт для синтаксиса оболочки." + }, + "stateless": "Без состояния", + "statelessHelp": "Пропускать сохранение сессии. Каждый запуск начинается с чистого листа без загрузки предыдущих сообщений. Экономит токены для cron-задач, которым не нужен прошлый контекст.", + "toast": { + "created": "Cron-задача создана", + "createdDesc": "{{name}} добавлена", + "enabled": "Cron-задача включена", + "disabled": "Cron-задача отключена", + "deleted": "Cron-задача удалена", + "triggered": "Cron-задача запущена", + "failedCreate": "Не удалось создать cron-задачу", + "failedToggle": "Не удалось переключить cron-задачу", + "failedDelete": "Не удалось удалить cron-задачу", + "failedRun": "Не удалось запустить cron-задачу", + "updated": "Задача обновлена", + "failedUpdate": "Не удалось обновить задачу" + }, + "payload": { + "agent": "Агент", + "command": "Команда" + } +} diff --git a/ui/web/src/i18n/locales/ru/events.json b/ui/web/src/i18n/locales/ru/events.json new file mode 100644 index 00000000..2cb82f5d --- /dev/null +++ b/ui/web/src/i18n/locales/ru/events.json @@ -0,0 +1,36 @@ +{ + "title": "События в реальном времени", + "description": "События WebSocket в реальном времени по всем командам и агентам", + "live": "В реальном времени", + "paused": "Приостановлено", + "resume": "Возобновить", + "pause": "Пауза", + "clear": "Очистить", + "scrollToBottom": "Прокрутить вниз", + "eventCount": "{{count}} событие", + "eventCountPlural": "{{count}} событий", + "categories": { + "all": "Все", + "task": "Задача", + "message": "Сообщение", + "agent": "Агент", + "teamCrud": "CRUD команды", + "agentLink": "Связь агента" + }, + "filters": { + "allTeams": "Все команды", + "allUsers": "Все пользователи", + "allChats": "Все чаты" + }, + "global": "Глобально", + "emptyTitle": "Пока нет событий", + "emptyPaused": "Захват событий приостановлен. Возобновите, чтобы увидеть новые события.", + "emptyWaiting": "Ожидание событий в реальном времени... События появятся по мере работы агентов.", + "detail": { + "copy": "Копировать", + "copied": "Скопировано" + }, + "toolBadge": { "error": "ошибка", "activated": "активировано", "ok": "ок" }, + "moreArgs": "...ещё {{count}}", + "parentLabel": "родитель:" +} diff --git a/ui/web/src/i18n/locales/ru/hooks.json b/ui/web/src/i18n/locales/ru/hooks.json new file mode 100644 index 00000000..ba2dd2f6 --- /dev/null +++ b/ui/web/src/i18n/locales/ru/hooks.json @@ -0,0 +1,149 @@ +{ + "title": "Хуки агента", + "subtitle": "Хуки жизненного цикла: блокировка, наблюдение, внедрение контекста между событиями агента", + "empty": "Хуки пока не настроены", + "nav": "Хуки", + "beta": { + "title": "Хуки (Бета)", + "badge": "Бета", + "description": "Хуки позволяют перехватывать события жизненного цикла агента, чтобы разрешать, блокировать или переписывать происходящее. Сейчас в бета-версии — ожидайте шероховатостей и следите за журналом аудита.", + "howItWorksTitle1": "1. Выберите событие", + "howItWorksBody1": "Каждый хук срабатывает на одном событии, таком как user_prompt_submit, pre_tool_use или stop. Блокирующие события ждут вашего решения перед продолжением.", + "howItWorksTitle2": "2. Выберите обработчик", + "howItWorksBody2": "Script выполняет JavaScript в песочнице. HTTP отправляет запрос на ваш вебхук. Prompt спрашивает LLM. Встроенные хуки (значок «Система») поставляются с GoClaw, и в них можно менять только переключатель включения.", + "howItWorksTitle3": "3. Протестируйте перед включением", + "howItWorksBody3": "Используйте «Запустить хук» с образцом события, чтобы просмотреть решение, длительность и любые изменения ввода — ничто не затрагивает реальную сессию до включения.", + "collapse": "Скрыть", + "learnMore": "Как хуки работают с навыками и MCP", + "modalTitle": "Хуки + Навыки + MCP", + "modalIntro": "Хуки интегрируются с системой навыков GoClaw и серверами MCP (Model Context Protocol), давая вам гранулярный контроль над конвейером агента.", + "modalSkillTitle": "Хуки × Навыки", + "modalSkillBody": "Когда вызывается навык, агент генерирует события pre_tool_use и post_tool_use. Хук на pre_tool_use может проверить имя навыка и аргументы, затем разрешить, заблокировать или переписать их перед выполнением. Хуки post_tool наблюдают за результатами для логирования или цепочек.", + "modalMcpTitle": "Хуки × Серверы MCP", + "modalMcpBody": "Инструменты MCP, зарегистрированные с внешних серверов, отображаются как обычные инструменты в конвейере агента. Хуки срабатывают на тех же событиях pre_tool_use / post_tool_use — специальная настройка не нужна. Используйте regex-матчер для нацеливания на конкретные имена инструментов MCP (напр. mcp__.*).", + "modalBuiltinTitle": "Встроенные хуки (Система)", + "modalBuiltinBody": "Встроенные хуки, такие как PII Redactor, выполняются как скрипты в песочнице с повышенными привилегиями: они могут переписывать ввод пользователя (rawInput) и аргументы инструментов до того, как агент их увидит. Пользовательские скрипты не могут — это обеспечивается на уровне диспетчера.", + "modalClose": "Понятно" + }, + "actions": { + "create": "Создать хук", + "edit": "Изменить", + "delete": "Удалить", + "test": "Тест", + "toggle": "Включить/Отключить" + }, + "filters": { + "event": "Событие", + "type": "Обработчик", + "scope": "Область", + "enabled": "Статус", + "all": "Все" + }, + "table": { + "name": "Имя", + "event": "Событие", + "type": "Обработчик", + "scope": "Область", + "matcher": "Матчер", + "enabled": "Включено", + "actions": "Действия", + "systemBadge": "Система" + }, + "form": { + "title_create": "Создать хук", + "title_edit": "Изменить хук", + "name": "Имя", + "namePlaceholder": "напр. Блокировать конфиденциальные инструменты", + "agents": "Агенты", + "agentsHint": "Выберите, к каким агентам применяется этот хук", + "event": "Событие", + "handlerType": "Тип обработчика", + "scope": "Область", + "matcher": "Матчер (regex)", + "matcherHint": "Применяется к имени инструмента перед срабатыванием (необязательно, кроме prompt)", + "ifExpr": "Условие (CEL)", + "ifExprHint": "Вычисляется по tool_input; синтаксис cel-go", + "timeout": "Тайм-аут (мс)", + "onTimeout": "При тайм-ауте", + "priority": "Приоритет", + "enabled": "Включено", + "url": "URL", + "method": "HTTP-метод", + "headers": "Заголовки", + "bodyTemplate": "Шаблон тела", + "promptTemplate": "Шаблон промпта", + "model": "Модель", + "maxInvocationsPerTurn": "Макс. вызовов за ход", + "scriptSource": "Исходный код скрипта (ES5.1 JavaScript)", + "scriptSchemaHelp": "Экспортируйте функцию handle(event), возвращающую {decision: \"allow\"|\"block\", reason?: string, updatedInput?: object}. Лимит 32 КиБ.", + "compileError": "Синтаксическая ошибка в строке {{line}}, столбец {{col}}", + "builtinReadonly": "Встроенный хук — можно изменить только переключатель включения.", + "saveToggle": "Сохранить переключатель", + "save": "Сохранить", + "cancel": "Отмена" + }, + "tabs": { + "overview": "Обзор", + "config": "Конфигурация", + "test": "Тест", + "history": "История" + }, + "test": { + "title": "Тестировать хук", + "sampleEvent": "Образец события", + "sampleEventHint": "Составьте фиктивное событие, чтобы запустить хук против него — ничто здесь не затрагивает реальную сессию.", + "toolName": "Имя инструмента", + "toolNameHint": "Используется матчерами pre/post_tool_use (напр. bash, edit, web_search).", + "toolInput": "Ввод инструмента (JSON)", + "toolInputHint": "Аргументы, передаваемые инструменту. Должны быть корректным JSON.", + "rawInput": "Сырое сообщение пользователя", + "rawInputPlaceholder": "Необязательно — текст запроса пользователя", + "rawInputHint": "Используется хуками user_prompt_submit. Оставьте пустым для событий инструментов.", + "parseError": "Ввод инструмента должен быть корректным JSON", + "resultTitle": "Результат", + "resultHint": "Что вернул диспетчер, включая любые изменения ввода от встроенных хуков.", + "emptyState": "Запустите хук, чтобы увидеть, как он ответит.", + "fire": "Запустить хук", + "firing": "Выполнение…", + "decision": "Решение", + "duration": "Длительность", + "reason": "Причина", + "stdout": "Stdout", + "stderr": "Stderr", + "statusCode": "Код статуса", + "updatedInput": "Обновлённый ввод (diff)", + "toolNamePickerPlaceholder": "Выберите или введите имя инструмента...", + "overwriteConfirm": "Текущий ввод будет заменён шаблоном. Продолжить?" + }, + "history": { + "title": "История выполнения", + "empty": "Выполнений пока нет", + "phaseFourNote": "Пагинация истории появится в фазе 4", + "decision": "Решение", + "duration": "Длительность", + "error": "Ошибка", + "timestamp": "Когда" + }, + "toast": { + "created": "Хук создан", + "updated": "Хук обновлён", + "deleted": "Хук удалён", + "toggled": "Хук переключён", + "failedCreate": "Не удалось создать хук", + "failedUpdate": "Не удалось обновить хук", + "failedDelete": "Не удалось удалить хук", + "failedTest": "Тест не удался" + }, + "validation": { + "promptRequiresMatcher": "Хуки prompt требуют матчер или if_expr", + "invalidRegex": "Недопустимый regex", + "promptTemplateRequired": "Шаблон промпта обязателен", + "scriptSourceRequired": "Исходный код скрипта обязателен" + }, + "decision": { + "allow": "Разрешить", + "block": "Блокировать", + "error": "Ошибка", + "timeout": "Тайм-аут" + } +} diff --git a/ui/web/src/i18n/locales/ru/import-export.json b/ui/web/src/i18n/locales/ru/import-export.json new file mode 100644 index 00000000..3acff758 --- /dev/null +++ b/ui/web/src/i18n/locales/ru/import-export.json @@ -0,0 +1,131 @@ +{ + "title": "Импорт и экспорт", + "description": "Экспортируйте агентов в переносимый архив или импортируйте из него.", + "beta": "Бета", + "betaWarning": "Импорт и экспорт находятся в бета-версии. Всегда создавайте резервную копию перед импортом в существующего агента.", + "tabs": { + "teams": "Команды", + "agents": "Агенты", + "skillsMcp": "Навыки и MCP", + "export": "Экспорт", + "import": "Импорт" + }, + "comingSoon": { + "title": "Скоро", + "description": "Этот раздел пока недоступен." + }, + "export": { + "title": "Экспорт агента", + "agent": "Агент", + "agentPlaceholder": "Выберите агента", + "sections": "Разделы", + "preset": "Пресет", + "presetsLabel": "Пресеты", + "infoNote": "Используйте вкладку «Команды», чтобы экспортировать агентов вместе с их командой.", + "startExport": "Экспорт", + "exporting": "Экспорт...", + "preview": "Предпросмотр", + "previewLoading": "Загрузка предпросмотра...", + "download": "Скачать", + "done": "Экспорт завершён", + "errorTitle": "Экспорт не удался", + "selectAgent": "Выберите агента для экспорта.", + "noAgents": "Нет доступных агентов." + }, + "import": { + "title": "Импорт агента", + "mode": "Режим", + "modeNew": "Создать нового агента", + "modeMerge": "Объединить с существующим", + "agentKey": "Ключ агента", + "agentKeyPlaceholder": "напр. my-agent", + "agentKeyHint": "Строчные буквы, цифры, дефисы. Оставьте пустым, чтобы использовать ключ из архива.", + "targetAgent": "Целевой агент", + "targetAgentPlaceholder": "Выберите целевого агента", + "file": "Файл архива (.tar.gz)", + "filePlaceholder": "Нажмите для выбора или перетащите", + "sections": "Разделы для объединения", + "startImport": "Импорт", + "importing": "Импорт...", + "done": "Импорт завершён", + "agentCreated": "Агент создан: {{key}}", + "mergedInto": "Объединено с: {{key}}", + "errorTitle": "Импорт не удался", + "manifestLabel": "Информация об архиве", + "agentFrom": "Агент из архива: {{key}}", + "exportedAt": "Экспортировано: {{date}}" + }, + "sections": { + "config": "Конфигурация", + "configDesc": "Конфигурация агента (модель, инструменты и т. д.)", + "context_files": "Файлы контекста", + "context_filesDesc": "Файлы контекста уровня агента (SOUL.md, IDENTITY.md и т. д.)", + "user_data": "Данные пользователей", + "user_dataDesc": "Все данные по каждому пользователю (файлы контекста, профили, переопределения)", + "user_context_files": "Файлы контекста пользователей", + "user_context_filesDesc": "Файлы контекста по каждому пользователю (USER.md на пользователя)", + "user_profiles": "Профили пользователей", + "user_profilesDesc": "Записи профилей рабочего пространства пользователей", + "user_overrides": "Переопределения пользователей", + "user_overridesDesc": "Переопределения модели/провайдера по каждому пользователю", + "memory": "Память", + "memoryDesc": "Все документы памяти (глобальные + по каждому пользователю)", + "memory_global": "Глобальная память", + "memory_globalDesc": "Общие документы памяти", + "memory_per_user": "Память по пользователям", + "memory_per_userDesc": "Индивидуальные документы памяти пользователей", + "knowledge_graph": "Граф знаний", + "knowledge_graphDesc": "Сущности и связи KG", + "cron": "Задачи cron", + "cronDesc": "Запланированные задачи (импортируются отключёнными)", + "workspace": "Файлы рабочего пространства", + "workspaceDesc": "Файлы каталога рабочего пространства агента", + "sessions": "Сессии", + "sessionsDesc": "Сессии чата и сообщения", + "media": "Медиа", + "mediaDesc": "Загруженные медиафайлы", + "comingSoon": "Скоро" + }, + "presets": { + "minimal": "Минимальный", + "standard": "Стандартный", + "complete": "Полный" + }, + "teamExportNote": "Экспорт команды включает всех агентов-участников и их данные (файлы контекста, память, граф знаний, cron, рабочее пространство).", + "teamSelectPlaceholder": "Выберите команду...", + "teamDropHere": "Перетащите файл архива команды сюда или нажмите для выбора", + "teamDropFormats": "Поддерживаются файлы .tar.gz", + "teamDontClose": "Не закрывайте эту страницу — идёт импорт", + "teamImportAnother": "Импортировать ещё", + "teamTryAgain": "Повторить", + "teamChangeFile": "Изменить файл", + "teamPreview": { + "members": "{{count}} участников", + "agents": "{{count}} агентов", + "tasks": "{{count}} задач", + "links": "{{count}} связей", + "loading": "Загрузка предпросмотра...", + "error": "Не удалось загрузить предпросмотр" + }, + "skillsMcp": { + "skillsTab": "Навыки", + "mcpTab": "Серверы MCP", + "skillsNote": "Экспортируются только пользовательские навыки (не системные). Системные навыки не включаются.", + "mcpNote": "Конфигурации серверов MCP экспортируются без ключей API, переменных окружения и заголовков. Вам потребуется заново ввести секреты после импорта.", + "customSkills": "{{count}} пользовательских навыков", + "grants": "{{count}} разрешений агентов", + "servers": "{{count}} серверов", + "noSkills": "Нет пользовательских навыков для экспорта.", + "noServers": "Нет серверов MCP для экспорта.", + "exportSkills": "Экспортировать навыки", + "exportMcp": "Экспортировать серверы MCP", + "importSkills": "Импортировать навыки", + "importMcp": "Импортировать серверы MCP", + "importedSkills": "Навыки импортированы", + "skippedSkills": "Навыки пропущены (уже существуют)", + "importedServers": "Серверы импортированы", + "skippedServers": "Серверы пропущены (уже существуют)", + "dropSkills": "Перетащите файл архива навыков сюда или нажмите для выбора", + "dropMcp": "Перетащите файл архива MCP сюда или нажмите для выбора" + } +} diff --git a/ui/web/src/i18n/locales/ru/login.json b/ui/web/src/i18n/locales/ru/login.json new file mode 100644 index 00000000..1b6d29da --- /dev/null +++ b/ui/web/src/i18n/locales/ru/login.json @@ -0,0 +1,41 @@ +{ + "subtitle": "Войдите в панель управления шлюзом", + "selectTenant": "Выберите арендатора", + "selectTenantDescription": "Выберите арендатора, чтобы продолжить", + "allTenantsOption": "Все арендаторы", + "allTenantsDescription": "Просмотр данных всех арендаторов", + "noAccess": "Нет доступа", + "noAccessDescription": "У вас нет доступа ни к одному арендатору. Обратитесь к администратору.", + "tabs": { + "token": "Токен", + "pairing": "Привязка" + }, + "token": { + "userId": "ID пользователя", + "userIdPlaceholder": "ваш-user-id", + "userIdHint": "Используйте \"system\" для полного доступа к системе", + "gatewayToken": "Токен шлюза", + "tokenPlaceholder": "Bearer-токен", + "connect": "Подключиться", + "connecting": "Подключение...", + "errorInvalidCredentials": "Неверные учётные данные. Проверьте токен и ID пользователя.", + "errorServer": "Сервер вернул {{status}}. Пожалуйста, попробуйте снова.", + "errorCannotConnect": "Не удаётся подключиться к шлюзу. Проверьте, запущен ли сервер." + }, + "pairing": { + "userId": "ID пользователя", + "userIdPlaceholder": "ваш-user-id", + "noTokenNeeded": "Токен не требуется. Будет сгенерирован код привязки для одобрения администратором.", + "requestAccess": "Запросить доступ", + "connecting": "Подключение...", + "errorConnectionLost": "Соединение потеряно. Пожалуйста, попробуйте снова.", + "errorCannotConnect": "Не удалось подключиться к шлюзу.", + "askAdmin": "Попросите администратора одобрить этот код:", + "orRun": "Или выполните:", + "waitingForApproval": "Ожидание одобрения...", + "cancel": "Отмена", + "approved": "Доступ одобрен! Перенаправление..." + }, + "noAccessHint": "Попросите администратора добавить вас к арендатору или используйте другую учётную запись.", + "logout": "Выйти" +} diff --git a/ui/web/src/i18n/locales/ru/logs.json b/ui/web/src/i18n/locales/ru/logs.json new file mode 100644 index 00000000..c515adc1 --- /dev/null +++ b/ui/web/src/i18n/locales/ru/logs.json @@ -0,0 +1,15 @@ +{ + "title": "Журналы", + "description": "Отслеживание журналов в реальном времени", + "descriptionLive": "Отслеживание в реальном времени на уровне {{level}}", + "live": "В реальном времени", + "start": "Запустить", + "stop": "Остановить", + "clear": "Очистить", + "filterPlaceholder": "Фильтровать журналы...", + "logLevelTitle": "Уровень журнала", + "emptyWaiting": "Ожидание журналов...", + "emptyFilter": "Нет журналов, соответствующих фильтру.", + "emptyStart": "Нажмите «Запустить», чтобы начать потоковую передачу журналов.", + "count": "{{filtered}}/{{total}}" +} diff --git a/ui/web/src/i18n/locales/ru/mcp.json b/ui/web/src/i18n/locales/ru/mcp.json new file mode 100644 index 00000000..51f34602 --- /dev/null +++ b/ui/web/src/i18n/locales/ru/mcp.json @@ -0,0 +1,174 @@ +{ + "title": "MCP-серверы", + "description": "Управление подключениями к серверам Model Context Protocol", + "addServer": "Добавить сервер", + "searchPlaceholder": "Поиск серверов...", + "emptyTitle": "Нет MCP-серверов", + "emptyDescription": "Добавьте свой первый MCP-сервер, чтобы начать.", + "noMatchTitle": "Нет подходящих серверов", + "noMatchDescription": "Попробуйте другой поисковый запрос.", + "columns": { + "name": "Имя", + "transport": "Транспорт", + "tools": "Инструменты", + "agents": "Агенты", + "enabled": "Включено", + "createdBy": "Создал", + "actions": "Действия" + }, + "viewTools": "Просмотр инструментов", + "manageGrants": "Управление доступом", + "reconnect": "Переподключить", + "delete": { + "title": "Удалить MCP-сервер", + "description": "Вы уверены, что хотите удалить \"{{name}}\"? Это также удалит все связанные права доступа.", + "confirmLabel": "Удалить" + }, + "form": { + "createTitle": "Добавить MCP-сервер", + "editTitle": "Редактировать MCP-сервер", + "name": "Имя *", + "nameHint": "Только строчные буквы, цифры и дефисы", + "displayName": "Отображаемое имя", + "displayNamePlaceholder": "Мой MCP-сервер", + "transport": "Транспорт *", + "command": "Команда *", + "args": "Аргументы (через пробел)", + "argsPlaceholder": "--flag1 --flag2=\"значение с пробелами\"", + "url": "URL *", + "headers": "Заголовки", + "headerKeyPlaceholder": "Имя заголовка", + "headerValuePlaceholder": "Значение заголовка", + "addHeader": "Добавить заголовок", + "env": "Переменные окружения", + "envKeyPlaceholder": "Имя переменной", + "envValuePlaceholder": "Значение", + "addVariable": "Добавить переменную", + "toolPrefix": "Префикс инструмента", + "toolPrefixHint": "Автоматически выводится из имени, если не задан.", + "timeout": "Тайм-аут (секунды)", + "enabled": "Включено", + "testConnection": "Проверить подключение", + "testing": "Проверка...", + "toolsFound": "Найдено инструментов: {{count}}", + "cancel": "Отмена", + "create": "Создать", + "update": "Обновить", + "saving": "Сохранение...", + "errors": { + "nameRequired": "Имя и транспорт обязательны", + "nameSlug": "Имя должно быть корректным слагом (только строчные буквы, цифры и дефисы)", + "commandRequired": "Команда обязательна для транспорта stdio", + "urlRequired": "URL обязателен для транспорта SSE/HTTP", + "connectionFailed": "Подключение не удалось" + }, + "requireUserCredentials": "Требовать учётные данные пользователя", + "requireUserCredentialsHint": "Каждый пользователь должен настроить свои учётные данные. Сервер отключён для пользователей без личных учётных данных.", + "toolHints": "Подсказки для ИИ-агента (необязательно)", + "toolHintsHint": "Заметки, написанные администратором, добавляются к описанию каждого инструмента, чтобы LLM знала об особенностях сервера.", + "toolHintsGlobal": "Общая подсказка", + "toolHintsGlobalPlaceholder": "напр. Все инструменты ожидают JS-выражения. Не добавляйте точку с запятой в конце.", + "toolHintsGlobalHint": "Применяется к каждому инструменту этого сервера.", + "toolHintsPerTool": "Подсказки по инструментам", + "toolHintsToolNamePlaceholder": "Имя инструмента (напр. search)", + "toolHintsToolValuePlaceholder": "Текст подсказки, показываемый LLM", + "toolHintsAdd": "Добавить подсказку", + "oauth": { + "title": "Аутентификация OAuth", + "titleHint": "Подключение к MCP-серверам, требующим авторизации OAuth 2.1.", + "enable": "Включить OAuth", + "useDcr": "Динамическая регистрация клиента (DCR)", + "useDcrHint": "Автоматически регистрироваться как OAuth-клиент. Отключите, чтобы ввести учётные данные вручную.", + "grantType": "Тип разрешения", + "pkce": "Authorization Code + PKCE (публичный клиент)", + "authCode": "Authorization Code (конфиденциальный клиент)", + "clientCreds": "Client Credentials", + "authEndpoint": "URL авторизации", + "authEndpointPlaceholder": "https://auth.example.com/oauth/authorize", + "tokenEndpoint": "URL токена", + "tokenEndpointPlaceholder": "https://auth.example.com/oauth/token", + "clientId": "Client ID", + "clientSecret": "Client Secret", + "clientSecretPlaceholder": "Оставьте пустым, чтобы сохранить текущее", + "scope": "Области доступа", + "scopePlaceholder": "напр. read write offline_access", + "modePerUser": "Режим аутентификации: для каждого пользователя (каждый должен авторизоваться отдельно)", + "modeGlobal": "Режим аутентификации: глобальный (общий токен для всех пользователей)", + "authorize": "Авторизовать", + "reauthorize": "Повторно авторизовать", + "revoke": "Отозвать", + "authorized": "Авторизовано", + "notAuthorized": "Не авторизовано", + "expired": "Токен истёк", + "authorizing": "Открытие авторизации...", + "authSuccess": "Авторизация выполнена", + "authFailed": "Авторизация не удалась", + "savingFirst": "Сохранение сервера перед авторизацией..." + } + }, + "grants": { + "title": "Доступ агентов — {{name}}", + "currentGrants": "Текущие права доступа", + "allToolsAllowed": "Разрешены все инструменты", + "addGrant": "Добавить доступ агенту", + "editGrant": "Редактировать доступ", + "selectAgent": "Выберите агента...", + "toolAllowList": "Список разрешённых инструментов (необязательно)", + "toolDenyList": "Список запрещённых инструментов (необязательно)", + "allowPlaceholder": "Выберите инструменты для разрешения...", + "denyPlaceholder": "Выберите инструменты для запрета...", + "grant": "Предоставить", + "update": "Обновить", + "cancel": "Отмена", + "agentRequired": "Агент обязателен", + "failedGrant": "Не удалось предоставить доступ", + "failedRevoke": "Не удалось отозвать доступ" + }, + "tools": { + "title": "Инструменты — {{name}}", + "prefix": "префикс:", + "discovering": "Обнаружение инструментов...", + "noToolsTitle": "Инструменты не обнаружены", + "noToolsDescription": "Возможно, сервер офлайн или у него нет зарегистрированных инструментов.", + "failedLoad": "Не удалось загрузить инструменты", + "filterPlaceholder": "Фильтр инструментов...", + "noMatch": "Нет подходящих инструментов" + }, + "toast": { + "created": "MCP-сервер создан", + "updated": "MCP-сервер обновлён", + "deleted": "MCP-сервер удалён", + "failedCreate": "Не удалось создать MCP-сервер", + "failedUpdate": "Не удалось обновить MCP-сервер", + "failedDelete": "Не удалось удалить MCP-сервер", + "reconnected": "Подключение к MCP-серверу сброшено", + "failedReconnect": "Не удалось переподключить MCP-сервер" + }, + "userCredentials": { + "title": "Мои учётные данные", + "description": "Задайте свои личные учётные данные для этого MCP-сервера. Они переопределяют настройки сервера только для вашей учётной записи.", + "titleAdmin": "Учётные данные пользователя", + "descriptionAdmin": "Задайте учётные данные для конкретного пользователя на этом MCP-сервере. Они переопределяют настройки сервера только для выбранного пользователя.", + "selectUser": "Выберите пользователя", + "apiKey": "API-ключ", + "apiKeyPlaceholder": "Оставьте пустым, чтобы сохранить текущий", + "headers": "Заголовки", + "env": "Переменные окружения", + "headerPlaceholder": "КЛЮЧ=ЗНАЧЕНИЕ, по одному в строке", + "save": "Сохранить", + "delete": "Удалить", + "deleteAll": "Удалить все", + "cancel": "Отмена", + "saved": "Учётные данные сохранены", + "deleted": "Учётные данные удалены", + "hasApiKey": "API-ключ задан", + "hasHeaders": "Заголовки заданы", + "hasEnv": "Переменные окружения заданы", + "noCredentials": "Учётные данные не заданы", + "addHeader": "Добавить заголовок", + "addEnv": "Добавить переменную", + "saveFailed": "Не удалось сохранить учётные данные", + "deleteFailed": "Не удалось удалить учётные данные", + "mergeHint": "Пользователи чатов (Telegram, Discord и т. д.) должны сначала быть объединены в пользователя арендатора на странице «Контакты», прежде чем у них появятся личные учётные данные." + } +} diff --git a/ui/web/src/i18n/locales/ru/memory.json b/ui/web/src/i18n/locales/ru/memory.json new file mode 100644 index 00000000..8eb768fd --- /dev/null +++ b/ui/web/src/i18n/locales/ru/memory.json @@ -0,0 +1,239 @@ +{ + "title": "Память", + "description": "Просмотр и управление документами памяти агента, фрагментами и эмбеддингами", + "search": "Поиск", + "create": "Создать", + "indexAll": "Индексировать все", + "indexing": "Индексация...", + "emptyTitle": "Нет документов памяти", + "emptyAgentDescription": "У этого агента пока нет документов памяти.", + "emptyGlobalDescription": "Документы памяти не найдены ни у одного агента.", + "filters": { + "agent": "Агент", + "allAgents": "Все агенты", + "scope": "Область", + "allScope": "Все (глобальные + личные)", + "selectAgent": "Выберите агента" + }, + "tabs": { + "documents": "Документы", + "knowledgeGraph": "Граф знаний" + }, + "columns": { + "path": "Путь", + "agent": "Агент", + "scope": "Область", + "hash": "Хеш", + "updated": "Обновлено", + "actions": "Действия" + }, + "scopeLabel": { + "personal": "Личная", + "global": "Глобальная" + }, + "delete": { + "title": "Удалить документ памяти", + "description": "Вы уверены, что хотите удалить \"{{path}}\"? Это также удалит все связанные фрагменты и эмбеддинги.", + "confirmLabel": "Удалить" + }, + "createDialog": { + "title": "Создать документ памяти", + "path": "Путь *", + "pathPlaceholder": "напр. notes/project-overview.md", + "content": "Содержимое *", + "contentPlaceholder": "Содержимое документа...", + "agentId": "Агент *", + "agentIdPlaceholder": "Выберите агента...", + "scope": "Область", + "existingScope": "Существующая область", + "customScope": "Свой вариант", + "selectGroupUser": "Выберите группу/пользователя...", + "scopeHint": "Глобальная = доступна всем пользователям. Личная = ограничена конкретным пользователем или групповым чатом.", + "autoIndex": "Автоиндексация после создания", + "agentRequired": "Пожалуйста, выберите агента", + "pathRequired": "Путь обязателен", + "contentRequired": "Содержимое обязательно", + "cancel": "Отмена", + "create": "Создать", + "creating": "Создание..." + }, + "documentDialog": { + "title": "Документ памяти", + "path": "Путь:", + "agent": "Агент:", + "scope": "Область:", + "hash": "Хеш:", + "updated": "Обновлено:", + "content": "Содержимое", + "chunks": "Фрагменты ({{count}})", + "chunkIndex": "Фрагмент {{index}}", + "noChunks": "Нет фрагментов", + "embedded": "Эмбеддинг создан", + "noEmbedding": "Нет эмбеддинга" + }, + "searchDialog": { + "title": "Поиск по памяти", + "query": "Поисковый запрос", + "queryPlaceholder": "Введите поисковый запрос...", + "agentId": "ID агента", + "agentIdPlaceholder": "Фильтр по агенту...", + "limit": "Лимит", + "search": "Поиск", + "searching": "Поиск...", + "results": "Результаты", + "noResults": "Ничего не найдено", + "similarity": "сходство", + "path": "Путь:" + }, + "kg": { + "pageTitle": "Граф знаний", + "pageDescription": "Исследуйте сущности и связи, извлечённые из памяти агента.", + "selectAgentTitle": "Выберите агента", + "selectAgentDescription": "Выберите агента, чтобы просмотреть его граф знаний.", + "emptyTitle": "Нет сущностей", + "emptyDescription": "У этого агента пока нет сущностей графа знаний.", + "emptySearchDescription": "Ни одна сущность не соответствует вашему запросу.", + "stats": { + "entities": "Сущности: {{count}}", + "relations": "Связи: {{count}}" + }, + "search": { + "placeholder": "Поиск сущностей...", + "button": "Поиск", + "clear": "Очистить" + }, + "refresh": "Обновить", + "extract": "Извлечь", + "columns": { + "name": "Имя", + "type": "Тип", + "description": "Описание", + "confidence": "Уверенность", + "actions": "Действия", + "observations": "Наблюдения", + "relations": "Связи" + }, + "dedup": { + "button": "Дедупликация", + "title": "Дублирующиеся сущности", + "description": "Проверьте и объедините сущности, которые могут быть дубликатами, на основе сходства эмбеддингов.", + "noCandidates": "Кандидатов на дубликаты не найдено.", + "similar": "сходство", + "merge": "Объединить", + "dismiss": "Отклонить", + "merged": "Сущности успешно объединены.", + "mergeFailed": "Не удалось объединить сущности.", + "dismissed": "Кандидат отклонён.", + "dismissFailed": "Не удалось отклонить кандидата.", + "scan": "Сканировать все", + "scanning": "Сканирование...", + "scanComplete": "Сканирование завершено.", + "scanFailed": "Сканирование не удалось.", + "count": "{{count}} кандидат(ов)" + }, + "deleteEntity": { + "title": "Удалить сущность", + "description": "Удалить \"{{name}}\"? Это также удалит все связанные отношения.", + "confirmLabel": "Удалить" + }, + "entity": { + "externalId": "Внешний ID:", + "confidence": "Уверенность:", + "description": "Описание:", + "source": "Источник:", + "properties": "Свойства:", + "relations": "Связи", + "traverse": "Обход", + "depthOption": "уровень {{depth}}", + "tabs": { + "table": "Таблица", + "graph": "Граф" + }, + "traversing": "Обход...", + "loading": "Загрузка...", + "noRelations": "Связи не найдены.", + "showAll": "Показать все", + "traversalResults": "Результаты обхода ({{count}})", + "direction": { + "outgoing": "→ исходящая", + "incoming": "← входящая" + }, + "columns": { + "direction": "Направление", + "relation": "Связь", + "target": "Цель", + "confidence": "Уверенность" + } + }, + "extractDialog": { + "title": "Извлечь сущности из текста", + "providerLabel": "Провайдер извлечения", + "modelLabel": "Модель извлечения", + "textLabel": "Текст для извлечения", + "textPlaceholder": "Вставьте текст беседы, заметки или любой контент для извлечения сущностей и связей...", + "extract": "Извлечь", + "extracting": "Извлечение...", + "cancel": "Отмена", + "agentId": "ID агента", + "agentIdPlaceholder": "Оставьте пустым для глобального" + }, + "graphView": { + "loading": "Загрузка графа...", + "empty": "Нет сущностей для визуализации", + "nodes": "{{count}} узлов", + "edges": "{{count}} рёбер", + "selected": "Выбрано: {{name}}", + "limitNote": "Показаны первые {{limit}} из {{total}} сущностей", + "limitHint": "Граф ограничен {{limit}} сущностями для производительности. Используйте табличный вид для просмотра всех {{total}} сущностей." + } + }, + "toast": { + "created": "Документ памяти создан", + "deleted": "Документ памяти удалён", + "indexed": "Все документы проиндексированы", + "failedCreate": "Не удалось создать документ", + "failedDelete": "Не удалось удалить документ", + "failedIndex": "Не удалось проиндексировать документы", + "entitySaved": "Сущность сохранена", + "entitySaveFailed": "Не удалось сохранить сущность", + "entityDeleted": "Сущность удалена", + "entityDeleteFailed": "Не удалось удалить сущность", + "extractionComplete": "Извлечение завершено", + "extractionFailed": "Извлечение не удалось", + "traversalFailed": "Обход не удался", + "unknownError": "Неизвестная ошибка", + "docCreated": "Документ создан", + "docCreateFailed": "Не удалось создать документ", + "docUpdated": "Документ обновлён", + "docUpdateFailed": "Не удалось обновить документ", + "docDeleted": "Документ удалён", + "docDeleteFailed": "Не удалось удалить документ", + "docIndexed": "Документ проиндексирован", + "docIndexFailed": "Не удалось проиндексировать документ", + "allIndexed": "Все документы проиндексированы", + "allIndexFailed": "Не удалось проиндексировать все", + "searchFailed": "Поиск не удался", + "docLoadFailed": "Не удалось загрузить документ" + }, + "tabs": { + "documents": "Документы", + "episodic": "Эпизодическая память" + }, + "episodic": { + "noData": "Пока нет эпизодических воспоминаний", + "noDataHint": "Сводки создаются автоматически из сессий агента", + "selectAgent": "Выберите агента для просмотра эпизодических воспоминаний", + "searchPlaceholder": "Поиск по эпизодической памяти...", + "clearSearch": "Очистить", + "results": "результатов", + "turns": "реплик", + "tokens": "токенов", + "expand": "Показать полную сводку", + "collapse": "Скрыть сводку", + "source": { + "session": "Сессия", + "v2_daily": "Ежедневно", + "manual": "Вручную" + } + } +} diff --git a/ui/web/src/i18n/locales/ru/nodes.json b/ui/web/src/i18n/locales/ru/nodes.json new file mode 100644 index 00000000..cf8d9845 --- /dev/null +++ b/ui/web/src/i18n/locales/ru/nodes.json @@ -0,0 +1,35 @@ +{ + "title": "Узлы", + "description": "Управление привязанными устройствами и ожидающими запросами на привязку", + "emptyTitle": "Нет устройств", + "emptyDescription": "Нет привязанных устройств или ожидающих запросов на привязку.", + "pendingRequests": "Ожидающие запросы ({{count}})", + "pairedDevices": "Привязанные устройства ({{count}})", + "sender": "Отправитель:", + "chat": "Чат:", + "columns": { + "channel": "Канал", + "senderId": "ID отправителя", + "paired": "Привязано", + "by": "Кем", + "actions": "Действия" + }, + "approve": "Одобрить", + "deny": "Отклонить", + "revoke": "Отозвать", + "confirmApprove": { + "title": "Одобрить привязку", + "description": "Одобрить запрос на привязку от {{channel}}:{{senderId}} (код: {{code}})? Это устройство сможет взаимодействовать с агентами.", + "confirmLabel": "Одобрить" + }, + "confirmDeny": { + "title": "Отклонить привязку", + "description": "Отклонить запрос на привязку от {{channel}}:{{senderId}} (код: {{code}})? Запрос будет удалён.", + "confirmLabel": "Отклонить" + }, + "confirmRevoke": { + "title": "Отозвать устройство", + "description": "Отозвать привязку для {{channel}}:{{senderId}}? Устройству потребуется повторная привязка.", + "confirmLabel": "Отозвать" + } +} diff --git a/ui/web/src/i18n/locales/ru/overview.json b/ui/web/src/i18n/locales/ru/overview.json new file mode 100644 index 00000000..cde49aa6 --- /dev/null +++ b/ui/web/src/i18n/locales/ru/overview.json @@ -0,0 +1,93 @@ +{ + "title": "Панель управления", + "description": "Обзор шлюза и использование квот", + "tabs": { + "overview": "Обзор", + "usage": "Использование" + }, + "statCards": { + "requestsToday": "Запросов сегодня", + "tokensToday": "Токенов сегодня", + "costToday": "Стоимость за сегодня", + "agents": "Агенты", + "channels": "Каналы", + "channelsAttention": "{{count}} требуют внимания", + "running": "выполняется", + "online": "онлайн", + "users": "{{count}} пользователей", + "inOut": "{{input}} вход / {{output}} выход" + }, + "providers": { + "noProvidersTitle": "Провайдеры LLM не настроены", + "noEnabledTitle": "Нет включённых провайдеров LLM", + "noProvidersDesc": "Необходимо добавить хотя бы одного провайдера LLM, прежде чем агенты смогут работать. ", + "noEnabledDesc": "Все провайдеры сейчас отключены. Включите хотя бы одного, чтобы начать использовать агентов. ", + "goToSettings": "Перейти к настройкам провайдеров" + }, + "systemHealth": { + "title": "Состояние системы", + "uptime": "Время работы", + "version": "Версия", + "database": "База данных", + "providers": "Провайдеры", + "tools": "Инструменты", + "sessions": "Сессии", + "clients": "Клиенты", + "channels": "Каналы", + "needsAttention": "Требует внимания", + "channelsNeedingAttention": "{{count}} каналов", + "failedCount": "{{count}} с ошибкой", + "warningCountOne": "{{count}} предупреждение", + "warningCountOther": "{{count}} предупреждений", + "moreAttention": "+ещё {{count}}", + "active": "{{count}} активно", + "none": "нет", + "runtimes": "Среды выполнения" + }, + "connectedClients": { + "title": "Подключённые клиенты", + "noClients": "Нет подключённых клиентов", + "columns": { + "ip": "IP", + "user": "Пользователь", + "role": "Роль", + "connected": "Подключён" + }, + "you": "вы" + }, + "cronJobs": { + "title": "Задачи Cron", + "manage": "Управление", + "noJobs": "Задачи cron не настроены", + "disabled": "отключено" + }, + "recentRequests": { + "title": "Недавние запросы", + "viewAll": "Показать все", + "noRequests": "Нет недавних запросов", + "columns": { + "time": "Время", + "name": "Название", + "user": "Пользователь", + "channel": "Канал", + "tokens": "Токены", + "duration": "Длительность", + "status": "Статус" + } + }, + "quotaUsage": { + "title": "Использование квоты", + "columns": { + "userGroup": "Пользователь / Группа", + "hour": "Час", + "day": "День", + "week": "Неделя" + }, + "noLimit": "без лимита" + }, + "embedding": { + "title": "Эмбеддинги", + "notConfigured": "Не настроено", + "configure": "Настроить в разделе «Провайдеры» →" + } +} diff --git a/ui/web/src/i18n/locales/ru/packages.json b/ui/web/src/i18n/locales/ru/packages.json new file mode 100644 index 00000000..14bb2c67 --- /dev/null +++ b/ui/web/src/i18n/locales/ru/packages.json @@ -0,0 +1,125 @@ +{ + "title": "Среда выполнения и пакеты", + "description": "Управление средами выполнения и установленными пакетами внутри активного контейнера среды выполнения GoClaw", + "runtimes": { + "title": "Среды выполнения", + "scopeTitle": "Статус сред выполнения в рамках контейнера", + "scopeDesc": "Эти проверки выполняются внутри активного контейнера среды выполнения GoClaw. Среды выполнения, установленные на хосте, изменения профиля оболочки и бинарные файлы, управляемые nvm, здесь не используются.", + "minimalImageHint": "Опубликованный образ latest включает Python, но не Node. Используйте образ full для Node/npm и встроенных дополнений, base для минимального образа, пересоберите с ENABLE_NODE=true / ENABLE_FULL_SKILLS=true или установите среды выполнения вручную в этом контейнере.", + "available": "Доступно", + "missing": "Отсутствует", + "missingInContainer": "Отсутствует в контейнере" + }, + "system": { + "title": "Системные пакеты", + "placeholder": "Имя пакета (напр. github-cli)" + }, + "pip": { + "title": "Пакеты Python", + "placeholder": "Имя пакета (напр. pandas)" + }, + "npm": { + "title": "Пакеты Node", + "placeholder": "Имя пакета (напр. typescript)" + }, + "github": { + "title": "Бинарные файлы GitHub", + "placeholder": "owner/repo[@tag] (напр. cli/cli@v2.45.0)", + "browse": "Обзор релизов", + "muslWarning": "Alpine Linux использует musl libc. Бинарные файлы, скомпилированные под glibc, могут не работать во время выполнения. Предпочитайте статические или совместимые с musl релизы.", + "muslDismiss": "Закрыть", + "pickerTitle": "Выберите релиз из {{repo}}", + "pickerEmpty": "Релизы для {{repo}} не найдены", + "pickerLoading": "Загрузка релизов...", + "pickerMatchingAssets": "{{count}} подходящих / {{total}} всего", + "pickerPrerelease": "Предварительный выпуск", + "pickerSelect": "Выбрать", + "columns": { + "repo": "Репозиторий", + "tag": "Версия", + "binaries": "Бинарные файлы", + "installedAt": "Установлено" + } + }, + "updates": { + "available": "Доступно обновлений: {{count}}", + "none": "Все пакеты актуальны", + "refresh": "Обновить", + "refreshing": "Обновление...", + "lastCheckedAgo": "Последняя проверка {{ago}}", + "neverChecked": "Ещё не проверялось", + "update": "Обновить", + "updateAll": "Обновить все", + "updating": "Обновление {{name}}...", + "updateSucceeded": "{{name}} обновлён до {{version}}", + "updateFailed": "Не удалось обновить {{name}}: {{reason}}", + "updateAllResult": "Успешно: {{succeeded}}, с ошибкой: {{failed}}", + "confirmAllTitle": "Обновить пакетов: {{count}}?", + "confirmAllBody": "Это может занять несколько минут. Отдельные обновления применяются последовательно.", + "selected": "Выбрано: {{count}}", + "manifestDesyncWarn": "Бинарный файл обновлён, но не удалось сохранить манифест. Требуется ручное восстановление для {{name}}.", + "cacheStale": "Кэш обновлений устарел. Пожалуйста, сначала обновите.", + "adminOnly": "Требуется доступ администратора", + "empty": "Нет доступных обновлений", + "source": { + "github": "GitHub", + "pip": "pip", + "npm": "npm", + "apk": "apk" + }, + "filter": { + "all": "Все источники", + "label": "Фильтр" + }, + "unavailable": { + "pip": "pip не установлен", + "npm": "npm не установлен", + "apk": "apk недоступен в этой системе" + }, + "button": { + "tooltip": { + "github": "Обновить из релиза GitHub", + "pip": "Обновить через pip", + "npm": "Обновить через npm", + "apk": "Обновить через apk (системный пакет)" + } + }, + "summary": { + "perSource": "{{source}}: {{count}}" + } + }, + "actions": { + "install": "Установить", + "uninstall": "Удалить", + "installing": "Установка...", + "uninstalling": "Удаление..." + }, + "messages": { + "installSuccess": "{{name}} успешно установлен", + "installError": "Не удалось установить {{name}}", + "uninstallSuccess": "{{name}} успешно удалён", + "uninstallError": "Не удалось удалить {{name}}" + }, + "confirmUninstall": { + "title": "Подтвердить удаление", + "description": "Вы уверены, что хотите удалить {{name}}? Это действие нельзя отменить." + }, + "table": { + "name": "Имя", + "version": "Версия", + "actions": "Действия", + "empty": "Пакеты не установлены" + }, + "tabs": { + "system": "Система", + "python": "Python", + "node": "Node", + "github": "GitHub", + "cliCredentials": "Учётные данные CLI" + }, + "runtimesHeader": { + "title": "Среды выполнения", + "available": "Доступно", + "missing": "Отсутствует" + } +} diff --git a/ui/web/src/i18n/locales/ru/pending-messages.json b/ui/web/src/i18n/locales/ru/pending-messages.json new file mode 100644 index 00000000..f211bfd9 --- /dev/null +++ b/ui/web/src/i18n/locales/ru/pending-messages.json @@ -0,0 +1,50 @@ +{ + "title": "Ожидающие сообщения", + "description": "Буферизованные сообщения каналов, ожидающие обработки агентом", + "emptyTitle": "Нет ожидающих сообщений", + "emptyDescription": "Буферизованные группы сообщений не найдены. Сообщения появляются здесь, когда каналы буферизуют входящие сообщения до обработки агентом.", + "howItWorks": { + "title": "Как это работает", + "step1": "Сообщения из каналов (Telegram, Discord и т. д.) буферизуются здесь, пока агент занят или не в сети.", + "step2": "Когда буфер достигает порога (по умолчанию: 50 сообщений), включается автосжатие — LLM обобщает старые сообщения, сохраняя только 15 самых свежих.", + "step3": "При следующем ходе агента сжатая сводка + недавние сообщения внедряются как контекст, чтобы агент наверстал упущенное, не будучи перегруженным.", + "compactAction": "Сжать — вручную запустить обобщение LLM сейчас. Полезно, когда вы хотите, чтобы агент обработал чистую сводку вместо потока сырых сообщений.", + "clearAction": "Очистить — навсегда удалить все буферизованные сообщения группы. Используйте, когда сообщения больше не актуальны." + }, + "columns": { + "channel": "Канал", + "group": "Группа", + "messages": "Сообщения", + "status": "Статус", + "lastActivity": "Последняя активность", + "actions": "Действия" + }, + "status": { + "compacted": "Сжато", + "raw": "Сырое" + }, + "compact": "Сжать", + "compacting": "Сжатие…", + "clear": "Очистить", + "confirmClear": { + "title": "Очистить сообщения?", + "description": "Это навсегда удалит все сообщения в {{channel}} / {{key}}. Это действие нельзя отменить.", + "cancel": "Отмена", + "confirm": "Очистить" + }, + "dialog": { + "title": "Сообщения — {{name}}", + "noMessages": "Сообщения не найдены.", + "summary": "Сводка" + }, + "toast": { + "compacted": "Сжатие завершено", + "compactedSummarized": "Обобщено до {{count}} сообщений", + "compactedDeleted": "Все сообщения удалены (провайдер LLM недоступен)", + "compacting": "Сжатие…", + "compactingDesc": "Обобщение LLM выполняется в фоне. Список обновится автоматически.", + "failedCompact": "Сжатие не удалось", + "cleared": "Сообщения очищены", + "failedClear": "Не удалось очистить сообщения" + } +} diff --git a/ui/web/src/i18n/locales/ru/providers.json b/ui/web/src/i18n/locales/ru/providers.json new file mode 100644 index 00000000..52f7bffe --- /dev/null +++ b/ui/web/src/i18n/locales/ru/providers.json @@ -0,0 +1,388 @@ +{ + "title": "Провайдеры", + "description": "Управление провайдерами LLM", + "addProvider": "Добавить провайдера", + "searchPlaceholder": "Поиск провайдеров...", + "emptyTitle": "Нет провайдеров", + "emptyDescription": "Добавьте своего первого провайдера LLM, чтобы начать.", + "noMatchTitle": "Провайдеры не найдены", + "noMatchDescription": "Попробуйте другой поисковый запрос.", + "columns": { + "name": "Имя", + "type": "Тип", + "apiBase": "Базовый URL API", + "apiKey": "Ключ API", + "status": "Статус", + "actions": "Действия" + }, + "apiKey": { + "oauthToken": "Токен OAuth", + "notSet": "Не задан" + }, + "card": { + "apiKeySet": "Ключ API задан", + "authenticated": "Аутентифицирован", + "oauthAccount": "Аккаунт OAuth", + "oauthAlias": "Псевдоним: {{name}}", + "connected": "Подключено", + "signInNeeded": "Требуется вход", + "disabled": "Отключено" + }, + "quota": { + "checking": "Проверка квоты", + "plan": "Тариф", + "lastChecked": "Проверено {{value}}", + "failure": { + "billing": { + "label": "Оплата", + "description": "Оплата рабочего пространства или права доступа сейчас блокируют этот аккаунт." + }, + "exhausted": { + "label": "Исчерпано", + "description": "Этот аккаунт выполнил вход, но одно из его основных окон квоты исчерпано." + }, + "reauth": { + "label": "Повторный вход", + "description": "Войдите снова, чтобы обновить доступ к рабочему пространству." + }, + "forbidden": { + "label": "Запрещено", + "description": "Этот аккаунт не может прочитать данные квоты вышестоящего сервиса." + }, + "needs_setup": { + "label": "Требуется настройка", + "description": "Войдите снова, чтобы GoClaw мог восстановить метаданные рабочего пространства аккаунта ChatGPT." + }, + "retry_later": { + "label": "Повторить позже", + "description": "Эндпоинт квоты временно недоступен или ограничен по частоте запросов." + }, + "unavailable": { + "label": "Недоступно", + "description": "Данные квоты сейчас недоступны для этого аккаунта." + } + } + }, + "form": { + "createTitle": "Добавить провайдера", + "editTitle": "Редактировать провайдера", + "name": "Имя *", + "namePlaceholder": "напр. openrouter", + "nameHint": "Строчные буквы, цифры, дефисы", + "displayName": "Отображаемое имя", + "displayNamePlaceholder": "OpenRouter", + "providerType": "Тип провайдера *", + "apiKey": "Ключ API", + "apiKeyPlaceholder": "sk-...", + "apiKeyEditPlaceholder": "Оставьте как есть или введите новый ключ", + "apiKeySetHint": "Ключ API задан. Очистите поле и введите новое значение, чтобы изменить его.", + "apiBase": "Базовый URL API", + "enabled": "Включено", + "cancel": "Отмена", + "close": "Закрыть", + "create": "Создать", + "save": "Сохранить", + "update": "Обновить", + "saving": "Сохранение...", + "creating": "Создание...", + "alreadyAdded": "(уже добавлено)", + "nameFixed": "Имя", + "configure": "Настройте подключение к провайдеру LLM.", + "configureOauth": "Подключите один аккаунт OpenAI Codex OAuth.", + "oauthAlias": "Псевдоним аккаунта *", + "oauthAliasPlaceholder": "напр. primary", + "oauthAliasHint": "Строчный псевдоним, используемый в маршрутизации и пулах.", + "oauthDisplayNamePlaceholder": "Основной аккаунт Codex", + "oauthDisplayNameHint": "Необязательная метка, отображаемая в панели управления.", + "lowercaseHint": "Строчные буквы, цифры, дефисы" + }, + "delete": { + "title": "Удалить провайдера", + "description": "Вы уверены, что хотите удалить «{{name}}»? Это действие нельзя отменить.", + "confirmLabel": "Удалить" + }, + "types": { + "anthropic_native": "Anthropic", + "openai_compat": "OpenAI Compat", + "gemini_native": "Gemini", + "openrouter": "OpenRouter", + "groq": "Groq", + "deepseek": "DeepSeek", + "mistral": "Mistral", + "xai": "xAI", + "minimax_native": "MiniMax", + "cohere": "Cohere", + "perplexity": "Perplexity", + "chatgpt_oauth": "Подписка ChatGPT (OAuth)", + "yescale": "YesScale", + "acp": "ACP" + }, + "acp": { + "description": "Оркестрация ACP-совместимых агентов кодирования в виде подпроцессов. Ключ API не требуется.", + "binary": "Бинарный файл", + "binaryPlaceholder": "claude", + "binaryHint": "Путь или имя бинарного файла ACP-агента (напр. claude, codex, gemini)", + "args": "Аргументы", + "argsPlaceholder": "--profile goclaw", + "argsHint": "Аргументы через пробел, передаваемые бинарному файлу", + "idleTTL": "Idle TTL", + "idleTTLPlaceholder": "5m", + "idleTTLHint": "Завершать простаивающие процессы по истечении этого времени (напр. 5m, 1h)", + "permMode": "Режим разрешений", + "permModeApproveAll": "Одобрять всё", + "permModeApproveReads": "Одобрять только чтение", + "permModeDenyAll": "Отклонять всё", + "workDir": "Рабочий каталог", + "workDirPlaceholder": "/tmp/workspace", + "workDirHint": "Рабочий каталог для подпроцессов агента (необязательно)" + }, + "ollama": { + "title": "Конфигурация Ollama", + "description": "Настройки, специфичные для Ollama", + "numCtx": "Контекстное окно (токены)", + "numCtxPlaceholder": "Оставьте пустым для автоопределения из модели", + "numCtxHelp": "Максимальное количество токенов. Оставьте пустым для автоопределения через Ollama API.", + "thinkingEnabled": "Токены рассуждений / размышлений", + "thinkingEnabledDefault": "По умолчанию (выкл.)", + "thinkingEnabledOn": "Вкл.", + "thinkingEnabledOff": "Выкл.", + "thinkingEnabledHelp": "Определяет, будет ли этот провайдер запрашивать у модели выдачу видимых токенов рассуждений («размышлений»). По умолчанию отключено, чтобы избежать раздутого вывода цепочки рассуждений от моделей вроде qwq или deepseek-r1." + }, + "oauth": { + "checkingStatus": "Проверка статуса аутентификации...", + "authSuccessful": "Аутентификация успешна!", + "authSuccessfulDesc": "Этот именованный аккаунт OpenAI/Codex готов к использованию.", + "activeProvider": "Псевдоним аккаунта", + "authenticated": "Аутентифицирован — аккаунт", + "active": "активен", + "poolBadge": "Пул готов", + "roundRobinBadge": "Круговой перебор", + "modelPrefixHint": "Используйте префикс модели", + "modelPrefixExample": "в конфигурации агента (например {{example}}).", + "multiAccountHint": "Добавьте больше псевдонимов позже из пула агентов, чтобы включить объединение в пул или круговой перебор.", + "aliasRequired": "Введите корректный строчный псевдоним аккаунта перед началом OpenAI Codex OAuth.", + "removeToken": "Отключить аккаунт", + "signInDesc": "Войдите с помощью вашей подписки ChatGPT/Codex. Отдельный ключ API не требуется.", + "waiting": "Ожидание аутентификации... Завершите вход в открывшемся окне.", + "remoteVpsHint": "После входа ваш браузер попытается открыть", + "remoteVpsError": "и покажет ошибку «Не удаётся получить доступ к странице». Это нормально — скопируйте полный URL из адресной строки браузера и вставьте его ниже.", + "pasteUrlPlaceholder": "http://localhost:1455/auth/callback?code=...&state=...", + "submit": "Отправить", + "signInWithChatGPT": "Подключить аккаунт OpenAI", + "starting": "Запуск...", + "remoteVps": "Удалённый сервер/VPS?", + "loggedOut": "Выход выполнен", + "loggedOutDesc": "Аккаунт OpenAI Codex OAuth отключён", + "oauthFailed": "Ошибка OAuth", + "exchangeFailed": "Ошибка обмена", + "logoutFailed": "Ошибка выхода", + "done": "Готово" + }, + "cli": { + "description": "Claude CLI использует ваш локальный", + "descriptionSuffix": "бинарный файл. Ключ API не требуется.", + "checkingAuth": "Проверка аутентификации...", + "authenticatedAs": "Аутентифицирован как", + "switchAccount": "Сменить аккаунт?", + "switchAccountInstructions": "Выполните в терминале сервера:", + "switchAccountRecheck": "Затем нажмите", + "switchAccountRecheckSuffix": "для повторной проверки.", + "notAuthenticated": "Не аутентифицирован", + "runOnServer": "Выполните в терминале сервера:", + "recheckButton": "Проверить снова" + }, + "detail": { + "title": "Сведения о провайдере", + "advanced": "Дополнительно", + "identity": "Идентификация", + "identityDesc": "Идентификация провайдера", + "connection": "Подключение", + "connectionDesc": "Конфигурация эндпоинта API", + "acpConfig": "Конфигурация ACP", + "acpConfigDesc": "Настройки подпроцесса агента", + "cliConfig": "Claude CLI", + "cliConfigDesc": "Локальная аутентификация CLI", + "oauthConfig": "Подписка ChatGPT (OAuth)", + "oauthConfigDesc": "Управление входом для этого именованного аккаунта OpenAI/Codex", + "nameReadonly": "Идентификатор провайдера, изменить нельзя", + "oauthAliasReadonly": "Псевдоним аккаунта OAuth, изменить нельзя", + "providerType": "Тип провайдера", + "apiKeySection": "Ключ API", + "apiKeySectionDesc": "Учётные данные аутентификации", + "statusSection": "Статус", + "statusSectionDesc": "Доступность провайдера", + "enabledDesc": "Провайдер активен и доступен для агентов", + "embeddingSection": "Эмбеддинги", + "oauthAccountUsage": "Использование аккаунта", + "oauthAccountUsageDesc": "Как этот псевдоним работает в рамках объединения OpenAI Codex, принадлежащего провайдеру.", + "oauthAccountUsageManagedDesc": "Этот псевдоним в настоящее время является участником пула, управляемого {{provider}}.", + "oauthAccountUsageOwnerDesc": "Этому псевдониму сейчас принадлежит переиспользуемый пул с {{count}} дополнительными аккаунтами.", + "oauthPoolRole": { + "member": "Участник пула", + "owner": "Владелец пула", + "standalone": "Отдельный" + }, + "oauthAliasLabel": "Псевдоним аккаунта", + "oauthAccountBadge": "Подписка ChatGPT (OAuth)", + "oauthModelPrefix": "Префикс модели", + "oauthQuotaTitle": "Статус квоты", + "oauthQuotaDescription": "Последняя квота рабочего пространства ChatGPT для этого аккаунта.", + "oauthModelPrefixCopied": "Префикс модели скопирован", + "oauthPreferredHint": "Задайте этот псевдоним в поле «Провайдер» агента, чтобы сделать его основным псевдонимом OpenAI.", + "oauthProviderDefaultHint": "Используйте настройки пула по умолчанию ниже, чтобы задать переиспользуемую политику маршрутизации OpenAI Codex для каждого агента, указывающего на этот псевдоним.", + "oauthRoutingHint": "Страницы агентов теперь показывают действующий пул для этого агента, а также наследует ли он эти настройки по умолчанию или сохраняет собственное переопределение.", + "oauthManagedByHint": "Этот псевдоним управляется провайдером {{provider}}. Изменяйте членство в пуле там, а не в этом аккаунте.", + "reasoningDefaultsTitle": "Настройки рассуждений по умолчанию", + "reasoningDefaultsDescription": "Задайте политику рассуждений по умолчанию для агентов, использующих этого провайдера. Итоговое усилие во время выполнения всё равно нормализуется относительно выбранной модели каждого агента.", + "reasoningPreset": "Пресет по умолчанию", + "reasoningExpertMode": "Экспертные настройки по умолчанию", + "reasoningExpertModeDesc": "Используйте расширенные настройки рассуждений по умолчанию, принадлежащие провайдеру, когда нужны усилия вроде auto, none, minimal или xhigh.", + "reasoningPreviewDescription": "Предпросмотр явных метаданных о возможностях рассуждений для {{model}}.", + "reasoningPreviewLabel": "Предпросмотр возможностей", + "reasoningPreviewDefault": "Рассуждения модели по умолчанию для предпросмотра: {{level}}", + "reasoningPreviewEmpty": "Для этого провайдера пока не возвращено моделей с явной поддержкой рассуждений.", + "reasoningRequestedEffort": "Запрошенное усилие", + "reasoningFallbackBehavior": "Поведение при откате", + "codexPoolDefaultsTitle": "Настройки пула по умолчанию", + "codexPoolDefaultsDescription": "Задайте переиспользуемый пул OpenAI Codex, принадлежащий этому псевдониму, для агентов, использующих его как основного провайдера.", + "poolManagedByDescription": "Этот провайдер является участником пула, принадлежащего {{owner}}. Чтобы изменить настройки пула, перейдите к владельцу пула.", + "oauthDisplayNameRecommendation": "Добавьте отображаемое имя, если хотите более понятные метки, когда в пуле появляется несколько аккаунтов OpenAI Codex OAuth.", + "poolActivityTitle": "Активность пула", + "poolActivityDescription": "Совокупная активность во время выполнения по всем агентам, использующим пул Codex этого провайдера.", + "poolActivityRefresh": "Обновить", + "poolActivityTopAgents": "Агенты", + "poolActivityTopAgentsTitle": "Топ агентов, использующих этот пул", + "poolActivityRequests": "зап.", + "poolActivityEmpty": "Активности пока нет", + "poolActivityEmptyDesc": "Активность пула появится здесь, как только агенты начнут маршрутизировать запросы через этот пул." + }, + "embedding": { + "enable": "Включить эмбеддинги", + "enableDesc": "Разрешить этому провайдеру генерировать текстовые эмбеддинги для памяти, навыков и графа знаний", + "model": "Модель эмбеддингов", + "dimensions": "Размерность вывода", + "dimensionsHint": "Память GoClaw в настоящее время хранит эмбеддинги в vector(1536). Оставьте это поле пустым, если модель уже выдаёт 1536, или задайте ровно 1536, если провайдер поддерживает усечение из более крупных нативных размерностей.", + "dimensionsInvalid": "Память GoClaw в настоящее время поддерживает только 1536.", + "apiBase": "Базовый URL API эмбеддингов", + "apiBasePlaceholder": "(как у провайдера)", + "apiBaseHint": "Переопределите, если эндпоинт эмбеддингов отличается от основного базового URL API провайдера", + "verify": "Проверить эмбеддинги", + "verifyFailed": "Проверка эмбеддингов не удалась", + "dimensionsMismatch": "{{count}} размерностей — не соответствует требуемым 1536" + }, + "pricing": { + "title": "Стоимость моделей", + "description": "Синхронизируйте цены OpenRouter и переопределите стоимость моделей этого провайдера для лимитов использования.", + "skippedDescription": "Лимиты использования пропускают провайдеров подписки и локальных провайдеров, но цены всё равно можно подготовить для последующей маршрутизации через API.", + "sync": "Синхронизировать OpenRouter", + "model": "Модель", + "saveOverride": "Сохранить переопределение", + "catalogMatches": "Совпадения в каталоге OpenRouter", + "source": "Источник", + "configuredFields": "Настроенные поля", + "override": "Переопределение", + "emptyOverrides": "Для этого провайдера нет переопределений стоимости.", + "deleteOverride": "Удалить переопределение", + "fields": { + "input": "Ввод", + "output": "Вывод", + "cache_read": "Чтение кэша", + "cache_write": "Запись кэша", + "reasoning": "Рассуждения", + "request": "Запрос", + "image": "Изображение", + "web_search": "Веб-поиск" + }, + "toast": { + "synced": "Синхронизировано {{count}} моделей OpenRouter", + "syncFailed": "Не удалось синхронизировать цены OpenRouter", + "saved": "Переопределение стоимости сохранено", + "saveFailed": "Не удалось сохранить переопределение стоимости", + "deleted": "Переопределение стоимости удалено", + "deleteFailed": "Не удалось удалить переопределение стоимости" + } + }, + "reasoning": { + "off": "Выкл.", + "offDesc": "Расширенные размышления по умолчанию отключены", + "auto": "Авто", + "autoDesc": "Попросить модель использовать собственное усилие рассуждений по умолчанию", + "none": "Нет", + "noneDesc": "Явно отключить рассуждения провайдера, сохранив расширенный контракт", + "minimal": "Минимальное", + "minimalDesc": "Использовать наименьшее поддерживаемое усилие рассуждений выше «нет»", + "low": "Низкое", + "lowDesc": "Бюджет ~4K токенов", + "medium": "Среднее", + "mediumDesc": "Бюджет ~10-16K токенов", + "high": "Высокое", + "highDesc": "Бюджет ~32K токенов", + "xhigh": "Очень высокое", + "xhighDesc": "Использовать максимальное поддерживаемое усилие рассуждений", + "downgrade": "Понизить", + "downgradeDesc": "Нормализовать до ближайшего поддерживаемого более низкого уровня или отключить, если более низкого поддерживаемого уровня не существует", + "provider_default": "Использовать значение модели по умолчанию", + "provider_defaultDesc": "Позволить выбранной модели откатиться к собственному усилию рассуждений по умолчанию" + }, + "list": { + "poolOwner": "Владелец пула", + "poolMember": "Участник пула", + "poolAvailable": "Пул доступен", + "poolBadge": "Пул", + "standalone": "Отдельный", + "reasoningDefault": "Рассуждения {{level}}", + "managedBy": "Управляется {{provider}}", + "memberCount_one": "{{count}} участник", + "memberCount_other": "{{count}} участников", + "memberOverflow": "+{{count}}", + "strategy": { + "roundRobin": "Круговой перебор", + "priorityOrder": "По приоритету", + "primaryFirst": "Сначала основной" + }, + "status": { + "needsSignIn": "Требуется вход", + "disabled": "Отключено" + } + }, + "pageHint": { + "title": "Аккаунты подписки ChatGPT (OAuth)", + "description": "Каждый псевдоним — это один аккаунт Codex. Владельцы пулов управляют аккаунтами-участниками здесь, а страницы агентов показывают только действующий маршрут.", + "connected_one": "{{count}} подключённый аккаунт", + "connected_other": "{{count}} подключённых аккаунтов", + "owners_one": "{{count}} владелец пула", + "owners_other": "{{count}} владельцев пулов", + "members_one": "{{count}} участник", + "members_other": "{{count}} участников" + }, + "poolCta": { + "unpooledCount": "{{count}} аккаунтов готовы к объединению в пул", + "setupPool": "Настроить пул", + "dismiss": "Скрыть" + }, + "poolWizard": { + "title": "Настройка пула", + "description": "Объедините аккаунты ChatGPT для автоматического переключения при сбоях и балансировки нагрузки.", + "selectOwner": "Владелец пула", + "selectOwnerHint": "Основной аккаунт, которому принадлежит конфигурация пула.", + "selectMembers": "Участники пула", + "selectMembersHint": "Выберите аккаунты для включения в пул.", + "selectAll": "Выбрать все", + "deselectAll": "Снять выбор со всех", + "selectedCount": "Выбрано: {{count}}", + "strategy": "Стратегия", + "createPool": "Создать пул", + "noMembers": "Выберите хотя бы одного участника для создания пула.", + "success": "Пул успешно создан.", + "error": "Не удалось создать пул." + }, + "toast": { + "created": "Провайдер создан", + "createdDesc": "{{name}} добавлен", + "updated": "Провайдер обновлён", + "deleted": "Провайдер удалён", + "failedCreate": "Не удалось создать провайдера", + "failedUpdate": "Не удалось обновить провайдера", + "failedDelete": "Не удалось удалить провайдера" + } +} diff --git a/ui/web/src/i18n/locales/ru/sessions.json b/ui/web/src/i18n/locales/ru/sessions.json new file mode 100644 index 00000000..e2d9d2ec --- /dev/null +++ b/ui/web/src/i18n/locales/ru/sessions.json @@ -0,0 +1,57 @@ +{ + "title": "Сессии", + "description": "Просмотр сессий бесед", + "searchPlaceholder": "Поиск сессий...", + "emptyTitle": "Пока нет сессий", + "emptyDescription": "Сессии появятся здесь, как только вы начнёте общение.", + "noMatchTitle": "Нет подходящих сессий", + "noMatchDescription": "Попробуйте другой поисковый запрос.", + "columns": { + "session": "Сессия", + "agent": "Агент", + "context": "Контекст", + "messages": "Сообщения", + "updated": "Обновлено" + }, + "contextBar": { + "compacted": "Сжато раз: {{count}}" + }, + "detail": { + "reset": "Сбросить", + "delete": "Удалить", + "deleteTitle": "Удалить сессию", + "deleteDescription": "Это навсегда удалит все сообщения в этой сессии.", + "resetTitle": "Сбросить сессию", + "resetDescription": "Это очистит все сообщения, но сохранит сессию.", + "confirmDelete": "Удалить", + "confirmReset": "Сбросить", + "noMessages": "В этой сессии нет сообщений", + "summary": "Сводка", + "showMore": "Показать больше", + "showLess": "Показать меньше", + "showDetails": "показать детали", + "hide": "скрыть", + "systemMessage": "Системное сообщение", + "messages": "сообщений", + "timeline": { + "title": "Хронология запуска", + "loading": "загрузка", + "count": "{{count}} элементов", + "runStatus": "запуск", + "assistant": "ассистент", + "toolCall": "вызов инструмента", + "toolResult": "результат инструмента", + "trace": "трассировка", + "activity": "активность", + "failed": "не удалось" + } + }, + "toast": { + "deleted": "Сессия удалена", + "deleteFailed": "Не удалось удалить сессию", + "reset": "Сессия сброшена", + "resetFailed": "Не удалось сбросить сессию", + "updated": "Сессия обновлена", + "updateFailed": "Не удалось обновить сессию" + } +} diff --git a/ui/web/src/i18n/locales/ru/setup.json b/ui/web/src/i18n/locales/ru/setup.json new file mode 100644 index 00000000..be4fe721 --- /dev/null +++ b/ui/web/src/i18n/locales/ru/setup.json @@ -0,0 +1,122 @@ +{ + "layout": { + "subtitle": "Давайте запустим ваш шлюз в работу" + }, + "steps": { + "provider": "Провайдер", + "model": "Модель", + "agent": "Агент", + "channel": "Канал", + "channelOptional": "(Необязательно)" + }, + "complete": { + "title": "Настройка завершена!", + "ready": "Ваша система готова к работе!", + "description": "Провайдер, агент и канал настроены. Вы можете управлять ими в любое время из панели управления.", + "goToDashboard": "Перейти в панель управления" + }, + "provider": { + "title": "Настройка провайдера LLM", + "descriptionCli": "Подключение через локальную установку Claude CLI. Ключ API не требуется.", + "descriptionOauth": "Войдите, чтобы создать одну именованную учётную запись OpenAI Codex OAuth. Задайте ей псевдоним сейчас, а затем добавьте другие псевдонимы для участников пула или циклического распределения.", + "description": "Подключитесь к AI-провайдеру для работы ваших агентов. Вам понадобится ключ API.", + "providerType": "Тип провайдера", + "providerTypeHint": "Сервис LLM, к которому вы хотите подключиться. Для доступа к нескольким моделям рекомендуется OpenRouter.", + "name": "Название", + "nameHint": "Внутренний идентификатор этого провайдера. Генерируется автоматически из типа провайдера.", + "oauthAlias": "Псевдоним учётной записи", + "oauthAliasPlaceholder": "например, primary", + "oauthAliasHint": "Используйте уникальный псевдоним в нижнем регистре, например primary, work или team, для этой учётной записи OpenAI/Codex. Псевдоним становится префиксом провайдера/модели, используемым агентами.", + "displayName": "Отображаемое имя (необязательно)", + "displayNameHint": "Понятная метка для этой учётной записи OpenAI/Codex. Полезно, когда вы позже добавите другие псевдонимы.", + "displayNamePlaceholder": "Основная учётная запись Codex", + "apiKey": "Ключ API *", + "apiKeyHint": "Секретный ключ вашего провайдера. Шифруется на стороне сервера и никогда не раскрывается в ответах API.", + "apiBase": "Базовый URL API", + "apiBaseHint": "URL-адрес конечной точки для запросов к API. Заполняется автоматически на основе типа провайдера. Переопределяйте только при использовании собственного прокси.", + "creating": "Создание...", + "create": "Создать провайдера", + "errors": { + "apiKeyRequired": "Требуется ключ API", + "oauthProviderNotFound": "Аутентификация OAuth прошла успешно, но провайдер не найден. Пожалуйста, попробуйте снова.", + "failedCreate": "Не удалось создать провайдера" + } + }, + "model": { + "title": "Выбор и проверка модели", + "description": "Выберите модель и проверьте, что подключение к провайдеру работает.", + "provider": "Провайдер:", + "model": "Модель *", + "modelHint": "AI-модель для использования. Выберите из списка или введите ID модели вручную, если её нет в списке.", + "loadingModels": "Загрузка моделей...", + "selectModel": "Выберите или введите ID модели", + "noModelsHint": "Этот провайдер не предоставляет список моделей — введите ID модели вручную.", + "verified": "Проверено", + "verify": "Проверить", + "verifying": "Проверка...", + "continue": "Продолжить", + "modelVerified": "Модель проверена", + "modelVerifiedDesc": "{{model}} работает корректно", + "verificationFailed": "Проверка не удалась. Проверьте ключ API и модель." + }, + "agent": { + "title": "Создайте вашего первого агента", + "description": "Агент — это ваш AI-ассистент. Настройте его имя и характер.", + "provider": "Провайдер:", + "model": "Модель:", + "displayName": "Отображаемое имя", + "displayNameHint": "Будет сгенерировано автоматически при вызове на основе характера агента.", + "agentKey": "Ключ агента", + "agentKeyHint": "Уникальный идентификатор агента. Только строчные буквы, цифры и дефисы.", + "agentKeyPlaceholder": "my-agent", + "personality": "Характер агента", + "personalityHint": "Опишите роль и поведение вашего агента. AI сгенерирует из этого контекстные файлы при вызове.", + "personalityPlaceholder": "Опишите характер, назначение и поведение вашего агента...", + "personalityHintBottom": "Настройте этот запрос, чтобы сформировать характер и компетенции вашего агента.", + "selfEvolve": "Саморазвитие", + "selfEvolveDesc": "Разрешить агенту со временем развивать свой стиль общения через SOUL.md", + "gatewayOperatorAccess": "Доступ оператора шлюза", + "gatewayOperatorAccessDesc": "Предоставить отзываемый доступ SecureCLI к локальному CLI goclaw для этого первого агента.", + "gatewayOperatorGranted": "Доступ оператора шлюза предоставлен", + "gatewayOperatorGrantedDesc": "Агент может использовать локальный CLI goclaw. Просмотрите или отзовите доступ в разделе «Пакеты → Учётные данные CLI».", + "gatewayOperatorWarning": "Предупреждение о доступе оператора шлюза", + "creating": "Создание...", + "create": "Создать агента", + "summoningFailed": "Вызов не удался. Пожалуйста, измените настройки и попробуйте снова.", + "errors": { + "noProvider": "Нет доступного провайдера", + "failedCreate": "Не удалось создать агента" + } + }, + "channel": { + "title": "Подключение канала", + "description": "Подключите платформу обмена сообщениями к вашему агенту. Вы всегда можете добавить каналы позже.", + "skip": "Пропустить", + "skipFinish": "Пропустить и завершить", + "agent": "Агент:", + "channelType": "Тип канала", + "channelTypeHint": "Платформа обмена сообщениями для подключения. Например, Telegram, Discord, Zalo.", + "name": "Название", + "nameHint": "Внутренний идентификатор этого экземпляра канала. Генерируется автоматически из типа канала.", + "namePlaceholder": "my-telegram", + "displayName": "Отображаемое имя", + "displayNameHint": "Понятное имя, отображаемое в интерфейсе. Необязательно.", + "displayNamePlaceholder": "Бот продаж", + "credentials": "Учётные данные", + "credentialsHint": "Шифруются на стороне сервера. Никогда не возвращаются в ответах API.", + "creating": "Создание...", + "create": "Создать канал", + "errors": { + "noAgent": "Нет доступного агента", + "requiredFields": "Обязательно: {{fields}}", + "failedCreate": "Не удалось создать канал" + } + }, + "common": { + "back": "Назад" + }, + "skipSetup": "Пропустить настройку и перейти в панель управления", + "skipSetupConfirm": "Вы уверены, что хотите пропустить настройку? Вы всегда можете настроить провайдеров и агентов позже из панели управления.", + "switchTenant": "Сменить", + "allTenants": "Все арендаторы" +} diff --git a/ui/web/src/i18n/locales/ru/sidebar.json b/ui/web/src/i18n/locales/ru/sidebar.json new file mode 100644 index 00000000..1dabb3f3 --- /dev/null +++ b/ui/web/src/i18n/locales/ru/sidebar.json @@ -0,0 +1,50 @@ +{ + "groups": { + "core": "Основное", + "conversations": "Беседы", + "connectivity": "Подключения", + "capabilities": "Возможности", + "data": "Данные", + "monitoring": "Мониторинг", + "system": "Система" + }, + "nav": { + "overview": "Обзор", + "chat": "Чат", + "agents": "Агенты", + "agentTeams": "Связи и команды агентов", + "sessions": "Сессии", + "pendingMessages": "Ожидающие сообщения", + "channels": "Каналы", + "webhooks": "Вебхуки", + "contacts": "Контакты", + "skills": "Навыки", + "cron": "Cron", + "hooks": "Хуки", + "customTools": "Пользовательские инструменты", + "builtinTools": "Встроенные инструменты", + "mcpServers": "Серверы MCP", + "memory": "Память", + "vault": "Хранилище знаний", + "knowledgeGraph": "Граф знаний", + "traces": "Трассировки", + "realtimeEvents": "События в реальном времени", + "usage": "Использование", + "logs": "Журналы", + "storage": "Хранилище", + "providers": "Провайдеры", + "config": "Конфигурация", + "approvals": "Одобрения", + "importExport": "Импорт и экспорт", + "nodes": "Узлы", + "tts": "TTS", + "activity": "Активность", + "cliCredentials": "Учётные данные CLI", + "apiKeys": "Ключи API", + "apiDocs": "Документация API", + "packages": "Пакеты", + "tenants": "Арендаторы", + "backupRestore": "Резервное копирование и восстановление", + "workstations": "Рабочие станции" + } +} diff --git a/ui/web/src/i18n/locales/ru/skills.json b/ui/web/src/i18n/locales/ru/skills.json new file mode 100644 index 00000000..a168b20a --- /dev/null +++ b/ui/web/src/i18n/locales/ru/skills.json @@ -0,0 +1,270 @@ +{ + "title": "Навыки", + "description": "Управление навыками и возможностями агентов", + "searchPlaceholder": "Поиск навыков...", + "emptyTitle": "Нет навыков", + "emptyDescription": "Навыки ещё не зарегистрированы.", + "noMatchTitle": "Нет подходящих навыков", + "noMatchDescription": "Попробуйте другой поисковый запрос.", + "columns": { + "name": "Имя", + "description": "Описание", + "source": "Источник", + "author": "Автор", + "agents": "Агенты", + "owner": "Владелец", + "visibility": "Режим доступа", + "accessMode": "Режим доступа", + "status": "Статус", + "actions": "Действия" + }, + "noDescription": "Нет описания", + "status": { + "unmanaged": "Не управляется", + "missingDepsCount": "{{count}} отсутствует" + }, + "filters": { + "label": "Фильтр", + "all": "Все навыки", + "attention": "Требуют внимания", + "missing-deps": "Отсутствуют зависимости", + "disabled": "Отключённые", + "archived": "Архивированные", + "unmanaged": "Не управляемые", + "clear": "Сбросить фильтры" + }, + "sort": { + "label": "Сортировка", + "name": "Имя", + "deps": "Отсутствующие зависимости", + "version": "Версия" + }, + "health": { + "total": "Всего", + "attention": "Требуют внимания", + "missingDeps": "Отсутствуют зависимости", + "disabled": "Отключённые", + "archived": "Архивированные", + "unmanaged": "Не управляемые" + }, + "owner": "Владелец", + "unknownOwner": "Неизвестно", + "grants": { + "title": "Доступ агентов для {{name}}", + "manage": "Управление доступом агентов", + "current": "Текущие права доступа агентов", + "none": "Прав доступа агентов пока нет.", + "add": "Добавить доступ", + "update": "Обновить доступ", + "grant": "Предоставить", + "save": "Сохранить", + "revoke": "Отозвать доступ", + "selectAgent": "Выберите агента", + "allowManage": "Разрешить этому агенту редактировать или удалять навык", + "canManage": "Может редактировать", + "grantAllAgents": "Предоставить всем агентам", + "grantAllPartial": "Не удалось предоставить доступ {{count}} агенту(ам)", + "loadFailed": "Не удалось загрузить права доступа", + "saveFailed": "Не удалось сохранить доступ", + "revokeFailed": "Не удалось отозвать доступ" + }, + "bulk": { + "selected": "Выбрано: {{count}}", + "selectSkill": "Выбрать {{name}}", + "selectPage": "Выбрать видимые навыки", + "enable": "Включить", + "disable": "Отключить", + "grantAllAgents": "Предоставить всем агентам", + "delete": "Удалить", + "clear": "Очистить", + "enabled": "Включено навыков: {{count}}", + "disabled": "Отключено навыков: {{count}}", + "grantedAllAgents": "Всем агентам предоставлен доступ к {{count}} навыку(ам)", + "deleted": "Удалено навыков: {{count}}", + "failed": "Массовое действие не удалось", + "deleteTitle": "Удалить выбранные навыки", + "deleteDescription": "Это удалит {{count}} выбранных пользовательских навыков. Введите DELETE для подтверждения.", + "customAvailable": "Доступно пользовательских действий: {{count}}", + "skippedSystem": "Пропущено системных навыков: {{count}}", + "customOnlyReason": "Это действие применимо только к пользовательским навыкам", + "noAgentsReason": "Нет доступных текущих агентов" + }, + "export": { + "format": "Формат архива", + "download": "Скачать", + "downloadSelected": "Скачать выбранные", + "downloading": "Загрузка...", + "downloadSuccess": "Загрузка начата", + "downloadFailed": "Загрузка не удалась" + }, + "visibility": { + "clickToCycle": "Нажмите, чтобы изменить режим доступа" + }, + "accessMode": { + "private": "Только владелец", + "internal": "Агенты с доступом", + "public": "Все агенты арендатора", + "unknown": "Неизвестно ({{value}})", + "clickToCycle": "Нажмите, чтобы изменить режим доступа" + }, + "delete": { + "title": "Удалить навык", + "description": "Вы уверены, что хотите удалить \"{{name}}\"? Это действие нельзя отменить.", + "confirmLabel": "Удалить" + }, + "upload": { + "title": "Загрузить навыки", + "description": "Загрузите ZIP-файлы, содержащие SKILL.md с YAML-фронтматтером (name, description, slug). Максимум {{max}} МБ на файл.", + "button": "Загрузить", + "uploading": "Загрузка...", + "dropOrClick": "Нажмите или перетащите .zip-файлы", + "dropHere": "Перетащите .zip-файлы сюда", + "onlyZip": "Принимаются только .zip-файлы", + "cancel": "Отмена", + "done": "Готово", + "tooLarge": "Файл превышает установленный лимит загрузки", + "invalidZip": "Некорректный ZIP-файл", + "noSkillMd": "ZIP должен содержать SKILL.md в корне", + "emptySkillMd": "SKILL.md пуст", + "noFrontmatter": "В SKILL.md отсутствует YAML-фронтматтер", + "nameRequired": "В фронтматтере SKILL.md обязательно поле name", + "invalidSlug": "Некорректный формат слага", + "failed": "Загрузка не удалась", + "validating": "Проверка...", + "validCount": "{{valid}} из {{total}} корректны", + "uploadCount": "Загрузить ({{count}})", + "successCount": "{{success}} из {{total}} загружено успешно", + "remove": "Удалить файл", + "tooManySkills": "ZIP содержит слишком много навыков (максимум 50)", + "multiDetected": "Обнаружено навыков: {{count}}", + "skillUnchanged": "Содержимое не изменилось — пропущено", + "slugConflict": "Слаг конфликтует с системным навыком", + "unchanged": "Без изменений", + "new": "Новый", + "agentManagers": "Предоставить агентам права управления", + "managerAgentsHelp": "Выбранные текущие агенты смогут обновлять этот навык позже.", + "selectAllAgents": "Выбрать всех агентов", + "selectAllCurrentAgents": "Выбрать всех текущих агентов", + "clearAgents": "Очистить агентов", + "noAgents": "Нет доступных агентов", + "selectedAgentsCount": "Выбрано {{selected}} из {{total}} текущих агентов", + "summaryUploaded": "Загружено: {{count}}", + "summaryWarnings": "Предупреждений: {{count}}", + "summaryUnchanged": "Без изменений: {{count}}", + "summaryFailed": "Не удалось: {{count}}", + "securityViolations": "Нарушения безопасности:", + "violationLine": "Строка {{line}}: {{reason}}" + }, + "edit": { + "title": "Редактировать навык", + "name": "Имя", + "description": "Описание", + "visibility": "Режим доступа", + "accessMode": "Режим доступа", + "tags": "Теги", + "addTag": "Добавить тег...", + "add": "Добавить", + "cancel": "Отмена", + "save": "Сохранить", + "saving": "Сохранение...", + "privateOption": "Приватный (только владелец)", + "internalOption": "Внутренний (агенты/пользователи с доступом)", + "publicOption": "Публичный (все агенты)" + }, + "system": "Системный", + "tabs": { + "core": "Основные", + "custom": "Пользовательские" + }, + "toggle": { + "enable": "Включить навык", + "disable": "Отключить навык", + "disabled": "Отключено" + }, + "tenant": { + "enabled": "Включено", + "disabled": "Отключено", + "default": "По умолчанию", + "overrideHint": "Переопределение этого навыка для арендатора", + "resetDefault": "Сбросить к значению по умолчанию" + }, + "deps": { + "rescan": "Пересканировать зависимости", + "rescanning": "Сканирование...", + "rescanSuccess": "Обновлено навыков: {{count}}", + "missing": "Отсутствуют: {{deps}}", + "install": "Установить зависимости", + "installAll": "Установить все зависимости", + "installing": "Установка...", + "installSuccess": "Зависимости успешно установлены", + "installPartial": "Некоторые зависимости не удалось установить", + "installItem": "Установить", + "itemSuccess": "Установлено", + "itemError": "Не удалось", + "missingTitle": "Отсутствующие зависимости", + "systemLabel": "Система", + "pythonLabel": "Python", + "nodeLabel": "Node.js", + "statusActive": "Активен", + "statusArchived": "Архивирован", + "runtimeMissing": "Отсутствуют предварительные требования среды выполнения", + "runtimeMissingDesc": "Основным навыкам требуются среды выполнения Python и/или Node.js внутри контейнера среды выполнения GoClaw. Среды выполнения, установленные на хосте, здесь не используются. Если вы используете опубликованный образ latest, отсутствие сред выполнения в нём ожидаемо.", + "runtimeMissingAction": "Откройте «Пакеты», чтобы проверить или установить среды выполнения в этом контейнере.", + "runtimeRequired": "Сначала установите среды выполнения контейнера" + }, + "agents": { + "creator": "Создатель", + "managers": "Менеджеры" + }, + "detail": { + "title": "Сведения о навыке", + "content": "Содержимое", + "files": "Файлы", + "version": "Версия:", + "current": "(текущая)", + "noContent": "Содержимое недоступно.", + "fileSearch": "Поиск файлов...", + "filesShown": "{{shown}} из {{total}} файлов", + "copyLink": "Копировать ссылку", + "copySuccess": "Ссылка скопирована", + "copyFailed": "Не удалось скопировать ссылку", + "editContent": "Редактировать содержимое", + "editContentLoading": "Загрузка...", + "editContentSystemHint": "Системные навыки доступны только для чтения и не могут быть отредактированы из веб-интерфейса.", + "editContentLoadFailed": "Не удалось загрузить содержимое навыка для редактирования" + }, + "evolution": { + "tab": "Эволюция", + "title": "Саморазвитие", + "enabled": "Включено", + "disabled": "Отключено", + "status": "Статус: {{status}}", + "unavailable": "Данные эволюции для этого навыка недоступны.", + "modes": { + "suggest_only": "Только предложения", + "auto_analyze": "Автоанализ" + }, + "metrics": "Метрики", + "total": "Всего", + "succeeded": "Успешно", + "failed": "Неудачно", + "successRate": "Доля успеха", + "suggestions": "Предложения", + "noSuggestions": "Нет предложений.", + "noReason": "Причина не указана.", + "approve": "Одобрить", + "reject": "Отклонить", + "apply": "Применить", + "activity": "Активность", + "noActivity": "Нет недавней активности." + }, + "toast": { + "updated": "Навык обновлён", + "updateFailed": "Не удалось обновить навык", + "deleted": "Навык удалён", + "deleteFailed": "Не удалось удалить навык", + "rescanUpdated": "Обновлено навыков: {{count}}", + "rescanNoChanges": "Все навыки актуальны", + "rescanFailed": "Не удалось пересканировать зависимости" + } +} diff --git a/ui/web/src/i18n/locales/ru/storage.json b/ui/web/src/i18n/locales/ru/storage.json new file mode 100644 index 00000000..8b66c932 --- /dev/null +++ b/ui/web/src/i18n/locales/ru/storage.json @@ -0,0 +1,25 @@ +{ + "title": "Хранилище", + "description": "Обозреватель файлов рабочей области", + "descriptionWithPath": "{{path}} — {{size}}", + "sizeCacheInfo": "Размер хранилища кэшируется на 60 минут", + "unsupportedFile": "Предпросмотр этого типа файла не поддерживается", + "download": "Скачать", + "delete": { + "fileTitle": "Удалить файл", + "folderTitle": "Удалить папку", + "description": "Вы уверены, что хотите удалить {{name}}?", + "folderWarning": " Всё содержимое будет удалено рекурсивно.", + "undone": " Это действие нельзя отменить.", + "deleting": "Удаление...", + "confirmLabel": "Удалить" + }, + "upload": { + "title": "Загрузка в хранилище", + "description": "Загрузка файлов в каталог данных" + }, + "toast": { + "deleted": "Файл удалён", + "deleteFailed": "Не удалось удалить файл" + } +} diff --git a/ui/web/src/i18n/locales/ru/system-settings.json b/ui/web/src/i18n/locales/ru/system-settings.json new file mode 100644 index 00000000..40b46be0 --- /dev/null +++ b/ui/web/src/i18n/locales/ru/system-settings.json @@ -0,0 +1,103 @@ +{ + "title": "Системные настройки", + "save": "Сохранить", + "cancel": "Отмена", + "saving": "Сохранение...", + "saved": "Настройки сохранены", + "loadFailed": "Не удалось загрузить настройки", + "saveFailed": "Не удалось сохранить настройки", + "embedding": { + "title": "Эмбеддинги", + "description": "Провайдер и модель для векторных эмбеддингов и семантического поиска.", + "importance": "Эмбеддинги критичны для точности восстановления памяти, поиска навыков, графа знаний и сопоставления задач. Изменения затрагивают всю систему.", + "provider": "Провайдер эмбеддингов", + "model": "Модель эмбеддингов", + "providerTip": "Провайдер LLM, используемый для генерации текстовых эмбеддингов.", + "modelTip": "Модель эмбеддингов (напр. text-embedding-3-small). Должна поддерживать эндпоинт /embeddings.", + "providerPlaceholder": "Выберите провайдера", + "modelPlaceholder": "text-embedding-3-small", + "supportedProviders": "Проверено 1536d: OpenAI (text-embedding-3-small/large), Gemini (gemini-embedding-001 + dimensions), Mistral (codestral-embed), Cohere (embed-v4), DashScope (text-embedding-v3 + dimensions). Настройте размерность в параметрах провайдера для моделей с нативной размерностью > 1536.", + "verify": "Проверить эмбеддинги", + "verifyRequired": "Проверьте модель эмбеддингов перед сохранением.", + "verifyFailed": "Проверка не удалась", + "dimensions": "{{count}} измерений", + "dimensionsMismatch": "{{count}} измерений — не соответствует требуемым 1536", + "maxChunkLen": "Макс. длина фрагмента", + "maxChunkLenHint": "Максимум символов на фрагмент памяти перед разбиением.", + "chunkOverlap": "Перекрытие фрагментов", + "chunkOverlapHint": "Перекрытие символов между последовательными фрагментами для непрерывности контекста." + }, + "ux": { + "title": "Поведение UX", + "description": "Управляйте тем, как шлюз отображает активность агента пользователям.", + "intentClassify": "Классификация сообщений занятого агента", + "intentClassifyHint": "Когда агент уже обрабатывает предыдущий ход, классифицировать новые сообщения как вопросы о статусе, запросы на отмену, корректирующие инструкции или новые запросы.", + "intentClassifyInfo": "Это применяется только пока сессия занята; это не определяет, нужен ли запросу командный рабочий процесс.", + "teamWorkClassify": "Классификация командной работы", + "teamWorkClassifyHint": "Классифицировать новые запросы как прямую обработку или командный/делегирующий рабочий процесс.", + "teamWorkClassifyInfo": "Когда запрос выходит за рамки текущего агента или должен быть разбит, система использует эмбеддинги как признак и просит модель выбрать между прямой обработкой и командным/делегирующим рабочим процессом. Система также проверяет разрешения лидера/участника; участники могут создавать запросы на помощь только когда команда включила запросы участников и автодиспетчеризацию.", + "teamWorkClassifyEmbeddingRequired": "Настройте эмбеддинги перед включением классификации командной работы." + }, + "kg": { + "title": "Граф знаний", + "description": "Извлекайте сущности и связи из памяти агента в граф знаний с возможностью поиска.", + "provider": "Провайдер извлечения", + "model": "Модель извлечения", + "providerTip": "Провайдер LLM, используемый для извлечения сущностей из памяти.", + "modelTip": "Модель для извлечения сущностей/связей (напр. claude-sonnet-4-20250514).", + "providerPlaceholder": "Выберите провайдера", + "modelPlaceholder": "Выберите модель", + "minConfidence": "Мин. уверенность", + "minConfidenceHint": "Минимальный порог уверенности (0–1) для извлечённых сущностей.", + "info": "Автоматически извлекает сущности и связи, когда агенты записывают в память. Оставьте провайдера пустым для отключения." + }, + "bg": { + "title": "Фоновые воркеры", + "description": "Провайдер и модель для фоновых задач (обогащение хранилища, консолидация, сновидения).", + "provider": "Провайдер", + "model": "Модель", + "providerTip": "Провайдер LLM для фоновых задач. Оставьте пустым для использования значения агента по умолчанию.", + "modelTip": "Модель для фоновых задач. Оставьте пустым для использования значения провайдера по умолчанию.", + "providerPlaceholder": "(агент по умолчанию)", + "modelPlaceholder": "(по умолчанию)", + "info": "Используется для обогащения хранилища (обобщение документов), консолидации (сводки сессий) и сновидений. Оставьте пустым, чтобы вернуться к провайдеру агента по умолчанию." + }, + "skills": { + "title": "Навыки", + "description": "Настройки пакетов навыков и слэш-команд арендатора.", + "maxUploadSize": "Макс. размер загрузки", + "maxUploadSizeHint": "Лимит ZIP на файл в МБ. Допустимый диапазон: 1-500.", + "slashEnabled": "Включить слэш-команды", + "slashEnabledHint": "Обнаруживать /skill-name и /use skill-name в начале запросов пользователя.", + "slashSuggest": "Предлагать похожие навыки", + "slashSuggestHint": "Показывать близкие совпадения, когда запрошенный навык не найден.", + "slashPartial": "Разрешить частичное совпадение", + "slashPartialHint": "Позволять уникальным префиксам вроде /frontend активировать подходящие навыки.", + "slashPrefix": "Префикс команды", + "slashPrefixHint": "Одиночный символ, используемый перед командами навыков." + }, + "compaction": { + "title": "Сжатие ожидающих сообщений", + "description": "Обобщайте длинную историю ожидающих сообщений, чтобы оставаться в пределах контекста.", + "providerPlaceholder": "(авто)", + "threshold": "Порог", + "thresholdHint": "Сообщений до запуска сжатия.", + "keepRecent": "Сохранять недавние", + "keepRecentHint": "Сообщения, сохраняемые после сжатия.", + "maxTokens": "Макс. токенов", + "maxTokensHint": "Лимит токенов для сжатой сводки.", + "info": "Когда групповой чат превышает порог, LLM автоматически обобщает старые сообщения в единую сжатую запись, сохраняя самые свежие для контекста." + }, + "moreConfig": "Больше настроек", + "alert": { + "message": "Сбой фоновых воркеров: {{reason}}. Такие функции, как консолидация памяти и обогащение хранилища, могут не работать.", + "openSettings": "Исправить в настройках", + "reason": { + "auth": "Ошибка аутентификации (недействительный или истёкший ключ API)", + "auth_permanent": "Ключ API отозван или отключён", + "billing": "Проблема с оплатой (кредиты исчерпаны или требуется оплата)", + "model_not_found": "Модель не найдена (недействительное имя модели или недоступна)", + "unknown": "Неизвестная ошибка провайдера" + } + } +} diff --git a/ui/web/src/i18n/locales/ru/teams.json b/ui/web/src/i18n/locales/ru/teams.json new file mode 100644 index 00000000..e29b17bb --- /dev/null +++ b/ui/web/src/i18n/locales/ru/teams.json @@ -0,0 +1,410 @@ +{ + "title": "Команды", + "description": "Управление вашими командами агентов", + "createTeam": "Создать команду", + "searchPlaceholder": "Поиск команд...", + "emptyTitle": "Пока нет команд", + "emptyDescription": "Создайте свою первую команду, чтобы начать.", + "noMatchTitle": "Команды не найдены", + "noMatchDescription": "Попробуйте другой поисковый запрос.", + "viewCard": "Вид карточками", + "viewList": "Вид списком", + "scope": { + "all": "Все области", + "label": "Область" + }, + "delete": { + "title": "Удалить команду", + "description": "Вы уверены, что хотите удалить «{{name}}»? Это действие нельзя отменить.", + "confirmLabel": "Удалить" + }, + "detail": { + "lead": "Руководитель", + "memberCount": "{{count}} участник", + "memberCountPlural": "{{count}} участников", + "more": "ещё", + "deleteDescription": "Вы уверены, что хотите удалить «{{name}}»? Все участники, задачи и настройки будут безвозвратно удалены.", + "tabs": { + "tasks": "Задачи", + "members": "Участники", + "delegations": "Делегирования", + "workspace": "Рабочее пространство", + "settings": "Настройки" + } + }, + "members": { + "title": "Участники", + "noMembers": "Нет участников", + "addMember": "Добавить участника", + "addMemberTooltip": "В качестве участников команды можно добавлять только предопределённых агентов. Открытые агенты работают в контексте отдельного пользователя и не имеют общего контекста, необходимого для командной работы.", + "selectAgent": "Выберите агента для добавления...", + "noAvailableAgents": "Нет доступных агентов", + "searchAgents": "Поиск агентов...", + "adding": "Добавление...", + "add": "Добавить", + "added": "Участник добавлен", + "failedAdd": "Не удалось добавить участника", + "removed": "Участник удалён", + "failedRemove": "Не удалось удалить участника", + "removeMember": "Удалить участника", + "columns": { + "agent": "Агент", + "frontmatter": "Метаданные", + "role": "Роль", + "addedAt": "Добавлен", + "actions": "Действия" + } + }, + "tasks": { + "title": "Задачи", + "loading": "Загрузка задач...", + "noTasks": "Пока нет задач", + "noTasksDescription": "Задачи создаются агентами команды в процессе совместной работы.", + "refresh": "Обновить", + "createTask": "Создать задачу", + "createTaskTitle": "Создать задачу", + "subjectPlaceholder": "Тема задачи...", + "descriptionPlaceholder": "Необязательное описание...", + "priorityLabel": "Приоритет", + "assign": "Назначить", + "assignTo": "Назначить...", + "unassigned": "Не назначено", + "filters": { + "active": "Активные", + "pending": "Ожидающие", + "inProgress": "В работе", + "inReview": "На проверке", + "completed": "Завершённые", + "all": "Все" + }, + "columns": { + "id": "ID", + "subject": "Тема", + "status": "Статус", + "owner": "Владелец", + "priority": "Приоритет", + "taskId": "ID задачи", + "created": "Создано", + "actions": "Действия" + }, + "detail": { + "title": "Сведения о задаче", + "subject": "Тема", + "status": "Статус:", + "priority": "Приоритет:", + "owner": "Владелец:", + "type": "Тип:", + "blockedBy": "Заблокировано:", + "description": "Описание", + "result": "Результат", + "created": "Создано:", + "updated": "Обновлено:", + "taskId": "ID задачи:", + "noDescription": "Нет описания", + "noResult": "Результата пока нет", + "progress": "Прогресс", + "approve": "Одобрить", + "reject": "Отклонить", + "comments": "Комментарии", + "noComments": "Пока нет комментариев", + "commentPlaceholder": "Добавить комментарий...", + "addComment": "Комментировать", + "timeline": "Хронология", + "attachments": "Вложения", + "followupStatus": "Ожидание ответа", + "followupMessage": "Текст напоминания:", + "followupCount": "Отправлено напоминаний: {{count}}", + "followupCountMax": "Отправлено напоминаний: {{count}}/{{max}}", + "followupNext": "Следующее напоминание:", + "followupDone": "Все напоминания отправлены", + "download": "Скачать" + }, + "delete": "Удалить", + "deleteConfirm": "Удалить эту задачу? Это действие нельзя отменить.", + "selected": "Выбрано: {{count}}", + "deleteSelected": "Удалить выбранные", + "deleteBulkTitle": "Удалить задачи", + "deleteBulkConfirm": "Это безвозвратно удалит {{count}} задач(и). Введите «delete» для подтверждения.", + "badges": { + "awaitingReply": "ожидает ответа" + } + }, + "delegations": { + "noTitle": "Нет делегирований", + "noDescription": "Для этой команды не найдено записей о делегированиях.", + "refresh": "Обновить", + "columns": { + "sourceTarget": "Источник / Цель", + "task": "Задача", + "status": "Статус", + "duration": "Длительность", + "time": "Время" + } + }, + "workspace": { + "title": "Рабочее пространство", + "noFiles": "Пока нет файлов рабочего пространства", + "noFilesDescription": "Файлы создаются агентами команды в процессе совместной работы.", + "refresh": "Обновить", + "delete": "Удалить", + "confirmDelete": "Удалить этот файл?", + "columns": { + "fileName": "Имя файла", + "size": "Размер", + "uploadedBy": "Загружено", + "scope": "Область", + "updated": "Обновлено" + }, + "detail": { + "title": "Содержимое файла", + "empty": "Файл пуст или является бинарным" + }, + "upload": { + "title": "Загрузить в рабочее пространство", + "description": "Загрузите файлы для чтения агентами в процессе совместной работы" + }, + "crossScopeMoveError": "Нельзя перемещать файлы между разными областями", + "moveFailed": "Не удалось переместить файл" + }, + "auditLogs": { + "title": "Журналы аудита", + "empty": "Пока нет событий", + "loadMore": "Загрузить ещё", + "event": { + "created": "Создано", + "claimed": "Взято в работу", + "assigned": "Назначено", + "dispatched": "Отправлено", + "completed": "Завершено", + "failed": "Не удалось", + "cancelled": "Отменено", + "reviewed": "Проверено", + "approved": "Одобрено", + "rejected": "Отклонено", + "commented": "Прокомментировано", + "progress": "Прогресс", + "updated": "Обновлено", + "stale": "Устарело" + } + }, + "settings": { + "title": "Настройки команды", + "name": "Имя", + "displayName": "Отображаемое имя", + "description": "Описание", + "leadAgent": "Ведущий агент", + "status": "Статус", + "active": "Активна", + "inactive": "Неактивна", + "save": "Сохранить настройки", + "saving": "Сохранение...", + "saved": "Сохранено", + "failedSave": "Не удалось сохранить", + "userAccessControl": "Управление доступом пользователей", + "allowedUsers": "Разрешённые пользователи", + "allowedUsersHint": "Если задано, только эти пользователи могут запускать рабочие процессы команды. Пусто = разрешены все.", + "deniedUsers": "Заблокированные пользователи", + "deniedUsersHint": "Всегда заблокированы, переопределяет список разрешённых.", + "channelRestrictions": "Ограничения каналов", + "allowedChannels": "Разрешённые каналы", + "allowedChannelsHint": "Если задано, только эти каналы могут активировать рабочие процессы команды. Пусто = разрешены все.", + "deniedChannels": "Заблокированные каналы", + "deniedChannelsHint": "Сообщения из этих каналов всегда блокируются.", + "notifications": "Уведомления", + "notifyDispatched": "Задача отправлена", + "notifyDispatchedHint": "Уведомлять, когда задача назначается участнику команды.", + "notifyProgress": "Прогресс задачи", + "notifyProgressHint": "Уведомлять, когда участник обновляет прогресс задачи.", + "notifyFailed": "Задача не выполнена", + "notifyFailedHint": "Уведомлять при сбое задачи.", + "notifySlowTool": "Оповещение о медленном инструменте", + "notifySlowToolHint": "Системное оповещение, когда вызов инструмента занимает аномально много времени. Всегда напрямую — никогда через руководителя.", + "notifyMode": "Режим доставки", + "notifyModeHint": "Как уведомления доставляются в канал чата.", + "notifyModeDirect": "Напрямую", + "notifyModeDirectDesc": "Отправлять уведомления прямо в канал чата. Быстро, без обработки ИИ.", + "notifyModeLeader": "Через руководителя", + "notifyModeLeaderDesc": "Агент-руководитель естественно переформулирует обновления перед отправкой пользователю.", + "notifyModeLeaderWarning": "Режим руководителя использует ИИ для переформулирования сообщений (расходует токены, может быть медленнее).", + "notifyCompleted": "Уведомлять при завершении задачи", + "notifyCommented": "Уведомлять при комментировании задачи", + "notifyNewTask": "Уведомлять при создании новой задачи", + "memberRequests": "Запросы участников", + "memberRequestsEnabled": "Разрешить задачи-запросы участников", + "memberRequestsEnabledDesc": "Участники могут создавать задачи-запросы, чтобы попросить помощи у других членов команды", + "memberRequestsAutoDispatch": "Автоматически отправлять исполнителю", + "memberRequestsAutoDispatchDesc": "Запросы отправляются немедленно; иначе они остаются в ожидании проверки руководителем", + "searchUsers": "Поиск пользователей...", + "selectChannel": "Выберите канал...", + "escalationPolicy": "Политика эскалации", + "escalationMode": "Режим эскалации", + "escalationModeHint": "Определяет, как обрабатываются привилегированные действия в рабочем пространстве (закрепление, теги, удаление чужих файлов, set_template), запрошенные агентами.", + "blockerEscalation": "Эскалация блокировок", + "blockerEscalationEnabled": "Включить эскалацию блокировок", + "blockerEscalationHint": "Когда участники сообщают о блокировке, задача автоматически завершается сбоем, и руководитель уведомляется для повтора с обновлёнными инструкциями.", + "escalationModeAuto": "Авто (по умолчанию)", + "escalationModeAutoDesc": "Руководитель на базе LLM оценивает запрос — либо отклоняет, либо создаёт задачу на проверку. Никогда не выполняет напрямую.", + "escalationModeReview": "Проверка", + "escalationModeReviewDesc": "Всегда создаёт ожидающую задачу для одобрения человеком. Без автоотклонения.", + "escalationModeReject": "Отклонить", + "escalationModeRejectDesc": "Отклоняет все запросы на привилегированные действия сразу. Режим блокировки.", + "escalationModeNone": "Нет", + "escalationModeNoneDesc": "Без политики эскалации. Ведущий агент выполняет действия напрямую.", + "escalationActions": "Действия эскалации", + "escalationActionsHint": "Выберите, какие действия в рабочем пространстве требуют эскалации. Если ничего не выбрано, эскалации подлежат все привилегированные действия.", + "followupReminders": "Напоминания", + "followupInterval": "Интервал напоминаний (минуты)", + "followupIntervalHint": "Сколько ждать перед отправкой каждого напоминания пользователю.", + "followupMaxReminders": "Макс. напоминаний", + "followupMaxRemindersHint": "Максимальное число напоминаний на задачу. 0 = без ограничений (напоминать, пока пользователь не ответит).", + "workspace": "Рабочее пространство", + "workspaceScope": "Область рабочего пространства", + "workspaceScopeHint": "Управляйте тем, как организованы файлы рабочего пространства команды.", + "workspaceScopeIsolated": "Изолированная", + "workspaceScopeIsolatedDesc": "У каждого разговора своя папка рабочего пространства. Файлы из одного чата не видны в другом.", + "workspaceScopeShared": "Общая", + "workspaceScopeSharedDesc": "Все разговоры используют одну папку рабочего пространства. Файлы, созданные в любом чате, видны всем.", + "teamVersion": "Версия команды", + "versionBasic": "Базовая", + "versionAdvanced": "Расширенная", + "versionBasicDesc": "Управление задачами и общее рабочее пространство.", + "versionAdvancedDesc": "Все возможности V1 плюс блокировка выполнения, напоминания, рабочий процесс проверки, отслеживание прогресса, комментарии, журнал аудита и автовосстановление.", + "whatsNew": "В чём отличие?", + "versionModal": { + "title": "Возможности Super Team", + "feature": "Возможность", + "taskManagement": "Управление задачами", + "taskManagementDesc": "Создание, назначение, завершение, отмена задач", + "sharedWorkspace": "Общее рабочее пространство", + "sharedWorkspaceDesc": "Файлы, версии, командная работа", + "executionLocking": "Блокировка выполнения", + "executionLockingDesc": "Предотвращает конфликты задач с автовосстановлением", + "followupReminders": "Напоминания", + "followupRemindersDesc": "Автоматически напоминать в ожидании ответа пользователя", + "reviewWorkflow": "Рабочий процесс проверки", + "reviewWorkflowDesc": "Отправлять на проверку человеком перед завершением", + "progressTracking": "Отслеживание прогресса", + "progressTrackingDesc": "Обновления процентов и шагов в реальном времени", + "commentsAudit": "Комментарии и аудит", + "commentsAuditDesc": "Комментарии к задачам и полная история событий", + "autoRecovery": "Автовосстановление", + "autoRecoveryDesc": "Зависшие задачи автоматически сбрасываются по тайм-ауту", + "escalationPolicy": "Эскалация блокировок", + "escalationPolicyDesc": "Участники могут сообщать о блокировках — задача автоматически завершается сбоем, и руководитель уведомляется для повтора", + "comingSoon": "Скоро", + "downgradeNote": "Понижение версии безопасно — данные V2 сохраняются, но скрываются.", + "betaNote": "Некоторые возможности сейчас в бета-версии.", + "gotIt": "Понятно" + } + }, + "create": { + "title": "Создать команду", + "name": "Имя *", + "namePlaceholder": "напр. Research Team", + "nameHint": "Строчные буквы, цифры, дефисы", + "description": "Описание", + "descriptionPlaceholder": "Необязательное описание команды...", + "leadAgent": "Ведущий агент *", + "selectLeadAgent": "Выберите ведущего агента...", + "loadingAgents": "Загрузка агентов...", + "noActiveAgents": "Активные агенты не найдены. Сначала создайте агентов.", + "members": "Участники", + "membersTooltip": "В качестве участников команды можно добавлять только предопределённых агентов. Открытые агенты работают в контексте отдельного пользователя и не имеют общего контекста, необходимого для командной работы.", + "needMembers": "Выберите хотя бы 1 участника для команды", + "searchMembers": "Найдите и добавьте участников...", + "displayName": "Отображаемое имя", + "displayNamePlaceholder": "Support Team", + "cancel": "Отмена", + "create": "Создать", + "creating": "Создание..." + }, + "rename": { + "nameAria": "Переименовать команду", + "descAria": "Редактировать описание команды", + "descPlaceholder": "Описание команды", + "addDescription": "+ Добавить описание", + "emptyError": "Имя команды обязательно", + "staleError": "Команда была изменена в другом месте — ваше изменение отменено" + }, + "toast": { + "created": "Команда создана", + "deleted": "Команда удалена", + "updated": "Команда обновлена", + "memberAdded": "Участник добавлен", + "memberRemoved": "Участник удалён", + "failedCreate": "Не удалось создать команду", + "failedDelete": "Не удалось удалить команду", + "failedUpdate": "Не удалось обновить команду", + "failedAddMember": "Не удалось добавить участника", + "failedRemoveMember": "Не удалось удалить участника", + "taskCreated": "Задача создана", + "taskDeleted": "Задача удалена", + "taskAssigned": "Задача назначена", + "taskApproved": "Задача одобрена", + "taskRejected": "Задача отклонена", + "tasksBulkDeleted": "Задачи удалены", + "failedCreateTask": "Не удалось создать задачу", + "failedDeleteTask": "Не удалось удалить задачу", + "failedAssignTask": "Не удалось назначить задачу", + "failedApproveTask": "Не удалось одобрить задачу", + "failedRejectTask": "Не удалось отклонить задачу", + "failedBulkDeleteTasks": "Не удалось удалить задачи", + "workspaceFileDeleted": "Файл удалён", + "failedDeleteWorkspaceFile": "Не удалось удалить файл" + }, + "board": { + "kanban": "Доска", + "list": "Список", + "groupBy": "Группировать по", + "groupByStatus": "Статус", + "groupByOwner": "Владелец", + "groupByType": "Тип", + "running": "Выполняется", + "locked": "Агент выполняется", + "emptyColumn": "Нет задач", + "unassigned": "Не назначено", + "createViaChat": "Пообщайтесь с агентом команды, чтобы создать задачи" + }, + "sidebar": { + "members": "Участники", + "workspace": "Рабочее пространство", + "delegations": "Делегирования", + "settings": "Настройки", + "addMember": "Добавить участника" + }, + "tabs": { + "teams": "Команды агентов", + "board": "Доска", + "links": "Связи агентов", + "settings": "Настройки" + }, + "links": { + "title": "Связи агентов", + "empty": "Связи агентов не настроены.", + "create": "Создать связь", + "editLink": "Редактировать связь", + "save": "Сохранить", + "source": "Исходный агент", + "target": "Целевой агент", + "selectAgent": "Выберите агента...", + "direction": "Направление", + "outbound": "Исходящее", + "inbound": "Входящее", + "bidirectional": "Двунаправленное", + "description": "Описание", + "descriptionPlaceholder": "Необязательное описание...", + "maxConcurrent": "Макс. одновременных", + "status": "Статус", + "active": "Активна", + "disabled": "Отключена", + "deleteTitle": "Удалить связь", + "confirmDelete": "Удалить эту связь агентов? Это действие нельзя отменить.", + "deleted": "Связь удалена", + "deleteFailed": "Не удалось удалить связь", + "created": "Связь создана", + "createFailed": "Не удалось создать связь", + "updated": "Связь обновлена", + "updateFailed": "Не удалось обновить связь", + "sameAgentError": "Исходный и целевой агенты должны различаться." + } +} diff --git a/ui/web/src/i18n/locales/ru/tenants.json b/ui/web/src/i18n/locales/ru/tenants.json new file mode 100644 index 00000000..c4a80d2b --- /dev/null +++ b/ui/web/src/i18n/locales/ru/tenants.json @@ -0,0 +1,33 @@ +{ + "title": "Арендаторы", + "description": "Управление арендаторами и доступом пользователей", + "createTenant": "Создать арендатора", + "name": "Имя", + "slug": "Слаг", + "status": "Статус", + "users": "Пользователи", + "role": "Роль", + "addUser": "Добавить пользователя", + "removeUser": "Удалить пользователя", + "userId": "ID пользователя", + "active": "Активен", + "suspended": "Приостановлен", + "archived": "Архивирован", + "allTenants": "Все арендаторы", + "currentTenant": "Текущий арендатор", + "noTenants": "Арендаторы не найдены", + "confirmRemoveUser": "Удалить этого пользователя из арендатора?", + "slugHelp": "URL-безопасный идентификатор (только строчные буквы и дефисы)", + "detail": "Сведения об арендаторе", + "back": "Назад к арендаторам", + "userManagement": "Управление пользователями", + "addUserTitle": "Добавить пользователя в арендатора", + "selectRole": "Выберите роль", + "roleOwner": "Владелец", + "roleAdmin": "Администратор", + "roleOperator": "Оператор", + "roleMember": "Участник", + "roleViewer": "Наблюдатель", + "noUsers": "В этом арендаторе нет пользователей", + "created": "Создан" +} diff --git a/ui/web/src/i18n/locales/ru/tools.json b/ui/web/src/i18n/locales/ru/tools.json new file mode 100644 index 00000000..71ac15f0 --- /dev/null +++ b/ui/web/src/i18n/locales/ru/tools.json @@ -0,0 +1,286 @@ +{ + "custom": { + "title": "Пользовательские инструменты", + "description": "Управление пользовательскими инструментами на основе shell для агентов", + "createTool": "Создать инструмент", + "searchPlaceholder": "Поиск инструментов...", + "emptyTitle": "Нет пользовательских инструментов", + "emptyDescription": "Создайте свой первый пользовательский инструмент, чтобы начать.", + "noMatchTitle": "Инструменты не найдены", + "noMatchDescription": "Попробуйте другой поисковый запрос.", + "noDescription": "Нет описания", + "columns": { + "name": "Имя", + "description": "Описание", + "scope": "Область", + "enabled": "Включено", + "timeout": "Тайм-аут", + "actions": "Действия" + }, + "scope": { + "agent": "Агент", + "global": "Глобальный" + }, + "delete": { + "title": "Удалить пользовательский инструмент", + "description": "Вы уверены, что хотите удалить «{{name}}»? Это действие нельзя отменить.", + "confirmLabel": "Удалить" + }, + "form": { + "createTitle": "Создать пользовательский инструмент", + "editTitle": "Редактировать инструмент", + "name": "Имя *", + "namePlaceholder": "my-tool", + "nameHint": "Только строчные буквы, цифры и дефисы", + "description": "Описание", + "descriptionPlaceholder": "Что делает этот инструмент...", + "command": "Команда *", + "commandHint": "Shell-шаблон. Используйте {{.key}} для подстановки параметров.", + "parameters": "Параметры (JSON Schema)", + "workingDir": "Рабочий каталог", + "workingDirPlaceholder": "/path/to/dir", + "timeout": "Тайм-аут (секунды)", + "agentId": "ID агента (необязательно)", + "agentIdPlaceholder": "Оставьте пустым для глобальной области", + "enabled": "Включено", + "cancel": "Отмена", + "create": "Создать", + "update": "Обновить", + "saving": "Сохранение...", + "errors": { + "nameRequired": "Имя и команда обязательны", + "nameSlug": "Имя должно быть корректным слагом (только строчные буквы, цифры, дефисы)", + "invalidJson": "Параметры должны быть корректным JSON" + } + }, + "toast": { + "created": "Инструмент создан", + "createdDesc": "{{name}} добавлен", + "updated": "Инструмент обновлён", + "deleted": "Инструмент удалён", + "failedCreate": "Не удалось создать инструмент", + "failedUpdate": "Не удалось обновить инструмент", + "failedDelete": "Не удалось удалить инструмент" + } + }, + "builtin": { + "title": "Встроенные инструменты", + "description": "Управление системными встроенными инструментами. Включайте/отключайте или настраивайте параметры глобально.", + "searchPlaceholder": "Поиск инструментов...", + "emptyTitle": "Нет встроенных инструментов", + "emptyDescription": "Встроенные инструменты ещё не были инициализированы.", + "noMatchTitle": "Инструменты не найдены", + "noMatchDescription": "Попробуйте другой поисковый запрос.", + "toolCount": "{{count}} инструмент", + "toolCountPlural": "{{count}} инструментов", + "categoryCount": "{{count}} категорий", + "requires": "треб.", + "requiresTooltip": "Требуется: {{list}}", + "deprecated": "Устарел", + "deprecatedTooltip": "Этот инструмент устарел и больше недоступен", + "settings": "Настройки", + "configuredVia": "Настраивается на странице конфигурации", + "tenantOverride": "Переопределение арендатора", + "tenantEnabled": "Включено для этого арендатора", + "tenantDisabled": "Отключено для этого арендатора", + "tenantDefault": "Используется значение по умолчанию", + "resetToDefault": "Сбросить к значению по умолчанию", + "tenantOverrideBadge": "Переопределение арендатора", + "tenantOverrideHint": "Редактирование переопределения этого арендатора. Изменения применяются только к данному арендатору.", + "tenantOverrideNewHint": "Создание нового переопределения для этого арендатора. Предзаполнено глобальными значениями по умолчанию.", + "resetToGlobalDefault": "Сбросить к глобальному значению по умолчанию", + "unconfiguredWarning": "{{count}} инструментов требуют настройки провайдера, чтобы агенты могли использовать их в полную силу.", + "unconfiguredAction": "Настроить", + "categories": { + "filesystem": "Файловая система", + "runtime": "Среда выполнения", + "web": "Веб", + "memory": "Память", + "media": "Медиа", + "browser": "Браузер", + "sessions": "Сессии", + "messaging": "Сообщения", + "scheduling": "Планирование", + "subagents": "Агенты", + "skills": "Навыки", + "delegation": "Делегирование", + "teams": "Команды" + }, + "settingsDialog": { + "title": "Настройки — {{name}}", + "cancel": "Отмена", + "save": "Сохранить", + "saving": "Сохранение...", + "noFields": "Для этого инструмента нет настраиваемых параметров.", + "toast": { + "saved": "Настройки сохранены", + "failed": "Не удалось сохранить настройки" + } + }, + "jsonDialog": { + "title": "Настройки: {{name}}", + "description": "Редактируйте специфичные для инструмента настройки в формате JSON. Изменения вступают в силу сразу после сохранения.", + "formatJson": "Форматировать JSON", + "invalidJsonSyntax": "Некорректный синтаксис JSON", + "invalidJson": "Некорректный JSON", + "cannotFormat": "Не удаётся отформатировать: некорректный JSON", + "cancel": "Отмена", + "save": "Сохранить", + "saving": "Сохранение..." + }, + "kgSettings": { + "title": "Настройки графа знаний", + "description": "Настройте извлечение сущностей из записей в память. Требуется провайдер/модель с поддержкой структурированного вывода JSON.", + "extractionProvider": "Провайдер извлечения", + "extractionModel": "Модель извлечения", + "providerTip": "Провайдер LLM, используемый для извлечения сущностей и связей из текста.", + "modelTip": "ID модели для извлечения. Должна поддерживать структурированный вывод JSON.", + "minConfidence": "Мин. уверенность", + "minConfidenceHint": "Сущности с оценкой уверенности ниже этого значения отбрасываются (0.0–1.0).", + "autoExtract": "Автоизвлечение при записи в память", + "autoExtractHint": "Автоматически извлекать сущности, когда агенты записывают в файлы памяти.", + "cancel": "Отмена", + "save": "Сохранить", + "saving": "Сохранение..." + }, + "execSettings": { + "title": "Настройки выполнения", + "description": "Настройте тайм-аут команды на хосте для встроенного инструмента выполнения.", + "timeoutSeconds": "Тайм-аут (секунды)", + "timeoutHint": "Допустимый диапазон: {{min}}-{{max}} секунд.", + "sandboxHint": "Запуски в песочнице Docker используют настройку тайм-аута песочницы агента.", + "cancel": "Отмена", + "save": "Сохранить", + "saving": "Сохранение..." + }, + "searchChain": { + "title": "Цепочка провайдеров веб-поиска", + "description": "Перетащите, чтобы изменить порядок провайдеров поиска. Первым пробуется первый включённый провайдер с корректным ключом API.", + "maxResults": "Макс. результатов", + "locked": "Постоянный резервный вариант", + "cancel": "Отмена", + "save": "Сохранить", + "saving": "Сохранение...", + "providers": { + "exa": "Exa", + "tavily": "Tavily", + "brave": "Brave Search", + "duckduckgo": "DuckDuckGo" + }, + "apiKey": "Ключ API", + "apiKeySet": "✓ Ключ задан", + "apiKeyChange": "Изменить", + "apiKeyPlaceholder": "Введите ключ API", + "apiKeyReplacePlaceholder": "Введите новый ключ для замены" + }, + "ttsForm": { + "title": "Настройки TTS", + "description": "Настройте провайдера синтеза речи и режим автоприменения.", + "primary": "Основной провайдер", + "auto": "Автоматический режим", + "autoOff": "Выкл.", + "autoInbound": "Входящие голосовые", + "autoTagged": "С тегом", + "autoAlways": "Всегда", + "autoOffDesc": "Только когда агент явно использует инструмент tts", + "autoInboundDesc": "Автоприменение, когда пользователь отправляет голос/аудио", + "autoTaggedDesc": "Применять, когда текст содержит директиву [[tts]]", + "autoAlwaysDesc": "Всегда преобразовывать финальный ответ в речь", + "cancel": "Отмена", + "save": "Сохранить", + "saving": "Сохранение..." + }, + "sttForm": { + "title": "Настройки STT", + "description": "Настройте провайдеров распознавания речи для транскрибации голосовых сообщений.", + "providersLabel": "Провайдеры", + "providersPlaceholder": "Выберите провайдеров", + "elevenlabsSectionTitle": "ElevenLabs", + "elevenlabsApiKeyLabel": "Ключ API", + "elevenlabsDefaultLanguageLabel": "Язык по умолчанию", + "proxySectionTitle": "Прокси", + "proxyUrlLabel": "URL прокси", + "proxyApiKeyLabel": "Ключ API прокси", + "proxyTenantIdLabel": "ID арендатора прокси", + "whatsappEnabledLabel": "Включить STT для WhatsApp", + "whatsappPrivacyWarning": "Включение STT для WhatsApp нарушает сквозное шифрование голосовых сообщений, отправляемых этому агенту.", + "providersRequiredError": "Должен быть выбран хотя бы один провайдер.", + "cancel": "Отмена", + "save": "Сохранить", + "saving": "Сохранение...", + "saveSuccess": "Настройки STT сохранены", + "saveFailed": "Не удалось сохранить настройки STT" + }, + "extractorChain": { + "title": "Веб-загрузка — цепочка извлечения", + "description": "Упорядоченный список экстракторов контента. Каждый URL обрабатывается сверху вниз, пока один из них не вернёт качественный контент.", + "timeout": "Тайм-аут", + "baseUrl": "Базовый URL", + "save": "Сохранить", + "saving": "Сохранение…", + "cancel": "Отмена", + "extractors": { + "defuddle": "Defuddle (Cloudflare Worker)", + "defuddleDesc": "Извлекает чистый markdown через fetch.goclaw.sh с помощью библиотеки Defuddle.", + "html-to-markdown": "Встроенный HTML→Markdown", + "html-to-markdownDesc": "Внутрипроцессный конвертер на основе DOM с обнаружением скрытых элементов." + } + }, + "mediaChain": { + "providerChainSuffix": "— цепочка провайдеров", + "description": "Настройте и упорядочьте резервных провайдеров. Перетащите для изменения порядка — первым пробуется первый включённый провайдер.", + "newProvider": "Новый провайдер", + "provider": "Провайдер", + "model": "Модель", + "selectProvider": "Выберите провайдера", + "loadingModels": "Загрузка...", + "selectModel": "Выберите модель", + "timeout": "Тайм-аут", + "retries": "Повторы", + "settings": "Настройки", + "noProviders": "Провайдеры не настроены. Добавьте одного ниже.", + "addProvider": "Добавить провайдера", + "cancel": "Отмена", + "save": "Сохранить", + "saving": "Сохранение...", + "imageModelLabel": "Модель изображений", + "imageModelDefaultOption": "По умолчанию · gpt-image-2 (рекомендуется)", + "imageModelLegacyOption": "Устаревшая · gpt-image-1.5" + }, + "toast": { + "toggled": "Инструмент обновлён", + "failedToggle": "Не удалось обновить инструмент" + }, + "descriptions": { + "read_file": "Прочитать содержимое файла из рабочего пространства агента по пути", + "write_file": "Записать содержимое в файл в рабочем пространстве, создавая каталоги по мере необходимости", + "list_files": "Вывести список файлов и каталогов по заданному пути в рабочем пространстве", + "edit": "Применить точечные правки поиска и замены к существующим файлам без переписывания всего файла", + "exec": "Выполнить команду shell в рабочем пространстве и вернуть stdout/stderr", + "web_search": "Искать информацию в вебе с помощью поисковой системы (Brave или DuckDuckGo)", + "web_fetch": "Загрузить веб-страницу или эндпоинт API и извлечь её текстовое содержимое", + "memory_search": "Искать в долговременной памяти агента с помощью семантического сходства", + "memory_get": "Получить конкретный документ памяти по пути к файлу", + "knowledge_graph_search": "Искать сущности, связи и наблюдения в графе знаний агента", + "read_image": "Анализировать изображения с помощью провайдера LLM с поддержкой зрения", + "read_document": "Анализировать документы (PDF, Word, Excel, PowerPoint, CSV и др.) с помощью провайдера LLM с поддержкой документов", + "create_image": "Генерировать изображения по текстовым запросам с помощью провайдера генерации изображений", + "read_audio": "Анализировать аудиофайлы (речь, музыка, звуки) с помощью провайдера LLM с поддержкой аудио", + "read_video": "Анализировать видеофайлы с помощью провайдера LLM с поддержкой видео", + "create_video": "Генерировать видео по текстовым описаниям с помощью ИИ", + "create_audio": "Генерировать музыку или звуковые эффекты по текстовым описаниям с помощью ИИ", + "tts": "Преобразовать текст в естественно звучащую речь", + "browser": "Автоматизировать взаимодействие с браузером: навигация по страницам, клики по элементам, заполнение форм, скриншоты", + "sessions_list": "Вывести список активных сессий чата по всем каналам", + "session_status": "Получить текущий статус и метаданные конкретной сессии чата", + "sessions_history": "Получить историю сообщений конкретной сессии чата", + "sessions_send": "Отправить сообщение в активную сессию чата от имени агента", + "message": "Отправить проактивное сообщение пользователю в подключённом канале (Telegram, Discord и др.)", + "cron": "Планировать периодические задачи или управлять ими с помощью выражений cron, времени или интервалов", + "spawn": "Запустить субагента для выполнения задачи в фоновом режиме", + "skill_search": "Искать доступные навыки по ключевому слову или описанию, чтобы найти подходящие возможности", + "use_skill": "Активировать навык для использования его специализированных возможностей (маркер трассировки)", + "team_tasks": "Просматривать, создавать, обновлять и завершать задачи на доске задач команды" + } + } +} diff --git a/ui/web/src/i18n/locales/ru/topbar.json b/ui/web/src/i18n/locales/ru/topbar.json new file mode 100644 index 00000000..da7a5808 --- /dev/null +++ b/ui/web/src/i18n/locales/ru/topbar.json @@ -0,0 +1,35 @@ +{ + "expandSidebar": "Развернуть боковую панель", + "collapseSidebar": "Свернуть боковую панель", + "openMenu": "Открыть меню", + "systemSettings": "Системные настройки", + "toggleTheme": "Переключить тему", + "logout": "Выйти", + "logoutConfirm": "Вы уверены, что хотите выйти?", + "language": "Язык", + "timezone": "Часовой пояс", + "pairingRequests": "Запросы на привязку", + "pendingPairingRequest": "{{count}} ожидающий запрос на привязку", + "pendingPairingRequests": "{{count}} ожидающих запросов на привязку", + "apiKeys": "Ключи API", + "documents": "Документы", + "about": { + "menuItem": "О программе", + "title": "О GoClaw", + "version": "Версия", + "sourceCode": "Исходный код", + "license": "Лицензия", + "documentation": "Документация", + "reportBug": "Сообщить об ошибке", + "upToDate": "Актуальная версия", + "updateAvailable": "Доступно обновление: {{version}}", + "viewRelease": "Смотреть заметки о выпуске →", + "releaseNotes": "Заметки о выпуске" + }, + "languages": { + "en": "English", + "vi": "Tiếng Việt", + "zh": "中文", + "ko": "한국어" + } +} diff --git a/ui/web/src/i18n/locales/ru/traces.json b/ui/web/src/i18n/locales/ru/traces.json new file mode 100644 index 00000000..bbb28c69 --- /dev/null +++ b/ui/web/src/i18n/locales/ru/traces.json @@ -0,0 +1,130 @@ +{ + "title": "Трассировки", + "description": "Трассировки вызовов LLM и данные о производительности", + "filterPlaceholder": "Фильтр по ID агента...", + "filter": "Фильтр", + "allAgents": "Все агенты", + "allChannels": "Все каналы", + "status": { + "running": "Выполняется", + "completed": "Завершено", + "error": "Ошибка", + "cancelled": "Отменено" + }, + "filters": { + "search": "Поиск трассировок или ID трассировки...", + "status": "Статус", + "allStatuses": "Все статусы", + "advanced": "Расширенные", + "from": "С", + "to": "По", + "minInputTokens": "Мин. входных токенов", + "maxInputTokens": "Макс. входных токенов", + "minOutputTokens": "Мин. выходных токенов", + "maxOutputTokens": "Макс. выходных токенов", + "minToolCalls": "Мин. вызовов инструментов", + "maxToolCalls": "Макс. вызовов инструментов", + "toolName": "Имя инструмента", + "hasToolCalls": "Вызовы инструментов", + "anyToolCalls": "Любые вызовы инструментов", + "withToolCalls": "С вызовами инструментов", + "withoutToolCalls": "Без вызовов инструментов", + "clearFilter": "Сбросить фильтр", + "clearAll": "Сбросить всё", + "chips": { + "query": "Поиск", + "agentId": "Агент", + "status": "Статус", + "channel": "Канал", + "from": "С", + "to": "По", + "minInputTokens": "Мин. вход", + "maxInputTokens": "Макс. вход", + "minOutputTokens": "Мин. выход", + "maxOutputTokens": "Макс. выход", + "minToolCalls": "Мин. инструментов", + "maxToolCalls": "Макс. инструментов", + "toolName": "Инструмент", + "hasToolCalls": "Вызовы инструментов" + } + }, + "emptyTitle": "Нет трассировок", + "emptyDescription": "Трассировки не найдены. Трассировки записываются, когда агенты обрабатывают запросы.", + "columns": { + "name": "Имя", + "status": "Статус", + "duration": "Длительность", + "tokens": "Токены", + "spans": "Спаны", + "time": "Время" + }, + "source": { + "direct": "Напрямую", + "group": "Группа", + "cron": "Cron", + "team": "Команда", + "ws": "Веб", + "unknown": "Другое" + }, + "unnamed": "Без имени", + "stopRun": "Остановить запуск", + "cached": "из кеша", + "detail": { + "title": "Сведения о трассировке", + "copyTraceId": "Копировать ID трассировки", + "notFound": "Трассировка не найдена.", + "name": "Имя:", + "status": "Статус:", + "duration": "Длительность:", + "channel": "Канал:", + "tokens": "Токены:", + "spans": "Спаны:", + "started": "Начато:", + "delegatedFrom": "Делегировано от:", + "createdAt": "Создано:", + "input": "Ввод", + "output": "Вывод", + "copy": "Копировать", + "spansCount": "Спаны ({{count}})", + "llmCalls": "LLM", + "toolCalls": "инструмент", + "export": "Экспортировать трассировку", + "stopRun": "Остановить запуск" + }, + "span": { + "model": "Модель:", + "tokens": "Токены:", + "reasoning": "Рассуждение:", + "source": "источник", + "requested": "запрошено", + "effective": "фактически", + "fallback": "резервное", + "modelDefault": "по умолчанию для модели", + "supportedLevels": "поддерживается:", + "sourceValue": { + "provider_default": "по умолчанию у провайдера", + "reasoning": "расширенные настройки агента", + "thinking_level": "устаревший уровень мышления", + "unset": "не задано" + }, + "createdAt": "Создано:", + "input": "Ввод:", + "output": "Вывод:", + "copy": "Копировать", + "startTime": "Начало:", + "endTime": "Конец:", + "cacheRead": "чтение", + "cacheWrite": "запись", + "thinking": "мышление", + "cached": "из кеша" + }, + "toast": { + "failedLoad": "Не удалось загрузить трассировки", + "abortStopped": "Запуск остановлен", + "abortForced": "Запуск принудительно остановлен (очистка ещё выполняется в фоне)", + "abortAlreadyAborting": "Остановка уже выполняется", + "abortNotFound": "Запуск уже завершён", + "abortUnauthorized": "Нет прав на остановку этого запуска", + "abortFailed": "Не удалось остановить запуск" + } +} diff --git a/ui/web/src/i18n/locales/ru/tts.json b/ui/web/src/i18n/locales/ru/tts.json new file mode 100644 index 00000000..f26763b1 --- /dev/null +++ b/ui/web/src/i18n/locales/ru/tts.json @@ -0,0 +1,265 @@ +{ + "title": "Синтез речи (TTS)", + "description": "Настройка провайдеров TTS и параметров автоприменения", + "save": "Сохранить", + "saving": "Сохранение...", + "saveChanges": "Сохранить изменения", + "status": { + "title": "Статус", + "configured": "Настроено", + "disabled": "Отключено", + "noProvider": "Провайдер TTS не настроен", + "activeProvider": "Основной провайдер: {{provider}}, Авто: {{auto}}" + }, + "general": { + "title": "Общие настройки", + "primaryProvider": "Основной провайдер", + "autoApplyMode": "Режим автоприменения", + "replyMode": "Режим ответов", + "maxTextLength": "Максимальная длина текста", + "timeout": "Тайм-аут (мс)" + }, + "providers": { + "none": "Нет (отключено)", + "openai": "OpenAI", + "elevenlabs": "ElevenLabs", + "edge": "Edge (бесплатно)", + "minimax": "MiniMax", + "gemini": "Google Gemini" + }, + "autoModes": { + "off": "Выкл.", + "offDesc": "Агент может использовать инструмент TTS вручную", + "always": "Всегда", + "alwaysDesc": "Все ответы озвучиваются", + "inbound": "Входящие", + "inboundDesc": "Только когда пользователь отправляет голос/аудио", + "tagged": "По тегу", + "taggedDesc": "Только когда ответ содержит тег [[tts]]" + }, + "replyModes": { + "final": "Только финальные", + "finalDesc": "Только финальные ответы", + "all": "Все", + "allDesc": "Все ответы, включая инструменты/блоки" + }, + "providerSettings": "Настройки {{provider}}", + "openai": { + "apiKey": "API-ключ", + "apiBase": "Базовый URL API", + "model": "Модель", + "voice": "Голос", + "speed": { + "label": "Скорость", + "help": "Множитель скорости речи — 0.25 = самая медленная, 4.0 = самая быстрая." + }, + "responseFormat": { + "label": "Формат вывода", + "help": "Кодировка аудио, возвращаемая API (mp3, opus, aac, flac, wav, pcm)." + }, + "instructions": { + "label": "Инструкции", + "help": "Системный стилевой промпт — поддерживается только моделью gpt-4o-mini-tts." + } + }, + "elevenlabs": { + "apiKey": "API-ключ", + "baseUrl": "Базовый URL", + "voiceId": "ID голоса", + "modelId": "ID модели", + "voiceSettings": { + "stability": { + "label": "Стабильность", + "help": "0 = больше выразительных вариаций, 1 = более согласованно и стабильно." + }, + "similarityBoost": { + "label": "Усиление сходства", + "help": "Насколько точно вывод соответствует оригинальному клону голоса (0 = креативно, 1 = похоже)." + }, + "style": { + "label": "Усиление стиля", + "help": "Усиливает стиль голоса. 0 = нет; более высокие значения добавляют выразительности." + }, + "useSpeakerBoost": { + "label": "Усиление диктора", + "help": "Повышает чёткость голоса и сходство с целевым диктором." + }, + "speed": { + "label": "Скорость", + "help": "Скорость речи — диапазон от 0.7 (медленно) до 1.2 (быстро)." + } + }, + "applyTextNormalization": { + "label": "Нормализация текста", + "help": "Как раскрываются числа и сокращения (авто, вкл. или выкл.)." + }, + "seed": { + "label": "Seed (расширенное)", + "help": "Задайте фиксированный seed для воспроизводимого результата. 0 = случайно." + }, + "optimizeStreamingLatency": { + "label": "Задержка потока (расширенное)", + "help": "Уровень оптимизации задержки: 0 = нет, 4 = максимум." + }, + "languageCode": { + "label": "Код языка", + "help": "Подсказка языка ISO 639-1, напр. 'en' или 'vi'. Расширенное." + } + }, + "edge": { + "voice": "Голос", + "speechRate": "Темп речи", + "hint": "Edge TTS бесплатен и не требует API-ключа. Установите командой: pip install edge-tts", + "rate": { + "label": "Темп речи", + "help": "Регулировка темпа в процентах. -50% = намного медленнее, +100% = намного быстрее." + }, + "pitch": { + "label": "Высота тона", + "help": "Сдвиг высоты тона в Гц относительно нейтральной. Диапазон: от -50 Гц до +50 Гц." + }, + "volume": { + "label": "Громкость", + "help": "Регулировка громкости в процентах. -50% = тише, +100% = громче." + } + }, + "minimax": { + "apiKey": "API-ключ", + "groupId": "ID группы *", + "groupIdPlaceholder": "Обязательно для MiniMax", + "apiBase": "Базовый URL API", + "model": "Модель", + "voiceId": "ID голоса", + "speed": { + "label": "Скорость", + "help": "Множитель скорости речи — 0.5 = самая медленная, 2.0 = самая быстрая." + }, + "vol": { + "label": "Громкость", + "help": "Множитель громкости — 0.01 = почти беззвучно, 10.0 = самая громкая." + }, + "pitch": { + "label": "Высота тона", + "help": "Сдвиг высоты тона в полутонах. -12 = на октаву ниже, +12 = на октаву выше." + }, + "emotion": { + "label": "Эмоция", + "help": "Эмоциональный тон, применяемый к синтезированному голосу." + }, + "textNormalization": { + "label": "Нормализация текста", + "help": "Раскрываются ли числа и символы перед синтезом." + }, + "audio": { + "format": { + "label": "Формат аудио", + "help": "Кодировка вывода: mp3, pcm, flac или wav." + }, + "sampleRate": { + "label": "Частота дискретизации", + "help": "Частота дискретизации аудио в Гц. Для большинства случаев оставьте значение по умолчанию." + }, + "bitrate": { + "label": "Битрейт (только MP3)", + "help": "Битрейт MP3 в бит/с. Применяется только при формате mp3." + }, + "channel": { + "label": "Каналы", + "help": "1 = моно (меньший файл), 2 = стерео." + } + } + }, + "gemini": { + "apiKey": "API-ключ", + "apiBase": "Базовый URL API", + "voice": "Голос", + "model": "Модель", + "multiSpeaker": "Несколько дикторов", + "multiSpeakerHint": "Поддерживается до 2 дикторов", + "audioTags": "Аудиотеги", + "audioTagsHint": "Вставляйте выразительные теги, например [laughs], прямо в текст", + "speakerName": "Имя диктора", + "speakerVoice": "Голос диктора", + "addSpeaker": "Добавить диктора", + "removeSpeaker": "Удалить", + "maxSpeakersReached": "Достигнут максимум в 2 диктора", + "filterAll": "Все", + "filterEmotion": "Эмоция", + "filterPacing": "Темп", + "filterEffect": "Эффект", + "filterVoiceQuality": "Качество голоса", + "searchPlaceholder": "Поиск тегов…", + "insertTag": "Вставить", + "multiSpeakerMode": { + "title": "Режим нескольких дикторов", + "addSpeaker": "Добавить диктора", + "speakerName": "Имя диктора", + "speakerVoice": "Голос", + "removeSpeaker": "Удалить", + "limitReached": "Максимум 2 диктора" + }, + "audioTagsPanel": { + "title": "Аудиотеги", + "search": "Поиск тегов", + "categoryEmotion": "Эмоция", + "categoryPacing": "Темп", + "categoryEffect": "Эффект", + "categoryVoiceQuality": "Качество голоса", + "insertHint": "Нажмите на тег, чтобы вставить в позицию курсора" + }, + "previewBadge": "Предпросмотр" + }, + "voice_label": "Голос", + "voice_placeholder": "Выберите голос…", + "voice_picker": { + "requires_provider": "Сначала настройте провайдер TTS", + "enter_voice_id": "Введите voice_id вручную" + }, + "voice_refresh": "Обновить", + "voice_preview": "Предпросмотр", + "voice_stop_preview": "Стоп", + "voice_no_voices": "Нет доступных голосов", + "voice_save_config_first": "Сначала сохраните API-ключ, чтобы загрузить голоса", + "voice_loading": "Загрузка голосов…", + "voice_preview_error": "Предпросмотр не удался — обновление списка", + "model_label": "Модель", + "model_placeholder": "По умолчанию (арендатор/система)", + "empty_state": { + "title": "TTS не настроен", + "description": "Настройте провайдер TTS, чтобы включить голос для агентов.", + "cta": "Настроить TTS", + "non_owner_hint": "Обратитесь к владельцу рабочего пространства для настройки TTS." + }, + "override": { + "label": "Переопределить голос для этого агента", + "inherits": "Наследует: {{provider}} · {{voice}} · {{model}}", + "params": { + "title": "Тонкая настройка" + } + }, + "test": { + "button": "Проверить этот голос", + "sample_text": "Здравствуйте, это проверка голоса." + }, + "testConnection": { + "label": "Проверить подключение", + "testing": "Проверка…", + "success": "Подключение успешно", + "failed": "Подключение не удалось" + }, + "playground": { + "title": "Тестовая площадка", + "inputLabel": "Текст для синтеза", + "placeholder": "Введите текст, чтобы прослушать…", + "play": "Воспроизвести", + "stop": "Стоп", + "synthesizing": "Синтез…", + "playbackFailed": "Воспроизведение не удалось", + "requiresProvider": "Выберите провайдер выше, чтобы включить тестовое воспроизведение." + }, + "advanced": { + "title": "Расширенные настройки", + "count": "{{count}} расширенных настроек", + "count_one": "{{count}} расширенная настройка" + } +} diff --git a/ui/web/src/i18n/locales/ru/usage.json b/ui/web/src/i18n/locales/ru/usage.json new file mode 100644 index 00000000..17876328 --- /dev/null +++ b/ui/web/src/i18n/locales/ru/usage.json @@ -0,0 +1,172 @@ +{ + "title": "Использование", + "description": "Использование токенов и расходы по агентам", + "emptyTitle": "Нет данных об использовании", + "emptyDescription": "Данные об использовании появятся здесь после обработки запросов агентами.", + "summary": { + "sessions": "{{count}} сессий", + "inputTokens": "Входные токены", + "outputTokens": "Выходные токены", + "total": "Всего" + }, + "recentRecords": "Недавние записи", + "columns": { + "agent": "Агент", + "model": "Модель", + "provider": "Провайдер", + "input": "Вход", + "output": "Выход", + "total": "Всего", + "channel": "Канал", + "cost": "Стоимость", + "status": "Статус", + "actions": "Действия" + }, + "caps": { + "title": "Лимиты использования", + "description": "Ограничьте бюджет ИИ по токенам и стоимости в разрезе арендатора, агента, провайдера и модели.", + "window": "Окно", + "windows": { + "hour": "Почасовой", + "day": "Ежедневный", + "week": "Еженедельный", + "month": "Ежемесячный" + }, + "agent": "Агент", + "provider": "Провайдер", + "model": "Модель", + "allAgents": "Все агенты", + "allProviders": "Все провайдеры", + "maxTokens": "Макс. токенов", + "maxCost": "Макс. стоимость USD", + "create": "Создать лимит", + "edit": "Изменить лимит", + "save": "Сохранить лимит", + "cancel": "Отменить изменение", + "scope": "Область", + "tokens": "Токены", + "cost": "Стоимость", + "empty": "Политик лимитов пока нет.", + "tenantScope": "Лимит арендатора", + "tenantScoped": "На уровне арендатора", + "agentScoped": "На уровне агента", + "agentBudgetSource": "Бюджет агента", + "agentBudgetManaged": "Очистите это в поле месячного бюджета агента.", + "enabled": "Включено", + "disabled": "Отключено", + "delete": "Удалить лимит", + "recentBlocks": "Недавние блокировки", + "blocked": "Заблокировано", + "toast": { + "created": "Лимит использования создан", + "updated": "Лимит использования обновлён", + "createFailed": "Не удалось создать лимит использования", + "updateFailed": "Не удалось обновить лимит использования", + "deleted": "Лимит использования удалён", + "deleteFailed": "Не удалось удалить лимит использования" + } + }, + "analytics": { + "title": "Аналитика использования", + "period24h": "24 ч", + "period7d": "7 дней", + "period30d": "30 дней", + "periodCustom": "Произвольный", + "allAgents": "Все агенты", + "allProviders": "Все провайдеры", + "allChannels": "Все каналы", + "activeFilters": "Активные фильтры", + "clearAll": "Очистить всё", + "exportCsv": "Экспорт CSV", + "requests": "Запросы", + "tokens": "Токены", + "cost": "Стоимость", + "errors": "Ошибки", + "uniqueUsers": "Уникальные пользователи", + "llmCalls": "Вызовы LLM", + "toolCalls": "Вызовы инструментов", + "trendUp": "+{{value}}%", + "trendDown": "{{value}}%", + "vsPrevious": "по сравнению с предыдущим периодом", + "configurePricing": "Настройте цены моделей для учёта расходов", + "noData": "Нет данных за выбранный период", + "tokenChart": { + "title": "Использование токенов во времени", + "input": "Входные токены", + "output": "Выходные токены", + "cache": "Токены чтения кэша", + "thinking": "Токены рассуждений", + "total": "Всего" + }, + "requestChart": { + "title": "Объём запросов и ошибки", + "requests": "Запросы", + "errors": "Ошибки", + "errorRate": "Доля ошибок" + }, + "distribution": { + "provider": "Распределение по провайдерам", + "model": "Распределение по моделям", + "channel": "Распределение по каналам", + "other": "Другое", + "calls": "вызовов" + }, + "durationChart": { + "title": "Длительность и производительность", + "avgDuration": "Средняя длительность", + "errorRate": "Доля ошибок %" + }, + "knowledgeChart": { + "title": "Рост памяти и графа знаний", + "memoryDocs": "Документы памяти", + "memoryChunks": "Фрагменты памяти", + "kgEntities": "Сущности KG", + "kgRelations": "Связи KG" + }, + "topModels": { + "title": "Топ моделей", + "model": "Модель", + "provider": "Провайдер", + "llmCalls": "Вызовы LLM", + "inputTokens": "Входные токены", + "outputTokens": "Выходные токены", + "avgDuration": "Средняя длительность", + "cost": "Стоимость" + }, + "events": { + "title": "Аналитика событий ресурсов", + "description": "Подсчёт активности инструментов, навыков, MCP и среды выполнения по трассируемым выполнениям.", + "tabs": { + "tool": "Инструменты", + "skill": "Навыки", + "mcp_tool": "MCP", + "runtime_tool": "Среда выполнения" + }, + "metrics": { + "calls": "Вызовы", + "errorRate": "Доля ошибок", + "tokens": "Токены", + "avgDuration": "Средняя длительность", + "cost": "Стоимость" + }, + "activeBuckets": "{{count}} активных сегментов", + "unknownSource": "Неизвестно", + "empty": "Нет данных о событиях для этого выбора.", + "table": { + "resource": "Ресурс", + "source": "Источник", + "calls": "Вызовы", + "errors": "Ошибки", + "errorRate": "Доля ошибок", + "avgDuration": "Средняя длительность", + "tokens": "Токены", + "cost": "Стоимость" + } + }, + "tooltip": { + "date": "Дата", + "total": "Всего", + "errorRate": "{{value}}% доля ошибок" + } + } +} diff --git a/ui/web/src/i18n/locales/ru/v3-capabilities.json b/ui/web/src/i18n/locales/ru/v3-capabilities.json new file mode 100644 index 00000000..939dceae --- /dev/null +++ b/ui/web/src/i18n/locales/ru/v3-capabilities.json @@ -0,0 +1,56 @@ +{ + "title": "Возможности агента V3", + "subtitle": "Ключевые системы, обеспечивающие интеллект вашего агента", + "note": "Все возможности всегда включены для агентов v3.", + "tabs": { + "pipeline": "Конвейер", + "memory": "Память", + "knowledge": "Знания", + "orchestration": "Оркестрация" + }, + "pipeline": { + "title": "8-этапный конвейер выполнения", + "description": "Каждое сообщение проходит через 8 структурированных этапов в 3 фазах.", + "setup": "Подготовка", + "setupDesc": "Обогащает контекст конфигурацией агента, метаданными рабочего пространства, историей пользователя и автоматически внедряемыми воспоминаниями из прошлых сессий.", + "iteration": "Цикл итераций", + "iterationDesc": "Повторяется до завершения задачи или достижения максимума итераций.", + "think": "Мышление", + "thinkDesc": "Рассуждение LLM с полным контекстом", + "prune": "Обрезка", + "pruneDesc": "Управление токенами и сжатие", + "tools": "Инструменты", + "toolsDesc": "Выполнение вызовов инструментов от LLM", + "observe": "Наблюдение", + "observeDesc": "Захват и проверка вывода", + "checkpoint": "Контрольная точка", + "checkpointDesc": "Сохранение промежуточного состояния", + "finalize": "Завершение", + "finalizeDesc": "Сохраняет метаданные, генерирует события для консолидации памяти и очищает выполнение." + }, + "memory": { + "title": "3-уровневая система памяти", + "description": "Прогрессивная память от разговора до долговременных знаний.", + "l0Title": "L0 Рабочая память", + "l0Desc": "История сессии с автосжатием на основе токенов. Когда история превышает порог окна контекста, старые сообщения обобщаются через LLM и усекаются. Сброс памяти извлекает ключевые факты в постоянные файлы перед сжатием.", + "l1Title": "L1 Эпизодическая память", + "l1Desc": "Сводки сессий, создаваемые после каждого разговорного прохода. Каждая получает абстракт L0 (~50 токенов) для быстрого внедрения. Гибридный поиск FTS + вектор с TTL 90 дней. Релевантные абстракты автоматически внедряются в системный промпт каждый ход.", + "l2Title": "L2 Семантическая память", + "l2Desc": "Граф знаний с сущностями и связями, извлечёнными через LLM (порог уверенности 0.75). Автодедупликация объединяет похожие сущности. Сновидения консолидируют 5+ эпизодических записей в долговременные документы фактов, доступные для поиска через инструменты памяти.", + "eventFlow": "На основе событий: session.completed запускает создание L1, что запускает извлечение L2 и сновидения." + }, + "knowledge": { + "kgTitle": "Граф знаний", + "kgDesc": "Сущности (люди, проекты, технологии) и их связи извлекаются из сводок сессий через LLM. Темпоральное версионирование отслеживает, когда факты были действительны. Поиск через инструмент knowledge_graph_search с многошаговым обходом до 5 уровней в глубину.", + "vaultTitle": "Хранилище знаний", + "vaultDesc": "Реестр документов с разрешением [[wikilink]]. Унифицированный поиск объединяет документы хранилища (40%), эпизодические воспоминания (30%) и сущности KG (30%) в единый ранжированный набор результатов. Хеширование SHA-256 обнаруживает изменения содержимого для инкрементного индексирования.", + "dreamingTitle": "Сновидения", + "dreamingDesc": "Когда накапливается 5+ сводок сессий, LLM синтезирует долговременные факты: предпочтения пользователя, архитектуру проекта, повторяющиеся паттерны и ключевые решения с обоснованием. Хранятся как документы памяти с возможностью поиска. С задержкой в 10-минутных интервалах на агента." + }, + "orchestration": { + "delegateTitle": "Делегирование агентов", + "delegateDesc": "Агенты делегируют задачи связанным агентам через agent_links с проверкой разрешений. Синхронный режим блокируется до настраиваемого тайм-аута в ожидании результатов. Асинхронный режим срабатывает немедленно и объявляет результаты обратно через шину сообщений по завершении.", + "evolutionTitle": "Самоэволюция", + "evolutionDesc": "Отслеживает использование инструментов и метрики извлечения на еженедельном цикле. Обнаруживает три паттерна: низкое использование извлечения (< 20%), высокую частоту сбоев инструментов (< 10% успеха) и повторяющиеся паттерны инструментов (> 100 вызовов/неделю). Предлагает корректировки конфигурации с защищённым ограничителями автоприменением и откатом." + } +} diff --git a/ui/web/src/i18n/locales/ru/vault.json b/ui/web/src/i18n/locales/ru/vault.json new file mode 100644 index 00000000..ebf10a24 --- /dev/null +++ b/ui/web/src/i18n/locales/ru/vault.json @@ -0,0 +1,117 @@ +{ + "title": "Хранилище знаний", + "description": "Реестр документов с вики-ссылками и семантическим поиском", + "search": "Поиск", + "searchPlaceholder": "Поиск документов...", + "filterAgent": "Агент", + "allAgents": "Все агенты", + "allTypes": "Все", + "noDocuments": "В хранилище нет документов", + "noResults": "Нет результатов поиска", + "searchFailed": "Поиск не удался", + "create": "Создать", + "createDoc": "Создать документ", + "edit": "Изменить", + "save": "Сохранить", + "saving": "Сохранение...", + "cancel": "Отмена", + "delete": "Удалить", + "confirmDelete": "Удалить этот документ?", + "yes": "Да", + "no": "Нет", + "createLink": "Добавить ссылку", + "addLink": "Добавить ссылку", + "deleteLink": "Удалить ссылку", + "columns": { + "title": "Заголовок", + "agent": "Агент", + "path": "Путь", + "type": "Тип", + "scope": "Область", + "updated": "Обновлено" + }, + "detail": { + "metadata": "Метаданные", + "outlinks": "Исходящие ссылки", + "backlinks": "Обратные ссылки", + "noLinks": "Нет ссылок", + "contentPreview": "Предпросмотр содержимого", + "fileNotFound": "Файл не найден или недоступен", + "emptyContent": "Содержимое отсутствует", + "binaryFile": "Двоичный файл — предпросмотр недоступен" + }, + "fields": { + "title": "Заголовок", + "titlePlaceholder": "Заголовок документа", + "path": "Путь", + "pathPlaceholder": "напр. notes/my-doc.md", + "docType": "Тип", + "scope": "Область", + "fromDoc": "Из", + "toDoc": "В документ", + "selectDoc": "Выберите документ...", + "linkType": "Тип ссылки", + "linkContext": "Контекст", + "linkContextPlaceholder": "Необязательный контекст для этой ссылки" + }, + "scope": { + "personal": "Личное", + "team": "Команда", + "shared": "Общее" + }, + "type": { + "context": "Контекст", + "memory": "Память", + "note": "Заметка", + "skill": "Навык", + "episodic": "Эпизодическое", + "media": "Медиа", + "document": "Документ" + }, + "rescanTooltip": "Пересканировать рабочее пространство", + "enriching": "Обогащение", + "enrichComplete": "Обогащение завершено", + "enrichError": "Ошибка обогащения", + "enrichStopped": "Обогащение остановлено", + "enrichStopFailed": "Не удалось остановить обогащение", + "stopEnrich": "Остановить обогащение", + "stopEnrichConfirm": "Вы уверены, что хотите остановить процесс обогащения? Уже обработанные документы сохранят свои сводки.", + "stop": "Остановить", + "errors": "ошибок", + "rescanNew": "{{count}} новых", + "rescanUpdated": "{{count}} обновлено", + "rescanReenqueued": "{{count}} снова поставлено в очередь на обогащение", + "rescanUnchanged": "{{count}} без изменений", + "rescanTruncated": "сканирование усечено (достигнут лимит)", + "rescanNoFiles": "Новые файлы не найдены", + "rescanBusy": "Пересканирование уже выполняется", + "rescanError": "Пересканирование не удалось", + "upload": { + "title": "Загрузить в хранилище", + "destination": "Назначение", + "shared": "Общее", + "agent": "Агент", + "team": "Команда", + "selectAgent": "Выберите агента...", + "selectTeam": "Выберите команду...", + "dropzone": "Перетащите файлы сюда или нажмите для выбора", + "dropzoneHint": "Только текстовые файлы (.md, .txt, .json, .yaml, .csv, ...)", + "files": "Файлы ({{count}})", + "uploading": "Загрузка...", + "upload": "Загрузить" + }, + "toast": { + "uploadSuccess": "Загружено файлов: {{count}}", + "uploadFailed": "Загрузка не удалась", + "docCreated": "Документ создан", + "docCreateFailed": "Не удалось создать документ", + "docUpdated": "Документ обновлён", + "docUpdateFailed": "Не удалось обновить документ", + "docDeleted": "Документ удалён", + "docDeleteFailed": "Не удалось удалить документ", + "linkCreated": "Ссылка создана", + "linkCreateFailed": "Не удалось создать ссылку", + "linkDeleted": "Ссылка удалена", + "linkDeleteFailed": "Не удалось удалить ссылку" + } +} diff --git a/ui/web/src/i18n/locales/ru/webhooks.json b/ui/web/src/i18n/locales/ru/webhooks.json new file mode 100644 index 00000000..ec99c317 --- /dev/null +++ b/ui/web/src/i18n/locales/ru/webhooks.json @@ -0,0 +1,157 @@ +{ + "title": "Вебхуки", + "description": "Входящие HTTP-вебхуки, которые запускают агента (LLM) или отправляют сообщение в канал.", + "addWebhook": "Создать вебхук", + "searchPlaceholder": "Поиск вебхуков...", + "showRevoked": "Показать отозванные", + "emptyTitle": "Нет вебхуков", + "emptyDescription": "Создайте свой первый вебхук, чтобы открыть агента или канал по HTTP.", + "neverUsed": "Не использовался", + "pager": { + "prev": "Назад", + "next": "Вперёд", + "pageOf": "Страница {{page}} из {{total}}", + "noMore": "Больше вебхуков нет." + }, + "kind": { + "llm": "LLM", + "message": "Сообщение" + }, + "status": { + "active": "Активен", + "revoked": "Отозван" + }, + "columns": { + "name": "Имя", + "webhookId": "ID вебхука", + "kind": "Тип", + "target": "Цель", + "status": "Статус", + "lastUsed": "Последнее использование", + "actions": "Действия" + }, + "actions": { + "test": "Тест", + "calls": "История доставки", + "edit": "Изменить", + "rotate": "Сменить секрет", + "revoke": "Отозвать" + }, + "form": { + "createTitle": "Создать вебхук", + "editTitle": "Изменить вебхук", + "description": "Настройте, как этот вебхук аутентифицируется и что он запускает.", + "name": "Имя", + "namePlaceholder": "напр. обработчик лидов CRM", + "kind": "Тип", + "messageStandardOnly": "Только редакция Standard", + "agent": "Агент", + "agentPlaceholder": "Выберите агента", + "agentHint": "Требуется для вызова и тестирования вебхука LLM.", + "none": "Нет", + "channel": "Канал", + "channelPlaceholder": "Выберите канал", + "channelAny": "Любой (указывать в каждом запросе)", + "rateLimit": "Лимит частоты (запросов / минуту)", + "ipAllowlist": "Белый список IP", + "ipAllowlistPlaceholder": "напр. 203.0.113.0/24, 198.51.100.7", + "ipAllowlistHint": "IP или CIDR через запятую. Пусто — разрешить все.", + "requireHmac": "Требовать HMAC", + "requireHmacHint": "Отклонять аутентификацию по bearer-токену; требовать только подписанные запросы.", + "localhostOnly": "Только localhost", + "localhostOnlyHint": "Принимать запросы только с 127.0.0.1 / ::1.", + "localhostOnlyLite": "Принудительно включено в редакции Lite.", + "cancel": "Отмена", + "create": "Создать", + "save": "Сохранить", + "saving": "Сохранение..." + }, + "secret": { + "title": "Секрет вебхука", + "description": "Скопируйте эти значения сейчас — они показываются только один раз.", + "warning": "Этот секрет больше не будет показан. Храните его надёжно.", + "webhookId": "ID вебхука (заголовок X-Webhook-Id)", + "bearer": "Секрет (Bearer-токен)", + "hmacKey": "Ключ подписи HMAC", + "copy": "Копировать", + "copied": "Скопировано", + "usageHint": "Отправляйте секрет как 'Authorization: Bearer ' или подписывайте запросы ключом HMAC.", + "done": "Готово" + }, + "test": { + "title": "Тестировать вебхук: {{name}}", + "description": "Вызовите этот вебхук на стороне сервера, используя вашу сессию администратора (секрет не нужен).", + "messageWarning": "Это отправит реальное сообщение в целевой чат.", + "channelName": "Имя канала", + "channelNamePlaceholder": "telegram-main", + "input": "Ввод", + "inputPlaceholder": "Введите запрос для отправки агенту", + "chatId": "ID чата", + "chatIdPlaceholder": "Идентификатор чата получателя", + "content": "Содержимое", + "result": "Результат", + "sent": "Отправлено в {{channel}} (чат {{chatId}})", + "run": "Отправить тест", + "running": "Отправка...", + "close": "Закрыть", + "failed": "Тест не удался" + }, + "calls": { + "title": "История доставки: {{name}}", + "description": "Недавние вызовы этого вебхука.", + "statusAll": "Все статусы", + "status": { + "queued": "В очереди", + "running": "Выполняется", + "done": "Готово", + "failed": "Ошибка", + "dead": "Мёртв" + }, + "cols": { + "status": "Статус", + "mode": "Режим", + "attempts": "Попытки", + "created": "Создан", + "detail": "Детали" + }, + "emptyTitle": "Доставок пока нет", + "emptyDescription": "Вызовы этого вебхука появятся здесь.", + "noMore": "Больше вызовов нет.", + "pageOf": "Страница {{page}} из {{total}}", + "prev": "Назад", + "next": "Вперёд" + }, + "callDetail": { + "title": "Детали доставки", + "description": "Полная запись одного вызова вебхука.", + "loading": "Загрузка...", + "deliveryId": "ID доставки", + "idempotencyKey": "Ключ идемпотентности", + "callbackUrl": "URL обратного вызова", + "created": "Создан", + "started": "Запущен", + "completed": "Завершён", + "nextAttempt": "Следующая попытка", + "lastError": "Последняя ошибка", + "requestPayload": "Полезная нагрузка запроса", + "response": "Ответ" + }, + "rotate": { + "title": "Сменить секрет", + "description": "Смена делает недействительным текущий секрет для «{{name}}». Новый секрет будет показан один раз." + }, + "revoke": { + "title": "Отозвать вебхук", + "description": "Отзыв навсегда отключает «{{name}}». Это нельзя отменить." + }, + "toast": { + "created": "Вебхук создан", + "updated": "Вебхук обновлён", + "secretRotated": "Секрет сменён", + "revoked": "Вебхук отозван", + "failedCreate": "Не удалось создать вебхук", + "failedUpdate": "Не удалось обновить вебхук", + "failedRotate": "Не удалось сменить секрет", + "failedRevoke": "Не удалось отозвать вебхук" + } +} diff --git a/ui/web/src/i18n/locales/ru/workstations.json b/ui/web/src/i18n/locales/ru/workstations.json new file mode 100644 index 00000000..8fbd5bba --- /dev/null +++ b/ui/web/src/i18n/locales/ru/workstations.json @@ -0,0 +1,82 @@ +{ + "title": "Рабочие станции", + "description": "Управление подключениями к удалённым рабочим станциям (SSH, Docker) для выполнения команд агентами.", + "addWorkstation": "Добавить рабочую станцию", + "emptyTitle": "Рабочие станции не настроены", + "emptyDescription": "Добавьте рабочую станцию, чтобы агенты могли выполнять команды на удалённых машинах.", + "backend": { + "ssh": "SSH", + "docker": "Docker" + }, + "status": { + "active": "Активна", + "inactive": "Неактивна" + }, + "columns": { + "name": "Имя", + "key": "Ключ", + "backend": "Бэкенд", + "status": "Статус", + "created": "Создана", + "actions": "Действия" + }, + "actions": { + "edit": "Изменить", + "delete": "Удалить", + "test": "Проверить подключение", + "activate": "Активировать", + "deactivate": "Деактивировать" + }, + "createDialog": { + "title": "Добавить рабочую станцию", + "description": "Настройте новое подключение к удалённой рабочей станции.", + "nameLabel": "Отображаемое имя", + "namePlaceholder": "Мой сервер разработки", + "keyLabel": "Ключ рабочей станции", + "keyPlaceholder": "dev-server", + "keyHint": "Строчные буквы, цифры, дефисы. Используется агентами для ссылки на эту рабочую станцию.", + "backendLabel": "Тип бэкенда", + "sshOption": "SSH", + "dockerOption": "Docker", + "hostLabel": "Хост", + "hostPlaceholder": "192.168.1.100", + "portLabel": "Порт", + "userLabel": "Пользователь SSH", + "userPlaceholder": "ubuntu", + "identityFileLabel": "Файл идентификации (необязательно)", + "identityFilePlaceholder": "~/.ssh/id_rsa", + "containerLabel": "Имя / ID контейнера", + "containerPlaceholder": "my-container", + "dockerHostLabel": "Docker-хост (необязательно)", + "dockerHostPlaceholder": "unix:///var/run/docker.sock", + "cancel": "Отмена", + "create": "Создать" + }, + "deleteDialog": { + "title": "Удалить рабочую станцию", + "description": "Вы уверены, что хотите удалить «{{name}}»? Это нельзя отменить.", + "confirmLabel": "Удалить" + }, + "testResult": { + "success": "Подключение успешно", + "notImplemented": "Проверка подключения пока недоступна" + }, + "activity": { + "title": "Недавняя активность", + "emptyTitle": "Активности пока нет", + "emptyDescription": "События выполнения появятся здесь, как только агенты запустят команды.", + "columns": { + "action": "Действие", + "cmdPreview": "Команда", + "exitCode": "Код выхода", + "duration": "Длительность", + "agent": "Агент", + "timestamp": "Время" + }, + "actions": { + "exec": "Выполнить", + "deny": "Отклонено" + }, + "loadMore": "Загрузить ещё" + } +} diff --git a/ui/web/src/i18n/locales/vi/config.json b/ui/web/src/i18n/locales/vi/config.json index 6dfaaddb..0da57655 100644 --- a/ui/web/src/i18n/locales/vi/config.json +++ b/ui/web/src/i18n/locales/vi/config.json @@ -371,6 +371,7 @@ "systemMessages.locale.vi": "Tiếng Việt", "systemMessages.locale.zh": "Tiếng Trung", "systemMessages.locale.ko": "Tiếng Hàn", + "systemMessages.locale.ru": "Tiếng Nga", "systemMessages.override": "Mẫu ghi đè", "systemMessages.overrideTip": "Mẫu sẽ được gửi thay cho thông báo có sẵn. Biến dùng dạng {{name}}.", "systemMessages.defaultTemplate": "Mẫu mặc định", diff --git a/ui/web/src/i18n/locales/zh/config.json b/ui/web/src/i18n/locales/zh/config.json index 6c347959..a1b7a9ca 100644 --- a/ui/web/src/i18n/locales/zh/config.json +++ b/ui/web/src/i18n/locales/zh/config.json @@ -371,6 +371,7 @@ "systemMessages.locale.vi": "越南语", "systemMessages.locale.zh": "中文", "systemMessages.locale.ko": "韩语", + "systemMessages.locale.ru": "俄语", "systemMessages.override": "覆盖模板", "systemMessages.overrideTip": "替代内置消息发送的模板。变量使用 {{name}}。", "systemMessages.defaultTemplate": "默认模板", diff --git a/ui/web/src/lib/constants.ts b/ui/web/src/lib/constants.ts index b312c027..e817ddae 100644 --- a/ui/web/src/lib/constants.ts +++ b/ui/web/src/lib/constants.ts @@ -20,7 +20,7 @@ export const LOCAL_STORAGE_KEYS = { TIMEZONE: "goclaw:timezone", } as const; -export const SUPPORTED_LANGUAGES = ["en", "vi", "zh", "ko"] as const; +export const SUPPORTED_LANGUAGES = ["en", "vi", "zh", "ko", "ru"] as const; export type Language = (typeof SUPPORTED_LANGUAGES)[number]; export const LANGUAGE_LABELS: Record = { @@ -28,4 +28,5 @@ export const LANGUAGE_LABELS: Record = { vi: "Tiếng Việt", zh: "中文", ko: "한국어", + ru: "Русский", }; diff --git a/ui/web/src/pages/config/sections/system-messages-section-utils.test.ts b/ui/web/src/pages/config/sections/system-messages-section-utils.test.ts index 545d4694..da6b963e 100644 --- a/ui/web/src/pages/config/sections/system-messages-section-utils.test.ts +++ b/ui/web/src/pages/config/sections/system-messages-section-utils.test.ts @@ -46,7 +46,7 @@ describe("system-messages-section-utils", () => { }); it("normalizes all supported locales for each known message definition", () => { - expect(SYSTEM_MESSAGE_LOCALES.map((locale) => locale.code)).toEqual(["en", "vi", "zh", "ko"]); + expect(SYSTEM_MESSAGE_LOCALES.map((locale) => locale.code)).toEqual(["en", "vi", "zh", "ko", "ru"]); const draft = normalizeSystemMessagesDraft( { @@ -91,6 +91,7 @@ describe("system-messages-section-utils", () => { vi: "Nhóm {{code}}", zh: "", ko: "", + ru: "", }, }, }); @@ -108,6 +109,7 @@ describe("system-messages-section-utils", () => { vi: "", zh: "", ko: "", + ru: "", }, }, }); @@ -132,6 +134,7 @@ describe("system-messages-section-utils", () => { vi: "", zh: "", ko: "", + ru: "", }, }, }); diff --git a/ui/web/src/pages/config/sections/system-messages-section-utils.ts b/ui/web/src/pages/config/sections/system-messages-section-utils.ts index 41b1413e..ba573397 100644 --- a/ui/web/src/pages/config/sections/system-messages-section-utils.ts +++ b/ui/web/src/pages/config/sections/system-messages-section-utils.ts @@ -19,6 +19,7 @@ export const SYSTEM_MESSAGE_LOCALES = [ { code: "vi", labelKey: "systemMessages.locale.vi" }, { code: "zh", labelKey: "systemMessages.locale.zh" }, { code: "ko", labelKey: "systemMessages.locale.ko" }, + { code: "ru", labelKey: "systemMessages.locale.ru" }, ] as const; export function systemMessageDefinitionsFromSchema(schema: Record | null | undefined): SystemMessageDefinition[] { diff --git a/ui/web/src/pages/hooks/__tests__/hooks-page.test.ts b/ui/web/src/pages/hooks/__tests__/hooks-page.test.ts index ec8537f1..a8363519 100644 --- a/ui/web/src/pages/hooks/__tests__/hooks-page.test.ts +++ b/ui/web/src/pages/hooks/__tests__/hooks-page.test.ts @@ -113,8 +113,16 @@ describe("hooks i18n key contracts", () => { expect(zhKeys).toEqual(enKeys); }); + it("ru locale has same top-level keys as en", async () => { + const en = await import("@/i18n/locales/en/hooks.json"); + const ru = await import("@/i18n/locales/ru/hooks.json"); + const enKeys = Object.keys(en as unknown as Record).sort(); + const ruKeys = Object.keys(ru as unknown as Record).sort(); + expect(ruKeys).toEqual(enKeys); + }); + it("decision keys exist in all locales", async () => { - for (const locale of ["en", "vi", "zh"]) { + for (const locale of ["en", "vi", "zh", "ru"]) { const mod = await import(`@/i18n/locales/${locale}/hooks.json`); const data = mod as unknown as Record>; expect(data.decision?.allow).toBeTruthy(); diff --git a/ui/web/src/pages/traces/__tests__/traces-i18n.test.ts b/ui/web/src/pages/traces/__tests__/traces-i18n.test.ts index 4fd30833..6a98de1f 100644 --- a/ui/web/src/pages/traces/__tests__/traces-i18n.test.ts +++ b/ui/web/src/pages/traces/__tests__/traces-i18n.test.ts @@ -5,14 +5,16 @@ describe("traces i18n filter keys", () => { const en = await import("@/i18n/locales/en/traces.json"); const vi = await import("@/i18n/locales/vi/traces.json"); const zh = await import("@/i18n/locales/zh/traces.json"); + const ru = await import("@/i18n/locales/ru/traces.json"); const enKeys = Object.keys(en as Record).sort(); expect(Object.keys(vi as Record).sort()).toEqual(enKeys); expect(Object.keys(zh as Record).sort()).toEqual(enKeys); + expect(Object.keys(ru as Record).sort()).toEqual(enKeys); }); it("filter labels exist in all locales", async () => { - for (const locale of ["en", "vi", "zh"]) { + for (const locale of ["en", "vi", "zh", "ru"]) { const mod = await import(`@/i18n/locales/${locale}/traces.json`); const data = mod as unknown as { filters?: Record }; for (const key of ["search", "status", "advanced", "from", "to", "toolName", "hasToolCalls", "clearAll"]) {