Files
crank/apps/ui/js/i18n.js
T
2026-04-02 23:15:02 +03:00

782 lines
42 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ═══════════════════════════════════════════════
Crank — i18n
Reads `crank_lang` from localStorage ('en'|'ru')
═══════════════════════════════════════════════ */
var TRANSLATIONS = {
en: {
// Nav
'nav.operations': 'Operations',
'nav.agents': 'Agents',
'nav.apikeys': 'API Keys',
'nav.logs': 'Logs',
'nav.usage': 'Usage',
'nav.settings': 'Settings',
'nav.logout': 'Log out',
'nav.workspaces': 'Your workspaces',
'nav.create_workspace': 'Create workspace',
'nav.notifications': 'Notifications',
// Operations page
'ops.title': 'Operations',
'ops.subtitle': 'Tool contracts that expose your upstream APIs as MCP tools',
'ops.new': 'New operation',
'ops.loading': 'Loading operations…',
'ops.empty.title': 'No operations found',
'ops.empty.sub': 'Try adjusting your search or filters.',
'ops.empty.initial.title': 'No operations yet',
'ops.empty.initial.sub': 'Create the first operation in this workspace to expose a live MCP tool.',
'ops.error.title': 'Backend connection failed',
'ops.delete.confirm': 'Delete this operation? This cannot be undone.',
'ops.delete.success.title': 'Operation deleted',
'ops.delete.success.message': '{name} was deleted.',
'ops.delete.error.title': 'Delete failed',
'ops.delete.error.message': 'Failed to delete operation',
'ops.action.edit': 'Edit operation',
'ops.action.delete':'Delete operation',
'ops.stats.synced': 'Catalog synced with backend',
'ops.stats.none': 'No operations yet',
'ops.stats.share': '{value}% of total',
'ops.stats.no_published': 'No published tools yet',
'ops.stats.requests_rollup': 'Summed from operation usage',
'ops.stats.no_traffic': 'No traffic recorded today',
'ops.stats.latency_weighted': "Weighted by today's calls",
'ops.stats.latency_waiting': 'Waiting for runtime samples',
'ops.results': 'Showing {shown} of {total}',
'ops.range': 'Showing {from}{to} of {total} operations',
'ops.filter.protocol': 'Protocol: {value}',
'ops.filter.category': 'Category: {value}',
'ops.filter.agent': 'Agent: {value}',
'ops.no_active_agents': 'No active agents',
// Stats
'stats.total': 'Total operations',
'stats.active': 'Active',
'stats.requests': 'Requests today',
'stats.latency': 'Avg. latency',
// Tabs
'tab.all': 'All',
'tab.active': 'Active',
'tab.draft': 'Draft',
'tab.error': 'Error',
'tab.inactive': 'Inactive',
// Filters & sort
'filter.search': 'Search operations...',
'filter.protocol': 'Protocol',
'filter.status': 'Status',
'filter.category': 'Category',
'filter.agent': 'Agent',
'filter.showing': 'Showing',
'filter.of': 'of',
'filter.clear': 'Clear filter',
'filter.clear_all': 'Clear all',
'filter.remove': 'Remove filter',
'sort.created_desc':'Sort: Last created',
'sort.created_asc': 'Sort: Oldest first',
'sort.name_asc': 'Sort: Name AZ',
'sort.name_desc': 'Sort: Name ZA',
// Table headers
'th.name': 'NAME',
'th.protocol': 'PROTOCOL',
'th.status': 'STATUS',
'th.created': 'CREATED',
'th.url': 'TARGET URL',
// Pagination
'page.showing': 'Showing',
'page.of': 'of',
'page.ops': 'operations',
// API Keys page
'apikeys.title': 'API Keys',
'apikeys.subtitle': 'Keys authenticate external MCP clients and workspace automation against Crank.',
'apikeys.new': 'Create key',
'apikeys.th.name': 'NAME',
'apikeys.th.scope': 'SCOPE',
'apikeys.th.created':'CREATED',
'apikeys.th.last': 'LAST USED',
'apikeys.callout.title': 'Keys are only shown once.',
'apikeys.callout.body': 'Copy and store them securely immediately after creation — Crank stores only a hash. `Last used` updates after successful MCP authentication.',
'apikeys.active.title': 'Active keys',
'apikeys.active.subtitle': '{active} active · {revoked} revoked',
'apikeys.search': 'Filter keys…',
'apikeys.th.prefix': 'KEY PREFIX',
'apikeys.th.scopes': 'SCOPES',
'apikeys.th.status': 'STATUS',
'apikeys.scope_ref': 'Scope reference',
'apikeys.scope.read': 'Initialize MCP sessions, ping the server, and list tools for a workspace agent.',
'apikeys.scope.write': 'Execute `tools/call` requests against published agent toolsets.',
'apikeys.scope.deploy': 'Reserved for deploy-scoped automation. Today it also permits MCP read/write flows.',
'apikeys.modal.title': 'Create API key',
'apikeys.modal.name': 'Key name',
'apikeys.modal.name_hint': 'A descriptive label to identify the key. Only visible to admins.',
'apikeys.modal.name_placeholder': 'e.g. Production, CI pipeline',
'apikeys.modal.scopes': 'Scopes',
'apikeys.modal.reveal_title': 'Copy this key now.',
'apikeys.modal.reveal_body': "It won't be shown again.",
'apikeys.modal.copy': 'Copy to clipboard',
'apikeys.modal.cancel': 'Cancel',
'apikeys.modal.create': 'Create key',
'apikeys.modal.done': "Done — I've copied the key",
'apikeys.status.active': 'Active',
'apikeys.status.revoked': 'Revoked',
'apikeys.last_used.never': 'Never',
'apikeys.empty.none': 'No API keys yet',
'apikeys.empty.search': 'No keys match your search',
'apikeys.loading': 'Loading…',
'apikeys.error.api': 'Workspace or API is unavailable',
'apikeys.error.load': 'Failed to load API keys',
'apikeys.confirm.revoke': 'Revoke this key? All requests using it will fail immediately.',
'apikeys.confirm.delete': 'Delete this key? This cannot be undone.',
'apikeys.toast.revoke_title': 'Key revoked',
'apikeys.toast.revoke_message': '{name} was revoked.',
'apikeys.toast.revoke_error_title': 'Revoke failed',
'apikeys.toast.revoke_error_message': 'Failed to revoke key',
'apikeys.toast.delete_title': 'Key deleted',
'apikeys.toast.delete_message': '{name} was deleted.',
'apikeys.toast.delete_error_title': 'Delete failed',
'apikeys.toast.delete_error_message': 'Failed to delete key',
'apikeys.toast.scope_missing_title': 'Missing scope',
'apikeys.toast.scope_missing_message': 'Select at least one scope before creating a key.',
'apikeys.toast.create_title': 'API key created',
'apikeys.toast.create_message': 'Copy the key now. It will not be shown again.',
'apikeys.toast.create_error_title': 'Key creation failed',
'apikeys.toast.create_error_message': 'Failed to create key',
'apikeys.toast.copy_title': 'API key copied',
'apikeys.toast.copy_message': 'Store the raw key securely. It cannot be revealed again.',
'apikeys.toast.prefix_title': 'Key prefix copied',
'apikeys.action.copy_prefix': 'Copy key prefix',
'apikeys.action.revoke': 'Revoke key',
'apikeys.action.delete': 'Delete',
'apikeys.creating': 'Creating…',
// Logs page
'logs.title': 'Logs',
'logs.subtitle': 'Real-time invocation log for all operations in this workspace.',
'logs.search': 'Search logs...',
'logs.level.all': 'All',
'logs.level.info': 'Info',
'logs.level.warn': 'Warn',
'logs.level.error': 'Error',
'logs.level.debug': 'Debug',
'logs.live': 'Live',
'logs.paused': 'Paused',
'logs.refresh': 'Refresh',
'logs.range.30m': 'Last 30 min',
'logs.range.1h': 'Last 1 hour',
'logs.range.6h': 'Last 6 hours',
'logs.range.24h': 'Last 24 hours',
'logs.range.7d': 'Last 7 days',
'logs.search_messages': 'Search messages…',
'logs.detail.agent': 'Agent',
'logs.detail.request': 'Request preview',
'logs.detail.response': 'Response preview',
'logs.detail.meta': 'Metadata',
'logs.loading.title': 'Loading logs…',
'logs.loading.sub': 'Fetching invocation history for the current workspace.',
'logs.error.title': 'Unable to load logs',
'logs.error.api': 'Admin API is not available',
'logs.error.workspace': 'Workspace is not selected',
'logs.error.load': 'Failed to load logs',
'logs.empty.title': 'No log entries found',
'logs.empty.filtered': 'Try widening the period or clearing the current search and level filters.',
'logs.empty.initial': 'Run tests or published MCP tools to start collecting invocation history.',
'logs.live.on.title': 'Live mode enabled',
'logs.live.on.body': 'Polling logs every 4 seconds.',
'logs.live.off.title': 'Live mode paused',
'logs.live.off.body': 'Automatic polling is paused.',
'logs.refresh.title': 'Logs refreshed',
'logs.refresh.body': 'The latest invocation records were loaded for the current workspace.',
// Usage page
'usage.title': 'Usage',
'usage.subtitle': 'Invocation metrics for all operations in your workspace.',
'usage.export': 'Export CSV',
'usage.period.7d': 'Last 7 days',
'usage.period.30d': 'Last 30 days',
'usage.period.90d': 'Last 90 days',
'usage.period.this_month': 'This month',
'usage.period.label.7d': 'last 7 days',
'usage.period.label.30d': 'last 30 days',
'usage.period.label.90d': 'last 90 days',
'usage.period.label.this_month': 'this month',
'usage.stats.total': 'Total invocations',
'usage.stats.success': 'Success rate',
'usage.stats.p50': 'Median latency (p50)',
'usage.stats.p99': 'p99 latency',
'usage.stats.across': 'Across {period}',
'usage.stats.success_calls': '{count} successful calls',
'usage.stats.median': 'Median latency for selected period',
'usage.stats.error_calls': '{count} error calls',
'usage.chart.title': 'Invocations over time',
'usage.chart.success': 'Success',
'usage.chart.error': 'Error',
'usage.chart.empty.title': 'No usage data yet',
'usage.chart.empty.sub': 'Invocation metrics will appear here after tests or published tool calls in the selected period.',
'usage.table.title': 'By operation',
'usage.table.subtitle': 'Breakdown for {period}',
'usage.table.th.operation': 'Operation',
'usage.table.th.protocol': 'Protocol',
'usage.table.th.calls': 'Calls',
'usage.table.th.errors': 'Errors',
'usage.table.th.error_rate': 'Error rate',
'usage.table.th.latency': 'Latency (p50 / p95 / p99)',
'usage.table.th.share': 'Traffic share',
'usage.table.empty.title': 'No operation breakdown yet',
'usage.table.empty.sub': 'The selected period has no invocation samples to break down by operation.',
'usage.table.share': '{value}% of workspace traffic',
'usage.loading.title': 'Loading usage…',
'usage.loading.sub': 'Aggregating invocation metrics for the selected workspace.',
'usage.error.title': 'Unable to load usage',
'usage.error.api': 'Admin API is not available',
'usage.error.workspace': 'Workspace is not selected',
'usage.error.load': 'Failed to load usage',
'usage.export.empty.title': 'No usage data loaded',
'usage.export.empty.body': 'Load usage data before exporting the CSV snapshot.',
'usage.export.done.title': 'Usage exported',
'usage.export.done.body': 'The current usage snapshot was exported as CSV.',
'usage.chart.ok': '{count} ok',
'usage.chart.errors': '{count} errors',
'usage.chart.week': 'Wk {index}',
'usage.csv.header': 'Operation,Protocol,Calls,Errors,Error Rate (%),p50 (ms),p95 (ms),p99 (ms)',
// Settings sidebar
'settings.title': 'Settings',
'settings.nav.workspace': 'Workspace',
'settings.nav.profile': 'Profile',
'settings.nav.members': 'Members',
'settings.nav.security': 'Security',
'settings.nav.notif': 'Notifications',
'settings.nav.danger': 'Danger zone',
// Settings — workspace section
'settings.ws.title': 'Workspace settings',
'settings.ws.subtitle': 'General information about your workspace',
'settings.ws.name': 'Workspace name',
'settings.ws.display': 'Display name',
'settings.ws.desc': 'Description',
'settings.ws.tz': 'Timezone',
'settings.ws.protocol': 'Default protocol',
// Settings — language
'settings.lang.title': 'Language',
'settings.lang.subtitle': 'Interface display language',
'settings.lang.en': 'English',
'settings.lang.ru': 'Русский',
// Settings — profile section
'settings.profile.title': 'Profile',
'settings.profile.name': 'Full name',
'settings.profile.email': 'Email',
'settings.profile.role': 'Role',
// Common buttons
'btn.save': 'Save changes',
'btn.cancel': 'Cancel',
'btn.create': 'Create',
'btn.delete': 'Delete',
'btn.revoke': 'Revoke',
'btn.copy': 'Copy',
// Agents page
'agents.title': 'Agents',
'agents.subtitle': 'Named MCP endpoints that expose a curated subset of operations to an LLM',
'agents.new': 'New agent',
'agents.stats.total': 'Total agents',
'agents.stats.total_sub': 'across this workspace',
'agents.stats.published': 'Published',
'agents.stats.published_sub': '{value}% of total',
'agents.stats.tools': 'Operations exposed',
'agents.stats.tools_sub': 'across {count} agents',
'agents.stats.calls': 'Calls today',
'agents.stats.calls_sub': 'all agents combined',
'agents.search': 'Search agents...',
'agents.results': '{shown} of {total} agents',
'agents.callout': 'Each agent gets its own MCP endpoint. Connect your LLM client to a specific agent so it only sees the tools it needs — not all {count} operations in this workspace.',
'agents.loading': 'Loading agents…',
'agents.error.title': 'Failed to load agents',
'agents.error.api': 'Workspace or API is unavailable',
'agents.error.load': 'Failed to load agents',
'agents.empty.initial.title': 'No agents yet',
'agents.empty.initial.sub': 'Create your first agent to get a dedicated MCP endpoint with a curated set of tools.',
'agents.empty.search.title': 'No agents match "{query}"',
'agents.empty.search.sub': 'Try a different search term.',
'agents.card.tools': 'tools',
'agents.card.keys': 'API keys',
'agents.card.calls_today': 'calls today',
'agents.card.created': 'Created {date}',
'agents.card.copy_endpoint': 'Copy endpoint',
'agents.action.edit': 'Edit',
'agents.action.archive': 'Archive',
'agents.action.delete': 'Delete',
'agents.lifecycle.publish': 'Publish',
'agents.lifecycle.unpublish': 'Unpublish',
'agents.lifecycle.restore_draft': 'Restore draft',
'agents.lifecycle.published': 'Published',
'agents.lifecycle.archived': 'Archived',
'agents.lifecycle.draft': 'Draft',
'agents.drawer.new_title': 'New agent',
'agents.drawer.edit_title': 'Edit agent',
'agents.drawer.new_sub': 'Configure a named MCP endpoint for your LLM',
'agents.drawer.edit_sub': 'Update agent settings and tool selection',
'agents.drawer.identity': 'Identity',
'agents.drawer.display_name': 'Display name',
'agents.drawer.slug': 'Slug',
'agents.drawer.description': 'Description',
'agents.drawer.optional': '(optional)',
'agents.drawer.required': 'required',
'agents.drawer.status': 'Status',
'agents.drawer.endpoint': 'MCP endpoint',
'agents.drawer.placeholder.name': 'Customer Support',
'agents.drawer.placeholder.slug': 'customer-support',
'agents.drawer.placeholder.description': 'What does this agent do? What LLM or use-case is it for?',
'agents.drawer.operations': 'Operations',
'agents.drawer.selected': '{count} selected',
'agents.drawer.operations_sub': 'Select which operations this agent exposes. LLMs connecting to this agent will only see these tools.',
'agents.drawer.filter_ops': 'Filter operations…',
'agents.drawer.ops_no_match': 'No operations match "{query}"',
'agents.drawer.ops_selected': '{count} operations selected',
'agents.drawer.clear_all': 'Clear all',
'agents.drawer.recommendation': "You've selected {count} tools. LLMs perform best with <15 tools per agent. Consider splitting into separate agents by use-case.",
'agents.drawer.cancel': 'Cancel',
'agents.drawer.create': 'Create agent',
'agents.drawer.save': 'Save changes',
'agents.toast.saved_title_create': 'Agent created',
'agents.toast.saved_title_update': 'Agent updated',
'agents.toast.saved_message': '{name} was saved with {count} tool bindings.',
'agents.toast.save_error_title': 'Agent update failed',
'agents.toast.save_error_message': 'Failed to save agent',
'agents.toast.delete_confirm': 'Delete this agent? This cannot be undone.',
'agents.toast.delete_title': 'Agent deleted',
'agents.toast.delete_message': '{name} was deleted.',
'agents.toast.delete_error_title': 'Delete failed',
'agents.toast.delete_error_message': 'Failed to delete agent',
'agents.toast.lifecycle_title': 'Agent lifecycle updated',
'agents.toast.lifecycle_publish': '{name} was published.',
'agents.toast.lifecycle_unpublish': '{name} was returned to draft.',
'agents.toast.lifecycle_archive': '{name} was archived.',
'agents.toast.lifecycle_error_title': 'Lifecycle update failed',
'agents.toast.lifecycle_error_message': 'Failed to update agent lifecycle',
'agents.toast.endpoint_title': 'MCP endpoint copied',
// Login
'login.title': 'Sign in',
'login.subtitle': 'Welcome back to your workspace',
'login.email': 'Email',
'login.password': 'Password',
'login.submit': 'Sign in',
'login.sso': 'Continue with Google',
},
ru: {
// Nav
'nav.operations': 'Операции',
'nav.agents': 'Агенты',
'nav.apikeys': 'API Ключи',
'nav.logs': 'Логи',
'nav.usage': 'Использование',
'nav.settings': 'Настройки',
'nav.logout': 'Выйти',
'nav.workspaces': 'Ваши воркспейсы',
'nav.create_workspace': 'Создать воркспейс',
'nav.notifications': 'Уведомления',
// Operations page
'ops.title': 'Операции',
'ops.subtitle': 'Контракты инструментов, открывающие ваши API как MCP-инструменты',
'ops.new': 'Новая операция',
'ops.loading': 'Загрузка операций…',
'ops.empty.title': 'Операции не найдены',
'ops.empty.sub': 'Попробуйте изменить поисковый запрос или фильтры.',
'ops.empty.initial.title': 'Операций пока нет',
'ops.empty.initial.sub': 'Создайте первую операцию в этом воркспейсе, чтобы открыть живой MCP-инструмент.',
'ops.error.title': 'Не удалось подключиться к backend',
'ops.delete.confirm': 'Удалить эту операцию? Действие нельзя отменить.',
'ops.delete.success.title': 'Операция удалена',
'ops.delete.success.message': 'Операция {name} удалена.',
'ops.delete.error.title': 'Не удалось удалить',
'ops.delete.error.message': 'Не удалось удалить операцию',
'ops.action.edit': 'Редактировать операцию',
'ops.action.delete':'Удалить операцию',
'ops.stats.synced': 'Каталог синхронизирован с backend',
'ops.stats.none': 'Операций пока нет',
'ops.stats.share': '{value}% от общего числа',
'ops.stats.no_published': 'Опубликованных инструментов пока нет',
'ops.stats.requests_rollup': 'Сумма по статистике операций',
'ops.stats.no_traffic': 'Трафик сегодня не зафиксирован',
'ops.stats.latency_weighted': 'Взвешено по сегодняшним вызовам',
'ops.stats.latency_waiting': 'Ожидание runtime-метрик',
'ops.results': 'Показано {shown} из {total}',
'ops.range': 'Показано {from}{to} из {total} операций',
'ops.filter.protocol': 'Протокол: {value}',
'ops.filter.category': 'Категория: {value}',
'ops.filter.agent': 'Агент: {value}',
'ops.no_active_agents': 'Нет активных агентов',
// Stats
'stats.total': 'Всего операций',
'stats.active': 'Активные',
'stats.requests': 'Запросов сегодня',
'stats.latency': 'Ср. задержка',
// Tabs
'tab.all': 'Все',
'tab.active': 'Активные',
'tab.draft': 'Черновик',
'tab.error': 'Ошибка',
'tab.inactive': 'Неактивные',
// Filters & sort
'filter.search': 'Поиск операций...',
'filter.protocol': 'Протокол',
'filter.status': 'Статус',
'filter.category': 'Категория',
'filter.agent': 'Агент',
'filter.showing': 'Показано',
'filter.of': 'из',
'filter.clear': 'Сбросить',
'filter.clear_all': 'Сбросить все',
'filter.remove': 'Убрать фильтр',
'sort.created_desc':'Сначала новые',
'sort.created_asc': 'Сначала старые',
'sort.name_asc': 'Название А–Я',
'sort.name_desc': 'Название Я–А',
// Table headers
'th.name': 'НАЗВАНИЕ',
'th.protocol': 'ПРОТОКОЛ',
'th.status': 'СТАТУС',
'th.created': 'СОЗДАН',
'th.url': 'ЦЕЛЕВОЙ URL',
// Pagination
'page.showing': 'Показано',
'page.of': 'из',
'page.ops': 'операций',
// API Keys page
'apikeys.title': 'API Ключи',
'apikeys.subtitle': 'Ключи аутентифицируют внешние MCP-клиенты и автоматизацию воркспейса в Crank.',
'apikeys.new': 'Создать ключ',
'apikeys.th.name': 'НАЗВАНИЕ',
'apikeys.th.scope': 'ОБЛАСТЬ',
'apikeys.th.created':'СОЗДАН',
'apikeys.th.last': 'ПОСЛЕДНЕЕ ИСПОЛЬЗОВАНИЕ',
'apikeys.callout.title': 'Ключ показывается только один раз.',
'apikeys.callout.body': 'Скопируйте и сохраните его сразу после создания — Crank хранит только хэш. `Последнее использование` обновляется после успешной MCP-аутентификации.',
'apikeys.active.title': 'Активные ключи',
'apikeys.active.subtitle': '{active} активных · {revoked} отозванных',
'apikeys.search': 'Фильтр ключей…',
'apikeys.th.prefix': 'ПРЕФИКС КЛЮЧА',
'apikeys.th.scopes': 'ОБЛАСТИ',
'apikeys.th.status': 'СТАТУС',
'apikeys.scope_ref': 'Справка по областям',
'apikeys.scope.read': 'Инициализация MCP-сессий, ping сервера и получение списка инструментов для агента воркспейса.',
'apikeys.scope.write': 'Выполнение `tools/call` для опубликованных наборов инструментов агента.',
'apikeys.scope.deploy': 'Зарезервировано для deploy-автоматизации. Сейчас также разрешает MCP read/write сценарии.',
'apikeys.modal.title': 'Создать API-ключ',
'apikeys.modal.name': 'Имя ключа',
'apikeys.modal.name_hint': 'Понятная метка для идентификации ключа. Видна только администраторам.',
'apikeys.modal.name_placeholder': 'например, Production, CI pipeline',
'apikeys.modal.scopes': 'Области',
'apikeys.modal.reveal_title': 'Скопируйте ключ сейчас.',
'apikeys.modal.reveal_body': 'Повторно он не будет показан.',
'apikeys.modal.copy': 'Скопировать',
'apikeys.modal.cancel': 'Отмена',
'apikeys.modal.create': 'Создать ключ',
'apikeys.modal.done': 'Готово — ключ скопирован',
'apikeys.status.active': 'Активен',
'apikeys.status.revoked': 'Отозван',
'apikeys.last_used.never': 'Никогда',
'apikeys.empty.none': 'API-ключей пока нет',
'apikeys.empty.search': 'Нет ключей по текущему поиску',
'apikeys.loading': 'Загрузка…',
'apikeys.error.api': 'Воркспейс или API недоступен',
'apikeys.error.load': 'Не удалось загрузить API-ключи',
'apikeys.confirm.revoke': 'Отозвать этот ключ? Все запросы с ним сразу начнут падать.',
'apikeys.confirm.delete': 'Удалить этот ключ? Действие нельзя отменить.',
'apikeys.toast.revoke_title': 'Ключ отозван',
'apikeys.toast.revoke_message': 'Ключ {name} отозван.',
'apikeys.toast.revoke_error_title': 'Не удалось отозвать',
'apikeys.toast.revoke_error_message': 'Не удалось отозвать ключ',
'apikeys.toast.delete_title': 'Ключ удален',
'apikeys.toast.delete_message': 'Ключ {name} удален.',
'apikeys.toast.delete_error_title': 'Не удалось удалить',
'apikeys.toast.delete_error_message': 'Не удалось удалить ключ',
'apikeys.toast.scope_missing_title': 'Не выбрана область',
'apikeys.toast.scope_missing_message': 'Выберите хотя бы одну область перед созданием ключа.',
'apikeys.toast.create_title': 'API-ключ создан',
'apikeys.toast.create_message': 'Скопируйте ключ сейчас. Повторно он не будет показан.',
'apikeys.toast.create_error_title': 'Не удалось создать ключ',
'apikeys.toast.create_error_message': 'Не удалось создать ключ',
'apikeys.toast.copy_title': 'API-ключ скопирован',
'apikeys.toast.copy_message': 'Сохраните исходный ключ в надежном месте. Повторно показать его нельзя.',
'apikeys.toast.prefix_title': 'Префикс ключа скопирован',
'apikeys.action.copy_prefix': 'Скопировать префикс ключа',
'apikeys.action.revoke': 'Отозвать ключ',
'apikeys.action.delete': 'Удалить',
'apikeys.creating': 'Создание…',
// Logs page
'logs.title': 'Логи',
'logs.subtitle': 'Живой журнал вызовов по всем операциям этого воркспейса.',
'logs.search': 'Поиск в логах...',
'logs.level.all': 'Все',
'logs.level.info': 'Info',
'logs.level.warn': 'Warn',
'logs.level.error': 'Error',
'logs.level.debug': 'Debug',
'logs.live': 'Live',
'logs.paused': 'Пауза',
'logs.refresh': 'Обновить',
'logs.range.30m': 'Последние 30 мин',
'logs.range.1h': 'Последний 1 час',
'logs.range.6h': 'Последние 6 часов',
'logs.range.24h': 'Последние 24 часа',
'logs.range.7d': 'Последние 7 дней',
'logs.search_messages': 'Поиск по сообщениям…',
'logs.detail.agent': 'Агент',
'logs.detail.request': 'Предпросмотр запроса',
'logs.detail.response': 'Предпросмотр ответа',
'logs.detail.meta': 'Метаданные',
'logs.loading.title': 'Загрузка логов…',
'logs.loading.sub': 'Получаем историю вызовов для текущего воркспейса.',
'logs.error.title': 'Не удалось загрузить логи',
'logs.error.api': 'Admin API недоступен',
'logs.error.workspace': 'Воркспейс не выбран',
'logs.error.load': 'Не удалось загрузить логи',
'logs.empty.title': 'Записей логов не найдено',
'logs.empty.filtered': 'Попробуйте расширить период или сбросить текущий поиск и фильтры уровня.',
'logs.empty.initial': 'Запустите тесты или опубликованные MCP-инструменты, чтобы начать собирать историю вызовов.',
'logs.live.on.title': 'Live режим включен',
'logs.live.on.body': 'Логи опрашиваются каждые 4 секунды.',
'logs.live.off.title': 'Live режим на паузе',
'logs.live.off.body': 'Автоматический опрос остановлен.',
'logs.refresh.title': 'Логи обновлены',
'logs.refresh.body': 'Получены последние записи вызовов для текущего воркспейса.',
// Usage page
'usage.title': 'Использование',
'usage.subtitle': 'Метрики вызовов по всем операциям этого воркспейса.',
'usage.export': 'Экспорт CSV',
'usage.period.7d': 'Последние 7 дней',
'usage.period.30d': 'Последние 30 дней',
'usage.period.90d': 'Последние 90 дней',
'usage.period.this_month': 'Этот месяц',
'usage.period.label.7d': 'последние 7 дней',
'usage.period.label.30d': 'последние 30 дней',
'usage.period.label.90d': 'последние 90 дней',
'usage.period.label.this_month': 'этот месяц',
'usage.stats.total': 'Всего вызовов',
'usage.stats.success': 'Доля успешных',
'usage.stats.p50': 'Медианная задержка (p50)',
'usage.stats.p99': 'Задержка p99',
'usage.stats.across': 'За период: {period}',
'usage.stats.success_calls': 'Успешных вызовов: {count}',
'usage.stats.median': 'Медианная задержка за выбранный период',
'usage.stats.error_calls': 'Ошибочных вызовов: {count}',
'usage.chart.title': 'Вызовы во времени',
'usage.chart.success': 'Успех',
'usage.chart.error': 'Ошибка',
'usage.chart.empty.title': 'Данных по использованию пока нет',
'usage.chart.empty.sub': 'Метрики появятся здесь после тестов или вызовов опубликованных инструментов за выбранный период.',
'usage.table.title': 'По операциям',
'usage.table.subtitle': 'Разбивка за период: {period}',
'usage.table.th.operation': 'Операция',
'usage.table.th.protocol': 'Протокол',
'usage.table.th.calls': 'Вызовы',
'usage.table.th.errors': 'Ошибки',
'usage.table.th.error_rate': 'Доля ошибок',
'usage.table.th.latency': 'Задержка (p50 / p95 / p99)',
'usage.table.th.share': 'Доля трафика',
'usage.table.empty.title': 'Разбивки по операциям пока нет',
'usage.table.empty.sub': 'За выбранный период нет вызовов, которые можно разложить по операциям.',
'usage.table.share': '{value}% трафика воркспейса',
'usage.loading.title': 'Загрузка использования…',
'usage.loading.sub': 'Собираем метрики вызовов для выбранного воркспейса.',
'usage.error.title': 'Не удалось загрузить использование',
'usage.error.api': 'Admin API недоступен',
'usage.error.workspace': 'Воркспейс не выбран',
'usage.error.load': 'Не удалось загрузить метрики использования',
'usage.export.empty.title': 'Данные по использованию не загружены',
'usage.export.empty.body': 'Сначала загрузите данные использования, а потом экспортируйте снимок CSV.',
'usage.export.done.title': 'Использование экспортировано',
'usage.export.done.body': 'Текущий снимок использования экспортирован в CSV.',
'usage.chart.ok': 'Успешных: {count}',
'usage.chart.errors': 'Ошибок: {count}',
'usage.chart.week': 'Нед. {index}',
'usage.csv.header': 'Операция,Протокол,Вызовы,Ошибки,Доля ошибок (%),p50 (мс),p95 (мс),p99 (мс)',
// Settings sidebar
'settings.title': 'Настройки',
'settings.nav.workspace': 'Воркспейс',
'settings.nav.profile': 'Профиль',
'settings.nav.members': 'Участники',
'settings.nav.security': 'Безопасность',
'settings.nav.notif': 'Уведомления',
'settings.nav.danger': 'Опасная зона',
// Settings — workspace section
'settings.ws.title': 'Настройки воркспейса',
'settings.ws.subtitle': 'Общая информация о вашем воркспейсе',
'settings.ws.name': 'Имя воркспейса',
'settings.ws.display': 'Отображаемое имя',
'settings.ws.desc': 'Описание',
'settings.ws.tz': 'Часовой пояс',
'settings.ws.protocol': 'Протокол по умолчанию',
// Settings — language
'settings.lang.title': 'Язык',
'settings.lang.subtitle': 'Язык отображения интерфейса',
'settings.lang.en': 'English',
'settings.lang.ru': 'Русский',
// Settings — profile section
'settings.profile.title': 'Профиль',
'settings.profile.name': 'Полное имя',
'settings.profile.email': 'Email',
'settings.profile.role': 'Роль',
// Common buttons
'btn.save': 'Сохранить',
'btn.cancel': 'Отмена',
'btn.create': 'Создать',
'btn.delete': 'Удалить',
'btn.revoke': 'Отозвать',
'btn.copy': 'Копировать',
// Agents page
'agents.title': 'Агенты',
'agents.subtitle': 'Именованные MCP endpoint-ы, открывающие LLM ограниченный набор операций',
'agents.new': 'Новый агент',
'agents.stats.total': 'Всего агентов',
'agents.stats.total_sub': 'в этом воркспейсе',
'agents.stats.published': 'Опубликованы',
'agents.stats.published_sub': '{value}% от общего числа',
'agents.stats.tools': 'Открытых операций',
'agents.stats.tools_sub': 'в {count} агентах',
'agents.stats.calls': 'Вызовов сегодня',
'agents.stats.calls_sub': 'по всем агентам',
'agents.search': 'Поиск агентов...',
'agents.results': '{shown} из {total} агентов',
'agents.callout': 'У каждого агента свой MCP endpoint. Подключайте LLM-клиент к конкретному агенту, чтобы он видел только нужные инструменты, а не все {count} операций этого воркспейса.',
'agents.loading': 'Загрузка агентов…',
'agents.error.title': 'Не удалось загрузить агентов',
'agents.error.api': 'Воркспейс или API недоступен',
'agents.error.load': 'Не удалось загрузить агентов',
'agents.empty.initial.title': 'Агентов пока нет',
'agents.empty.initial.sub': 'Создайте первого агента, чтобы получить отдельный MCP endpoint с нужным набором инструментов.',
'agents.empty.search.title': 'Нет агентов по запросу "{query}"',
'agents.empty.search.sub': 'Попробуйте другой поисковый запрос.',
'agents.card.tools': 'инструментов',
'agents.card.keys': 'API-ключей',
'agents.card.calls_today': 'вызовов сегодня',
'agents.card.created': 'Создан {date}',
'agents.card.copy_endpoint': 'Скопировать endpoint',
'agents.action.edit': 'Редактировать',
'agents.action.archive': 'Архивировать',
'agents.action.delete': 'Удалить',
'agents.lifecycle.publish': 'Опубликовать',
'agents.lifecycle.unpublish': 'Снять с публикации',
'agents.lifecycle.restore_draft': 'Вернуть в черновик',
'agents.lifecycle.published': 'Опубликован',
'agents.lifecycle.archived': 'Архивирован',
'agents.lifecycle.draft': 'Черновик',
'agents.drawer.new_title': 'Новый агент',
'agents.drawer.edit_title': 'Редактировать агента',
'agents.drawer.new_sub': 'Настройте именованный MCP endpoint для вашей LLM',
'agents.drawer.edit_sub': 'Обновите настройки агента и выбор инструментов',
'agents.drawer.identity': 'Идентичность',
'agents.drawer.display_name': 'Отображаемое имя',
'agents.drawer.slug': 'Slug',
'agents.drawer.description': 'Описание',
'agents.drawer.optional': '(необязательно)',
'agents.drawer.required': 'обязательно',
'agents.drawer.status': 'Статус',
'agents.drawer.endpoint': 'MCP endpoint',
'agents.drawer.placeholder.name': 'Customer Support',
'agents.drawer.placeholder.slug': 'customer-support',
'agents.drawer.placeholder.description': 'Что делает этот агент? Для какой LLM или сценария он нужен?',
'agents.drawer.operations': 'Операции',
'agents.drawer.selected': 'Выбрано: {count}',
'agents.drawer.operations_sub': 'Выберите операции, которые открывает этот агент. LLM, подключенная к агенту, увидит только эти инструменты.',
'agents.drawer.filter_ops': 'Фильтр операций…',
'agents.drawer.ops_no_match': 'Нет операций по запросу "{query}"',
'agents.drawer.ops_selected': 'Выбрано операций: {count}',
'agents.drawer.clear_all': 'Очистить все',
'agents.drawer.recommendation': 'Сейчас выбрано {count} инструментов. LLM лучше работает, когда у агента меньше 15 инструментов. Подумайте о разбиении по сценариям.',
'agents.drawer.cancel': 'Отмена',
'agents.drawer.create': 'Создать агента',
'agents.drawer.save': 'Сохранить изменения',
'agents.toast.saved_title_create': 'Агент создан',
'agents.toast.saved_title_update': 'Агент обновлен',
'agents.toast.saved_message': '{name} сохранен, привязано инструментов: {count}.',
'agents.toast.save_error_title': 'Не удалось обновить агента',
'agents.toast.save_error_message': 'Не удалось сохранить агента',
'agents.toast.delete_confirm': 'Удалить этого агента? Действие нельзя отменить.',
'agents.toast.delete_title': 'Агент удален',
'agents.toast.delete_message': 'Агент {name} удален.',
'agents.toast.delete_error_title': 'Не удалось удалить',
'agents.toast.delete_error_message': 'Не удалось удалить агента',
'agents.toast.lifecycle_title': 'Жизненный цикл агента обновлен',
'agents.toast.lifecycle_publish': '{name} опубликован.',
'agents.toast.lifecycle_unpublish': '{name} возвращен в черновик.',
'agents.toast.lifecycle_archive': '{name} архивирован.',
'agents.toast.lifecycle_error_title': 'Не удалось обновить жизненный цикл',
'agents.toast.lifecycle_error_message': 'Не удалось обновить состояние агента',
'agents.toast.endpoint_title': 'MCP endpoint скопирован',
// Login
'login.title': 'Войти',
'login.subtitle': 'Добро пожаловать обратно',
'login.email': 'Email',
'login.password': 'Пароль',
'login.submit': 'Войти',
'login.sso': 'Войти через Google',
}
};
function t(key) {
var lang = localStorage.getItem('crank_lang') || 'en';
var tr = TRANSLATIONS[lang] || TRANSLATIONS.en;
return tr[key] !== undefined ? tr[key] : (TRANSLATIONS.en[key] !== undefined ? TRANSLATIONS.en[key] : key);
}
function tf(key, vars) {
return Object.keys(vars || {}).reduce(function(result, name) {
return result.replaceAll('{' + name + '}', vars[name]);
}, t(key));
}
function applyLang() {
// text content
document.querySelectorAll('[data-i18n]').forEach(function(el) {
el.textContent = t(el.getAttribute('data-i18n'));
});
// placeholder
document.querySelectorAll('[data-i18n-ph]').forEach(function(el) {
el.placeholder = t(el.getAttribute('data-i18n-ph'));
});
// title
document.querySelectorAll('[data-i18n-title]').forEach(function(el) {
el.title = t(el.getAttribute('data-i18n-title'));
});
// highlight active language button in settings
var lang = localStorage.getItem('crank_lang') || 'en';
document.querySelectorAll('.lang-btn').forEach(function(btn) {
btn.classList.toggle('active', btn.dataset.lang === lang);
});
}
function setLang(lang) {
localStorage.setItem('crank_lang', lang);
applyLang();
// Notify Alpine components to re-render reactive getters
window.dispatchEvent(new CustomEvent('crank:langchange', { detail: { lang: lang } }));
}
document.addEventListener('DOMContentLoaded', applyLang);