import i18n from 'i18next' import { initReactI18next } from 'react-i18next' import LanguageDetector from 'i18next-browser-languagedetector' i18n .use(LanguageDetector) .use(initReactI18next) .init({ fallbackLng: 'ru-RU', supportedLngs: ['ru-RU', 'en-US'], interpolation: { escapeValue: false }, resources: { 'ru-RU': { translation: { 'app.title': 'Ordinis НСИ ЦУОД', 'nav.dictionaries': 'Справочники', 'nav.audit': 'Аудит', 'nav.outbox': 'Outbox', 'nav.webhooks': 'Webhooks', 'webhooks.title': 'Webhook subscriptions', 'webhooks.subtitle': 'HTTP-доставка событий не-Kafka подписчикам', 'webhooks.empty': 'Нет подписок. Создайте первую через кнопку справа.', 'webhooks.active': 'Активна', 'webhooks.inactive': 'Отключена', 'webhooks.allScopes': 'все scope', 'webhooks.allDicts': 'все справочники', 'webhooks.allEvents': 'все события', 'webhooks.notFound': 'Подписка не найдена', 'webhooks.col.name': 'Имя', 'webhooks.col.url': 'URL', 'webhooks.col.scopes': 'Scope filter', 'webhooks.col.dictionaries': 'Справочники', 'webhooks.col.status': 'Статус', 'webhooks.action.create': 'Создать', 'webhooks.action.delete': 'Удалить', 'webhooks.action.rotateSecret': 'Сменить secret', 'webhooks.action.test': 'Тест ping', 'webhooks.test.success': 'Receiver получил test event и ответил 2xx', 'webhooks.test.failure': 'Receiver не принял test event', 'webhooks.test.rejected': 'Запрос не отправлен (SSRF guard / serialization)', 'webhooks.test.latency': 'latency', 'webhooks.confirmDelete': 'Удалить подписку «{{name}}»? Все pending deliveries удалятся каскадом.', 'webhooks.confirmRotate': 'Сменить HMAC secret? Старый сразу перестанет работать — receiver надо обновить заранее.', 'webhooks.field.name': 'Имя подписки', 'webhooks.field.url': 'Webhook URL', 'webhooks.field.urlHint': 'https:// или http://. Private CIDR (10/8, 192.168/16, 127/8) запрещены SSRF guard', 'webhooks.field.scopeFilter': 'Scope filter', 'webhooks.field.scopeFilterHint': 'Пусто = все scope', 'webhooks.field.dictionaryFilter': 'Справочники (CSV)', 'webhooks.field.dictionaryFilterHint': 'Через запятую: spacecraft, satellite_type. Пусто = все', 'webhooks.field.eventTypeFilter': 'Типы событий (CSV)', 'webhooks.field.eventTypeFilterHint': 'record.created, record.updated, record.closed. Пусто = все', 'webhooks.field.description': 'Описание', 'webhooks.field.createdBy': 'Создатель', 'webhooks.error.nameUrlRequired': 'Имя и URL обязательны', 'webhooks.error.urlScheme': 'URL должен начинаться с http:// или https://', 'webhooks.secret.title': 'HMAC secret для «{{name}}»', 'webhooks.secret.warningTitle': 'Скопируй сейчас!', 'webhooks.secret.warning': 'Этот secret показывается ОДИН раз. Дальше будет замаскирован. Передай receiver чтобы он мог проверять X-Ordinis-Signature header.', 'webhooks.secret.rotatedTitle': 'Secret обновлён', 'webhooks.secret.rotatedWarning': 'Новый secret. Старый перестал работать. Обнови receiver.', 'webhooks.secret.copy': 'Копировать', 'webhooks.secret.copied': 'Скопировано', 'webhooks.secret.acknowledge': 'Сохранил', 'webhooks.deliveries.title': 'История доставок', 'webhooks.deliveries.empty': 'Доставок ещё не было', 'webhooks.deliveries.col.eventId': 'Event ID', 'webhooks.deliveries.col.attempts': 'Попыток', 'webhooks.deliveries.col.lastAttempt': 'Последняя попытка', 'webhooks.deliveries.col.lastStatusCode': 'HTTP статус', 'webhooks.deliveries.col.lastError': 'Последняя ошибка', 'webhooks.deliveries.action.retry': 'Повторить доставку', 'webhooks.deliveries.confirmRetry': 'Сбросить attemptCount=0 и поставить в очередь повтор? Receiver получит payload снова.', 'webhooks.deliveries.filter.label': 'Фильтр по статусу', 'webhooks.deliveries.filter.clear': 'Сбросить', 'audit.title': 'Журнал аудита', 'audit.subtitle': 'Юридически значимый лог изменений НСИ', 'audit.filter.dictionary': 'Справочник', 'audit.filter.user': 'Пользователь', 'audit.filter.action': 'Действие', 'audit.filter.businessKey': 'Бизнес-ключ', 'audit.filter.from': 'С', 'audit.filter.to': 'По', 'audit.filter.reset': 'Сбросить', 'audit.filter.apply': 'Применить', 'audit.filter.active': 'Активные фильтры', 'audit.filter.removeChip': 'Убрать фильтр', 'audit.col.time': 'Время', 'audit.col.action': 'Действие', 'audit.col.dictionary': 'Справочник', 'audit.col.businessKey': 'Бизнес-ключ', 'audit.col.user': 'Пользователь', 'audit.col.scope': 'Scope', 'audit.col.trace': 'Trace', 'audit.col.diff': 'Изменения', 'audit.action.expand': 'Раскрыть', 'audit.action.export': 'Экспорт CSV', 'audit.empty': 'Нет записей по выбранным фильтрам', 'audit.action.CREATE': 'Создание', 'audit.action.UPDATE': 'Изменение', 'audit.action.CLOSE': 'Закрытие', 'audit.diff.before': 'Было', 'audit.diff.after': 'Стало', 'audit.page.of': 'Стр. {{cur}} из {{total}}', 'audit.page.size': 'На странице', 'audit.page.prev': 'Назад', 'audit.page.next': 'Вперёд', 'outbox.title': 'Outbox events', 'outbox.subtitle': 'Очередь публикации в Kafka и DLQ', 'outbox.stats.pending': 'В очереди', 'outbox.stats.dlq': 'В DLQ', 'outbox.dlq.col.id': 'ID', 'outbox.dlq.col.eventType': 'Тип', 'outbox.dlq.col.dictionary': 'Справочник', 'outbox.dlq.col.aggregateId': 'Aggregate ID', 'outbox.dlq.col.topic': 'Topic', 'outbox.dlq.col.attempts': 'Попыток', 'outbox.dlq.col.lastError': 'Последняя ошибка', 'outbox.dlq.col.dlqAt': 'В DLQ с', 'outbox.dlq.empty': 'DLQ пуста — все события публикуются успешно', 'header.scope': 'Доступный scope', 'dict.list.subtitle': 'Каталоги НСИ ЦУОД ОДХ', 'dict.list.search.placeholder': 'Поиск по названию, коду или описанию', 'dict.list.search.empty': 'Ничего не найдено', 'dict.list.found': 'Показано {{shown}} из {{total}}', 'dict.list.recordCount_one': '{{count}} запись', 'dict.list.recordCount_few': '{{count}} записи', 'dict.list.recordCount_many': '{{count}} записей', 'dict.list.recordCount_other': '{{count}} записей', 'dict.list.section.PUBLIC': 'Публичные', 'dict.list.section.INTERNAL': 'Внутренние', 'dict.list.section.RESTRICTED': 'Ограниченные', 'dict.empty': 'В этом справочнике пока нет записей', 'dict.col.businessKey': 'Бизнес-ключ', 'dict.col.scope': 'Scope', 'dict.col.validFrom': 'Действует с', 'dict.col.locale': 'Локаль', 'dict.col.actions': 'Действия', 'dict.list.records': 'записей', 'dict.action.create': 'Создать запись', 'dict.action.edit': 'Изменить', 'dict.action.close': 'Закрыть', 'dict.confirmClose.title': 'Закрыть запись?', 'dict.confirmClose.body': 'Запись «{{key}}» будет помечена как закрытая (valid_to = now). Действие обратимо через создание новой версии.', 'dict.confirmClose.reason': 'Причина (опционально)', 'dict.confirmClose.reasonPlaceholder': 'например, «дубликат» или «выведено из эксплуатации»', 'dict.bulk.selectAll': 'Выбрать все на странице', 'dict.bulk.selectRow': 'Выбрать запись', 'dict.bulk.selectedCount_one': 'Выбрано {{count}} из {{total}}', 'dict.bulk.selectedCount_few': 'Выбрано {{count}} из {{total}}', 'dict.bulk.selectedCount_many': 'Выбрано {{count}} из {{total}}', 'dict.bulk.selectedCount_other': 'Выбрано {{count}} из {{total}}', 'dict.bulk.clearSelection': 'Снять выделение', 'dict.bulk.closeAction': 'Закрыть выбранные', 'dict.bulk.exportAction': 'Экспорт CSV', 'dict.page.prev': 'Назад', 'dict.page.next': 'Вперёд', 'dict.page.of': 'Стр. {{cur}} из {{total}}', 'dict.page.range': 'Записи {{from}}–{{to}} из {{total}}', 'dict.bulk.confirmTitle': 'Массовое закрытие записей', 'dict.bulk.confirmBody': '{{count}} запис(ей) будут помечены как закрытые (valid_to = now). Действие обратимо через создание новой версии. Каждая запись закрывается отдельно — если часть уже закрыта, остальные обработаются.', 'dict.bulk.confirmAction': 'Закрыть {{count}}', 'dict.bulk.resultClosed': 'Закрыто: {{count}}', 'dict.bulk.resultSkipped': 'Пропущено (уже закрыто или не найдено): {{count}}', 'dict.bulk.resultErrors': 'Ошибок: {{count}}', 'dict.bulk.dismiss': 'Готово', 'form.identity': 'Идентификация', 'form.idSourceNote': 'Бизнес-ключ создастся автоматически из поля «{{field}}»', 'form.tabs.identity': 'Идентификация', 'form.tabs.description': 'Описание', 'form.tabs.extra': 'Дополнительно', 'form.businessKey': 'Бизнес-ключ', 'form.validFrom': 'Действует с', 'form.validFromHint': 'Когда запись становится активной. Пусто = с момента создания.', 'form.validTo': 'Действует до', 'form.validToHint': 'Когда перестаёт действовать. Пусто = бессрочно.', 'form.error.validToBeforeFrom': 'Дата окончания должна быть позже даты начала.', 'form.dataFields': 'Поля записи', 'form.cancel': 'Отмена', 'form.save': 'Сохранить', 'form.saving': 'Сохранение...', 'form.error.required': 'Обязательное поле', 'scope.PUBLIC': 'PUBLIC — публичный', 'scope.INTERNAL': 'INTERNAL — внутренний', 'scope.RESTRICTED': 'RESTRICTED — ограниченный', 'schema.action.create': 'Создать справочник', 'schema.action.edit': 'Изменить справочник', 'schema.action.editSchema': 'Схема', 'schema.tabs.metadata': 'Метаданные', 'schema.tabs.schema': 'Поля', 'schema.tabs.preview': 'JSON', 'schema.name': 'Имя', 'schema.nameHint': 'snake_case, начинается с буквы (например my_dictionary)', 'schema.nameInvalid': 'Только snake_case: a-z, 0-9, _; начинается с буквы', 'schema.displayName': 'Отображаемое имя', 'schema.description': 'Описание', 'schema.scope': 'Scope', 'schema.bundle': 'Bundle', 'schema.bundleHint': 'Обычно cuod', 'schema.template.label': 'Создать по шаблону', 'schema.template.hint': 'Скопирует схему, scope и locale выбранного словаря. Имя оставьте уникальное; версия сбросится на 1.0.0.', 'schema.template.placeholder': '— без шаблона —', 'schema.version': 'Версия схемы', 'schema.supportedLocales': 'Поддерживаемые локали', 'schema.defaultLocale': 'Локаль по умолчанию', 'schema.property': 'Поле', 'schema.required': 'Обязательное', 'schema.pattern': 'Pattern (regex)', 'schema.enumValues': 'Значения enum (через запятую)', 'schema.enumValuesHint': 'Например: ACTIVE, INACTIVE, RETIRED', 'schema.arrayEnumOptional': 'Допустимые значения (опц., через запятую)', 'schema.unique': 'Уникальное', 'schema.uniqueHint': 'Дубликаты по этому полю запрещены', 'schema.idSource': 'Использовать как ID (бизнес-ключ)', 'schema.idSourceHint': 'Бизнес-ключ записи будет автоматически = значению этого поля', 'schema.idSourceManual': 'Ввод вручную', 'schema.versionSuggest': 'Рекомендуемая версия: {{v}}', 'schema.diff.blockedTitle': 'Несовместимое изменение схемы', 'schema.diff.blockedBody': 'Эти изменения сломают существующие записи. Сохранение заблокировано. Создайте новый справочник или откатите изменения.', 'schema.diff.compatibleTitle': 'Изменения схемы (совместимые)', 'history.title': 'История версий', 'history.empty': 'История пуста', 'history.current': 'текущая', 'history.active': 'активна', 'history.scheduled': 'запланирована', 'history.closed': 'закрыта', 'history.validFrom': 'действует с', 'history.validTo': 'действует до', 'history.updatedBy': 'кем', 'history.viewData': 'данные записи (JSON)', 'schema.empty': 'Полей нет — добавь первое', 'schema.addProperty': 'Добавить поле', 'schema.delete': 'Удалить', 'schema.moveUp': 'Выше', 'schema.moveDown': 'Ниже', 'schema.kind.label': 'Тип', 'schema.kind.string': 'Строка', 'schema.kind.integer': 'Целое', 'schema.kind.number': 'Число', 'schema.kind.boolean': 'Логическое', 'schema.kind.enum': 'Enum (выбор из списка)', 'schema.kind.reference': 'Ссылка на словарь (FK)', 'schema.kind.localized': 'I18N строка (ru-RU/en-US/...)', 'schema.kind.date': 'Дата', 'schema.kind.datetime': 'Дата+время', 'schema.kind.array_string': 'Массив строк', 'schema.kind.opaque': 'Сложный (JSON)', 'schema.reference.title': 'FK на запись справочника', 'schema.reference.hint': 'Значение поля будет проверяться при сохранении: оно должно совпадать с business-key или указанным полем существующей записи целевого словаря.', 'schema.reference.targetDict': 'Целевой словарь', 'schema.reference.targetField': 'Поле', 'schema.reference.targetFieldHint': 'Обычно code или business_key', 'schema.reference.required': 'Заполните оба: словарь и поле — иначе FK не сработает.', 'schema.opaque.title': 'Сложное поле — редактируется только через JSON-таб', 'schema.opaque.hint': 'Nested object / array of objects. Поля-табе нет редактора, raw schema показан ниже. Меняй через вкладку "JSON".', 'field.name': 'Наименование', 'field.designator': 'Designator (COSPAR)', 'field.norad_id': 'NORAD ID', 'field.status': 'Статус', 'field.country': 'Страна', 'field.operator': 'Оператор', 'field.mass_kg': 'Масса (кг)', 'field.mission': 'Миссия', 'field.orbit': 'Орбита', 'field.launch_date': 'Дата запуска', 'field.decay_date': 'Дата схода', 'field.spectrum_bands': 'Спектральные диапазоны', 'field.satellite_type_code': 'Тип КА (код)', 'field.code': 'Код', 'field.description': 'Описание', 'field.location': 'Местоположение', 'field.coordinates': 'Координаты', 'field.frequency_bands': 'Частотные диапазоны', 'loading': 'Загрузка...', 'error.failed': 'Не удалось загрузить данные', 'auth.login': 'Войти', 'auth.logout': 'Выйти', 'dict.filter.searchPlaceholder': 'Поиск по записям…', 'dict.filter.scopeLabel': 'Фильтр по scope', 'dict.filter.matched': 'Найдено {{count}} из {{total}}', 'dict.filter.clear': 'Сбросить', 'dict.filter.noMatches': 'По фильтру записей не найдено', 'aoi.button': 'AOI фильтр', 'aoi.activeBbox': 'AOI: bbox {{value}}', 'aoi.activePolygon': 'AOI: полигон ({{points}} точек)', 'aoi.clear': 'Сбросить AOI', 'aoi.dialog.title': 'Выбор области интереса (AOI)', 'aoi.dialog.help': 'Нарисуйте прямоугольник или полигон. Применяется как фильтр к записям со spatial geometry.', 'aoi.dialog.apply': 'Применить', 'aoi.dialog.drawRectangle': 'Прямоугольник', 'aoi.dialog.drawPolygon': 'Полигон', 'aoi.dialog.clear': 'Очистить', 'aoi.dialog.polygonHint': 'Клик — добавить точку, двойной клик — закрыть полигон (минимум 3 точки)', 'aoi.dialog.mapAriaLabel': 'Карта для выбора области интереса', 'aoi.error.noShape': 'Сначала нарисуйте область на карте', 'aoi.error.invalid': 'Геометрия некорректна — попробуйте перерисовать', }, }, 'en-US': { translation: { 'app.title': 'Ordinis MDM', 'nav.dictionaries': 'Dictionaries', 'nav.audit': 'Audit', 'nav.outbox': 'Outbox', 'nav.webhooks': 'Webhooks', 'webhooks.title': 'Webhook subscriptions', 'webhooks.subtitle': 'HTTP delivery of events to non-Kafka consumers', 'webhooks.empty': 'No subscriptions yet. Create one via the button on the right.', 'webhooks.active': 'Active', 'webhooks.inactive': 'Disabled', 'webhooks.allScopes': 'all scopes', 'webhooks.allDicts': 'all dictionaries', 'webhooks.allEvents': 'all events', 'webhooks.notFound': 'Subscription not found', 'webhooks.col.name': 'Name', 'webhooks.col.url': 'URL', 'webhooks.col.scopes': 'Scope filter', 'webhooks.col.dictionaries': 'Dictionaries', 'webhooks.col.status': 'Status', 'webhooks.action.create': 'Create', 'webhooks.action.delete': 'Delete', 'webhooks.action.rotateSecret': 'Rotate secret', 'webhooks.action.test': 'Test ping', 'webhooks.test.success': 'Receiver got the test event and replied 2xx', 'webhooks.test.failure': 'Receiver rejected the test event', 'webhooks.test.rejected': 'Request not sent (SSRF guard / serialization)', 'webhooks.test.latency': 'latency', 'webhooks.confirmDelete': 'Delete subscription "{{name}}"? All pending deliveries will be cascade-deleted.', 'webhooks.confirmRotate': 'Rotate HMAC secret? The old one stops working immediately — update the receiver first.', 'webhooks.field.name': 'Subscription name', 'webhooks.field.url': 'Webhook URL', 'webhooks.field.urlHint': 'https:// or http://. Private CIDRs (10/8, 192.168/16, 127/8) blocked by SSRF guard', 'webhooks.field.scopeFilter': 'Scope filter', 'webhooks.field.scopeFilterHint': 'Empty = all scopes', 'webhooks.field.dictionaryFilter': 'Dictionaries (CSV)', 'webhooks.field.dictionaryFilterHint': 'Comma-separated: spacecraft, satellite_type. Empty = all', 'webhooks.field.eventTypeFilter': 'Event types (CSV)', 'webhooks.field.eventTypeFilterHint': 'record.created, record.updated, record.closed. Empty = all', 'webhooks.field.description': 'Description', 'webhooks.field.createdBy': 'Created by', 'webhooks.error.nameUrlRequired': 'Name and URL are required', 'webhooks.error.urlScheme': 'URL must start with http:// or https://', 'webhooks.secret.title': 'HMAC secret for "{{name}}"', 'webhooks.secret.warningTitle': 'Copy now!', 'webhooks.secret.warning': 'This secret is shown ONCE. It will be masked afterwards. Pass it to the receiver so it can verify the X-Ordinis-Signature header.', 'webhooks.secret.rotatedTitle': 'Secret rotated', 'webhooks.secret.rotatedWarning': 'New secret. Old one stopped working. Update the receiver.', 'webhooks.secret.copy': 'Copy', 'webhooks.secret.copied': 'Copied', 'webhooks.secret.acknowledge': 'Saved it', 'webhooks.deliveries.title': 'Delivery history', 'webhooks.deliveries.empty': 'No deliveries yet', 'webhooks.deliveries.col.eventId': 'Event ID', 'webhooks.deliveries.col.attempts': 'Attempts', 'webhooks.deliveries.col.lastAttempt': 'Last attempt', 'webhooks.deliveries.col.lastStatusCode': 'HTTP status', 'webhooks.deliveries.col.lastError': 'Last error', 'webhooks.deliveries.action.retry': 'Retry delivery', 'webhooks.deliveries.confirmRetry': 'Reset attemptCount=0 and queue a retry? The receiver will get the payload again.', 'webhooks.deliveries.filter.label': 'Status filter', 'webhooks.deliveries.filter.clear': 'Clear', 'audit.title': 'Audit log', 'audit.subtitle': 'Legally significant log of NSI changes', 'audit.filter.dictionary': 'Dictionary', 'audit.filter.user': 'User', 'audit.filter.action': 'Action', 'audit.filter.businessKey': 'Business key', 'audit.filter.from': 'From', 'audit.filter.to': 'To', 'audit.filter.reset': 'Reset', 'audit.filter.apply': 'Apply', 'audit.filter.active': 'Active filters', 'audit.filter.removeChip': 'Remove filter', 'audit.col.time': 'Time', 'audit.col.action': 'Action', 'audit.col.dictionary': 'Dictionary', 'audit.col.businessKey': 'Business key', 'audit.col.user': 'User', 'audit.col.scope': 'Scope', 'audit.col.trace': 'Trace', 'audit.col.diff': 'Diff', 'audit.action.expand': 'Expand', 'audit.action.export': 'Export CSV', 'audit.empty': 'No entries match the filters', 'audit.action.CREATE': 'Create', 'audit.action.UPDATE': 'Update', 'audit.action.CLOSE': 'Close', 'audit.diff.before': 'Before', 'audit.diff.after': 'After', 'audit.page.of': 'Page {{cur}} of {{total}}', 'audit.page.size': 'Per page', 'audit.page.prev': 'Prev', 'audit.page.next': 'Next', 'outbox.title': 'Outbox events', 'outbox.subtitle': 'Publication queue and DLQ', 'outbox.stats.pending': 'Pending', 'outbox.stats.dlq': 'In DLQ', 'outbox.dlq.col.id': 'ID', 'outbox.dlq.col.eventType': 'Type', 'outbox.dlq.col.dictionary': 'Dictionary', 'outbox.dlq.col.aggregateId': 'Aggregate ID', 'outbox.dlq.col.topic': 'Topic', 'outbox.dlq.col.attempts': 'Attempts', 'outbox.dlq.col.lastError': 'Last error', 'outbox.dlq.col.dlqAt': 'DLQ since', 'outbox.dlq.empty': 'DLQ empty — all events publish successfully', 'header.scope': 'Allowed scope', 'dict.list.subtitle': 'Reference data catalogues', 'dict.list.search.placeholder': 'Search by name, code or description', 'dict.list.search.empty': 'Nothing found', 'dict.list.found': 'Showing {{shown}} of {{total}}', 'dict.list.recordCount_one': '{{count}} record', 'dict.list.recordCount_other': '{{count}} records', 'dict.list.section.PUBLIC': 'Public', 'dict.list.section.INTERNAL': 'Internal', 'dict.list.section.RESTRICTED': 'Restricted', 'dict.empty': 'No records in this dictionary yet', 'dict.col.businessKey': 'Business key', 'dict.col.scope': 'Scope', 'dict.col.validFrom': 'Valid from', 'dict.col.locale': 'Locale', 'dict.col.actions': 'Actions', 'dict.list.records': 'records', 'dict.action.create': 'Create record', 'dict.action.edit': 'Edit', 'dict.action.close': 'Close', 'dict.confirmClose.title': 'Close record?', 'dict.confirmClose.body': 'Record "{{key}}" will be marked as closed (valid_to = now). Reversible by creating a new version.', 'dict.confirmClose.reason': 'Reason (optional)', 'dict.confirmClose.reasonPlaceholder': 'e.g., "duplicate" or "decommissioned"', 'dict.bulk.selectAll': 'Select all on page', 'dict.bulk.selectRow': 'Select record', 'dict.bulk.selectedCount_one': '{{count}} of {{total}} selected', 'dict.bulk.selectedCount_other': '{{count}} of {{total}} selected', 'dict.bulk.clearSelection': 'Clear selection', 'dict.bulk.closeAction': 'Close selected', 'dict.bulk.exportAction': 'Export CSV', 'dict.page.prev': 'Prev', 'dict.page.next': 'Next', 'dict.page.of': 'Page {{cur}} of {{total}}', 'dict.page.range': 'Records {{from}}–{{to}} of {{total}}', 'dict.bulk.confirmTitle': 'Bulk close records', 'dict.bulk.confirmBody': '{{count}} record(s) will be marked as closed (valid_to = now). Reversible by creating a new version. Each record is closed individually — if some are already closed, others will still be processed.', 'dict.bulk.confirmAction': 'Close {{count}}', 'dict.bulk.resultClosed': 'Closed: {{count}}', 'dict.bulk.resultSkipped': 'Skipped (already closed or not found): {{count}}', 'dict.bulk.resultErrors': 'Errors: {{count}}', 'dict.bulk.dismiss': 'Done', 'form.identity': 'Identity', 'form.idSourceNote': 'Business key auto-derived from "{{field}}" field', 'form.tabs.identity': 'Identity', 'form.tabs.description': 'Description', 'form.tabs.extra': 'Additional', 'form.businessKey': 'Business key', 'form.validFrom': 'Valid from', 'form.validFromHint': 'When this record becomes active. Empty = from creation moment.', 'form.validTo': 'Valid to', 'form.validToHint': 'When it stops being valid. Empty = forever.', 'form.error.validToBeforeFrom': 'Valid-to must be later than valid-from.', 'form.dataFields': 'Record fields', 'form.cancel': 'Cancel', 'form.save': 'Save', 'form.saving': 'Saving...', 'form.error.required': 'Required field', 'scope.PUBLIC': 'PUBLIC', 'scope.INTERNAL': 'INTERNAL', 'scope.RESTRICTED': 'RESTRICTED', 'schema.action.create': 'Create dictionary', 'schema.action.edit': 'Edit dictionary', 'schema.action.editSchema': 'Schema', 'schema.tabs.metadata': 'Metadata', 'schema.tabs.schema': 'Fields', 'schema.tabs.preview': 'JSON', 'schema.name': 'Name', 'schema.nameHint': 'snake_case, starts with letter (e.g. my_dictionary)', 'schema.nameInvalid': 'Only snake_case: a-z, 0-9, _; must start with letter', 'schema.displayName': 'Display name', 'schema.description': 'Description', 'schema.scope': 'Scope', 'schema.bundle': 'Bundle', 'schema.bundleHint': 'Usually cuod', 'schema.template.label': 'Create from template', 'schema.template.hint': 'Copies schema, scope and locales from the selected dictionary. Use a unique name; version resets to 1.0.0.', 'schema.template.placeholder': '— no template —', 'schema.version': 'Schema version', 'schema.supportedLocales': 'Supported locales', 'schema.defaultLocale': 'Default locale', 'schema.property': 'Field', 'schema.required': 'Required', 'schema.pattern': 'Pattern (regex)', 'schema.enumValues': 'Enum values (comma-separated)', 'schema.enumValuesHint': 'E.g.: ACTIVE, INACTIVE, RETIRED', 'schema.arrayEnumOptional': 'Allowed values (optional, comma-separated)', 'schema.unique': 'Unique', 'schema.uniqueHint': 'Duplicates by this field are not allowed', 'schema.idSource': 'Use as ID (business key)', 'schema.idSourceHint': 'Record business key auto-derived from this field value', 'schema.idSourceManual': 'Manual input', 'schema.versionSuggest': 'Suggested version: {{v}}', 'schema.diff.blockedTitle': 'Breaking schema change', 'schema.diff.blockedBody': 'These changes break existing records. Save blocked. Create a new dictionary or revert.', 'schema.diff.compatibleTitle': 'Schema changes (compatible)', 'history.title': 'Version history', 'history.empty': 'No history', 'history.current': 'current', 'history.active': 'active', 'history.scheduled': 'scheduled', 'history.closed': 'closed', 'history.validFrom': 'valid from', 'history.validTo': 'valid to', 'history.updatedBy': 'by', 'history.viewData': 'record data (JSON)', 'schema.empty': 'No fields yet — add the first one', 'schema.addProperty': 'Add field', 'schema.delete': 'Delete', 'schema.moveUp': 'Move up', 'schema.moveDown': 'Move down', 'schema.kind.label': 'Type', 'schema.kind.string': 'String', 'schema.kind.integer': 'Integer', 'schema.kind.number': 'Number', 'schema.kind.boolean': 'Boolean', 'schema.kind.enum': 'Enum (pick from list)', 'schema.kind.reference': 'Reference to dictionary (FK)', 'schema.kind.localized': 'I18N string (ru-RU/en-US/...)', 'schema.kind.date': 'Date', 'schema.kind.datetime': 'Date+time', 'schema.kind.array_string': 'Array of strings', 'schema.kind.opaque': 'Complex (JSON)', 'schema.opaque.title': 'Complex field — edit only via JSON tab', 'schema.opaque.hint': 'Nested object / array of objects. No editor in Fields tab, raw schema shown below. Edit via JSON tab.', 'schema.reference.title': 'FK to dictionary record', 'schema.reference.hint': 'Value is validated on save: must match the business-key or specified field of an existing record in the target dictionary.', 'schema.reference.targetDict': 'Target dictionary', 'schema.reference.targetField': 'Field', 'schema.reference.targetFieldHint': 'Usually code or business_key', 'schema.reference.required': 'Fill both target dictionary and field — otherwise FK will not work.', 'field.name': 'Name', 'field.designator': 'Designator (COSPAR)', 'field.norad_id': 'NORAD ID', 'field.status': 'Status', 'field.country': 'Country', 'field.operator': 'Operator', 'field.mass_kg': 'Mass (kg)', 'field.mission': 'Mission', 'field.orbit': 'Orbit', 'field.launch_date': 'Launch date', 'field.decay_date': 'Decay date', 'field.spectrum_bands': 'Spectrum bands', 'field.satellite_type_code': 'Satellite type code', 'field.code': 'Code', 'field.description': 'Description', 'field.location': 'Location', 'field.coordinates': 'Coordinates', 'field.frequency_bands': 'Frequency bands', 'loading': 'Loading...', 'error.failed': 'Failed to load data', 'auth.login': 'Sign in', 'auth.logout': 'Sign out', 'dict.filter.searchPlaceholder': 'Search records…', 'dict.filter.scopeLabel': 'Filter by scope', 'dict.filter.matched': 'Matched {{count}} of {{total}}', 'dict.filter.clear': 'Clear', 'dict.filter.noMatches': 'No records match the filter', 'aoi.button': 'AOI filter', 'aoi.activeBbox': 'AOI: bbox {{value}}', 'aoi.activePolygon': 'AOI: polygon ({{points}} points)', 'aoi.clear': 'Clear AOI', 'aoi.dialog.title': 'Select Area of Interest (AOI)', 'aoi.dialog.help': 'Draw a rectangle or polygon. Applied as filter to records with spatial geometry.', 'aoi.dialog.apply': 'Apply', 'aoi.dialog.drawRectangle': 'Rectangle', 'aoi.dialog.drawPolygon': 'Polygon', 'aoi.dialog.clear': 'Clear', 'aoi.dialog.polygonHint': 'Click adds vertex, double-click closes polygon (min 3 vertices)', 'aoi.dialog.mapAriaLabel': 'Map for selecting area of interest', 'aoi.error.noShape': 'Draw an area on the map first', 'aoi.error.invalid': 'Invalid geometry — please redraw', }, }, }, }) export default i18n