diff --git a/apps/web/src/components/analytics/monitoring-health-card.tsx b/apps/web/src/components/analytics/monitoring-health-card.tsx index c884d7e..4415205 100644 --- a/apps/web/src/components/analytics/monitoring-health-card.tsx +++ b/apps/web/src/components/analytics/monitoring-health-card.tsx @@ -27,10 +27,10 @@ export function MonitoringHealthCard({ return ( ) } diff --git a/apps/web/src/components/analytics/network-overview-analytics-card.tsx b/apps/web/src/components/analytics/network-overview-analytics-card.tsx index 389a288..52c76e3 100644 --- a/apps/web/src/components/analytics/network-overview-analytics-card.tsx +++ b/apps/web/src/components/analytics/network-overview-analytics-card.tsx @@ -40,15 +40,15 @@ export function NetworkOverviewAnalyticsCard({ /> - {loading ? '…' : 'онлайн'} + {loading ? '…' : 'в сети'} ), }, diff --git a/apps/web/src/components/dashboard/dashboard-kpi-sparkline-row.tsx b/apps/web/src/components/dashboard/dashboard-kpi-sparkline-row.tsx index 154086d..9c47892 100644 --- a/apps/web/src/components/dashboard/dashboard-kpi-sparkline-row.tsx +++ b/apps/web/src/components/dashboard/dashboard-kpi-sparkline-row.tsx @@ -40,7 +40,7 @@ function buildMetrics({ { id: 'bgp', title: 'BGP готовность', - label: 'Established / включённые', + label: 'Установлено / включено', value: loading || network.peersEnabled === 0 ? '—' : `${bgpPct}%`, delta: loading ? '…' : bgpPct >= 90 ? 'стабильно' : 'внимание', deltaVariant: bgpPct >= 90 ? 'success-light' : bgpPct >= 50 ? 'warning-light' : 'destructive-light', @@ -62,13 +62,13 @@ function buildMetrics({ { id: 'speakers', title: 'Спикеры', - label: 'Online / всего', + label: 'В сети / всего', value: loading ? '—' : `${network.speakersOnline}/${network.speakersTotal}`, delta: loading || network.speakersTotal === 0 ? '…' : network.speakersOnline === network.speakersTotal - ? 'все online' + ? 'все в сети' : 'частично', deltaVariant: network.speakersOnline === network.speakersTotal ? 'success-light' : 'warning-light', diff --git a/apps/web/src/components/dashboard/dashboard-quick-links.tsx b/apps/web/src/components/dashboard/dashboard-quick-links.tsx index 7a6c6a9..db624ba 100644 --- a/apps/web/src/components/dashboard/dashboard-quick-links.tsx +++ b/apps/web/src/components/dashboard/dashboard-quick-links.tsx @@ -7,7 +7,7 @@ const ACTIONS: QuickActionItem[] = [ { id: 'lookup', title: 'Проверка IP/домена', - description: 'Проверка IP в списках и BGP-сообществах.', + description: 'Проверка IP в списках и BGP community.', to: '/lookup', icon: , iconClassName: 'text-primary', @@ -22,8 +22,8 @@ const ACTIONS: QuickActionItem[] = [ }, { id: 'communities', - title: 'BGP-сообщества', - description: 'Справочник BGP-сообществ для политик экспорта.', + title: 'BGP community', + description: 'Справочник BGP community для политик экспорта.', to: '/directories', icon: , iconClassName: 'text-info', diff --git a/apps/web/src/components/directories/community-create-dialog.tsx b/apps/web/src/components/directories/community-create-dialog.tsx index d19fd82..9739c06 100644 --- a/apps/web/src/components/directories/community-create-dialog.tsx +++ b/apps/web/src/components/directories/community-create-dialog.tsx @@ -71,7 +71,7 @@ export function CommunityFormDialog({ ) } diff --git a/apps/web/src/components/directories/doh-profile-create-dialog.tsx b/apps/web/src/components/directories/doh-profile-create-dialog.tsx index c14bda5..54c2fee 100644 --- a/apps/web/src/components/directories/doh-profile-create-dialog.tsx +++ b/apps/web/src/components/directories/doh-profile-create-dialog.tsx @@ -70,7 +70,7 @@ export function DohProfileFormDialog({ if (timeoutMs.trim() !== '') { const ms = Number(timeoutMs) if (!Number.isFinite(ms) || !Number.isInteger(ms) || ms <= 0) { - toast.error('Timeout должен быть целым числом > 0') + toast.error('Таймаут должен быть целым числом больше 0') return } timeout = ms @@ -136,7 +136,7 @@ export function DohProfileFormDialog({ />
- + [ { id: 'api', - label: 'API health', - value: healthOk ? 'OK' : '—', + label: 'Состояние API', + value: healthOk ? 'норма' : '—', unit: '', percent: healthOk ? 100 : 0, icon: , diff --git a/apps/web/src/components/modules/module-cdn-source-dialog.tsx b/apps/web/src/components/modules/module-cdn-source-dialog.tsx index 6774f84..e0fe796 100644 --- a/apps/web/src/components/modules/module-cdn-source-dialog.tsx +++ b/apps/web/src/components/modules/module-cdn-source-dialog.tsx @@ -14,8 +14,8 @@ import { normalizeCdnSourceKind } from '@/lib/modules/helpers' import type { BgpCommunity, CdnPreviewResponse, CdnSource, CdnSourceCreate } from '@/types/api' const CDN_KIND_ITEMS = [ - { value: 'plaintext', label: 'plaintext' }, - { value: 'json', label: 'json' }, + { value: 'plaintext', label: 'Текст' }, + { value: 'json', label: 'JSON' }, ] as const interface ModuleCdnSourceDialogProps { diff --git a/apps/web/src/components/modules/module-edit-dialog.tsx b/apps/web/src/components/modules/module-edit-dialog.tsx index f5ab3c5..a91268d 100644 --- a/apps/web/src/components/modules/module-edit-dialog.tsx +++ b/apps/web/src/components/modules/module-edit-dialog.tsx @@ -160,7 +160,7 @@ export function ModuleEditDialog({

- Выключенный модуль не участвует в refresh и apply. + Выключенный модуль не участвует в обновлении и применении.

( - {row.original.source_kind} + {cdnSourceKindRu(row.original.source_kind)} ), }, { diff --git a/apps/web/src/components/network/network-discovered-peers-card.tsx b/apps/web/src/components/network/network-discovered-peers-card.tsx index f299d04..569d311 100644 --- a/apps/web/src/components/network/network-discovered-peers-card.tsx +++ b/apps/web/src/components/network/network-discovered-peers-card.tsx @@ -35,7 +35,7 @@ import type { PeerDiscoveryRow, SpeakerRow } from '@/types/api' function speakerLabel(s: SpeakerRow): string { if (s.role === 'master') { const host = s.agent_domain ?? s.endpoint - return host ? `CP · ${host}` : 'CP (master)' + return host ? `Плоскость · ${host}` : 'Плоскость управления' } return s.agent_domain ?? s.endpoint ?? `${s.id.slice(0, 8)}…` } @@ -51,7 +51,7 @@ const filterFields: FilterFieldConfig[] = [ icon: , type: 'text', className: 'w-48', - placeholder: 'IP или Neighbor ID…', + placeholder: 'IP или ID соседа…', }, ] @@ -94,7 +94,7 @@ export function NetworkDiscoveredPeersCard({ id: 'neighbor_id', accessorFn: (row) => row.neighbor_id || row.neighbor, header: ({ column }) => ( - + ), cell: ({ row }) => ( ), - meta: { headerTitle: 'Neighbor ID' }, + meta: { headerTitle: 'ID соседа' }, }, { accessorKey: 'remote_asn', @@ -212,7 +212,7 @@ export function NetworkDiscoveredPeersCard({ title="Одобрить пира" description={ approveTarget - ? `Neighbor ID ${approveTarget.neighbor_id || '—'} · ${approveTarget.neighbor} AS${approveTarget.remote_asn ?? '?'}` + ? `ID соседа ${approveTarget.neighbor_id || '—'} · ${approveTarget.neighbor} AS${approveTarget.remote_asn ?? '?'}` : undefined } className="sm:max-w-sm" diff --git a/apps/web/src/components/network/network-kpi.tsx b/apps/web/src/components/network/network-kpi.tsx index 75e493e..ea9e992 100644 --- a/apps/web/src/components/network/network-kpi.tsx +++ b/apps/web/src/components/network/network-kpi.tsx @@ -55,7 +55,7 @@ export function NetworkKpi({ } size="sm" > - {loading ? '…' : 'онлайн'} + {loading ? '…' : 'в сети'} ), to: '/network', diff --git a/apps/web/src/components/network/network-peers-card.tsx b/apps/web/src/components/network/network-peers-card.tsx index 60bf245..a7ca332 100644 --- a/apps/web/src/components/network/network-peers-card.tsx +++ b/apps/web/src/components/network/network-peers-card.tsx @@ -39,11 +39,11 @@ const PEER_TABS = [ const SESSION_STATE_OPTIONS = [ { value: 'Established', label: 'Установлена' }, - { value: 'Idle', label: 'Idle' }, - { value: 'Active', label: 'Active' }, - { value: 'Connect', label: 'Connect' }, - { value: 'OpenSent', label: 'OpenSent' }, - { value: 'OpenConfirm', label: 'OpenConfirm' }, + { value: 'Idle', label: 'Простой' }, + { value: 'Active', label: 'Поиск' }, + { value: 'Connect', label: 'Соединение' }, + { value: 'OpenSent', label: 'Open отправлен' }, + { value: 'OpenConfirm', label: 'Open подтверждён' }, ] function createDefaultPeerFilters(): Filter[] { diff --git a/apps/web/src/components/network/network-speakers-card.tsx b/apps/web/src/components/network/network-speakers-card.tsx index ac02e39..8b2ee07 100644 --- a/apps/web/src/components/network/network-speakers-card.tsx +++ b/apps/web/src/components/network/network-speakers-card.tsx @@ -31,14 +31,14 @@ function speakerDeleteLabel(s: SpeakerRow): string { const SPEAKER_TABS = [ { id: 'all', label: 'Все' }, - { id: 'online', label: 'Online' }, - { id: 'offline', label: 'Offline' }, + { id: 'online', label: 'В сети' }, + { id: 'offline', label: 'Не в сети' }, ] const ROLE_OPTIONS = [ - { value: 'primary', label: 'primary' }, - { value: 'secondary', label: 'secondary' }, - { value: 'speaker', label: 'speaker' }, + { value: 'primary', label: 'Основной' }, + { value: 'secondary', label: 'Резервный' }, + { value: 'speaker', label: 'Спикер' }, ] function createDefaultSpeakerFilters(): Filter[] { @@ -52,7 +52,7 @@ const speakerFilterFields: FilterFieldConfig[] = [ icon: , type: 'text', className: 'w-52', - placeholder: 'endpoint…', + placeholder: 'Адрес агента…', }, { key: 'role', @@ -128,7 +128,7 @@ export function NetworkSpeakersCard({ <> [] = [ @@ -20,7 +20,7 @@ export const speakerColumns: ColumnDef[] = [ { accessorKey: 'role', header: ({ column }) => , - cell: ({ row }) => {row.original.role}, + cell: ({ row }) => {speakerRoleRu(row.original.role)}, meta: { headerTitle: 'Роль' }, }, { diff --git a/apps/web/src/components/network/peer-form-dialog.tsx b/apps/web/src/components/network/peer-form-dialog.tsx index 62ab2cc..d3f6573 100644 --- a/apps/web/src/components/network/peer-form-dialog.tsx +++ b/apps/web/src/components/network/peer-form-dialog.tsx @@ -22,7 +22,7 @@ interface PeerFormDialogProps { function speakerLabel(s: SpeakerRow): string { if (s.role === 'master') { const host = s.agent_domain ?? s.endpoint - return host ? `CP · ${host}` : 'CP (master)' + return host ? `Плоскость · ${host}` : 'Плоскость управления' } return s.agent_domain ?? s.endpoint ?? `${s.id.slice(0, 8)}…` } diff --git a/apps/web/src/components/network/speaker-form-dialog.tsx b/apps/web/src/components/network/speaker-form-dialog.tsx index 19ad0ea..6b0a4f9 100644 --- a/apps/web/src/components/network/speaker-form-dialog.tsx +++ b/apps/web/src/components/network/speaker-form-dialog.tsx @@ -74,7 +74,7 @@ export function SpeakerFormDialog({ open, onOpenChange }: SpeakerFormDialogProps const ep = endpoint.trim() || (agentDomain.trim() ? `https://${agentDomain.trim()}` : '') if (!ep) { - toast.error('Укажите endpoint или agent domain') + toast.error('Укажите конечную точку или домен агента') return } const body: BgpSpeakerCreate = { @@ -95,7 +95,7 @@ export function SpeakerFormDialog({ open, onOpenChange }: SpeakerFormDialogProps open={open} onOpenChange={onOpenChange} title="Новый спикер" - description="BIRD-агент на ноде реплики или control plane" + description="BIRD-агент на ноде реплики или плоскости управления" className="sm:max-w-md" footer={ <> @@ -122,7 +122,7 @@ export function SpeakerFormDialog({ open, onOpenChange }: SpeakerFormDialogProps label="Роль" items={[ { value: 'replica', label: 'Реплика' }, - { value: 'master', label: 'Мастер (CP)' }, + { value: 'master', label: 'Мастер (плоскость)' }, ]} value={role} onValueChange={(v) => setRole(v ?? 'replica')} @@ -146,7 +146,7 @@ export function SpeakerFormDialog({ open, onOpenChange }: SpeakerFormDialogProps />
- + } title={`Откатиться к ревизии ${row.original.id.slice(0, 8)}…?`} - description="Будет создана новая ревизия на основе выбранной. Требуется роль operator." + description="Будет создана новая ревизия на основе выбранной. Требуется роль оператора." confirmLabel="Откатить" destructive onConfirm={() => rollbackMutation.mutate(row.original.id)} diff --git a/apps/web/src/components/reui/data-grid/data-grid-column-filter.tsx b/apps/web/src/components/reui/data-grid/data-grid-column-filter.tsx index 12d7d4f..61a312f 100644 --- a/apps/web/src/components/reui/data-grid/data-grid-column-filter.tsx +++ b/apps/web/src/components/reui/data-grid/data-grid-column-filter.tsx @@ -61,7 +61,7 @@ function DataGridColumnFilter({
{selectedValues.size > 2 ? ( - {selectedValues.size} selected + {selectedValues.size} выбрано ) : ( options @@ -94,7 +94,7 @@ function DataGridColumnFilter({
{filteredOptions.length === 0 ? (
- No results found. + Ничего не найдено.
) : (
@@ -170,7 +170,7 @@ function DataGridColumnFilter({ }} className="hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground rounded-md relative flex cursor-pointer items-center justify-center px-2 py-1.5 text-sm outline-hidden select-none" > - Clear filters + Сбросить фильтры
diff --git a/apps/web/src/components/reui/data-grid/data-grid-pagination.tsx b/apps/web/src/components/reui/data-grid/data-grid-pagination.tsx index 16c2d29..e3eeb8e 100644 --- a/apps/web/src/components/reui/data-grid/data-grid-pagination.tsx +++ b/apps/web/src/components/reui/data-grid/data-grid-pagination.tsx @@ -39,11 +39,11 @@ function DataGridPagination(props: DataGridPaginationProps): React.JSX.Element { sizes: [5, 10, 25, 50, 100], sizesSkeleton: , moreLimit: 5, - info: "{from} - {to} of {count}", + info: "{from}–{to} из {count}", infoSkeleton: , - rowsPerPageLabel: "Rows per page", - previousPageLabel: "Go to previous page", - nextPageLabel: "Go to next page", + rowsPerPageLabel: "Строк на странице", + previousPageLabel: "Предыдущая страница", + nextPageLabel: "Следующая страница", ellipsisText: "...", } diff --git a/apps/web/src/components/reui/data-grid/data-grid-table-dnd-rows.tsx b/apps/web/src/components/reui/data-grid/data-grid-table-dnd-rows.tsx index b4ee534..12360c7 100644 --- a/apps/web/src/components/reui/data-grid/data-grid-table-dnd-rows.tsx +++ b/apps/web/src/components/reui/data-grid/data-grid-table-dnd-rows.tsx @@ -73,7 +73,7 @@ function DataGridTableDndRowHandle({ className }: { className?: string }) { "size-7 cursor-grab opacity-70 hover:bg-transparent hover:opacity-100 active:cursor-grabbing", className )} - aria-label="Drag to reorder row" + aria-label="Перетащить строку" disabled >
diff --git a/apps/web/src/components/reui/filters.tsx b/apps/web/src/components/reui/filters.tsx index 8f6a943..00c0e85 100644 --- a/apps/web/src/components/reui/filters.tsx +++ b/apps/web/src/components/reui/filters.tsx @@ -123,75 +123,70 @@ export interface FilterI18nConfig { // Default English i18n configuration export const DEFAULT_I18N: FilterI18nConfig = { - // UI Labels - addFilter: "Filter", - searchFields: "Filter...", - noFieldsFound: "No filters found.", - noResultsFound: "No results found.", - select: "Select...", - true: "True", - false: "False", - min: "Min", - max: "Max", - to: "to", - typeAndPressEnter: "Type and press Enter to add tag", - selected: "selected", - selectedCount: "selected", + addFilter: "Фильтр", + searchFields: "Фильтр…", + noFieldsFound: "Поля не найдены.", + noResultsFound: "Ничего не найдено.", + select: "Выбрать…", + true: "Да", + false: "Нет", + min: "Мин.", + max: "Макс.", + to: "—", + typeAndPressEnter: "Введите и нажмите Enter", + selected: "выбрано", + selectedCount: "выбрано", percent: "%", defaultCurrency: "$", defaultColor: "#000000", - addFilterTitle: "Add filter", + addFilterTitle: "Добавить фильтр", - // Operators operators: { - is: "is", - isNot: "is not", - isAnyOf: "is any of", - isNotAnyOf: "is not any of", - includesAll: "includes all", - excludesAll: "excludes all", - before: "before", - after: "after", - between: "between", - notBetween: "not between", - contains: "contains", - notContains: "does not contain", - startsWith: "starts with", - endsWith: "ends with", - isExactly: "is exactly", - equals: "equals", - notEquals: "not equals", - greaterThan: "greater than", - lessThan: "less than", - overlaps: "overlaps", - includes: "includes", - excludes: "excludes", - includesAllOf: "includes all of", - includesAnyOf: "includes any of", - empty: "is empty", - notEmpty: "is not empty", + is: "равно", + isNot: "не равно", + isAnyOf: "любое из", + isNotAnyOf: "кроме", + includesAll: "включает все", + excludesAll: "исключает все", + before: "до", + after: "после", + between: "между", + notBetween: "вне диапазона", + contains: "содержит", + notContains: "не содержит", + startsWith: "начинается с", + endsWith: "заканчивается на", + isExactly: "точно", + equals: "равно", + notEquals: "не равно", + greaterThan: "больше", + lessThan: "меньше", + overlaps: "пересекается", + includes: "включает", + excludes: "исключает", + includesAllOf: "включает все", + includesAnyOf: "включает любое", + empty: "пусто", + notEmpty: "не пусто", }, - // Placeholders placeholders: { - enterField: (fieldType: string) => `Enter ${fieldType}...`, - selectField: "Select...", - searchField: (fieldName: string) => `Search ${fieldName.toLowerCase()}...`, - enterKey: "Enter key...", - enterValue: "Enter value...", + enterField: (fieldType: string) => `Введите ${fieldType}…`, + selectField: "Выбрать…", + searchField: (fieldName: string) => `Поиск ${fieldName.toLowerCase()}…`, + enterKey: "Введите ключ…", + enterValue: "Введите значение…", }, - // Helper functions helpers: { formatOperator: (operator: string) => operator.replace(/_/g, " "), }, - // Validation validation: { - invalidEmail: "Invalid email format", - invalidUrl: "Invalid URL format", - invalidTel: "Invalid phone format", - invalid: "Invalid input format", + invalidEmail: "Некорректный email", + invalidUrl: "Некорректный URL", + invalidTel: "Некорректный телефон", + invalid: "Некорректное значение", }, } diff --git a/apps/web/src/components/schedule/schedule-agenda-panel.tsx b/apps/web/src/components/schedule/schedule-agenda-panel.tsx index 35d2b2e..0d32aec 100644 --- a/apps/web/src/components/schedule/schedule-agenda-panel.tsx +++ b/apps/web/src/components/schedule/schedule-agenda-panel.tsx @@ -107,7 +107,7 @@ export function ScheduleAgendaPanel({ return (
diff --git a/apps/web/src/components/schedule/schedule-modules-grid.tsx b/apps/web/src/components/schedule/schedule-modules-grid.tsx index dcfc504..30253d1 100644 --- a/apps/web/src/components/schedule/schedule-modules-grid.tsx +++ b/apps/web/src/components/schedule/schedule-modules-grid.tsx @@ -43,7 +43,7 @@ export function ScheduleModulesGrid({ cell: ({ row }) => ( {row.original.cron_expr ?? - (row.original.refresh_interval_sec ? `${row.original.refresh_interval_sec}s` : '—')} + (row.original.refresh_interval_sec ? `${row.original.refresh_interval_sec} с` : '—')} ), meta: { headerTitle: 'Расписание' }, diff --git a/apps/web/src/components/settings/appearance-settings-tab.tsx b/apps/web/src/components/settings/appearance-settings-tab.tsx index 99fc40f..4ebe7f1 100644 --- a/apps/web/src/components/settings/appearance-settings-tab.tsx +++ b/apps/web/src/components/settings/appearance-settings-tab.tsx @@ -90,16 +90,16 @@ export function AppearanceSettingsTab() { = { @@ -40,12 +40,12 @@ const BIRD_LABELS: Record = { bird_local_ipv4: 'Локальный IPv4', bird_local_ipv6: 'Локальный IPv6', bird_local_asn: 'Локальный ASN', - bird_bgp_source_ipv4: 'BGP source IPv4', - bird_bgp_source_ipv6: 'BGP source IPv6', + bird_bgp_source_ipv4: 'Исходный IPv4 BGP', + bird_bgp_source_ipv6: 'Исходный IPv6 BGP', peer_discovery_enabled: 'Автообнаружение пиров', - peer_discovery_ranges_v4: 'Discovery CIDR IPv4', - peer_discovery_ranges_v6: 'Discovery CIDR IPv6', - peer_discovery_require_external: 'Только external ASN', + peer_discovery_ranges_v4: 'CIDR автообнаружения IPv4', + peer_discovery_ranges_v6: 'CIDR автообнаружения IPv6', + peer_discovery_require_external: 'Только внешние ASN', } const BIRD_BOOL_ITEMS = [ @@ -55,10 +55,10 @@ const BIRD_BOOL_ITEMS = [ const BIRD_HINTS: Partial> = { peer_discovery_enabled: - 'Dynamic neighbor range в BIRD (карантин import/export none). Требует CIDR.', + 'Динамический диапазон соседей в BIRD (карантин import/export none). Нужны CIDR.', peer_discovery_ranges_v4: 'Через запятую или пробел, напр. 198.51.100.0/24 203.0.113.0/24', peer_discovery_ranges_v6: 'Опционально, напр. 2001:db8::/32', - peer_discovery_require_external: 'neighbor range … external (любой чужой ASN)', + peer_discovery_require_external: 'Диапазон соседей с внешним (любым чужим) ASN', } export function BirdSettingsTab() { @@ -103,8 +103,8 @@ export function BirdSettingsTab() { return (
@@ -184,11 +184,11 @@ export function BirdSettingsTab() { > {() => (