dns-worker 1.1.1 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +3 -3
  2. package/dist/serverfull.mjs +73 -69
  3. package/dist/serverfull.mjs.map +4 -4
  4. package/package.json +3 -1
  5. package/static/assets/AccountView-p5jSjMAE.js +4 -0
  6. package/static/assets/{AnalyticsView-C_g_jz14.js → AnalyticsView-BywwDzah.js} +2 -2
  7. package/static/assets/AuthView-qoUKd5PS.js +2 -0
  8. package/static/assets/{DestinationMap-jSW7gimP.js → DestinationMap-9Yc54Mdj.js} +1 -1
  9. package/static/assets/FilteringView-Bs3__NpV.js +1 -0
  10. package/static/assets/LogsView-B-U9oaxB.js +5 -0
  11. package/static/assets/{QRCodeCanvas-Cu_-aNyM.js → QRCodeCanvas-DQ-xNuwF.js} +2 -2
  12. package/static/assets/{RulesView-CfVeQv0-.js → RulesView-B2kL1QhI.js} +1 -1
  13. package/static/assets/SettingsView-DTGI6eSd.js +1 -0
  14. package/static/assets/SetupView-DE9MM-32.js +1 -0
  15. package/static/assets/{SwipeableDrawer-BrmEzhuP.js → SwipeableDrawer-B81ANpsc.js} +1 -1
  16. package/static/assets/{TrendChart-Dc5yJndv.js → TrendChart-CCR1W3fC.js} +3 -3
  17. package/static/assets/browser-Dc5iKjY4.js +1 -0
  18. package/static/assets/date-CpvuBM37.js +1 -0
  19. package/static/assets/{de-DE-CA3VPcoz.js → de-DE-kDnumQOF.js} +1 -1
  20. package/static/assets/{es-ES-CHvRWwd-.js → es-ES-CqKS7vHl.js} +1 -1
  21. package/static/assets/{fr-FR-CqGOuepx.js → fr-FR-BRcdpT35.js} +1 -1
  22. package/static/assets/getFlagEmoji-DRubsb7D.js +1 -0
  23. package/static/assets/index-Bheo8y3N.css +2 -0
  24. package/static/assets/index-dXN_pYn1.js +16 -0
  25. package/static/assets/{ja-JP-BJSbUSmh.js → ja-JP-C5jk6SuH.js} +1 -1
  26. package/static/assets/{ko-KR-Chzg6jwO.js → ko-KR-z38NtMFa.js} +1 -1
  27. package/static/assets/{ru-RU-CSyCGGPn.js → ru-RU-Bicx52lz.js} +1 -1
  28. package/static/assets/services-BVnEJBDq.js +1 -0
  29. package/static/assets/vendor-i18next-BKUzkAKP.js +1 -0
  30. package/static/assets/vendor-react-BmoHsW1x.js +11 -0
  31. package/static/assets/{vendor-ui-core-sfZn2C08.js → vendor-ui-core-CY_WyMIf.js} +1 -1
  32. package/static/assets/{vendor-utils-CnB7BG7m.js → vendor-utils-BWSB0jGn.js} +1 -1
  33. package/static/assets/{vi-VN-D7rcA1IX.js → vi-VN-CvS_mTAu.js} +1 -1
  34. package/static/assets/{zh-CN-CSPdaOkF.js → zh-CN-B0nRgJTD.js} +1 -1
  35. package/static/assets/{zh-TW-DETA1X3X.js → zh-TW-lc_SSoVI.js} +1 -1
  36. package/static/index.html +6 -6
  37. package/static/sw.js +1 -1
  38. package/static/assets/AccountView-CGRwz6ae.js +0 -4
  39. package/static/assets/AuthView-BQzmEyvO.js +0 -2
  40. package/static/assets/FilteringView-CQ7sX3Rp.js +0 -1
  41. package/static/assets/LogsView-eLfZMRVo.js +0 -5
  42. package/static/assets/SettingsView-agJiYEt5.js +0 -1
  43. package/static/assets/SetupView-CTgCUAgk.js +0 -1
  44. package/static/assets/browser-DPi2BX_1.js +0 -1
  45. package/static/assets/date-IKj2stuv.js +0 -1
  46. package/static/assets/getFlagEmoji-_Wxhq0lT.js +0 -1
  47. package/static/assets/index-919BqnYI.css +0 -2
  48. package/static/assets/index-CbTjGMTZ.js +0 -16
  49. package/static/assets/services-Cwvmit1q.js +0 -1
  50. package/static/assets/vendor-i18next-cO7FDf_e.js +0 -1
  51. package/static/assets/vendor-react-CHk5ngkq.js +0 -11
@@ -1,4 +1,4 @@
1
- const e={account:`Аккаунт`,logout:`Выйти`,selectProfile:`Выбрать профиль`,add:`Добавить`,newProfileName:`Имя нового профиля...`,profileNameFormatTip:`Совет по названию: 1-30 символов, не должно совпадать с существующими`,profileLimitExceeded:`Достигнут лимит профилей (макс. {{max}})`,profileNameExists:`Профиль с таким именем уже существует`,profileNameFormatError:`Неверный формат имени профиля`,create:`Создать`,welcome:`Добро пожаловать`,createProfileToStart:`Создайте профиль, чтобы начать.`,loading:`Загрузка...`,errorNetwork:`Сетевая ошибка`,confirmDelete:`Вы уверены, что хотите удалить этот профиль?`,confirmLogout:`Вы уверены, что хотите выйти?`,notFound:`404 - Страница не найдена`,notFoundDesc:`Страница, которую вы пытаетесь открыть, не существует или была удалена.`,backToHome:`На главную`,apac:`APAC`,mainlandChina:`Китай`,HongKong:`Гонконг`,Singapore:`Сингапур`,Tokyo:`Токио`,NorthAmerica:`Северная Америка`,nae:`США, восток`,naw:`США, запад`,eu:`Европа`,invalidFormat:`Неверный формат файла`,import:`Импорт`,importSuccess:`Профиль успешно импортирован`,importError:`Ошибка импорта профиля, проверьте формат и попробуйте позже`,save:`Сохранить`,saveSuccess:`Настройки сохранены`,close:`Закрыть`,cancel:`Отмена`,continue:`Продолжить`,back:`Назад`,copy:`Копировать`,copied:`Скопировано`,copyKey:`Скопировать новый ключ восстановления`,confirm:`Подтвердить`,delete:`Удалить`,rename:`Переименовать`,disable:`Отключить`,confirmDisable:`Подтвердить отключение`,modeServerfull:`Режим Serverfull`,modeCloudflare:`Режим Cloudflare Worker`},t={setup:`Установка`,rules:`Правила`,stats:`Статистика`,logs:`Логи`,filter:`Фильтры`,settings:`Настройки`},n={light:`Светлая`,dark:`Темная`,system:`Системная`},r=JSON.parse(`{"login":"Войти в DNS Worker","signup":"Создайте аккаунт, чтобы начать","welcomeBack":"С возвращением, введите учетные данные","protectInternet":"Защитите ваш первый прыжок в интернет","error":"Ошибка","username":"Имя пользователя","usernamePlaceholder":"Ваше имя пользователя","password":"Пароль","passwordPlaceholder":"Ваш пароль","loginBtn":"Войти","signupBtn":"Зарегистрироваться","totpVerification":"TOTP проверка","noAccount":"Нет аккаунта? Зарегистрируйтесь","haveAccount":"Уже есть аккаунт? Войти","formatTipUsername":"Формат имени: 5-32 символа (Unix-стиль: буквы, цифры, _, -)","formatTipPassword":"Формат пароля: 12-100 символов","authFailed":"Ошибка аутентификации, проверьте логин или пароль","networkError":"Сетевая ошибка, попробуйте позже","turnstileRequired":"Пожалуйста, пройдите проверку (CAPTCHA)","turnstileError":"Ошибка загрузки сервиса проверки, проверьте Site Key или конфигурацию домена","verifying":"Выполняется проверка безопасности...","passwordLeaked":"Ваш пароль был скомпрометирован. Для безопасности используйте случайный/сильный пароль.","usernameExists":"Имя пользователя уже занято, выберите другое.","skip":"Пропустить","showPassword":"Показать пароль","hidePassword":"Скрыть пароль","geolocationRequired":"Ошибка геолокации. Пожалуйста, попробуйте войти снова.","unauthorizedDefault":"Ваша сессия истекла, войдите снова.","unauthorizedGeo":"Обнаружено изменение геолокации, сессия завершена.","unauthorizedExpired":"Сессия истекла, войдите снова.","unauthorizedReuse":"Безопасность: обнаружен повторный вход, сессия завершена.","unauthorizedRevoked":"Ваша сессия была отозвана, войдите снова.","sessionLockTitle":"Блокировка неактивной сессии","sessionLockDesc":"Блокирует интерфейс 4-значным PIN-кодом, когда приложение неактивно, для защиты активных сессий от несанкционированного физического доступа.","enableSessionLock":"Включить блокировку сессии","inactivityTimeout":"Тайм-аут неактивности","timeout1m":"1 минута","timeout2m":"2 минуты","timeout5m":"5 минут","timeout15m":"15 минут","timeout30m":"30 минут","timeout60m":"60 минут","changePin":"Изменить PIN","disablePin":"Отключить блокировку сессии","configurePin":"Настроить PIN","newPin":"Новый 4-значный PIN","pinHelper":"Только цифры, например, 1234","confirmPin":"Подтвердить новый PIN","verifyIdentity":"Подтвердить личность","usePassword":"Использовать пароль","use2fa":"Использовать код TOTP","totpCode":"Код TOTP","currentPassword":"Текущий пароль","disablePinWarning":"Отключение PIN-кода полностью отключит функцию автоматической блокировки неактивной сессии.","pinFormatTip":"PIN-код должен состоять ровно из 4 цифр","pinMatchTip":"PIN-коды не совпадают","passwordRequired":"Для подтверждения требуется пароль","sessionLocked":"Сессия заблокирована из-за неактивности","sessionExpired":"Слишком много неудачных попыток. Сессия завершена.","pinIncorrect":"Неверный PIN-код","attemptsRemaining":"Осталось попыток: {{count}}","logoutAndReturn":"Выйти из сессии","lockNow":"Заблокировать сейчас","cfErrorWithDetails":"Ошибка Cloudflare Edge (Ошибка {{code}}: {{desc}}). Сервис временно недоступен, повторите попытку позже. {{rayId}}","cfErrorCodeOnly":"Ошибка Cloudflare Edge (Ошибка {{code}}). Сервис временно недоступен. {{rayId}}","cfErrorGeneric":"Ошибка Cloudflare Edge, сервис временно недоступен. Повторите попытку позже. {{rayId}}","htmlErrorGeneric":"Сервер вернул непредвиденный ответ (HTTP {{status}}). Повторите попытку позже.","tooManyAttempts":"Слишком много запросов или попыток. Пожалуйста, повторите попытку позже.","dbQuotaExceeded":"Превышена дневная квота чтения базы данных. Дождитесь ежедневного сброса.","noUsersRegistered":"Учётные записи отсутствуют. Пожалуйста, зарегистрируйтесь в качестве администратора.","registerAdminTitle":"Регистрация учётной записи администратора","registerAdminDesc":"В системе нет учётных записей. Первый пользователь станет системным администратором.","goToRegisterAdmin":"Зарегистрировать админа","dbNotInitialized":"База данных не инициализирована или отсутствуют таблицы. Выполните npm run db:migrate:prod в терминале.","verifyWithPasskey":"Подтвердить с помощью Passkey","orUseTotp":"Или используйте код аутентификатора","passwordRequiredFirst":"Сначала введите пароль","passkeyVerification":"Верификация Passkey","passkeyPromptDesc":"Подтвердите с помощью биометрии или ключа безопасности","forgotPassword":"Забыли пароль?","forgotPasswordTitle":"Сброс пароля","forgotPasswordDesc":"Безопасно восстановите доступ к аккаунту с помощью многофакторной аутентификации (Passkey или TOTP).","noMfaContactAdmin":"Для этого аккаунта не настроена многофакторная аутентификация (Passkey или TOTP). Обратитесь к системному администратору.","chooseMfaForReset":"Выберите способ подтверждения личности:","authViaPasskey":"Подтвердить с помощью Passkey","authViaPasskeyDesc":"Быстрая аутентификация по биометрии или аппаратному ключу","authViaTotp":"Подтвердить через приложение (TOTP)","authViaTotpDesc":"Введите 6-значный код из приложения аутентификатора","authViaRecoveryKey":"Использовать аварийный ключ восстановления","authViaRecoveryKeyDesc":"Введите 10-значный аварийный ключ восстановления","verifyAndProceed":"Проверить и продолжить","setNewPassword":"Установить новый пароль","setNewPasswordDesc":"Личность подтверждена. Пожалуйста, задайте новый пароль для вашего аккаунта.","newPassword":"Новый пароль","confirmNewPassword":"Подтвердите новый пароль","passwordMismatch":"Пароли не совпадают","saveAndReset":"Сохранить и сбросить","resetSuccessTitle":"Пароль успешно сброшен","resetSuccessDesc":"Пароль обновлен, все предыдущие сессии завершены. Войдите с новым паролем.","goToLogin":"Перейти ко входу","mfaSetupTitle":"Повышение безопасности аккаунта","mfaSetupDesc":"Настройка многофакторной аутентификации (MFA) защищает аккаунт и открывает беспарольный вход.","mfaRecommended":"Рекомендуется","passkeySignupDesc":"Используйте отпечаток пальца, сканирование лица или ключ безопасности. Быстро и надежно.","bindPasskeyNow":"Зарегистрировать Passkey","mfaUniversal":"Универсально","totpSignupDesc":"Используйте Google Authenticator, 1Password или любое стандартное приложение.","setupTotpBtn":"Настроить приложение-аутентификатор","skipMfaSetup":"Пропустить и завершить","backToMfaOptions":"Назад к вариантам","otherOptions":"Другие варианты","next":"Далее","mfaVerification":"Многофакторная аутентификация","totpPrompt":"Введите 6-значный динамический код из приложения аутентификации","recoveryPrompt":"Введите 30-значный ключ восстановления (5 групп по 6 цифр)","recoveryChecksumError":"Ошибка контрольной суммы: эта группа не делится на 11","recoveryChecksumErrorDesc":"Ошибка контрольной суммы в группе {{groups}}, проверьте и повторите попытку","backToPasskey":"Вернуться к аутентификации по Passkey","recoveryKeyRequired":"Пожалуйста, введите ваш ключ восстановления","totpFormatError":"Пожалуйста, введите действительный 6-значный код","registrationDisabledTitle":"Регистрация отключена","registrationDisabledDesc":"Регистрация новых пользователей в настоящее время закрыта. Обратитесь к администратору для получения учетной записи.","backToLogin":"Вернуться ко входу","jwtSecretPreset":"Обнаружен предустановленный JWT_SECRET. Пожалуйста, измените предустановленный JWT_SECRET в переменных окружения перед развертыванием.","jwtSecretMissing":"Отсутствует JWT_SECRET. Пожалуйста, настройте безопасный ключ в переменных окружения перед развертыванием.","jwtSecretInvalid":"Неверный формат JWT_SECRET, проверьте настройки.","keepLoggedIn":"Оставаться в системе {{days}} дн.","orUseOtherMfa":"или используйте другой способ","totpUseRecovery":"Использовать ключ восстановления","totpUseApp":"Использовать приложение-аутентификатор","mfaMethodTotp":"Приложение аутентификации (TOTP)","invalidTotp":"Неверный код подтверждения TOTP."}`),i={title:`Руководство по установке`,subtitle:`Настройте устройство для использования DNS Worker.`,regionOptimization:`Оптимизация региона (Windows)`,otherRegion:`Другие регионы`,copied:`Скопировано в буфер`,verifyConnection:`Проверить подключение`,verifying:`Проверка...`,connected:`Подключено`,notConnected:`Не подключено`,profileMatch:`Фильтрация через этот профиль активна.`,profileMismatch:`Obex используется, но ID профиля не совпадает.`,verifyHint:`После настройки нажмите кнопку для проверки.`,refreshStatus:`Обновить статус`,egressIp:`Исходящий IP`,currentLocation:`Текущее местоположение`,edgeServer:`Edge-сервер`,serverLocation:`Расположение сервера`,dohUrlTitle:`Текущий URL DoH`,accessPointTitle:`Access DoH URL`,defaultAccessPointName:`Устройство-1`,moreAccessPoints:`Еще точки доступа ↗`,accessPointName:`Имя точки доступа`,accessPointToken:`Ключ доступа`,addAccessPoint:`Добавить точку`,renameAccessPoint:`Переименовать`,rotateToken:`Ротация ключа`,rotateTokenConfirmDesc:`Ротация отключит все устройства со старым ключом. Продолжить?`,deleteAccessPoint:`Удалить`,deleteAccessPointConfirm:`Удалить эту точку доступа?`,manageAccessPoints:`Управление точками доступа`,searchAccessPoints:`Поиск точек доступа...`,accessPointNameExists:`Имя точки уже занято`,accessPointCreated:`Точка доступа создана`,accessPointRenamed:`Точка переименована`,accessPointDeleted:`Точка удалена`,accessPointNameFormatTip:`Имя: 1-30 символов (буквы, цифры, _, -)`,accessPointLimitExceeded:`Достигнут лимит (макс. {{max}})`,tokenRotated:`Ключ доступа обновлен`,whatIsDoh:`Что такое DoH?`,dohDesc:`DNS over HTTPS (DoH) — технология резолвинга через зашифрованный HTTPS.`,dohBenefit1:`Защита от перехвата провайдерами`,dohBenefit2:`Повышение безопасности DNS`,learnMore:`Подробнее`,copyUrl:`Копировать URL`,browsers:`Браузеры`,browserTitle:`Настройка браузеров (Chrome/Edge)`,browserSteps:`Настройки > Приватность и безопасность > Безопасность > Безопасный DNS > Пользовательский > Вставьте URL.`,browserTips:`Или вставьте URL в адресную строку:`,apple:`Apple`,appleTitle:`macOS / iOS / iPadOS / tvOS`,appleDesc:`Настройка Apple через профиль конфигурации.`,appleWarning:`Профили нельзя установить в "Режиме блокировки (Lockdown Mode)".`,downloadConfig:`Скачать профиль`,appleInstallHint:`После скачивания: Настройки > Профили для установки`,windows:`MS Windows`,windowsTitle:`Настройка Windows 11`,windowsStep0:`Настройки > Сеть и Интернет`,windowsStep1:`Свойства > DNS-серверы > Изменить > Вручную.`,windowsStep2:`Выберите и введите DNS IP:`,windowsStep3:`Включите DoH (пользовательский шаблон) и вставьте URL.`,windowsStep4:`Отключите откат к незашифрованному DNS.`,android:`Android`,androidTitle:`Android / Частный DNS (DoT и DoQ)`,androidDesc:`Поддерживает системный «Частный DNS» (DoT) или сторонние приложения для DoH.`,androidDotTitle:`Частный DNS (DoT / DoQ)`,androidDotDesc:`Настройки > Сеть и интернет > Персональный DNS > Имя хоста провайдера персонального DNS (также поддерживает клиенты DoQ):`,androidWildcardHint:`(Wildcard-сертификат, маршрутизирует в этот профиль)`,androidSingleDomainHint:`(Однодоменный сертификат, маршрутизирует в профиль по умолчанию)`,androidIpNotice:`Доступ через IP или локальный адрес. Android Частный DNS принимает только доменные имена; настройте SERVERFULL_DOT_DOMAIN в .env (напр. dns.example.com).`,androidWarning:`Примечание: системный «Частный DNS» Android использует протокол DoT. При развертывании в Cloudflare Workers используйте приложения вроде Intra для DoH. В режиме Serverfull с настроенным TLS вы можете напрямую использовать указанное выше имя хоста DoT.`,dynamicFromDomain:`Динамический (из "{{domain}}")`,dynamicFromDomainV6:`Динамический IPv6 (из "{{domain}}")`,linux:`Linux`,linuxTitle:`Linux (Cloudflared DoH прокси)`,linuxDesc:`Локальный DNS прокси для шифрования запросов.`,linuxStep1:`Запустите скрипт для установки cloudflared и создания systemd-сервиса (слушает 127.0.0.1:53).`,linuxStep2:`Установите системный DNS на 127.0.0.1.`,routeros:`MikroTik`,routerosTitle:`MikroTik RouterOS`,routerosDesc:`Используйте этот резолвер для всех устройств в сети, указав его в качестве DoH-сервера роутера.`,routerosStep1:`Выполните в терминале RouterOS (RouterOS 7):`,routerosNote:`Запросы, пересылаемые роутером, отображаются в журналах как запросы роутера, а не отдельных устройств. Если нужны журналы по устройствам, настройте устройства напрямую.`,routerosDocs:`Документация MikroTik по DNS`,accessPointsCardTitle:`Точки доступа`,currentAccessPoint:`Текущая точка доступа`,selectAccessPointToConfigure:`Выберите точку доступа для настройки`,quic:`QUIC & HTTP/3`,quicTitle:`DNS over QUIC (DoQ) & HTTP/3 (DoH3)`,quicDesc:`Нативная поддержка RFC 9250 (DNS over QUIC) и RFC 9460 (HTTP/3 & Encrypted Client Hello) с рукопожатием 0-RTT и защитой от перехвата SNI.`,doqTitle:`DNS over QUIC (DoQ - RFC 9250)`,doqAppHint:`Совместимо с современными клиентами, такими как AdGuard, Clash Meta (Mihomo), Sing-box и др.:`,doqWildcardHint:`(Wildcard-сертификат, маршрутизирует в этот профиль)`,doqSingleDomainHint:`(Сертификат для одного домена, маршрутизирует в профиль по умолчанию)`,doqIpNotice:`Для DoQ требуется TLS-сертификат и действительный домен. Укажите SERVERFULL_DOT_DOMAIN в .env и обновите страницу.`,h3Title:`HTTP/3 (DoH3) & ECH (Encrypted Client Hello)`,h3Desc:`Современные браузеры (Chrome/Firefox/Safari) автоматически согласовывают HTTP/3 и шифрование ECH через записи Alt-Svc и RFC 9460 HTTPS:`,ddrHint:`Интегрирован RFC 9460 Section 8 DDR (Discovery of Designated Resolvers): клиенты могут автоматически согласовывать и переходить на DoQ/DoT и ECH через запросы к _dns.`,e2eeDesc:`Шифрует журналы DNS на границе с помощью постквантового гибридного ключа (P256-MLKEM768). Облако не имеет доступа к данным; расшифровка происходит только локально с Passkey.`,detecting:`Определение...`,dotUnavailableCfTitle:`DoT / DoQ недоступен в режиме Cloudflare Worker`,dotUnavailableCfDesc:`В настоящее время работает в режиме Cloudflare Worker. Из-за сетевых ограничений прослушивание портов TCP/UDP 853 не поддерживается. Для использования DoT или DoQ выполните развертывание в режиме Serverfull; в текущем режиме используйте DoH.`},a={title:`Пользовательские правила`,subtitle:`Управление резолвингом доменов (блокировка, разрешение, перенаправление).`,action:`Действие`,typeBlock:`Блокировать (BLOCK)`,typeAllow:`Разрешить (ALLOW)`,typeRedirect:`Перенаправить (REDIRECT)`,pattern:`Шаблон домена`,patternPlaceholder:`example.com или *.test.com`,addRule:`Добавить правило`,redirectSettings:`Настройки перенаправления`,aRecord:`A запись (IPv4)`,aaaaRecord:`AAAA запись (IPv6)`,cnameRecord:`CNAME запись`,txtRecord:`TXT запись`,optional:`(опционально)`,ipv4Placeholder:`Напр: 1.2.3.4`,ipv6Placeholder:`Напр: 2001:db8::1`,cnamePlaceholder:`Напр: target.com`,txtPlaceholder:`Введите текст...`,saveRedirectRule:`Сохранить правило перенаправления`,noRulesTitle:`Нет правил`,noRulesDesc:`Вы еще не добавили пользовательских правил.`,tableAction:`Действие`,tablePattern:`Шаблон`,tableDetails:`Детали`,tableOps:`Опции`,labelBlock:`Блок`,labelAllow:`Разрешить`,labelRedirect:`Перенапр.`,detailBlock:`Заблокировано`,detailAllow:`Разрешено`,patternEmpty:`Шаблон домена не может быть пустым`,duplicatePattern:`Правило для этого домена уже существует`,addSuccess:`Правило добавлено`,addFailed:`Ошибка добавления правила`,updateSuccess:`Правило обновлено`,updateFailed:`Ошибка обновления правила`,tableCreatedAt:`Дата добавления`,clickToSort:`Нажмите для сортировки`,editRule:`Редактировать правило`,cancel:`Отмена`,saveChanges:`Сохранить изменения`,delete:`Удалить`,close:`Закрыть`},o={customRange:`Произвольный период`,startTime:`Начало`,endTime:`Конец`,apply:`Применить`,custom:`Свой`,totalQueries:`Всего запросов`,blocked:`Заблокировано`,redirected:`Перенаправлено`,blockRate:`Процент блокировок`,activeDevices:`Активные устройства`,activeIPs:`Активные IP`,queryTrend:`Тренд запросов`,noRecords:`Нет записей за этот период`,topAllowed:`Частые (Разрешенные)`,topBlocked:`Частые (Блокировки)`,noData:`Нет данных`,clientActivity:`Активность клиентов`,ipAddress:`IP-адрес`,location:`Локация`,queries:`Запросы`,destinationDistribution:`Распределение по направлениям`,requests:`Запросы`,topIsps:`Основные провайдеры`,pressToShowMore:`Нажмите, чтобы показать еще`,maximize:`Увеличить`,minimize:`Уменьшить`,loading:`Загрузка...`},s={title:`Логи запросов`,subtitle:`Просмотр истории DNS и блокировок.`,realtime:`В реальном времени`,allStatus:`Все статусы`,onlyPass:`Разрешенные`,onlyBlock:`Блокировки`,onlyRedirect:`Перенаправления`,searchPlaceholder:`Поиск домена...`,noResults:`Результатов не найдено`,noRecords:`Нет записей`,noResultsDesc:`Нет записей для домена "{{query}}".`,noRecordsDesc:`DNS-запросов за выбранный период не поступало.`,tableTime:`Время`,tableDomain:`Домен`,tableAnswer:`Ответ`,tableSource:`Источник`,tableType:`Тип`,tableStatus:`Статус`,tableReason:`Причина`,loadedAll:`Загружено записей: {{count}}`,realtimeLoadMoreTip:`Отключите «Реальное время» для загрузки еще`,filterAccessPoint:`Точка доступа`,allAccessPoint:`Все точки`,allCountry:`Все страны`,filterCountry:`Страна назначения`,allIsp:`Все провайдеры`,filterIsp:`Провайдер`,timeRange:`Временной диапазон`,clearFilters:`Очистить фильтры`,details:`Детали запроса`,detailAccessPoint:`Точка доступа`,basicInfo:`Базовая информация`,detailDomain:`Домен`,detailType:`Тип`,detailTime:`Время`,detailLatency:`Задержка`,detailProfile:`Используемый профиль`,detailStatus:`Статус`,detailUpstream:`Upstream-сервер`,detailReason:`Причина`,detailNoReason:`Нет (разрешено)`,detailECS:`ECS`,resolutionResult:`Результат резолвинга`,noResult:`Нет результата`,networkDetails:`Сетевые детали`,clientSource:`Клиент (источник)`,destination:`Назначение`,quickActions:`Быстрые действия`,actionAllow:`Разрешить`,actionBlock:`Блокировать`,actionRedirect:`Перенаправить`,statusPass:`Разрешено`,statusBlock:`Блок`,statusRedirect:`Перенаправлено`,export:`Экспорт журналов`,rewritten:`Перезаписано`,logDetails:`Подробности журнала`,searchCountry:`Поиск стран...`,noMatchingCountries:`Подходящие страны не найдены`,searchIsp:`Поиск провайдера...`,noMatchingIsps:`Подходящие провайдеры не найдены`,loadingIsps:`Загрузка провайдеров...`,loggingDisabledTitle:`Ведение журнала отключено`,loggingDisabledDesc:`Хранение журналов для этого профиля отключено. Журналы запросов не записываются. Чтобы просмотреть журналы, включите хранение журналов в настройках.`,detailIsClientEncrypted:`Клиентское шифрование`,encryptedLocked:`Да (Заблокировано)`,encryptedDecrypted:`Да (Расшифровано локально)`,encryptedYes:`Да`,notEncrypted:`Нет`,encryptedRecord:`[Зашифрованная запись]`,exportSuccess:`Журналы успешно экспортированы`,exportError:`Не удалось экспортировать журналы`},c={title:`Внешние списки фильтрации`,subtitle:`Подписки на сторонние списки (напр. AdGuard).`,formatDisclaimer:`Поддерживаются форматы AdGuard и обычные hosts-файлы. Поддерживаются базовые правила (||example.com^), обход «@@» белых списков, поддержка «$» и «*» не реализована. Домены матчатся со всеми поддоменами.`,syncAll:`Синхронизировать все`,urlPlaceholder:`URL списка...`,addSubscription:`Добавить подписку`,supportedFormats:`Поддерживаемые форматы:`,formatAdGuard:`Совместимо с ||domain.com^.`,formatHosts:`Совместимо с hosts (0.0.0.0 domain.com).`,noListsTitle:`Нет списков`,noListsDesc:`Вы еще не подписались ни на один список.`,tableUrl:`URL списка`,tableLastSync:`Последняя синхронизация`,tableStatus:`Статус`,tableOps:`Опции`,neverSynced:`Никогда`,enabled:`Норма`,disabled:`Ошибка синхронизации`,statusNormal:`Нормально`,statusDisabled:`Отключён`,statusOutdated:`Устарело`,statusMissing:`Отсутствует`,presetOisdBig:`OISD Big (Реклама/Малевэр)`,presetOisdNsfw:`OISD NSFW (Контент 18+)`,presetAdGuard:`AdGuard AdServers`,presetStevenBlack:`StevenBlack Hosts (Комбинированный)`,syncTaskStarted:`Синхронизация запущена`,syncCheckComplete:`Синхронизация завершена`,syncTaskFailed:`Ошибка синхронизации`,syncing:`Синхронизация...`,syncSuccess:`Синхронизировано`,syncError:`Ошибка`,syncErrorDesc:`Не удалось скачать список. Проверьте URL.`,deleteConfirm:`Удалить подписку?`,deleteSuccess:`Список удален`,deleteError:`Ошибка удаления`,addSuccess:`Список добавлен`,addError:`Ошибка добавления`,listEnabled:`Список активен`,listEnabledHint:`Отключите, чтобы прекратить фильтрацию по этому списку, не удаляя его. Подписка и история синхронизации сохраняются.`,listEnabledToast:`Список включён - фильтр пересобирается`,listDisabledToast:`Список отключён - фильтр пересобирается`,checkDomain:`Проверить домен`,checkDomainHint:`Списки хранятся в виде компактных фильтров, поэтому их записи нельзя просмотреть. Вы можете мгновенно проверить любой домен по этому списку.`,check:`Проверить`,checkBlocked:`Заблокировано этим списком`,checkNotBlocked:`Не заблокировано этим списком`,checkViaParent:`(совпадение по родительскому домену)`,checkNotSynced:`Этот список ещё не завершил синхронизацию, поэтому проверять пока нечего.`,checkListDisabled:`Этот список сейчас отключён, поэтому он ничего не фильтрует.`,checkProbabilisticRate:`Фильтры вероятностные: примерно 1 совпадение из {{odds}} - ложное срабатывание. Добавление исключения также покрывает все его поддомены.`,checkProbabilistic:`Фильтры вероятностные, поэтому небольшая часть совпадений - ложные срабатывания. Добавление исключения также покрывает все его поддомены.`,checkAllowAction:`Всё равно разрешить`,checkAllowed:`Исключение добавлено - теперь этот домен разрешается`,allowRuleAdded:`Исключение добавлено для {{domain}}`,listDetails:`Подробности списка`},l={title:`Настройки конфигурации`,subtitle:`Управление политикой DNS.`,saveChanges:`Сохранить`,saveSuccess:`Настройки сохранены`,saveError:`Ошибка сохранения`,upstreamTitle:`Upstream-серверы`,dohUrl:`DoH URL`,dnsIP:`DNS IP`,classicDns:`Классический DNS`,httpsOnly:`Только HTTPS`,upstreamDesc:`Все незаблокированные запросы будут пересылаться на этот сервер. Поддерживает DoH (HTTPS), DoT (tls://), DNS Stamp (sdns://) или классический DNS. Классический DNS передается через TCP.`,defaultPolicyTitle:`Фильтрация по умолчанию`,onNoMatch:`Если правил нет`,allowAll:`Разрешать всё`,blockAll:`Блокировать всё`,logRetentionTitle:`Хранение логов`,retentionDuration:`Срок хранения`,retentionDesc:`Журналы удаляются автоматически. `,skipLogOnPass:`Не записывать разрешенные запросы`,skipLogOnPassDesc:`Если включено, обычные разрешенные запросы не записываются в журнал. Сохраняются только заблокированные, перенаправленные или неудачные запросы.`,ecsTitle:`EDNS Client Subnet (ECS)`,ecsUpstreamHint:`ECS-подсеть, отправляемая на upstream-сервер`,sendEcs:`Использовать ECS`,customSubnet:`Пользовательская подсеть`,customIpv4:`IPv4`,customIpv6:`IPv6`,ipv4Hint:`(напр. 1.2.3.0/24)`,ipv6Hint:`(напр. 2001:db8::/32)`,testToolTitle:`Инструмент проверки`,testDomain:`Домен`,recordType:`Тип записи`,runTest:`Тест`,domainPlaceholder:`Напр. google.com`,hitRule:`Сработавшее правило`,defaultAllow:`Политика по умолчанию`,parseLatency:`Задержка`,sourceIp:`Исходный IP`,answerSection:`Ответ (Answer Section)`,noRecordsReturned:`Записей не возвращено.`,diagnostics:`Диагностика`,errorDetail:`Сведения об ошибке`,upstreamResponse:`Ответ вышестоящего сервера`,presetCloudflareSecurity:`Cloudflare Security`,presetCloudflareFamilies:`Cloudflare Families`,presetQuad9:`Quad9`,presetQuad9Ecs:`Quad9 ECS`,presetControlDFree:`ControlD Free`,presetControlDUncensored:`ControlD Uncensored`,presetAdGuard:`AdGuard`,presetAdGuardFamily:`AdGuard Family`,retention10m:`10 минут`,retention1h:`1 час`,retention24h:`24 часа`,retention7d:`7 дней`,retention30d:`30 дней`,retention90d:`90 дней (Максимум)`,blockModeTitle:`Режим ответа при блокировке`,blockModeDesc:`Что возвращать при блокировке`,default:`По умолч.`,NXDOMAIN:`NXDOMAIN`,NODATA:`NODATA`,customIP:`Свой IP`,export:`Экспорт`,exportSuccess:`Успешно`,exportError:`Ошибка экспорта`,nameUpdateSuccess:`Имя обновлено`,saving:`Сохранение...`,echTitle:`Перезапись ECH`,enableEch:`Включить перезапись ECH`,echDesc:`Для доменов через Cloudflare автоматически перезаписывает параметры ECH и внешний SNI в запросах HTTPS (Type 65) / SVCB (Type 64) для защиты конфиденциальности.`,echFrontingTitle:`Внешний SNI (ECH Fronting)`,echFrontingDesc:`Доменное имя, видимое в открытом виде во внешнем рукопожатии TLS`,echFrontingCustomPlaceholder:`Введите пользовательский домен fronting, например crypto.cloudflare.com`,echPreferred:`Рекомендуется`,whatIsEch:`Что такое ECH (Encrypted Client Hello)?`,echHelpDesc:`ECH — расширение конфиденциальности TLS 1.3, которое полностью шифрует реальное имя домена (Inner SNI), показывая наблюдателям только маскировочный домен (Outer SNI).`,echHelpBenefit1:`Предотвращает отслеживание провайдерами посещаемых вами поддоменов`,echHelpBenefit2:`В сочетании с DoH устраняет утечки открытого SNI`,retentionDisabled:`Отключено (В целях конфиденциальности)`,retentionDisabledDesc:`Ведение журнала отключено. Система не будет записывать журналы DNS-запросов и немедленно удалит существующие журналы.`,localRetentionTitle:`Локальное хранилище и срок хранения`,localRetentionDuration:`Локальный срок хранения`,localRetentionDesc:`Журналы сохраняются локально в базе данных SQLite браузера (OPFS), обеспечивая мгновенный поиск и экономя затраты на облако. Устаревшие локальные журналы удаляются автоматически.`,localRetention30d:`30 дней`,localRetention90d:`90 дней (Рекомендуется)`,localRetention180d:`180 дней`,localRetention365d:`365 дней (1 год)`,localRetentionUnlimited:`Без ограничений (До ручной очистки)`,localEngineStorage:`Движок хранилища`,localEngineOpfs:`Постоянное OPFS`,localEngineMemory:`Временное в памяти (Режим инкогнито)`,localCachedCount:`Кэшировано локально`,localCachedCountUnit:`{{count}} записей`,clearLocalData:`Очистить локальные журналы`,clearLocalDataConfirm:`Вы уверены, что хотите удалить все локальные журналы для этого профиля? (Журналы в облаке сохранятся)`,clearLocalDataSuccess:`Локальные журналы успешно удалены`,clearLocalDataError:`Ошибка очистки локальных журналов`,e2eeTitle:`Сквозное шифрование (E2EE)`,e2eeEnabled:`Включено`,e2eeDisabled:`Отключено`,e2eeDesc:`Шифрует журналы DNS на границе с помощью постквантового гибридного ключа (P256-MLKEM768). Облако не имеет доступа к данным; расшифровка происходит только локально с Passkey.`,e2eePasskeyRequired:`Для E2EE требуется Passkey (WebAuthn). Пожалуйста, добавьте Passkey в настройках аккаунта.`,e2eeGoToAccount:`Перейти в настройки аккаунта`,e2eeEnableSwitch:`Включить сквозное шифрование`,e2eeKeyType:`Алгоритм шифрования`,e2eeDeviceState:`Состояние на этом устройстве`,e2eeUnlocked:`Разблокировано на этом устройстве`,e2eeUnlockButton:`Подтвердить Passkey для разблокировки`,e2eeProtectedPasskeys:`Авторизованные Passkey`,e2eeDevicesUnit:`устройств(о)`,e2eeEnableSuccess:`E2EE успешно включено`,e2eeEnableError:`Не удалось включить E2EE`,e2eeDisableSuccess:`E2EE отключено`,e2eeDisableError:`Не удалось отключить E2EE`,e2eeUnlockSuccess:`Приватный ключ разблокирован на этом устройстве`,e2eeUnlockError:`Ошибка проверки Passkey`,e2eeDisableConfirm:`Вы уверены, что хотите отключить E2EE? Новые журналы будут сохраняться в открытом виде.`,e2eeUnlockPrompt:`Этот профиль использует сквозное шифрование. Подтвердите Passkey для расшифровки.`,e2eeUnlockNow:`Разблокировать журналы`,e2eeUnlockRecovery:`Разблокировать с помощью ключа восстановления`,securityTitle:`Безопасность и доступ`,rotateKeyTitle:`URL доступа`,rotateKeyDesc:`Создает новый URL-адрес доступа для этого профиля. Все устройства, использующие старый URL-адрес, немедленно потеряют соединение с DNS Worker и должны быть перенастроены с новым URL-адресом.`,rotateKeyBtn:`Сменить URL`,copied:`Скопировано в буфер обмена`,nameUpdateFailed:`Ошибка обновления`},u=JSON.parse(`{"title":"Настройки аккаунта","subtitle":"Управление учетными данными.","e2ee":{"title":"Сквозное шифрование (E2EE)","desc":"Использует клиентское постквантовое гибридное конвертное шифрование (P256-MLKEM768 + AES-256-GCM / NIST FIPS 203) с аппаратными Passkey. Данные шифруются на Edge; сервер и база данных хранят только шифротекст. Расшифровка выполняется исключительно на авторизованных устройствах.","pqcTag":"PQC / NIST FIPS 203","upgradePqcBtn":"Обновить до постквантового (PQC)","upgradePqcSuccess":"Успешно обновлено до постквантового гибридного шифрования (P256-MLKEM768)","upgradePqcError":"Сбой обновления до постквантового шифрования","enabled":"Включено","disabled":"Отключено","passkeyRequired":"Для сквозного шифрования требуется аппаратный Passkey (WebAuthn). Пожалуйста, добавьте Passkey в разделе MFA выше.","scopesTitle":"Категории зашифрованных данных","scopesSubtitle":"Выберите категории данных для сквозного шифрования с помощью аппаратного Passkey.","scopeLogsTitle":"Журналы DNS-запросов","scopeLogsDesc":"Шифрует доменные имена, IP-адреса клиентов, ответы и задержки перед сохранением в D1.","scopeRulesTitle":"Пользовательские правила и перенаправления","scopeRulesDesc":"Шифруемые на стороне клиента правила блокировки/разрешения и политики перенаправления.","scopeClientsTitle":"Метаданные устройств и клиентов","scopeClientsDesc":"Имена устройств точек доступа, псевдонимы IP и топология сети.","statusActive":"Защищено","statusPlanned":"Запланировано","profileSelect":"Настроить профиль","keyType":"Алгоритм шифрования","deviceState":"Текущее состояние устройства","unlocked":"Разблокировано на этом устройстве","unlockButton":"Подтвердить Passkey для разблокировки","protectedPasskeys":"Авторизованные ключи Passkey","devicesUnit":"ключ(ей)","enableSuccess":"Сквозное шифрование успешно включено","enableError":"Не удалось включить сквозное шифрование","disableSuccess":"Сквозное шифрование отключено","disableError":"Не удалось отключить сквозное шифрование","unlockSuccess":"Закрытый ключ успешно разблокирован на этом устройстве","unlockError":"Не удалось подтвердить Passkey и разблокировать","disableConfirm":"Вы уверены, что хотите отключить сквозное шифрование для этого профиля? Будущие журналы будут сохраняться в открытом виде. (Для расшифровки архивных журналов по-прежнему потребуется закрытый ключ)","noKeypairTitle":"Пара ключей сквозного шифрования не инициализирована","noKeypairDesc":"Для сквозного шифрования требуется создать выделенную пару постквантовых ключей (P256-MLKEM768). Закрытый ключ защищен конвертным шифрованием с использованием вашего Passkey или ключа восстановления.","initPasskeyBtn":"Подтвердить Passkey для создания пары ключей","addPasskeyBtn":"Добавить Passkey для аппаратного шифрования","initRecoveryBtn":"Создать пару ключей с помощью ключа восстановления","initWithRecoveryTitle":"Создать пару ключей с помощью ключа восстановления","initWithRecoveryDesc":"Введите текущий 30-значный экстренный ключ восстановления. Система создаст локальную пару постквантовых ключей (P256-MLKEM768). После использования ключ восстановления будет автоматически обновлен.","keypairGenSuccess":"Пара ключей сквозного шифрования успешно создана!","keypairGenError":"Не удалось создать пару ключей шифрования","unlockRecoveryButton":"Разблокировать с помощью ключа восстановления","unlockWithRecoveryTitle":"Разблокировать с помощью ключа восстановления","unlockWithRecoveryDesc":"Введите 30-значный ключ восстановления. Внимание: согласно правилу однократного использования, после проверки ключ будет немедленно обновлен.","recoveryKeyInputLabel":"Ключ восстановления","unlockConfirm":"Разблокировать закрытый ключ","recoveryUnlockFailed":"Недействительный ключ восстановления или ошибка расшифровки","generateKeypairBtn":"Создать пару ключей","enableSwitch":"Включить сквозное шифрование журналов запросов","enableSwitchDesc":"Шифрует доменные имена, IP-адреса клиентов, ответы и задержку апстрима на периферии перед сохранением в облаке.","trustBoundaryTitle":"Уведомление о границах безопасности и доверия","trustBoundaryDesc":"Весь трафик DNS-запросов неизбежно проходит через сервер хостинга (узлы Cloudflare Workers или серверную среду) во время разрешения, где запросы обрабатываются в памяти перед зашифрованной записью. Эта функция защищает данные в облачной базе данных (D1) от утечек; она не скрывает активный сетевой трафик от сервера, обрабатывающего запрос.","initFailed":"Не удалось инициализировать сквозное шифрование","lockDevice":"Заблокировать это устройство","lockSuccess":"Заблокировано, закрытый ключ удален из локального хранилища"},"jwtSecretWarningTitle":"Уведомление о безопасности: Слабый JWT-секрет","jwtSecretWarningDesc":"Текущий JWT_SECRET содержит менее 32 символов, что создает потенциальную угрозу безопасности. Пожалуйста, настройте переменную окружения KEK_v1 перед ротацией JWT_SECRET, чтобы обеспечить возможность расшифровки существующих данных TOTP и ключей восстановления.","roleAdmin":"Администратор","roleUser":"Пользователь","personalInfo":"Личные данные","username":"Имя пользователя","userId":"ID пользователя","changePassword":"Смена пароля","currentPassword":"Текущий пароль","newPassword":"Новый пароль","updatePassword":"Обновить","userManagement":"Управление (Админ)","createUser":"Создать пользователя","role":"Роль","createdAt":"Дата создания","lastActive":"Последняя активность","lastResolve":"Последний запрос","actions":"Действия","dangerZone":"Опасная зона","clearLogs":"Очистить логи","clearLogsDesc":"Удалит все логи DNS. Необратимо.","clearLogsBtn":"Очистить","deleteAccount":"Удалить аккаунт","deleteAccountDesc":"Удалит аккаунт и данные. Необратимо.","deleteAccountBtn":"Удалить","createNewUser":"Новый пользователь","initialPassword":"Пароль","userRole":"Роль","cancel":"Отмена","createNow":"Создать","formatTipUsername":"Формат: 5-32 символа (Unix-стиль)","formatTipPassword":"Формат: 12-100 символов","passwordSuccess":"Пароль изменен","updateFailed":"Ошибка","confirmDeleteUser":"Удалить пользователя?","confirmClearLogs":"Очистить логи?","clearLogsSuccess":"Логи очищены","confirmDeleteAccount":"ВНИМАНИЕ: Удалить аккаунт? \\\\n\\\\nВсе данные, правила и логи будут удалены. Необратимо!","inactivityPolicy":"Политика активности","inactivityDesc":"Для защиты конфиденциальности пользователей и экономии ресурсов все журналы запросов и конфигурации DNS аккаунтов, не проявлявших активности разрешения в течение 30 дней, будут автоматически удалены. Сам аккаунт будет удален при отсутствии входа в течение 90 дней.","systemSettings":"Система (Админ)","adminOnlyView":"Только для админов","featureToggle":"Переключатели","enableTurnstileSignup":"CAPTCHA при регистрации","enableTurnstileLogin":"CAPTCHA при входе","totp":{"title":"Приложение-аутентификатор (TOTP)","enabled":"Вкл","disabled":"Выкл","setupDesc":"Доп. защита. Используйте Google Authenticator и др.","setupBtn":"Настроить аутентификатор (TOTP)","scanQR":"Сканируйте QR:","orEnterManually":"Или введите ключ:","enterCode":"Введите код для подтверждения:","enterCodeHint":"6 цифр.","activate":"Активировать TOTP","skipPassword":"Вход без пароля (только TOTP)","disable":"Отключить аутентификатор (TOTP)","disableTitle":"Отключение аутентификатора (TOTP)","disableWarning":"После отключения TOTP, если не настроены ключи Passkey, ваш аккаунт будет защищен только паролем.","confirmDisable":"Отключить TOTP","recoveryKeysTitle":"Сохраните ключи восстановления","recoveryKeysWarning":"Сохраните ключ восстановления в надежном месте (например, в менеджере паролей). Каждый ключ одноразовый.","copyKeys":"Копировать","copied":"Скопировано!","done":"Готово","activeDesc":"Приложение аутентификатор (TOTP) активно и генерирует 6-значные коды.","invalidCode":"Пожалуйста, введите 6-значный код"},"activity":{"title":"История активности","action":"Событие","time":"Время","ip":"IP","session":"Сессия","empty":"Пусто.","loadMore":"Еще","signup":"Регистрация","loginSuccess":"Успешный вход","loginFail":"Ошибка входа","logout":"Выход","pwChangeSuccess":"Пароль изменен","pwChangeFail":"Ошибка смены пароля","totpVerifySuccess":"Проверка TOTP пройдена","totpVerifyFail":"Ошибка проверки TOTP","totpSetup":"Настроен аутентификатор TOTP","totpRemoved":"Аутентификатор TOTP отключен","recoveryKeyUsed":"Ключ восстановления","sessionRevoked":"Сессия завершена","reasons":{"user_active":"Выход","user_revoke":"Отозвано","expired":"Истекло","geolocation_mismatch":"Гео-ограничение","geolocation_missing":"Нет данных гео","token_reuse":"Повторный вход (безопасность)"},"passkeyRegistered":"Passkey зарегистрирован","passkeyDeleted":"Passkey удален","passkeyVerifySuccess":"Passkey подтвержден","passkeyVerifyFail":"Ошибка верификации Passkey","recoveryKeyRotated":"Ключ восстановления обновлен","device":"Устройство"},"sessions":{"title":"Активные сеансы","session":"Сеанс","device":"Устройство","ip":"IP-адрес","loginTime":"Время входа","expires":"Истекает","action":"Действие","current":"Текущий","revoke":"Отозвать","revokeFailed":"Не удалось отозвать сеанс.","empty":"Активных сеансов не найдено.","unknownDevice":"Неизвестное устройство","revokeOthers":"Отозвать другие сеансы","confirmRevokeOthers":"Отозвать другие сеансы","revokeOthersConfirmText":"Вы уверены, что хотите отозвать все остальные активные сеансы? Все остальные подключенные устройства будут немедленно отключены."},"passkey":{"title":"Passkey (Ключи доступа)","addBtn":"Добавить Passkey","desc":"Используйте биометрию (Touch ID, Face ID, Windows Hello) или аппаратные ключи безопасности (YubiKey) для быстрой и защищенной от фишинга аутентификации.","empty":"Passkey еще не добавлены.","addTitle":"Добавить Passkey","nameLabel":"Имя Passkey","namePlaceholder":"например: my_passkey","promptNotice":"После нажатия кнопки продолжения браузер предложит пройти биометрическую регистрацию или использовать ключ безопасности.","continue":"Продолжить","renameTitle":"Переименовать Passkey","deleteTitle":"Удалить Passkey","deleteConfirm":"Вы уверены, что хотите удалить ключ доступа \\"{{name}}\\"? Вы больше не сможете использовать его для аутентификации.","defaultName":"Passkey","notSupported":"Ваш браузер или окружение не поддерживают ключи доступа (WebAuthn).","regFailed":"Не удалось зарегистрировать Passkey","count":"Настроено: {{count}}","none":"Не настроено","created":"Создано","lastUsed":"Последнее использование","neverUsed":"Никогда не использовался","updated":"Passkey успешно обновлен","registered":"Passkey успешно зарегистрирован","deleted":"Passkey удален","appNameLabel":"Домен","appNameHelper":"Ключ доступа будет привязан к этому домену","formatTip":"Имя Passkey: 1-30 символов (только буквы, цифры, _, -)","verifyPrompt":"Пройдите проверку с помощью биометрии устройства или ключа безопасности.","changePwNotice":"При отправке вам будет предложено подтвердить личность через Passkey.","submitNotice":"При отправке вам будет предложено подтвердить личность через Passkey."},"mfaNone":"Нет","resetUserPassword":"Сбросить пароль","resetPasswordTitle":"Сброс пароля пользователя","resetPasswordWarning":"После сброса все активные сессии пользователя будут немедленно завершены.","resetPasswordSuccess":"Пароль успешно сброшен. Все активные сессии завершены.","confirmReset":"Подтвердить сброс","mfa":{"title":"Многофакторная аутентификация (MFA)","enabled":"Настроено","notConfigured":"Не настроено","desc":"Ключи доступа (Passkey) и приложения (TOTP) служат методами MFA. Настройте хотя бы один фактор для защиты аккаунта.","passwordlessTitle":"Беспарольный вход","passwordlessDesc":"При включении пароль не требуется — вход выполняется напрямую через Passkey или TOTP.","unconfiguredHint":"Методы MFA еще не настроены. Добавьте Passkey или приложение-аутентификатор ниже.","password":"Пароль","passkey":"Passkey","totp":"TOTP"},"recoveryKey":{"title":"Экстренный ключ восстановления","desc":"Экстренный ключ восстановления позволяет восстановить доступ к учетной записи или сбросить пароль в случае утери всех ключей доступа (Passkey) или приложений аутентификации. Для обеспечения безопасности с нулевым разглашением ключ хранится в виде необратимого криптографического хэша и не может быть просмотрен в открытом виде. После использования он немедленно обновляется автоматически.","configured":"Настроено","notConfigured":"Не настроено","rotateBtn":"Сменить ключ восстановления","yourKey":"Ваш 30-значный ключ восстановления","securityTip":"Храните этот ключ в секрете. Любой, у кого есть доступ к нему, может получить доступ к вашей учетной записи.","rotateConfirm":"Смена ключа немедленно аннулирует текущий ключ. Подтверждение разрешено только через существующий Passkey или исходный ключ восстановления. Продолжить?","confirmRotateBtn":"Продолжить смену","rotateVerifyTitle":"Подтверждение личности для смены ключа","rotateSuccess":"Ключ восстановления успешно обновлен! Сохраните новый ключ в надежном месте.","oneWayHash":"Необратимый криптографический хэш","oneWayTooltip":"Защищено односторонним хэшем SHA-256. Сервер не хранит открытый текст и не может его расшифровать.","newKeyGenerated":"Сгенерирован новый 30-значный ключ восстановления (Сохраните сейчас)","oneWayWarning":"Внимание: этот ключ отображается ТОЛЬКО ОДИН РАЗ. Сервер сохраняет только необратимый хэш. Скопируйте и сохраните его в надежном месте оффлайн.","autoRotatedTitle":"Ключ восстановления автоматически обновлен","autoRotatedNotice":"В соответствии с правилом однократного использования ваш предыдущий ключ был аннулирован, и сгенерирован новый 30-значный ключ. Скопируйте и надежно сохраните его оффлайн!","savedAndClose":"Я сохранил(а) ключ и продолжить","savedAndContinue":"Я сохранил(а) ключ и перейти в панель","descOneWay":"Экстренный ключ восстановления — это единственное средство для восстановления доступа к аккаунту и расшифровки данных E2EE в случае утери всех ключей Passkey или аутентификаторов. В целях безопасности с нулевым разглашением он хранится в виде необратимого хеша и не может быть просмотрен в открытом виде. Его можно заменить только с помощью текущего ключа восстановления или существующего Passkey. Ключ автоматически обновляется при использовании.","keyRequired":"Требуется ключ восстановления","verifyBtn":"Ключ восстановления","verifyPromptLabel":"Исходный ключ восстановления"},"verifyIdentity":"Проверка безопасности","verifyIdentityDesc":"Для защиты учетной записи подтвердите свою личность перед продолжением.","usePasskeyInstead":"Подтвердить с помощью Passkey","changePasswordWithPasskey":"Подтвердить через Passkey и изменить пароль","enableRegistration":"Разрешить регистрацию пользователей","timezone":"Часовой пояс","timezoneAuto":"Авто (по умолчанию в браузере)","totpCode":"Код аутентификатора"}`),d={item1Title:`Защищенный DNS Worker`,item1Desc:`DNS-резолвер на базе Cloudflare Workers & D1, оптимизированный для скорости и безопасности.`,item2Title:`Конфиденциальность DoH`,item2Desc:`Предотвращение перехвата и повышение конфиденциальности через DNS over HTTPS (RFC 8484).`,item3Title:`Глобальная Edge-сеть`,item3Desc:`Использование обширной сети Cloudflare для быстрого резолвинга с низкой задержкой.`,item4Title:`Точная фильтрация`,item4Desc:`Полная поддержка черных/белых списков, правил AdGuard и перенаправлений DNS-записей.`,item5Title:`Аналитика в реальном времени`,item5Desc:`Детальные логи запросов и комплексные дэшборды для мониторинга.`,item6Title:`Современный UI и темная тема`,item6Desc:`Высокоплотная панель управления, разработанная для инженеров, на базе Blueprint UI от Palantir.`,item7Title:`Облачная архитектура`,item7Desc:`Serverless-архитектура обеспечивает нулевое обслуживание и бесконечное масштабирование.`,item8Title:`Управление профилями`,item8Desc:`Создание и управление несколькими независимыми профилями для разных устройств.`,item9Title:`Управление точками доступа`,item9Desc:`Настройка отдельных DNS-точек для устройств для детальной изоляции логов и статистики.`},f={title:`Руководство по настройке и развертыванию DNS Worker`,subtitle:`Ваш экземпляр еще не полностью настроен. Пожалуйста, выполните следующие шаги, чтобы включить DNS Worker.`,dbStatus:`Связывание базы данных D1 (env.DB)`,jwtStatus:`Секретный ключ JWT (env.JWT_SECRET)`,configured:`Настроено`,missing:`Отсутствует / Не настроено`,refresh:`Обновить и проверить`,methodCli:`Развертывание через CLI`,methodDash:`Развертывание через панель Cloudflare`,copy:`Копировать`,copied:`Скопировано!`,stepDbTitle:`1. Привязать базу данных D1`,stepDbDesc:`Создайте базу данных D1 и привяжите ее к вашему воркеру. В терминале выполните:`,stepDbCommand1:`wrangler d1 create dns_worker_db`,stepDbDesc2:`Скопируйте вывод конфигурации D1 и вставьте его в wrangler.toml в раздел [[d1_databases]]:`,stepDbCommand2:`[[d1_databases]]
1
+ const e={account:`Аккаунт`,logout:`Выйти`,selectProfile:`Выбрать профиль`,add:`Добавить`,newProfileName:`Имя нового профиля...`,profileNameFormatTip:`Совет по названию: 1-30 символов, не должно совпадать с существующими`,profileLimitExceeded:`Достигнут лимит профилей (макс. {{max}})`,profileNameExists:`Профиль с таким именем уже существует`,profileNameFormatError:`Неверный формат имени профиля`,create:`Создать`,welcome:`Добро пожаловать`,createProfileToStart:`Создайте профиль, чтобы начать.`,loading:`Загрузка...`,errorNetwork:`Сетевая ошибка`,confirmDelete:`Вы уверены, что хотите удалить этот профиль?`,confirmLogout:`Вы уверены, что хотите выйти?`,notFound:`404 - Страница не найдена`,notFoundDesc:`Страница, которую вы пытаетесь открыть, не существует или была удалена.`,backToHome:`На главную`,apac:`APAC`,mainlandChina:`Китай`,HongKong:`Гонконг`,Singapore:`Сингапур`,Tokyo:`Токио`,NorthAmerica:`Северная Америка`,nae:`США, восток`,naw:`США, запад`,eu:`Европа`,invalidFormat:`Неверный формат файла`,import:`Импорт`,importSuccess:`Профиль успешно импортирован`,importError:`Ошибка импорта профиля, проверьте формат и попробуйте позже`,save:`Сохранить`,saveSuccess:`Настройки сохранены`,close:`Закрыть`,cancel:`Отмена`,continue:`Продолжить`,back:`Назад`,copy:`Копировать`,copied:`Скопировано`,copyKey:`Скопировать новый ключ восстановления`,confirm:`Подтвердить`,delete:`Удалить`,rename:`Переименовать`,disable:`Отключить`,confirmDisable:`Подтвердить отключение`,modeServerfull:`Режим Serverfull`,modeCloudflare:`Режим Cloudflare Worker`},t={setup:`Установка`,rules:`Правила`,stats:`Статистика`,logs:`Логи`,filter:`Фильтры`,settings:`Настройки`},n={light:`Светлая`,dark:`Темная`,system:`Системная`},r=JSON.parse(`{"login":"Войти в DNS Worker","signup":"Создайте аккаунт, чтобы начать","welcomeBack":"С возвращением, введите учетные данные","protectInternet":"Защитите ваш первый прыжок в интернет","error":"Ошибка","username":"Имя пользователя","usernamePlaceholder":"Ваше имя пользователя","password":"Пароль","passwordPlaceholder":"Ваш пароль","loginBtn":"Войти","signupBtn":"Зарегистрироваться","totpVerification":"TOTP проверка","noAccount":"Нет аккаунта? Зарегистрируйтесь","haveAccount":"Уже есть аккаунт? Войти","formatTipUsername":"Формат имени: 5-32 символа (Unix-стиль: буквы, цифры, _, -)","formatTipPassword":"Формат пароля: 12-100 символов","authFailed":"Ошибка аутентификации, проверьте логин или пароль","networkError":"Сетевая ошибка, попробуйте позже","turnstileRequired":"Пожалуйста, пройдите проверку (CAPTCHA)","turnstileError":"Ошибка загрузки сервиса проверки, проверьте Site Key или конфигурацию домена","verifying":"Выполняется проверка безопасности...","passwordLeaked":"Ваш пароль был скомпрометирован. Для безопасности используйте случайный/сильный пароль.","usernameExists":"Имя пользователя уже занято, выберите другое.","skip":"Пропустить","showPassword":"Показать пароль","hidePassword":"Скрыть пароль","geolocationRequired":"Ошибка геолокации. Пожалуйста, попробуйте войти снова.","unauthorizedDefault":"Ваша сессия истекла, войдите снова.","unauthorizedGeo":"Обнаружено изменение геолокации, сессия завершена.","unauthorizedExpired":"Сессия истекла, войдите снова.","unauthorizedReuse":"Безопасность: обнаружен повторный вход, сессия завершена.","unauthorizedRevoked":"Ваша сессия была отозвана, войдите снова.","sessionLockTitle":"Блокировка неактивной сессии","sessionLockDesc":"Блокирует интерфейс 4-значным PIN-кодом, когда приложение неактивно, для защиты активных сессий от несанкционированного физического доступа.","enableSessionLock":"Включить блокировку сессии","inactivityTimeout":"Тайм-аут неактивности","timeout1m":"1 минута","timeout2m":"2 минуты","timeout5m":"5 минут","timeout15m":"15 минут","timeout30m":"30 минут","timeout60m":"60 минут","changePin":"Изменить PIN","disablePin":"Отключить блокировку сессии","configurePin":"Настроить PIN","newPin":"Новый 4-значный PIN","pinHelper":"Только цифры, например, 1234","confirmPin":"Подтвердить новый PIN","verifyIdentity":"Подтвердить личность","usePassword":"Использовать пароль","use2fa":"Использовать код TOTP","totpCode":"Код TOTP","currentPassword":"Текущий пароль","disablePinWarning":"Отключение PIN-кода полностью отключит функцию автоматической блокировки неактивной сессии.","pinFormatTip":"PIN-код должен состоять ровно из 4 цифр","pinMatchTip":"PIN-коды не совпадают","passwordRequired":"Для подтверждения требуется пароль","sessionLocked":"Сессия заблокирована из-за неактивности","sessionExpired":"Слишком много неудачных попыток. Сессия завершена.","pinIncorrect":"Неверный PIN-код","attemptsRemaining":"Осталось попыток: {{count}}","logoutAndReturn":"Выйти из сессии","lockNow":"Заблокировать сейчас","cfErrorWithDetails":"Ошибка Cloudflare Edge (Ошибка {{code}}: {{desc}}). Сервис временно недоступен, повторите попытку позже. {{rayId}}","cfErrorCodeOnly":"Ошибка Cloudflare Edge (Ошибка {{code}}). Сервис временно недоступен. {{rayId}}","cfErrorGeneric":"Ошибка Cloudflare Edge, сервис временно недоступен. Повторите попытку позже. {{rayId}}","htmlErrorGeneric":"Сервер вернул непредвиденный ответ (HTTP {{status}}). Повторите попытку позже.","tooManyAttempts":"Слишком много запросов или попыток. Пожалуйста, повторите попытку позже.","dbQuotaExceeded":"Превышена дневная квота чтения базы данных. Дождитесь ежедневного сброса.","noUsersRegistered":"Учётные записи отсутствуют. Пожалуйста, зарегистрируйтесь в качестве администратора.","registerAdminTitle":"Регистрация учётной записи администратора","registerAdminDesc":"В системе нет учётных записей. Первый пользователь станет системным администратором.","goToRegisterAdmin":"Зарегистрировать админа","dbNotInitialized":"База данных не инициализирована или отсутствуют таблицы. Выполните npm run db:migrate:prod в терминале.","verifyWithPasskey":"Подтвердить с помощью Passkey","orUseTotp":"Или используйте код аутентификатора","passwordRequiredFirst":"Сначала введите пароль","passkeyVerification":"Верификация Passkey","passkeyPromptDesc":"Подтвердите с помощью биометрии или ключа безопасности","forgotPassword":"Забыли пароль?","forgotPasswordTitle":"Сброс пароля","forgotPasswordDesc":"Безопасно восстановите доступ к аккаунту с помощью многофакторной аутентификации (Passkey или TOTP).","noMfaContactAdmin":"Для этого аккаунта не настроена многофакторная аутентификация (Passkey или TOTP). Обратитесь к системному администратору.","chooseMfaForReset":"Выберите способ подтверждения личности:","authViaPasskey":"Подтвердить с помощью Passkey","authViaPasskeyDesc":"Быстрая аутентификация по биометрии или аппаратному ключу","authViaTotp":"Подтвердить через приложение (TOTP)","authViaTotpDesc":"Введите 6-значный код из приложения аутентификатора","authViaRecoveryKey":"Использовать аварийный ключ восстановления","authViaRecoveryKeyDesc":"Введите 10-значный аварийный ключ восстановления","verifyAndProceed":"Проверить и продолжить","setNewPassword":"Установить новый пароль","setNewPasswordDesc":"Личность подтверждена. Пожалуйста, задайте новый пароль для вашего аккаунта.","newPassword":"Новый пароль","confirmNewPassword":"Подтвердите новый пароль","passwordMismatch":"Пароли не совпадают","saveAndReset":"Сохранить и сбросить","resetSuccessTitle":"Пароль успешно сброшен","resetSuccessDesc":"Пароль обновлен, все предыдущие сессии завершены. Войдите с новым паролем.","goToLogin":"Перейти ко входу","mfaSetupTitle":"Повышение безопасности аккаунта","mfaSetupDesc":"Настройка многофакторной аутентификации (MFA) защищает аккаунт и открывает беспарольный вход.","mfaRecommended":"Рекомендуется","passkeySignupDesc":"Используйте отпечаток пальца, сканирование лица или ключ безопасности. Быстро и надежно.","bindPasskeyNow":"Зарегистрировать Passkey","mfaUniversal":"Универсально","totpSignupDesc":"Используйте Google Authenticator, 1Password или любое стандартное приложение.","setupTotpBtn":"Настроить приложение-аутентификатор","skipMfaSetup":"Пропустить и завершить","backToMfaOptions":"Назад к вариантам","otherOptions":"Другие варианты","next":"Далее","mfaVerification":"Многофакторная аутентификация","totpPrompt":"Введите 6-значный динамический код из приложения аутентификации","recoveryPrompt":"Введите 30-значный ключ восстановления (5 групп по 6 цифр)","recoveryChecksumError":"Ошибка контрольной суммы: эта группа не делится на 11","recoveryChecksumErrorDesc":"Ошибка контрольной суммы в группе {{groups}}, проверьте и повторите попытку","backToPasskey":"Вернуться к аутентификации по Passkey","recoveryKeyRequired":"Пожалуйста, введите ваш ключ восстановления","totpFormatError":"Пожалуйста, введите действительный 6-значный код","registrationDisabledTitle":"Регистрация отключена","registrationDisabledDesc":"Регистрация новых пользователей в настоящее время закрыта. Обратитесь к администратору для получения учетной записи.","backToLogin":"Вернуться ко входу","jwtSecretPreset":"Обнаружен предустановленный JWT_SECRET. Пожалуйста, измените предустановленный JWT_SECRET в переменных окружения перед развертыванием.","jwtSecretMissing":"Отсутствует JWT_SECRET. Пожалуйста, настройте безопасный ключ в переменных окружения перед развертыванием.","jwtSecretInvalid":"Неверный формат JWT_SECRET, проверьте настройки.","keepLoggedIn":"Оставаться в системе {{days}} дн.","orUseOtherMfa":"или используйте другой способ","totpUseRecovery":"Использовать ключ восстановления","totpUseApp":"Использовать приложение-аутентификатор","mfaMethodTotp":"Приложение аутентификации (TOTP)","invalidTotp":"Неверный код подтверждения TOTP."}`),i={title:`Руководство по установке`,subtitle:`Настройте устройство для использования DNS Worker.`,regionOptimization:`Оптимизация региона (Windows)`,otherRegion:`Другие регионы`,copied:`Скопировано в буфер`,verifyConnection:`Проверить подключение`,verifying:`Проверка...`,connected:`Подключено`,notConnected:`Не подключено`,profileMatch:`Фильтрация через этот профиль активна.`,profileMismatch:`Obex используется, но ID профиля не совпадает.`,verifyHint:`После настройки нажмите кнопку для проверки.`,refreshStatus:`Обновить статус`,egressIp:`Исходящий IP`,currentLocation:`Текущее местоположение`,edgeServer:`Edge-сервер`,serverLocation:`Расположение сервера`,dohUrlTitle:`Текущий URL DoH`,accessPointTitle:`Access DoH URL`,defaultAccessPointName:`Устройство-1`,moreAccessPoints:`Еще точки доступа ↗`,accessPointName:`Имя точки доступа`,accessPointToken:`Ключ доступа`,addAccessPoint:`Добавить точку`,renameAccessPoint:`Переименовать`,rotateToken:`Ротация ключа`,rotateTokenConfirmDesc:`Ротация отключит все устройства со старым ключом. Продолжить?`,deleteAccessPoint:`Удалить`,deleteAccessPointConfirm:`Удалить эту точку доступа?`,manageAccessPoints:`Управление точками доступа`,searchAccessPoints:`Поиск точек доступа...`,accessPointNameExists:`Имя точки уже занято`,accessPointCreated:`Точка доступа создана`,accessPointRenamed:`Точка переименована`,accessPointDeleted:`Точка удалена`,accessPointNameFormatTip:`Имя: 1-30 символов (буквы, цифры, _, -)`,accessPointLimitExceeded:`Достигнут лимит (макс. {{max}})`,tokenRotated:`Ключ доступа обновлен`,whatIsDoh:`Что такое DoH?`,dohDesc:`DNS over HTTPS (DoH) — технология резолвинга через зашифрованный HTTPS.`,dohBenefit1:`Защита от перехвата провайдерами`,dohBenefit2:`Повышение безопасности DNS`,learnMore:`Подробнее`,copyUrl:`Копировать URL`,browsers:`Браузеры`,browserTitle:`Настройка браузеров (Chrome/Edge)`,browserSteps:`Настройки > Приватность и безопасность > Безопасность > Безопасный DNS > Пользовательский > Вставьте URL.`,browserTips:`Или вставьте URL в адресную строку:`,apple:`Apple`,appleTitle:`macOS / iOS / iPadOS / tvOS`,appleDesc:`Настройка Apple через профиль конфигурации.`,appleWarning:`Профили нельзя установить в "Режиме блокировки (Lockdown Mode)".`,downloadConfig:`Скачать профиль`,appleInstallHint:`После скачивания: Настройки > Профили для установки`,windows:`MS Windows`,windowsTitle:`Настройка Windows 11`,windowsStep0:`Настройки > Сеть и Интернет`,windowsStep1:`Свойства > DNS-серверы > Изменить > Вручную.`,windowsStep2:`Выберите и введите DNS IP:`,windowsStep3:`Включите DoH (пользовательский шаблон) и вставьте URL.`,windowsStep4:`Отключите откат к незашифрованному DNS.`,android:`Android`,androidTitle:`Android / Частный DNS (DoT и DoQ)`,androidDesc:`Поддерживает системный «Частный DNS» (DoT) или сторонние приложения для DoH.`,androidDotTitle:`Частный DNS (DoT / DoQ)`,androidDotDesc:`Настройки > Сеть и интернет > Персональный DNS > Имя хоста провайдера персонального DNS (также поддерживает клиенты DoQ):`,androidWildcardHint:`(Wildcard-сертификат, маршрутизирует в этот профиль)`,androidSingleDomainHint:`(Однодоменный сертификат, маршрутизирует в профиль по умолчанию)`,androidIpNotice:`Доступ через IP или локальный адрес. Android Частный DNS принимает только доменные имена; настройте SERVERFULL_DOT_DOMAIN в .env (напр. dns.example.com).`,androidWarning:`Примечание: системный «Частный DNS» Android использует протокол DoT. При развертывании в Cloudflare Workers используйте приложения вроде Intra для DoH. В режиме Serverfull с настроенным TLS вы можете напрямую использовать указанное выше имя хоста DoT.`,dynamicFromDomain:`Динамический (из "{{domain}}")`,dynamicFromDomainV6:`Динамический IPv6 (из "{{domain}}")`,linux:`Linux`,linuxTitle:`Linux (Cloudflared DoH прокси)`,linuxDesc:`Локальный DNS прокси для шифрования запросов.`,linuxStep1:`Запустите скрипт для установки cloudflared и создания systemd-сервиса (слушает 127.0.0.1:53).`,linuxStep2:`Установите системный DNS на 127.0.0.1.`,routeros:`MikroTik`,routerosTitle:`MikroTik RouterOS`,routerosDesc:`Используйте этот резолвер для всех устройств в сети, указав его в качестве DoH-сервера роутера.`,routerosStep1:`Выполните в терминале RouterOS (RouterOS 7):`,routerosNote:`Запросы, пересылаемые роутером, отображаются в журналах как запросы роутера, а не отдельных устройств. Если нужны журналы по устройствам, настройте устройства напрямую.`,routerosDocs:`Документация MikroTik по DNS`,accessPointsCardTitle:`Точки доступа`,currentAccessPoint:`Текущая точка доступа`,selectAccessPointToConfigure:`Выберите точку доступа для настройки`,quic:`QUIC & HTTP/3`,quicTitle:`DNS over QUIC (DoQ) & HTTP/3 (DoH3)`,quicDesc:`Нативная поддержка RFC 9250 (DNS over QUIC) и RFC 9460 (HTTP/3 & Encrypted Client Hello) с рукопожатием 0-RTT и защитой от перехвата SNI.`,doqTitle:`DNS over QUIC (DoQ - RFC 9250)`,doqAppHint:`Совместимо с современными клиентами, такими как AdGuard, Clash Meta (Mihomo), Sing-box и др.:`,doqWildcardHint:`(Wildcard-сертификат, маршрутизирует в этот профиль)`,doqSingleDomainHint:`(Сертификат для одного домена, маршрутизирует в профиль по умолчанию)`,doqIpNotice:`Для DoQ требуется TLS-сертификат и действительный домен. Укажите SERVERFULL_DOT_DOMAIN в .env и обновите страницу.`,h3Title:`HTTP/3 (DoH3) & ECH (Encrypted Client Hello)`,h3Desc:`Современные браузеры (Chrome/Firefox/Safari) автоматически согласовывают HTTP/3 и шифрование ECH через записи Alt-Svc и RFC 9460 HTTPS:`,ddrHint:`Интегрирован RFC 9460 Section 8 DDR (Discovery of Designated Resolvers): клиенты могут автоматически согласовывать и переходить на DoQ/DoT и ECH через запросы к _dns.`,e2eeDesc:`Шифрует журналы DNS на границе с помощью постквантового гибридного ключа (P256-MLKEM768). Облако не имеет доступа к данным; расшифровка происходит только локально с Passkey.`,detecting:`Определение...`,dotUnavailableCfTitle:`DoT / DoQ недоступен в режиме Cloudflare Worker`,dotUnavailableCfDesc:`В настоящее время работает в режиме Cloudflare Worker. Из-за сетевых ограничений прослушивание портов TCP/UDP 853 не поддерживается. Для использования DoT или DoQ выполните развертывание в режиме Serverfull; в текущем режиме используйте DoH.`},a={title:`Пользовательские правила`,subtitle:`Управление резолвингом доменов (блокировка, разрешение, перенаправление).`,action:`Действие`,typeBlock:`Блокировать (BLOCK)`,typeAllow:`Разрешить (ALLOW)`,typeRedirect:`Перенаправить (REDIRECT)`,pattern:`Шаблон домена`,patternPlaceholder:`example.com или *.test.com`,addRule:`Добавить правило`,redirectSettings:`Настройки перенаправления`,aRecord:`A запись (IPv4)`,aaaaRecord:`AAAA запись (IPv6)`,cnameRecord:`CNAME запись`,txtRecord:`TXT запись`,optional:`(опционально)`,ipv4Placeholder:`Напр: 1.2.3.4`,ipv6Placeholder:`Напр: 2001:db8::1`,cnamePlaceholder:`Напр: target.com`,txtPlaceholder:`Введите текст...`,saveRedirectRule:`Сохранить правило перенаправления`,noRulesTitle:`Нет правил`,noRulesDesc:`Вы еще не добавили пользовательских правил.`,tableAction:`Действие`,tablePattern:`Шаблон`,tableDetails:`Детали`,tableOps:`Опции`,labelBlock:`Блок`,labelAllow:`Разрешить`,labelRedirect:`Перенапр.`,detailBlock:`Заблокировано`,detailAllow:`Разрешено`,patternEmpty:`Шаблон домена не может быть пустым`,duplicatePattern:`Правило для этого домена уже существует`,addSuccess:`Правило добавлено`,addFailed:`Ошибка добавления правила`,updateSuccess:`Правило обновлено`,updateFailed:`Ошибка обновления правила`,tableCreatedAt:`Дата добавления`,clickToSort:`Нажмите для сортировки`,editRule:`Редактировать правило`,cancel:`Отмена`,saveChanges:`Сохранить изменения`,delete:`Удалить`,close:`Закрыть`},o={customRange:`Произвольный период`,startTime:`Начало`,endTime:`Конец`,apply:`Применить`,custom:`Свой`,totalQueries:`Всего запросов`,blocked:`Заблокировано`,redirected:`Перенаправлено`,blockRate:`Процент блокировок`,activeDevices:`Активные устройства`,activeIPs:`Активные IP`,queryTrend:`Тренд запросов`,noRecords:`Нет записей за этот период`,topAllowed:`Частые (Разрешенные)`,topBlocked:`Частые (Блокировки)`,noData:`Нет данных`,clientActivity:`Активность клиентов`,ipAddress:`IP-адрес`,location:`Локация`,queries:`Запросы`,destinationDistribution:`Распределение по направлениям`,requests:`Запросы`,topIsps:`Основные провайдеры`,pressToShowMore:`Нажмите, чтобы показать еще`,maximize:`Увеличить`,minimize:`Уменьшить`,loading:`Загрузка...`},s={title:`Логи запросов`,subtitle:`Просмотр истории DNS и блокировок.`,realtime:`В реальном времени`,allStatus:`Все статусы`,onlyPass:`Разрешенные`,onlyBlock:`Блокировки`,onlyRedirect:`Перенаправления`,searchPlaceholder:`Поиск домена...`,noResults:`Результатов не найдено`,noRecords:`Нет записей`,noResultsDesc:`Нет записей для домена "{{query}}".`,noRecordsDesc:`DNS-запросов за выбранный период не поступало.`,tableTime:`Время`,tableDomain:`Домен`,tableAnswer:`Ответ`,tableSource:`Источник`,tableType:`Тип`,tableStatus:`Статус`,tableReason:`Причина`,loadedAll:`Загружено записей: {{count}}`,realtimeLoadMoreTip:`Отключите «Реальное время» для загрузки еще`,filterAccessPoint:`Точка доступа`,allAccessPoint:`Все точки`,allCountry:`Все страны`,filterCountry:`Страна назначения`,allIsp:`Все провайдеры`,filterIsp:`Провайдер`,timeRange:`Временной диапазон`,clearFilters:`Очистить фильтры`,details:`Детали запроса`,detailAccessPoint:`Точка доступа`,basicInfo:`Базовая информация`,detailDomain:`Домен`,detailType:`Тип`,detailTime:`Время`,detailLatency:`Задержка`,detailProfile:`Используемый профиль`,detailStatus:`Статус`,detailUpstream:`Upstream-сервер`,detailReason:`Причина`,detailNoReason:`Нет (разрешено)`,detailECS:`ECS`,resolutionResult:`Результат резолвинга`,noResult:`Нет результата`,networkDetails:`Сетевые детали`,clientSource:`Клиент (источник)`,destination:`Назначение`,quickActions:`Быстрые действия`,actionAllow:`Разрешить`,actionBlock:`Блокировать`,actionRedirect:`Перенаправить`,statusPass:`Разрешено`,statusBlock:`Блок`,statusRedirect:`Перенаправлено`,export:`Экспорт журналов`,rewritten:`Перезаписано`,logDetails:`Подробности журнала`,searchCountry:`Поиск стран...`,noMatchingCountries:`Подходящие страны не найдены`,searchIsp:`Поиск провайдера...`,noMatchingIsps:`Подходящие провайдеры не найдены`,loadingIsps:`Загрузка провайдеров...`,loggingDisabledTitle:`Ведение журнала отключено`,loggingDisabledDesc:`Хранение журналов для этого профиля отключено. Журналы запросов не записываются. Чтобы просмотреть журналы, включите хранение журналов в настройках.`,detailIsClientEncrypted:`Клиентское шифрование`,encryptedLocked:`Да (Заблокировано)`,encryptedDecrypted:`Да (Расшифровано локально)`,encryptedYes:`Да`,notEncrypted:`Нет`,encryptedRecord:`[Зашифрованная запись]`,exportSuccess:`Журналы успешно экспортированы`,exportError:`Не удалось экспортировать журналы`},c={title:`Внешние списки фильтрации`,subtitle:`Подписки на сторонние списки (напр. AdGuard).`,formatDisclaimer:`Поддерживаются форматы AdGuard и обычные hosts-файлы. Поддерживаются базовые правила (||example.com^), обход «@@» белых списков, поддержка «$» и «*» не реализована. Домены матчатся со всеми поддоменами.`,syncAll:`Синхронизировать все`,urlPlaceholder:`URL списка...`,addSubscription:`Добавить подписку`,supportedFormats:`Поддерживаемые форматы:`,formatAdGuard:`Совместимо с ||domain.com^.`,formatHosts:`Совместимо с hosts (0.0.0.0 domain.com).`,noListsTitle:`Нет списков`,noListsDesc:`Вы еще не подписались ни на один список.`,tableUrl:`URL списка`,tableLastSync:`Последняя синхронизация`,tableStatus:`Статус`,tableOps:`Опции`,neverSynced:`Никогда`,enabled:`Норма`,disabled:`Ошибка синхронизации`,statusNormal:`Нормально`,statusDisabled:`Отключён`,statusOutdated:`Устарело`,statusMissing:`Отсутствует`,presetOisdBig:`OISD Big (Реклама/Малевэр)`,presetOisdNsfw:`OISD NSFW (Контент 18+)`,presetAdGuard:`AdGuard AdServers`,presetStevenBlack:`StevenBlack Hosts (Комбинированный)`,syncTaskStarted:`Синхронизация запущена`,syncCheckComplete:`Синхронизация завершена`,syncTaskFailed:`Ошибка синхронизации`,syncing:`Синхронизация...`,syncSuccess:`Синхронизировано`,syncError:`Ошибка`,syncErrorDesc:`Не удалось скачать список. Проверьте URL.`,deleteConfirm:`Удалить подписку?`,deleteSuccess:`Список удален`,deleteError:`Ошибка удаления`,addSuccess:`Список добавлен`,addError:`Ошибка добавления`,listEnabled:`Список активен`,listEnabledHint:`Отключите, чтобы прекратить фильтрацию по этому списку, не удаляя его. Подписка и история синхронизации сохраняются.`,listEnabledToast:`Список включён - фильтр пересобирается`,listDisabledToast:`Список отключён - фильтр пересобирается`,checkDomain:`Проверить домен`,checkDomainHint:`Списки хранятся в виде компактных фильтров, поэтому их записи нельзя просмотреть. Вы можете мгновенно проверить любой домен по этому списку.`,check:`Проверить`,checkBlocked:`Заблокировано этим списком`,checkNotBlocked:`Не заблокировано этим списком`,checkViaParent:`(совпадение по родительскому домену)`,checkNotSynced:`Этот список ещё не завершил синхронизацию, поэтому проверять пока нечего.`,checkListDisabled:`Этот список сейчас отключён, поэтому он ничего не фильтрует.`,checkProbabilisticRate:`Фильтры вероятностные: примерно 1 совпадение из {{odds}} - ложное срабатывание. Добавление исключения также покрывает все его поддомены.`,checkProbabilistic:`Фильтры вероятностные, поэтому небольшая часть совпадений - ложные срабатывания. Добавление исключения также покрывает все его поддомены.`,checkAllowAction:`Всё равно разрешить`,checkAllowed:`Исключение добавлено - теперь этот домен разрешается`,allowRuleAdded:`Исключение добавлено для {{domain}}`,listDetails:`Подробности списка`},l={title:`Настройки конфигурации`,subtitle:`Управление политикой DNS.`,saveChanges:`Сохранить`,saveSuccess:`Настройки сохранены`,saveError:`Ошибка сохранения`,upstreamTitle:`Upstream-серверы`,dohUrl:`DoH URL`,dnsIP:`DNS IP`,classicDns:`Классический DNS`,httpsOnly:`Только HTTPS`,upstreamDesc:`Все незаблокированные запросы будут пересылаться на этот сервер. Поддерживает DoH (HTTPS), DoT (tls://), DNS Stamp (sdns://) или классический DNS. Классический DNS передается через TCP.`,defaultPolicyTitle:`Фильтрация по умолчанию`,onNoMatch:`Если правил нет`,allowAll:`Разрешать всё`,blockAll:`Блокировать всё`,logRetentionTitle:`Хранение логов`,retentionDuration:`Срок хранения`,retentionDesc:`Журналы удаляются автоматически. `,skipLogOnPass:`Не записывать разрешенные запросы`,skipLogOnPassDesc:`Если включено, обычные разрешенные запросы не записываются в журнал. Сохраняются только заблокированные, перенаправленные или неудачные запросы.`,ecsTitle:`EDNS Client Subnet (ECS)`,ecsUpstreamHint:`ECS-подсеть, отправляемая на upstream-сервер`,sendEcs:`Использовать ECS`,customSubnet:`Пользовательская подсеть`,customIpv4:`IPv4`,customIpv6:`IPv6`,ipv4Hint:`(напр. 1.2.3.0/24)`,ipv6Hint:`(напр. 2001:db8::/32)`,testToolTitle:`Инструмент проверки`,testDomain:`Домен`,recordType:`Тип записи`,runTest:`Тест`,domainPlaceholder:`Напр. google.com`,hitRule:`Сработавшее правило`,defaultAllow:`Политика по умолчанию`,parseLatency:`Задержка`,sourceIp:`Исходный IP`,answerSection:`Ответ (Answer Section)`,noRecordsReturned:`Записей не возвращено.`,diagnostics:`Диагностика`,errorDetail:`Сведения об ошибке`,upstreamResponse:`Ответ вышестоящего сервера`,presetCloudflareSecurity:`Cloudflare Security`,presetCloudflareFamilies:`Cloudflare Families`,presetQuad9:`Quad9`,presetQuad9Ecs:`Quad9 ECS`,presetControlDFree:`ControlD Free`,presetControlDUncensored:`ControlD Uncensored`,presetAdGuard:`AdGuard`,presetAdGuardFamily:`AdGuard Family`,retention10m:`10 минут`,retention1h:`1 час`,retention24h:`24 часа`,retention7d:`7 дней`,retention30d:`30 дней`,retention90d:`90 дней`,retention180d:`180 дней`,retention360d:`360 дней`,retentionMaxSuffix:`(Максимум)`,daysUnit:`дней`,blockModeTitle:`Режим ответа при блокировке`,blockModeDesc:`Что возвращать при блокировке`,default:`По умолч.`,NXDOMAIN:`NXDOMAIN`,NODATA:`NODATA`,customIP:`Свой IP`,export:`Экспорт`,exportSuccess:`Успешно`,exportError:`Ошибка экспорта`,nameUpdateSuccess:`Имя обновлено`,saving:`Сохранение...`,echTitle:`Перезапись ECH`,enableEch:`Включить перезапись ECH`,echDesc:`Для доменов через Cloudflare автоматически перезаписывает параметры ECH и внешний SNI в запросах HTTPS (Type 65) / SVCB (Type 64) для защиты конфиденциальности.`,echFrontingTitle:`Внешний SNI (ECH Fronting)`,echFrontingDesc:`Доменное имя, видимое в открытом виде во внешнем рукопожатии TLS`,echFrontingCustomPlaceholder:`Введите пользовательский домен fronting, например crypto.cloudflare.com`,echPreferred:`Рекомендуется`,whatIsEch:`Что такое ECH (Encrypted Client Hello)?`,echHelpDesc:`ECH — расширение конфиденциальности TLS 1.3, которое полностью шифрует реальное имя домена (Inner SNI), показывая наблюдателям только маскировочный домен (Outer SNI).`,echHelpBenefit1:`Предотвращает отслеживание провайдерами посещаемых вами поддоменов`,echHelpBenefit2:`В сочетании с DoH устраняет утечки открытого SNI`,retentionDisabled:`Отключено (В целях конфиденциальности)`,retentionDisabledDesc:`Ведение журнала отключено. Система не будет записывать журналы DNS-запросов и немедленно удалит существующие журналы.`,localRetentionTitle:`Локальное хранилище и срок хранения`,localRetentionDuration:`Локальный срок хранения`,localRetentionDesc:`Журналы сохраняются локально в базе данных SQLite браузера (OPFS), обеспечивая мгновенный поиск и экономя затраты на облако. Устаревшие локальные журналы удаляются автоматически.`,localRetention30d:`30 дней`,localRetention90d:`90 дней (Рекомендуется)`,localRetention180d:`180 дней`,localRetention365d:`365 дней (1 год)`,localRetentionUnlimited:`Без ограничений (До ручной очистки)`,localEngineStorage:`Движок хранилища`,localEngineOpfs:`Постоянное OPFS`,localEngineMemory:`Временное в памяти (Режим инкогнито)`,localCachedCount:`Кэшировано локально`,localCachedCountUnit:`{{count}} записей`,clearLocalData:`Очистить локальные журналы`,clearLocalDataConfirm:`Вы уверены, что хотите удалить все локальные журналы для этого профиля? (Журналы в облаке сохранятся)`,clearLocalDataSuccess:`Локальные журналы успешно удалены`,clearLocalDataError:`Ошибка очистки локальных журналов`,e2eeTitle:`Сквозное шифрование (E2EE)`,e2eeEnabled:`Включено`,e2eeDisabled:`Отключено`,e2eeDesc:`Шифрует журналы DNS на границе с помощью постквантового гибридного ключа (P256-MLKEM768). Облако не имеет доступа к данным; расшифровка происходит только локально с Passkey.`,e2eePasskeyRequired:`Для E2EE требуется Passkey (WebAuthn). Пожалуйста, добавьте Passkey в настройках аккаунта.`,e2eeGoToAccount:`Перейти в настройки аккаунта`,e2eeEnableSwitch:`Включить сквозное шифрование`,e2eeKeyType:`Алгоритм шифрования`,e2eeDeviceState:`Состояние на этом устройстве`,e2eeUnlocked:`Разблокировано на этом устройстве`,e2eeUnlockButton:`Подтвердить Passkey для разблокировки`,e2eeProtectedPasskeys:`Авторизованные Passkey`,e2eeDevicesUnit:`устройств(о)`,e2eeEnableSuccess:`E2EE успешно включено`,e2eeEnableError:`Не удалось включить E2EE`,e2eeDisableSuccess:`E2EE отключено`,e2eeDisableError:`Не удалось отключить E2EE`,e2eeUnlockSuccess:`Приватный ключ разблокирован на этом устройстве`,e2eeUnlockError:`Ошибка проверки Passkey`,e2eeDisableConfirm:`Вы уверены, что хотите отключить E2EE? Новые журналы будут сохраняться в открытом виде.`,e2eeUnlockPrompt:`Этот профиль использует сквозное шифрование. Подтвердите Passkey для расшифровки.`,e2eeUnlockNow:`Разблокировать журналы`,e2eeUnlockRecovery:`Разблокировать с помощью ключа восстановления`,securityTitle:`Безопасность и доступ`,rotateKeyTitle:`URL доступа`,rotateKeyDesc:`Создает новый URL-адрес доступа для этого профиля. Все устройства, использующие старый URL-адрес, немедленно потеряют соединение с DNS Worker и должны быть перенастроены с новым URL-адресом.`,rotateKeyBtn:`Сменить URL`,copied:`Скопировано в буфер обмена`,nameUpdateFailed:`Ошибка обновления`},u=JSON.parse(`{"title":"Настройки аккаунта","subtitle":"Управление учетными данными.","e2ee":{"title":"Сквозное шифрование (E2EE)","desc":"Использует клиентское постквантовое гибридное конвертное шифрование (P256-MLKEM768 + AES-256-GCM / NIST FIPS 203) с аппаратными Passkey. Данные шифруются на Edge; сервер и база данных хранят только шифротекст. Расшифровка выполняется исключительно на авторизованных устройствах.","pqcTag":"PQC / NIST FIPS 203","upgradePqcBtn":"Обновить до постквантового (PQC)","upgradePqcSuccess":"Успешно обновлено до постквантового гибридного шифрования (P256-MLKEM768)","upgradePqcError":"Сбой обновления до постквантового шифрования","enabled":"Включено","disabled":"Отключено","passkeyRequired":"Для сквозного шифрования требуется аппаратный Passkey (WebAuthn). Пожалуйста, добавьте Passkey в разделе MFA выше.","scopesTitle":"Категории зашифрованных данных","scopesSubtitle":"Выберите категории данных для сквозного шифрования с помощью аппаратного Passkey.","scopeLogsTitle":"Журналы DNS-запросов","scopeLogsDesc":"Шифрует доменные имена, IP-адреса клиентов, ответы и задержки перед сохранением в D1.","scopeRulesTitle":"Пользовательские правила и перенаправления","scopeRulesDesc":"Шифруемые на стороне клиента правила блокировки/разрешения и политики перенаправления.","scopeClientsTitle":"Метаданные устройств и клиентов","scopeClientsDesc":"Имена устройств точек доступа, псевдонимы IP и топология сети.","statusActive":"Защищено","statusPlanned":"Запланировано","profileSelect":"Настроить профиль","keyType":"Алгоритм шифрования","deviceState":"Текущее состояние устройства","unlocked":"Разблокировано на этом устройстве","unlockButton":"Подтвердить Passkey для разблокировки","protectedPasskeys":"Авторизованные ключи Passkey","devicesUnit":"ключ(ей)","enableSuccess":"Сквозное шифрование успешно включено","enableError":"Не удалось включить сквозное шифрование","disableSuccess":"Сквозное шифрование отключено","disableError":"Не удалось отключить сквозное шифрование","unlockSuccess":"Закрытый ключ успешно разблокирован на этом устройстве","unlockError":"Не удалось подтвердить Passkey и разблокировать","disableConfirm":"Вы уверены, что хотите отключить сквозное шифрование для этого профиля? Будущие журналы будут сохраняться в открытом виде. (Для расшифровки архивных журналов по-прежнему потребуется закрытый ключ)","noKeypairTitle":"Пара ключей сквозного шифрования не инициализирована","noKeypairDesc":"Для сквозного шифрования требуется создать выделенную пару постквантовых ключей (P256-MLKEM768). Закрытый ключ защищен конвертным шифрованием с использованием вашего Passkey или ключа восстановления.","initPasskeyBtn":"Подтвердить Passkey для создания пары ключей","addPasskeyBtn":"Добавить Passkey для аппаратного шифрования","initRecoveryBtn":"Создать пару ключей с помощью ключа восстановления","initWithRecoveryTitle":"Создать пару ключей с помощью ключа восстановления","initWithRecoveryDesc":"Введите текущий 30-значный экстренный ключ восстановления. Система создаст локальную пару постквантовых ключей (P256-MLKEM768). После использования ключ восстановления будет автоматически обновлен.","keypairGenSuccess":"Пара ключей сквозного шифрования успешно создана!","keypairGenError":"Не удалось создать пару ключей шифрования","unlockRecoveryButton":"Разблокировать с помощью ключа восстановления","unlockWithRecoveryTitle":"Разблокировать с помощью ключа восстановления","unlockWithRecoveryDesc":"Введите 30-значный ключ восстановления. Внимание: согласно правилу однократного использования, после проверки ключ будет немедленно обновлен.","recoveryKeyInputLabel":"Ключ восстановления","unlockConfirm":"Разблокировать закрытый ключ","recoveryUnlockFailed":"Недействительный ключ восстановления или ошибка расшифровки","generateKeypairBtn":"Создать пару ключей","enableSwitch":"Включить сквозное шифрование журналов запросов","enableSwitchDesc":"Шифрует доменные имена, IP-адреса клиентов, ответы и задержку апстрима на периферии перед сохранением в облаке.","trustBoundaryTitle":"Уведомление о границах безопасности и доверия","trustBoundaryDesc":"Весь трафик DNS-запросов неизбежно проходит через сервер хостинга (узлы Cloudflare Workers или серверную среду) во время разрешения, где запросы обрабатываются в памяти перед зашифрованной записью. Эта функция защищает данные в облачной базе данных (D1) от утечек; она не скрывает активный сетевой трафик от сервера, обрабатывающего запрос.","initFailed":"Не удалось инициализировать сквозное шифрование","lockDevice":"Заблокировать это устройство","lockSuccess":"Заблокировано, закрытый ключ удален из локального хранилища","enrollPasskeyBtn":"Связать текущий Passkey","enrollPasskeySuccess":"Passkey успешно привязан к E2EE!","enrollPasskeyFailed":"Не удалось привязать Passkey","resetKeypairBtn":"Сбросить пару ключей","confirmResetBtn":"Подтвердить сброс и повторную генерацию","resetKeypairTitle":"Сброс пары ключей E2EE","resetKeypairDesc":"Если ваш ключ восстановления не подходит или не расшифровывает, и Passkey не привязан, сброс удалит старые ключи с сервера и создаст новую постквантовую пару ключей (P256-MLKEM768).","resetKeypairPasskeyNotice":"После подтверждения будет вызван ваш настроенный Passkey для шифрования и авторизации новой пары ключей.","resetKeypairRecoveryNotice":"После подтверждения вам будет предложено ввести текущий ключ восстановления для создания и шифрования новой пары ключей.","resetAndInitPasskeySuccess":"Пара ключей успешно сброшена и повторно зашифрована с помощью Passkey!","resetSuccessEnterRecovery":"Старые ключи удалены. Пожалуйста, инициализируйте новую пару ключей с помощью ключа восстановления.","resetFailed":"Не удалось сбросить пару ключей"},"jwtSecretWarningTitle":"Уведомление о безопасности: Слабый JWT-секрет","jwtSecretWarningDesc":"Текущий JWT_SECRET содержит менее 32 символов, что создает потенциальную угрозу безопасности. Пожалуйста, настройте переменную окружения KEK_v1 перед ротацией JWT_SECRET, чтобы обеспечить возможность расшифровки существующих данных TOTP и ключей восстановления.","roleAdmin":"Администратор","roleUser":"Пользователь","personalInfo":"Личные данные","username":"Имя пользователя","userId":"ID пользователя","changePassword":"Смена пароля","currentPassword":"Текущий пароль","newPassword":"Новый пароль","updatePassword":"Обновить","userManagement":"Управление (Админ)","createUser":"Создать пользователя","role":"Роль","createdAt":"Дата создания","lastActive":"Последняя активность","lastResolve":"Последний запрос","actions":"Действия","dangerZone":"Опасная зона","clearLogs":"Очистить логи","clearLogsDesc":"Удалит все логи DNS. Необратимо.","currentDbSize":"Текущий размер базы данных","clearLogsBtn":"Очистить","deleteAccount":"Удалить аккаунт","deleteAccountDesc":"Удалит аккаунт и данные. Необратимо.","deleteAccountBtn":"Удалить","createNewUser":"Новый пользователь","initialPassword":"Пароль","userRole":"Роль","cancel":"Отмена","createNow":"Создать","formatTipUsername":"Формат: 5-32 символа (Unix-стиль)","formatTipPassword":"Формат: 12-100 символов","passwordSuccess":"Пароль изменен","updateFailed":"Ошибка","confirmDeleteUser":"Удалить пользователя?","confirmClearLogs":"Очистить логи?","clearLogsSuccess":"Логи очищены","confirmDeleteAccount":"ВНИМАНИЕ: Удалить аккаунт? \\\\n\\\\nВсе данные, правила и логи будут удалены. Необратимо!","inactivityPolicy":"Политика активности","inactivityDesc":"Для защиты конфиденциальности пользователей и экономии ресурсов все журналы запросов и конфигурации DNS аккаунтов, не проявлявших активности разрешения в течение 30 дней, будут автоматически удалены. Сам аккаунт будет удален при отсутствии входа в течение 90 дней.","systemSettings":"Система (Админ)","adminOnlyView":"Только для админов","featureToggle":"Переключатели","enableTurnstileSignup":"CAPTCHA при регистрации","enableTurnstileLogin":"CAPTCHA при входе","totp":{"title":"Приложение-аутентификатор (TOTP)","enabled":"Вкл","disabled":"Выкл","setupDesc":"Доп. защита. Используйте Google Authenticator и др.","setupBtn":"Настроить аутентификатор (TOTP)","scanQR":"Сканируйте QR:","orEnterManually":"Или введите ключ:","enterCode":"Введите код для подтверждения:","enterCodeHint":"6 цифр.","activate":"Активировать TOTP","skipPassword":"Вход без пароля (только TOTP)","disable":"Отключить аутентификатор (TOTP)","disableTitle":"Отключение аутентификатора (TOTP)","disableWarning":"После отключения TOTP, если не настроены ключи Passkey, ваш аккаунт будет защищен только паролем.","confirmDisable":"Отключить TOTP","recoveryKeysTitle":"Сохраните ключи восстановления","recoveryKeysWarning":"Сохраните ключ восстановления в надежном месте (например, в менеджере паролей). Каждый ключ одноразовый.","copyKeys":"Копировать","copied":"Скопировано!","done":"Готово","activeDesc":"Приложение аутентификатор (TOTP) активно и генерирует 6-значные коды.","invalidCode":"Пожалуйста, введите 6-значный код","testBtn":"Проверить код","testTitle":"Проверить приложение-аутентификатор (TOTP)","testDesc":"Введите 6-значный код подтверждения из приложения-аутентификатора, чтобы убедиться, что оно правильно синхронизировано и генерирует действительные коды.","testSuccess":"Проверка прошла успешно! Ваше приложение-аутентификатор настроено правильно.","testFailed":"Неверный код подтверждения или не синхронизировано время","verificationCode":"6-значный код подтверждения","verifyBtn":"Проверить сейчас"},"activity":{"title":"История активности","action":"Событие","time":"Время","ip":"IP","session":"Сессия","empty":"Пусто.","loadMore":"Еще","signup":"Регистрация","loginSuccess":"Успешный вход","loginFail":"Ошибка входа","logout":"Выход","pwChangeSuccess":"Пароль изменен","pwChangeFail":"Ошибка смены пароля","totpVerifySuccess":"Проверка TOTP пройдена","totpVerifyFail":"Ошибка проверки TOTP","totpSetup":"Настроен аутентификатор TOTP","totpRemoved":"Аутентификатор TOTP отключен","recoveryKeyUsed":"Ключ восстановления","sessionRevoked":"Сессия завершена","reasons":{"user_active":"Выход","user_revoke":"Отозвано","expired":"Истекло","geolocation_mismatch":"Гео-ограничение","geolocation_missing":"Нет данных гео","token_reuse":"Повторный вход (безопасность)"},"passkeyRegistered":"Passkey зарегистрирован","passkeyDeleted":"Passkey удален","passkeyVerifySuccess":"Passkey подтвержден","passkeyVerifyFail":"Ошибка верификации Passkey","recoveryKeyRotated":"Ключ восстановления обновлен","device":"Устройство"},"sessions":{"title":"Активные сеансы","session":"Сеанс","device":"Устройство","ip":"IP-адрес","loginTime":"Время входа","expires":"Истекает","action":"Действие","current":"Текущий","revoke":"Отозвать","revokeFailed":"Не удалось отозвать сеанс.","empty":"Активных сеансов не найдено.","unknownDevice":"Неизвестное устройство","revokeOthers":"Отозвать другие сеансы","confirmRevokeOthers":"Отозвать другие сеансы","revokeOthersConfirmText":"Вы уверены, что хотите отозвать все остальные активные сеансы? Все остальные подключенные устройства будут немедленно отключены."},"passkey":{"title":"Passkey (Ключи доступа)","addBtn":"Добавить Passkey","desc":"Используйте биометрию (Touch ID, Face ID, Windows Hello) или аппаратные ключи безопасности (YubiKey) для быстрой и защищенной от фишинга аутентификации.","empty":"Passkey еще не добавлены.","addTitle":"Добавить Passkey","nameLabel":"Имя Passkey","namePlaceholder":"например: my_passkey","promptNotice":"После нажатия кнопки продолжения браузер предложит пройти биометрическую регистрацию или использовать ключ безопасности.","continue":"Продолжить","renameTitle":"Переименовать Passkey","deleteTitle":"Удалить Passkey","deleteConfirm":"Вы уверены, что хотите удалить ключ доступа \\"{{name}}\\"? Вы больше не сможете использовать его для аутентификации.","defaultName":"Passkey","notSupported":"Ваш браузер или окружение не поддерживают ключи доступа (WebAuthn).","regFailed":"Не удалось зарегистрировать Passkey","count":"Настроено: {{count}}","none":"Не настроено","created":"Создано","lastUsed":"Последнее использование","neverUsed":"Никогда не использовался","updated":"Passkey успешно обновлен","registered":"Passkey успешно зарегистрирован","deleted":"Passkey удален","appNameLabel":"Домен","appNameHelper":"Ключ доступа будет привязан к этому домену","formatTip":"Имя Passkey: 1-30 символов (только буквы, цифры, _, -)","verifyPrompt":"Пройдите проверку с помощью биометрии устройства или ключа безопасности.","changePwNotice":"При отправке вам будет предложено подтвердить личность через Passkey.","submitNotice":"При отправке вам будет предложено подтвердить личность через Passkey.","testBtn":"Проверить ключ доступа","testThis":"Проверить этот ключ доступа","testSuccess":"Ключ доступа успешно проверен: {{name}}","testCancelled":"Проверка ключа доступа отменена","testFailed":"Ошибка проверки ключа доступа"},"mfaNone":"Нет","resetUserPassword":"Сбросить пароль","resetPasswordTitle":"Сброс пароля пользователя","resetPasswordWarning":"После сброса все активные сессии пользователя будут немедленно завершены.","resetPasswordSuccess":"Пароль успешно сброшен. Все активные сессии завершены.","confirmReset":"Подтвердить сброс","mfa":{"title":"Многофакторная аутентификация (MFA)","enabled":"Настроено","notConfigured":"Не настроено","desc":"Ключи доступа (Passkey) и приложения (TOTP) служат методами MFA. Настройте хотя бы один фактор для защиты аккаунта.","passwordlessTitle":"Беспарольный вход","passwordlessDesc":"При включении пароль не требуется — вход выполняется напрямую через Passkey или TOTP.","unconfiguredHint":"Методы MFA еще не настроены. Добавьте Passkey или приложение-аутентификатор ниже.","password":"Пароль","passkey":"Passkey","totp":"TOTP"},"recoveryKey":{"title":"Экстренный ключ восстановления","desc":"Экстренный ключ восстановления позволяет восстановить доступ к учетной записи или сбросить пароль в случае утери всех ключей доступа (Passkey) или приложений аутентификации. Для обеспечения безопасности с нулевым разглашением ключ хранится в виде необратимого криптографического хэша и не может быть просмотрен в открытом виде. После использования он немедленно обновляется автоматически.","configured":"Настроено","notConfigured":"Не настроено","rotateBtn":"Сменить ключ восстановления","yourKey":"Ваш 30-значный ключ восстановления","securityTip":"Храните этот ключ в секрете. Любой, у кого есть доступ к нему, может получить доступ к вашей учетной записи.","rotateConfirm":"Смена ключа немедленно аннулирует текущий ключ. Подтверждение разрешено только через существующий Passkey или исходный ключ восстановления. Продолжить?","confirmRotateBtn":"Продолжить смену","rotateVerifyTitle":"Подтверждение личности для смены ключа","rotateSuccess":"Ключ восстановления успешно обновлен! Сохраните новый ключ в надежном месте.","oneWayHash":"Необратимый криптографический хэш","oneWayTooltip":"Защищено односторонним хэшем SHA-256. Сервер не хранит открытый текст и не может его расшифровать.","newKeyGenerated":"Сгенерирован новый 30-значный ключ восстановления (Сохраните сейчас)","oneWayWarning":"Внимание: этот ключ отображается ТОЛЬКО ОДИН РАЗ. Сервер сохраняет только необратимый хэш. Скопируйте и сохраните его в надежном месте оффлайн.","autoRotatedTitle":"Ключ восстановления автоматически обновлен","autoRotatedNotice":"В соответствии с правилом однократного использования ваш предыдущий ключ был аннулирован, и сгенерирован новый 30-значный ключ. Скопируйте и надежно сохраните его оффлайн!","savedAndClose":"Я сохранил(а) ключ и продолжить","savedAndContinue":"Я сохранил(а) ключ и перейти в панель","descOneWay":"Экстренный ключ восстановления — это единственное средство для восстановления доступа к аккаунту и расшифровки данных E2EE в случае утери всех ключей Passkey или аутентификаторов. В целях безопасности с нулевым разглашением он хранится в виде необратимого хеша и не может быть просмотрен в открытом виде. Его можно заменить только с помощью текущего ключа восстановления или существующего Passkey. Ключ автоматически обновляется при использовании.","keyRequired":"Требуется ключ восстановления","verifyBtn":"Ключ восстановления","verifyPromptLabel":"Исходный ключ восстановления","rotateSuccessE2eeWarning":"Ключ восстановления успешно обновлен! Примечание: Поскольку E2EE ранее был заблокирован и его не удалось расшифровать, для старых данных по-прежнему требуется предыдущий ключ, или вы можете нажать «Сбросить пару ключей» ниже."},"verifyIdentity":"Проверка безопасности","verifyIdentityDesc":"Для защиты учетной записи подтвердите свою личность перед продолжением.","usePasskeyInstead":"Подтвердить с помощью Passkey","changePasswordWithPasskey":"Подтвердить через Passkey и изменить пароль","enableRegistration":"Разрешить регистрацию пользователей","timezone":"Часовой пояс","timezoneAuto":"Авто (по умолчанию в браузере)","totpCode":"Код аутентификатора"}`),d={item1Title:`Защищенный DNS Worker`,item1Desc:`DNS-резолвер на базе Cloudflare Workers & D1, оптимизированный для скорости и безопасности.`,item2Title:`Конфиденциальность DoH`,item2Desc:`Предотвращение перехвата и повышение конфиденциальности через DNS over HTTPS (RFC 8484).`,item3Title:`Глобальная Edge-сеть`,item3Desc:`Использование обширной сети Cloudflare для быстрого резолвинга с низкой задержкой.`,item4Title:`Точная фильтрация`,item4Desc:`Полная поддержка черных/белых списков, правил AdGuard и перенаправлений DNS-записей.`,item5Title:`Аналитика в реальном времени`,item5Desc:`Детальные логи запросов и комплексные дэшборды для мониторинга.`,item6Title:`Современный UI и темная тема`,item6Desc:`Высокоплотная панель управления, разработанная для инженеров, на базе Blueprint UI от Palantir.`,item7Title:`Облачная архитектура`,item7Desc:`Serverless-архитектура обеспечивает нулевое обслуживание и бесконечное масштабирование.`,item8Title:`Управление профилями`,item8Desc:`Создание и управление несколькими независимыми профилями для разных устройств.`,item9Title:`Управление точками доступа`,item9Desc:`Настройка отдельных DNS-точек для устройств для детальной изоляции логов и статистики.`},f={title:`Руководство по настройке и развертыванию DNS Worker`,subtitle:`Ваш экземпляр еще не полностью настроен. Пожалуйста, выполните следующие шаги, чтобы включить DNS Worker.`,dbStatus:`Связывание базы данных D1 (env.DB)`,jwtStatus:`Секретный ключ JWT (env.JWT_SECRET)`,configured:`Настроено`,missing:`Отсутствует / Не настроено`,refresh:`Обновить и проверить`,methodCli:`Развертывание через CLI`,methodDash:`Развертывание через панель Cloudflare`,copy:`Копировать`,copied:`Скопировано!`,stepDbTitle:`1. Привязать базу данных D1`,stepDbDesc:`Создайте базу данных D1 и привяжите ее к вашему воркеру. В терминале выполните:`,stepDbCommand1:`wrangler d1 create dns_worker_db`,stepDbDesc2:`Скопируйте вывод конфигурации D1 и вставьте его в wrangler.toml в раздел [[d1_databases]]:`,stepDbCommand2:`[[d1_databases]]
2
2
  binding = "DB"
3
3
  database_name = "dns_worker_db"
4
4
  database_id = "<ВАШ_D1_DATABASE_ID>"
@@ -0,0 +1 @@
1
+ import"./vendor-utils-BWSB0jGn.js";import{$ as e,$t as t,A as ee,At as n,B as r,Bt as i,Ct as a,D as o,Dt as s,Et as c,F as l,Ft as u,G as d,Gt as f,H as p,Ht as m,I as h,It as g,J as _,Jt as v,K as y,Kt as b,L as x,Lt as S,M as C,Mt as w,N as T,Nt as E,Ot as D,P as O,Pt as k,Q as A,Qt as j,R as M,Rt as N,St as P,Tt as F,U as I,Ut as L,V as R,Vt as z,W as B,Wt as V,X as H,Y as U,Z as W,_t as te,an as G,at as K,bt as q,cn as J,ct as Y,dt as X,en as Z,et as Q,ft as ne,gt as re,ht as ie,in as ae,it as $,j as oe,jt as se,k as ce,kt as le,lt as ue,mt as de,nn as fe,nt as pe,on as me,ot as he,pt as ge,q as _e,qt as ve,rn as ye,rt as be,sn as xe,st as Se,tn as Ce,tt as we,ut as Te,vt as Ee,wt as De,xt as Oe,yt as ke,z as Ae,zt as je}from"./index-dXN_pYn1.js";export{$ as exportProfileLogs};
@@ -0,0 +1 @@
1
+ var e=e=>typeof e==`string`,t=()=>{let e,t,n=new Promise((n,r)=>{e=n,t=r});return n.resolve=e,n.reject=t,n},n=e=>e==null?``:String(e),r=(e,t,n)=>{e.forEach(e=>{t[e]&&(n[e]=t[e])})},i=/###/g,a=e=>e&&e.includes(`###`)?e.replace(i,`.`):e,o=t=>!t||e(t),s=(t,n,r)=>{let i=e(n)?n.split(`.`):n,s=0;for(;s<i.length-1;){if(o(t))return{};let e=a(i[s]);!t[e]&&r&&(t[e]=new r),t=Object.prototype.hasOwnProperty.call(t,e)?t[e]:{},++s}return o(t)?{}:{obj:t,k:a(i[s])}},c=(e,t,n)=>{let{obj:r,k:i}=s(e,t,Object);if(r!==void 0||t.length===1){r[i]=n;return}let a=t[t.length-1],o=t.slice(0,t.length-1),c=s(e,o,Object);for(;c.obj===void 0&&o.length;)a=`${o[o.length-1]}.${a}`,o=o.slice(0,o.length-1),c=s(e,o,Object),c?.obj&&c.obj[`${c.k}.${a}`]!==void 0&&(c.obj=void 0);c.obj[`${c.k}.${a}`]=n},l=(e,t,n,r)=>{let{obj:i,k:a}=s(e,t,Object);i[a]=i[a]||[],i[a].push(n)},u=(e,t)=>{let{obj:n,k:r}=s(e,t);if(n&&Object.prototype.hasOwnProperty.call(n,r))return n[r]},d=(e,t,n)=>{let r=u(e,n);return r===void 0?u(t,n):r},f=(t,n,r)=>{for(let i in n)i!==`__proto__`&&i!==`constructor`&&(Object.prototype.hasOwnProperty.call(t,i)?e(t[i])||t[i]instanceof String||e(n[i])||n[i]instanceof String?r&&(t[i]=n[i]):f(t[i],n[i],r):t[i]=n[i]);return t},p=e=>e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,`\\$&`),m={"&":`&amp;`,"<":`&lt;`,">":`&gt;`,'"':`&quot;`,"'":`&#39;`,"/":`&#x2F;`},h=t=>e(t)?t.replace(/[&<>"'\/]/g,e=>m[e]):t,g=class{constructor(e){this.capacity=e,this.regExpMap=new Map,this.regExpQueue=[]}getRegExp(e){let t=this.regExpMap.get(e);if(t!==void 0)return t;let n=new RegExp(e);return this.regExpQueue.length===this.capacity&&this.regExpMap.delete(this.regExpQueue.shift()),this.regExpMap.set(e,n),this.regExpQueue.push(e),n}},_=[` `,`,`,`?`,`!`,`;`],v=new g(20),y=(e,t,n)=>{t||=``,n||=``;let r=_.filter(e=>!t.includes(e)&&!n.includes(e));if(r.length===0)return!0;let i=v.getRegExp(`(${r.map(e=>e===`?`?`\\?`:e).join(`|`)})`),a=!i.test(e);if(!a){let t=e.indexOf(n);t>0&&!i.test(e.substring(0,t))&&(a=!0)}return a},b=(e,t,n=`.`)=>{if(!e)return;if(e[t])return Object.prototype.hasOwnProperty.call(e,t)?e[t]:void 0;let r=t.split(n),i=e;for(let e=0;e<r.length;){if(!i||typeof i!=`object`)return;let t,a=``;for(let o=e;o<r.length;++o)if(o!==e&&(a+=n),a+=r[o],t=i[a],t!==void 0){if([`string`,`number`,`boolean`].includes(typeof t)&&o<r.length-1)continue;e+=o-e+1;break}i=t}return i},x=e=>e?.replace(/_/g,`-`),S={type:`logger`,log(e){this.output(`log`,e)},warn(e){this.output(`warn`,e)},error(e){this.output(`error`,e)},output(e,t){console?.[e]?.apply?.(console,t)}},C=new class t{constructor(e,t={}){this.init(e,t)}init(e,t={}){this.prefix=t.prefix||`i18next:`,this.logger=e||S,this.options=t,this.debug=t.debug}log(...e){return this.forward(e,`log`,``,!0)}warn(...e){return this.forward(e,`warn`,``,!0)}error(...e){return this.forward(e,`error`,``)}deprecate(...e){return this.forward(e,`warn`,`WARNING DEPRECATED: `,!0)}forward(t,n,r,i){return i&&!this.debug?null:(t=t.map(t=>e(t)?t.replace(/[\r\n\x00-\x1F\x7F]/g,` `):t),e(t[0])&&(t[0]=`${r}${this.prefix} ${t[0]}`),this.logger[n](t))}create(e){return new t(this.logger,{prefix:`${this.prefix}:${e}:`,...this.options})}clone(e){return e||=this.options,e.prefix=e.prefix||this.prefix,new t(this.logger,e)}},w=class{constructor(){this.observers={}}on(e,t){return e.split(` `).forEach(e=>{this.observers[e]||(this.observers[e]=new Map);let n=this.observers[e].get(t)||0;this.observers[e].set(t,n+1)}),this}off(e,t){if(this.observers[e]){if(!t){delete this.observers[e];return}this.observers[e].delete(t)}}once(e,t){let n=(...r)=>{t(...r),this.off(e,n)};return this.on(e,n),this}emit(e,...t){this.observers[e]&&Array.from(this.observers[e].entries()).forEach(([e,n])=>{for(let r=0;r<n;r++)e(...t)}),this.observers[`*`]&&Array.from(this.observers[`*`].entries()).forEach(([n,r])=>{for(let i=0;i<r;i++)n(e,...t)})}},T=new WeakSet,E=class extends w{constructor(e,t={ns:[`translation`],defaultNS:`translation`}){super(),this.data=e||{},this.options=t,this.options.keySeparator===void 0&&(this.options.keySeparator=`.`),this.options.ignoreJSONStructure===void 0&&(this.options.ignoreJSONStructure=!0)}addNamespaces(e){this.options.ns.includes(e)||this.options.ns.push(e)}removeNamespaces(e){let t=this.options.ns.indexOf(e);t>-1&&this.options.ns.splice(t,1)}getResource(t,n,r,i={}){let a=i.keySeparator===void 0?this.options.keySeparator:i.keySeparator,o=i.ignoreJSONStructure===void 0?this.options.ignoreJSONStructure:i.ignoreJSONStructure,s;t.includes(`.`)?s=t.split(`.`):(s=[t,n],r&&(Array.isArray(r)?s.push(...r):e(r)&&a?s.push(...r.split(a)):s.push(r)));let c=u(this.data,s);return!c&&!n&&!r&&t.includes(`.`)&&(t=s[0],n=s[1],r=s.slice(2).join(`.`)),c||!o||!e(r)?c:b(this.data?.[t]?.[n],r,a)}addResource(e,t,n,r,i={silent:!1}){let a=i.keySeparator===void 0?this.options.keySeparator:i.keySeparator,o=[e,t];n&&(o=o.concat(a?n.split(a):n)),e.includes(`.`)&&(o=e.split(`.`),r=t,t=o[1]),this.addNamespaces(t),c(this.data,o,r),i.silent||this.emit(`added`,e,t,n,r)}addResources(t,n,r,i={silent:!1}){for(let i in r)(e(r[i])||Array.isArray(r[i]))&&this.addResource(t,n,i,r[i],{silent:!0});i.silent||this.emit(`added`,t,n,r)}addResourceBundle(e,t,n,r,i,a={silent:!1,skipCopy:!1}){let o=[e,t];e.includes(`.`)&&(o=e.split(`.`),r=n,n=t,t=o[1]),this.addNamespaces(t);let s=u(this.data,o)||{};a.skipCopy||(n=JSON.parse(JSON.stringify(n))),r?f(s,n,i):s={...s,...n},c(this.data,o,s),a.silent||this.emit(`added`,e,t,n)}removeResourceBundle(e,t){this.hasResourceBundle(e,t)&&delete this.data[e][t],this.removeNamespaces(t),this.emit(`removed`,e,t)}hasResourceBundle(e,t){return this.getResource(e,t)!==void 0}getResourceBundle(e,t){return t||=this.options.defaultNS,this.getResource(e,t)}getDataByLanguage(e){return this.data[e]}hasLanguageSomeTranslations(e){let t=this.getDataByLanguage(e);return!!(t&&Object.keys(t)||[]).find(e=>{let n=t[e];return T.has(n)?!0:!n||Object.keys(n).length===0?!1:(typeof n==`object`&&T.add(n),!0)})}toJSON(){return this.data}},D={processors:{},addPostProcessor(e){this.processors[e.name]=e},handle(e,t,n,r,i){return e.forEach(e=>{t=this.processors[e]?.process(t,n,r,i)??t}),t}},O=Symbol(`i18next/PATH_KEY`);function k(){let e=[],t=Object.create(null),n;return t.get=(r,i)=>(n?.revoke?.(),i===O?e:(e.push(i),n=Proxy.revocable(r,t),n.proxy)),Proxy.revocable(Object.create(null),t).proxy}function A(e,t){let{[O]:n}=e(k()),r=t?.keySeparator??`.`,i=t?.nsSeparator??`:`,a=t?.enableSelector===`strict`;if(n.length>1&&i){let e=t?.ns,o=a?Array.isArray(e)?e:e?[e]:null:Array.isArray(e)?e:null;if(o&&(a?o:o.length>1?o.slice(1):[]).includes(n[0]))return`${n[0]}${i}${n.slice(1).join(r)}`}return n.join(r)}var j=t=>!e(t)&&typeof t!=`boolean`&&typeof t!=`number`,M=class t extends w{constructor(e,t={}){super(),r([`resourceStore`,`languageUtils`,`pluralResolver`,`interpolator`,`backendConnector`,`i18nFormat`,`utils`],e,this),this.options=t,this.options.keySeparator===void 0&&(this.options.keySeparator=`.`),this.logger=C.create(`translator`),this.checkedLoadedFor={}}changeLanguage(e){e&&(this.language=e)}exists(e,t={interpolation:{}}){let n={...t};if(e==null)return!1;let r=this.resolve(e,n);if(r?.res===void 0)return!1;let i=j(r.res);return!(n.returnObjects===!1&&i)}extractFromKey(t,n){let r=n.nsSeparator===void 0?this.options.nsSeparator:n.nsSeparator;r===void 0&&(r=`:`);let i=n.keySeparator===void 0?this.options.keySeparator:n.keySeparator,a=n.ns||this.options.defaultNS||[],o=r&&t.includes(r),s=!this.options.userDefinedKeySeparator&&!n.keySeparator&&!this.options.userDefinedNsSeparator&&!n.nsSeparator&&!y(t,r,i);if(o&&!s){let n=t.match(this.interpolator.nestingRegexp);if(n&&n.length>0)return{key:t,namespaces:e(a)?[a]:a};let o=t.split(r);(r!==i||r===i&&this.options.ns.includes(o[0]))&&(a=o.shift()),t=o.join(i)}return{key:t,namespaces:e(a)?[a]:a}}translate(n,r,i){let a=typeof r==`object`?{...r}:r;if(typeof a!=`object`&&this.options.overloadTranslationOptionHandler&&(a=this.options.overloadTranslationOptionHandler(arguments)),typeof a==`object`&&(a={...a}),a||={},n==null)return``;typeof n==`function`&&(n=A(n,{...this.options,...a})),Array.isArray(n)||(n=[String(n)]),n=n.map(e=>typeof e==`function`?A(e,{...this.options,...a}):String(e));let o=a.returnDetails===void 0?this.options.returnDetails:a.returnDetails,s=a.keySeparator===void 0?this.options.keySeparator:a.keySeparator,{key:c,namespaces:l}=this.extractFromKey(n[n.length-1],a),u=l[l.length-1],d=a.nsSeparator===void 0?this.options.nsSeparator:a.nsSeparator;d===void 0&&(d=`:`);let f=a.lng||this.language,p=a.appendNamespaceToCIMode||this.options.appendNamespaceToCIMode;if(f?.toLowerCase()===`cimode`)return p?o?{res:`${u}${d}${c}`,usedKey:c,exactUsedKey:c,usedLng:f,usedNS:u,usedParams:this.getUsedParamsDetails(a)}:`${u}${d}${c}`:o?{res:c,usedKey:c,exactUsedKey:c,usedLng:f,usedNS:u,usedParams:this.getUsedParamsDetails(a)}:c;let m=this.resolve(n,a),h=m?.res,g=m?.usedKey||c,_=m?.exactUsedKey||c,v=[`[object Number]`,`[object Function]`,`[object RegExp]`],y=a.joinArrays===void 0?this.options.joinArrays:a.joinArrays,b=!this.i18nFormat||this.i18nFormat.handleAsObject,x=a.count!==void 0&&!e(a.count),S=t.hasDefaultValue(a),C=x?this.pluralResolver.getSuffix(f,a.count,a):``,w=a.ordinal&&x?this.pluralResolver.getSuffix(f,a.count,{ordinal:!1}):``,T=x&&!a.ordinal&&a.count===0,E=T&&a[`defaultValue${this.options.pluralSeparator}zero`]||a[`defaultValue${C}`]||a[`defaultValue${w}`]||a.defaultValue,D=h;b&&!h&&S&&(D=E);let O=j(D),k=Object.prototype.toString.apply(D);if(b&&D&&O&&!v.includes(k)&&!(e(y)&&Array.isArray(D))){if(!a.returnObjects&&!this.options.returnObjects){this.options.returnedObjectHandler||this.logger.warn(`accessing an object - but returnObjects options is not enabled!`);let e=this.options.returnedObjectHandler?this.options.returnedObjectHandler(g,D,{...a,ns:l}):`key '${c} (${this.language})' returned an object instead of string.`;return o?(m.res=e,m.usedParams=this.getUsedParamsDetails(a),m):e}if(s){let e=Array.isArray(D),t=e?[]:{},n=e?_:g;for(let e in D)if(Object.prototype.hasOwnProperty.call(D,e)){let r=`${n}${s}${e}`;S&&!h?t[e]=this.translate(r,{...a,defaultValue:j(E)?E[e]:void 0,joinArrays:!1,ns:l}):t[e]=this.translate(r,{...a,joinArrays:!1,ns:l}),t[e]===r&&(t[e]=D[e])}h=t}}else if(b&&e(y)&&Array.isArray(h))h=h.join(y),h&&=this.extendTranslation(h,n,a,i);else{let e=!1,t=!1;!this.isValidLookup(h)&&S&&(e=!0,h=E),this.isValidLookup(h)||(t=!0,h=c);let r=(a.missingKeyNoValueFallbackToKey||this.options.missingKeyNoValueFallbackToKey)&&t?void 0:h,o=S&&E!==h&&this.options.updateMissing;if(t||e||o){if(this.logger.log(o?`updateKey`:`missingKey`,f,u,x&&!o?`${c}${this.pluralResolver.getSuffix(f,a.count,a)}`:c,o?E:h),s){let e=this.resolve(c,{...a,keySeparator:!1});e&&e.res&&this.logger.warn(`Seems the loaded translations were in flat JSON format instead of nested. Either set keySeparator: false on init or make sure your translations are published in nested format.`)}let e=[],t=this.languageUtils.getFallbackCodes(this.options.fallbackLng,a.lng||this.language);if(this.options.saveMissingTo===`fallback`&&t&&t[0])for(let n=0;n<t.length;n++)e.push(t[n]);else this.options.saveMissingTo===`all`?e=this.languageUtils.toResolveHierarchy(a.lng||this.language):e.push(a.lng||this.language);let n=(e,t,n)=>{let i=S&&n!==h?n:r;this.options.missingKeyHandler?this.options.missingKeyHandler(e,u,t,i,o,a):this.backendConnector?.saveMissing&&this.backendConnector.saveMissing(e,u,t,i,o,a),this.emit(`missingKey`,e,u,t,h)};this.options.saveMissing&&(this.options.saveMissingPlurals&&x?e.forEach(e=>{let t=this.pluralResolver.getSuffixes(e,a);T&&a[`defaultValue${this.options.pluralSeparator}zero`]&&!t.includes(`${this.options.pluralSeparator}zero`)&&t.push(`${this.options.pluralSeparator}zero`),t.forEach(t=>{n([e],c+t,a[`defaultValue${t}`]||E)})}):n(e,c,E))}h=this.extendTranslation(h,n,a,m,i),t&&h===c&&this.options.appendNamespaceToMissingKey&&(h=`${u}${d}${c}`),(t||e)&&this.options.parseMissingKeyHandler&&(h=this.options.parseMissingKeyHandler(this.options.appendNamespaceToMissingKey?`${u}${d}${c}`:c,e?h:void 0,a))}return o?(m.res=h,m.usedParams=this.getUsedParamsDetails(a),m):h}extendTranslation(t,n,r,i,a){if(this.i18nFormat?.parse)t=this.i18nFormat.parse(t,{...this.options.interpolation.defaultVariables,...r},r.lng||this.language||i.usedLng,i.usedNS,i.usedKey,{resolved:i});else if(!r.skipInterpolation){r.interpolation&&this.interpolator.init({...r,interpolation:{...this.options.interpolation,...r.interpolation}});let o=e(t)&&(r?.interpolation?.skipOnVariables===void 0?this.options.interpolation.skipOnVariables:r.interpolation.skipOnVariables),s;if(o){let e=t.match(this.interpolator.nestingRegexp);s=e&&e.length}let c=r.replace&&!e(r.replace)?r.replace:r;if(this.options.interpolation.defaultVariables&&(c={...this.options.interpolation.defaultVariables,...c}),t=this.interpolator.interpolate(t,c,r.lng||this.language||i.usedLng,r),o){let e=t.match(this.interpolator.nestingRegexp),n=e&&e.length;s<n&&(r.nest=!1)}!r.lng&&i&&i.res&&(r.lng=this.language||i.usedLng),r.nest!==!1&&(t=this.interpolator.nest(t,(...e)=>a?.[0]===e[0]&&!r.context?(this.logger.warn(`It seems you are nesting recursively key: ${e[0]} in key: ${n[0]}`),null):this.translate(...e,n),r)),r.interpolation&&this.interpolator.reset()}let o=r.postProcess||this.options.postProcess,s=e(o)?[o]:o;return t!=null&&s?.length&&r.applyPostProcessor!==!1&&(t=D.handle(s,t,n,this.options&&this.options.postProcessPassResolved?{i18nResolved:{...i,usedParams:this.getUsedParamsDetails(r)},...r}:r,this)),t}resolve(t,n={}){let r,i,a,o,s;return e(t)&&(t=[t]),Array.isArray(t)&&(t=t.map(e=>typeof e==`function`?A(e,{...this.options,...n}):e)),t.forEach(t=>{if(this.isValidLookup(r))return;let c=this.extractFromKey(t,n),l=c.key;i=l;let u=c.namespaces;this.options.fallbackNS&&(u=u.concat(this.options.fallbackNS));let d=n.count!==void 0&&!e(n.count),f=d&&!n.ordinal&&n.count===0,p=n.context!==void 0&&(e(n.context)||typeof n.context==`number`)&&n.context!==``,m=n.lngs?n.lngs:this.languageUtils.toResolveHierarchy(n.lng||this.language,n.fallbackLng);u.forEach(e=>{this.isValidLookup(r)||(s=e,!this.checkedLoadedFor[`${m[0]}-${e}`]&&this.utils?.hasLoadedNamespace&&!this.utils?.hasLoadedNamespace(s)&&(this.checkedLoadedFor[`${m[0]}-${e}`]=!0,this.logger.warn(`key "${i}" for languages "${m.join(`, `)}" won't get resolved as namespace "${s}" was not yet loaded`,`This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!`)),m.forEach(t=>{if(this.isValidLookup(r))return;o=t;let i=[l];if(this.i18nFormat?.addLookupKeys)this.i18nFormat.addLookupKeys(i,l,t,e,n);else{let e;d&&(e=this.pluralResolver.getSuffix(t,n.count,n));let r=`${this.options.pluralSeparator}zero`,a=`${this.options.pluralSeparator}ordinal${this.options.pluralSeparator}`;if(d&&(n.ordinal&&e.startsWith(a)&&i.push(l+e.replace(a,this.options.pluralSeparator)),i.push(l+e),f&&i.push(l+r)),p){let t=`${l}${this.options.contextSeparator||`_`}${n.context}`;i.push(t),d&&(n.ordinal&&e.startsWith(a)&&i.push(t+e.replace(a,this.options.pluralSeparator)),i.push(t+e),f&&i.push(t+r))}}let s;for(;s=i.pop();)this.isValidLookup(r)||(a=s,r=this.getResource(t,e,s,n))}))})}),{res:r,usedKey:i,exactUsedKey:a,usedLng:o,usedNS:s}}isValidLookup(e){return e!==void 0&&!(!this.options.returnNull&&e===null)&&!(!this.options.returnEmptyString&&e===``)}getResource(e,t,n,r={}){return this.i18nFormat?.getResource?this.i18nFormat.getResource(e,t,n,r):this.resourceStore.getResource(e,t,n,r)}getUsedParamsDetails(t={}){let n=[`defaultValue`,`ordinal`,`context`,`replace`,`lng`,`lngs`,`fallbackLng`,`ns`,`keySeparator`,`nsSeparator`,`returnObjects`,`returnDetails`,`joinArrays`,`postProcess`,`interpolation`],r=t.replace&&!e(t.replace),i=r?t.replace:t;if(r&&t.count!==void 0&&(i={...i,count:t.count}),this.options.interpolation.defaultVariables&&(i={...this.options.interpolation.defaultVariables,...i}),!r){i={...i};for(let e of n)delete i[e]}return i}static hasDefaultValue(e){for(let t in e)if(Object.prototype.hasOwnProperty.call(e,t)&&t.startsWith(`defaultValue`)&&e[t]!==void 0)return!0;return!1}},N=class{constructor(e){this.options=e,this.supportedLngs=this.options.supportedLngs||!1,this.logger=C.create(`languageUtils`),this.resolveHierarchyCache={}}clearCache(){this.resolveHierarchyCache={}}getScriptPartFromCode(e){if(e=x(e),!e||!e.includes(`-`))return null;let t=e.split(`-`);return t.length===2||(t.pop(),t[t.length-1].toLowerCase()===`x`)?null:this.formatLanguageCode(t.join(`-`))}getLanguagePartFromCode(e){if(e=x(e),!e||!e.includes(`-`))return e;let t=e.split(`-`);return this.formatLanguageCode(t[0])}formatLanguageCode(t){if(e(t)&&t.includes(`-`)){let e;try{e=Intl.getCanonicalLocales(t)[0]}catch{}return e&&this.options.lowerCaseLng&&(e=e.toLowerCase()),e||(this.options.lowerCaseLng?t.toLowerCase():t)}return this.options.cleanCode||this.options.lowerCaseLng?t.toLowerCase():t}isSupportedCode(e){return(this.options.load===`languageOnly`||this.options.nonExplicitSupportedLngs)&&(e=this.getLanguagePartFromCode(e)),!this.supportedLngs||!this.supportedLngs.length||this.supportedLngs.includes(e)}getBestMatchFromCodes(e){if(!e)return null;let t;return e.forEach(e=>{if(t)return;let n=this.formatLanguageCode(e);(!this.options.supportedLngs||this.isSupportedCode(n))&&(t=n)}),!t&&this.options.supportedLngs&&e.forEach(e=>{if(t)return;let n=this.getScriptPartFromCode(e);if(this.isSupportedCode(n))return t=n;let r=this.getLanguagePartFromCode(e);if(this.isSupportedCode(r))return t=r;t=this.options.supportedLngs.find(e=>e===r?!0:!e.includes(`-`)&&!r.includes(`-`)?!1:!!(e.includes(`-`)&&!r.includes(`-`)&&e.slice(0,e.indexOf(`-`))===r||e.startsWith(r)&&r.length>1))}),t||=this.getFallbackCodes(this.options.fallbackLng)[0],t}getFallbackCodes(t,n){if(!t)return[];if(typeof t==`function`&&(t=t(n)),e(t)&&(t=[t]),Array.isArray(t))return t;if(!n)return t.default||[];let r=t[n];return r||=t[this.getScriptPartFromCode(n)],r||=t[this.formatLanguageCode(n)],r||=t[this.getLanguagePartFromCode(n)],r||=t.default,r||[]}toResolveHierarchy(t,n){let r=this.options.fallbackLng,i=Array.isArray(r)?r.join(`|`):r;i!==this._cachedFallbackLng&&(this.resolveHierarchyCache={},this._cachedFallbackLng=i);let a=n===void 0||n===!1||e(n),o=n===void 0&&typeof this.options.fallbackLng==`function`,s=e(t)&&a&&!o,c=null;if(s){let e;e=n===void 0?`undefined`:n===!1?`boolean:false`:`string:${n}`,c=`${t.length}:${t}|${e}`}if(c!==null){let e=this.resolveHierarchyCache[c];if(e!==void 0)return e.slice()}let l=this.getFallbackCodes((n===!1?[]:n)||this.options.fallbackLng||[],t),u=[],d=e=>{e&&(this.isSupportedCode(e)?u.push(e):this.logger.warn(`rejecting language code not found in supportedLngs: ${e}`))};return e(t)&&(t.includes(`-`)||t.includes(`_`))?(this.options.load!==`languageOnly`&&d(this.formatLanguageCode(t)),this.options.load!==`languageOnly`&&this.options.load!==`currentOnly`&&d(this.getScriptPartFromCode(t)),this.options.load!==`currentOnly`&&d(this.getLanguagePartFromCode(t))):e(t)&&d(this.formatLanguageCode(t)),l.forEach(e=>{u.includes(e)||d(this.formatLanguageCode(e))}),c===null?u:(this.resolveHierarchyCache[c]=u,u.slice())}},P={zero:0,one:1,two:2,few:3,many:4,other:5},F={select:e=>e===1?`one`:`other`,resolvedOptions:()=>({pluralCategories:[`one`,`other`]})},ee=class{constructor(e,t={}){this.languageUtils=e,this.options=t,this.logger=C.create(`pluralResolver`),this.pluralRulesCache={}}clearCache(){this.pluralRulesCache={}}getRule(e,t={}){let n=x(e===`dev`?`en`:e),r=t.ordinal?`ordinal`:`cardinal`,i=JSON.stringify({cleanedCode:n,type:r});if(i in this.pluralRulesCache)return this.pluralRulesCache[i];let a;try{a=new Intl.PluralRules(n,{type:r})}catch{if(typeof Intl>`u`)return this.logger.error(`No Intl support, please use an Intl polyfill!`),F;if(!e.match(/-|_/))return F;let n=this.languageUtils.getLanguagePartFromCode(e);a=this.getRule(n,t)}return this.pluralRulesCache[i]=a,a}needsPlural(e,t={}){let n=this.getRule(e,t);return n||=this.getRule(`dev`,t),n?.resolvedOptions().pluralCategories.length>1}getPluralFormsOfKey(e,t,n={}){return this.getSuffixes(e,n).map(e=>`${t}${e}`)}getSuffixes(e,t={}){let n=this.getRule(e,t);return n||=this.getRule(`dev`,t),n?n.resolvedOptions().pluralCategories.sort((e,t)=>P[e]-P[t]).map(e=>`${this.options.prepend}${t.ordinal?`ordinal${this.options.prepend}`:``}${e}`):[]}getSuffix(e,t,n={}){let r=this.getRule(e,n);return r?`${this.options.prepend}${n.ordinal?`ordinal${this.options.prepend}`:``}${r.select(t)}`:(this.logger.warn(`no plural rule found for: ${e}`),this.getSuffix(`dev`,t,n))}},I=(t,n,r,i=`.`,a=!0)=>{let o=d(t,n,r);return!o&&a&&e(r)&&(o=b(t,r,i),o===void 0&&(o=b(n,r,i))),o},L=e=>e.replace(/\$/g,`$$$$`),R=class{constructor(e={}){this.logger=C.create(`interpolator`),this.options=e,this.format=e?.interpolation?.format||(e=>e),this.init(e)}init(e={}){e.interpolation||={escapeValue:!0};let{escape:t,escapeValue:n,useRawValueToEscape:r,prefix:i,prefixEscaped:a,suffix:o,suffixEscaped:s,formatSeparator:c,unescapeSuffix:l,unescapePrefix:u,nestingPrefix:d,nestingPrefixEscaped:f,nestingSuffix:m,nestingSuffixEscaped:g,nestingOptionsSeparator:_,maxReplaces:v,alwaysFormat:y}=e.interpolation;this.escape=t===void 0?h:t,this.escapeValue=n===void 0?!0:n,this.useRawValueToEscape=r===void 0?!1:r,this.prefix=i?p(i):a||`{{`,this.suffix=o?p(o):s||`}}`,this.formatSeparator=c||`,`,this.unescapePrefix=l?``:u?p(u):`-`,this.unescapeSuffix=this.unescapePrefix?``:l?p(l):``,this.nestingPrefix=d?p(d):f||p(`$t(`),this.nestingSuffix=m?p(m):g||p(`)`),this.nestingOptionsSeparator=_||`,`,this.maxReplaces=v||1e3,this.alwaysFormat=y===void 0?!1:y,this.resetRegExp()}reset(){this.options&&this.init(this.options)}resetRegExp(){let e=(e,t)=>e?.source===t?(e.lastIndex=0,e):new RegExp(t,`g`);this.regexp=e(this.regexp,`${this.prefix}(.+?)${this.suffix}`),this.regexpUnescape=e(this.regexpUnescape,`${this.prefix}${this.unescapePrefix}(.+?)${this.unescapeSuffix}${this.suffix}`),this.nestingRegexp=e(this.nestingRegexp,`${this.nestingPrefix}((?:[^()"']+|"[^"]*"|'[^']*'|\\((?:[^()]|"[^"]*"|'[^']*')*\\))*?)${this.nestingSuffix}`)}interpolate(t,r,i,a){let o,s,c,l=this.options&&this.options.interpolation&&this.options.interpolation.defaultVariables||{},u=e=>{if(!e.includes(this.formatSeparator)){let t=I(r,l,e,this.options.keySeparator,this.options.ignoreJSONStructure);return this.alwaysFormat?this.format(t,void 0,i,{...a,...r,interpolationkey:e}):t}let t=e.split(this.formatSeparator),n=t.shift().trim(),o=t.join(this.formatSeparator).trim();return this.format(I(r,l,n,this.options.keySeparator,this.options.ignoreJSONStructure),o,i,{...a,...r,interpolationkey:n})};this.resetRegExp(),!this.escapeValue&&typeof t==`string`&&/\$t\([^)]*\{[^}]*\{\{/.test(t)&&this.logger.warn(`nesting options string contains interpolated variables with escapeValue: false — if any of those values are attacker-controlled they can inject additional nesting options (e.g. redirect lng/ns). Sanitise untrusted input before passing it to t(), or keep escapeValue: true.`);let d=a?.missingInterpolationHandler||this.options.missingInterpolationHandler,f=a?.interpolation?.skipOnVariables===void 0?this.options.interpolation.skipOnVariables:a.interpolation.skipOnVariables;return[{regex:this.regexpUnescape,safeValue:e=>e},{regex:this.regexp,safeValue:e=>this.escapeValue?this.escape(e):e}].forEach(r=>{for(c=0;o=r.regex.exec(t);){let i=o[1].trim();if(s=u(i),s===void 0)if(typeof d==`function`){let n=d(t,o,a);s=e(n)?n:``}else if(a&&Object.prototype.hasOwnProperty.call(a,i))s=``;else if(f){s=o[0];continue}else this.logger.warn(`missed to pass in variable ${i} for interpolating ${t}`),s=``;else !e(s)&&!this.useRawValueToEscape&&(s=n(s));let l=r.safeValue(s);if(t=t.replace(o[0],L(l)),f?(r.regex.lastIndex+=l.length,r.regex.lastIndex-=o[0].length):r.regex.lastIndex=0,c++,c>=this.maxReplaces)break}}),t}nest(t,r,i={}){let a,o,s,c=(e,t)=>{let n=this.nestingOptionsSeparator;if(!e.includes(n))return e;let r=e.split(RegExp(`${p(n)}[ ]*{`)),i=`{${r[1]}`;e=r[0],i=this.interpolate(i,s);let a=i.match(/'/g),o=i.match(/"/g);((a?.length??0)%2==0&&!o||(o?.length??0)%2!=0)&&(i=i.replace(/'/g,`"`));try{s=JSON.parse(i),t&&(s={...t,...s})}catch(t){return this.logger.warn(`failed parsing options string in nesting for key ${e}`,t),`${e}${n}${i}`}return s.defaultValue&&s.defaultValue.includes(this.prefix)&&delete s.defaultValue,e};for(;a=this.nestingRegexp.exec(t);){let l=[];s={...i},s=s.replace&&!e(s.replace)?s.replace:s,s.applyPostProcessor=!1,delete s.defaultValue;let u=/{.*}/s.test(a[1])?a[1].lastIndexOf(`}`)+1:a[1].indexOf(this.formatSeparator);if(u!==-1&&(l=a[1].slice(u).split(this.formatSeparator).map(e=>e.trim()).filter(Boolean),a[1]=a[1].slice(0,u)),o=r(c.call(this,a[1].trim(),s),s),o&&a[0]===t&&!e(o))return o;e(o)||(o=n(o)),o||=(this.logger.warn(`missed to resolve ${a[1]} for nesting ${t}`),``),l.length&&(o=l.reduce((e,t)=>this.format(e,t,i.lng,{...i,interpolationkey:a[1].trim()}),o.trim())),t=t.replace(a[0],L(n(o))),this.regexp.lastIndex=0}return t}},te=e=>{let t=e.toLowerCase().trim(),n={};if(e.includes(`(`)){let r=e.split(`(`);t=r[0].toLowerCase().trim();let i=r[1].slice(0,-1);t===`currency`&&!i.includes(`:`)?n.currency||=i.trim():t===`relativetime`&&!i.includes(`:`)?n.range||=i.trim():i.split(`;`).forEach(e=>{if(e){let[t,...r]=e.split(`:`),i=r.join(`:`).trim().replace(/^'+|'+$/g,``),a=t.trim();n[a]||(n[a]=i),i===`false`&&(n[a]=!1),i===`true`&&(n[a]=!0),isNaN(i)||(n[a]=parseInt(i,10))}})}return{formatName:t,formatOptions:n}},z=e=>{let t={};return(n,r,i)=>{let a=i;i&&i.interpolationkey&&i.formatParams&&i.formatParams[i.interpolationkey]&&i[i.interpolationkey]&&(a={...a,[i.interpolationkey]:void 0});let o=r+JSON.stringify(a),s=t[o];return s||(s=e(x(r),i),t[o]=s),s(n)}},ne=e=>(t,n,r)=>e(x(n),r)(t),re=class{constructor(e={}){this.logger=C.create(`formatter`),this.options=e,this.init(e)}init(e,t={interpolation:{}}){this.formatSeparator=t.interpolation.formatSeparator||`,`;let n=t.cacheInBuiltFormats?z:ne;this.formats={number:n((e,t)=>{let n=new Intl.NumberFormat(e,{...t});return e=>n.format(e)}),currency:n((e,t)=>{let n=new Intl.NumberFormat(e,{...t,style:`currency`});return e=>n.format(e)}),datetime:n((e,t)=>{let n=new Intl.DateTimeFormat(e,{...t});return e=>n.format(e)}),relativetime:n((e,t)=>{let n=new Intl.RelativeTimeFormat(e,{...t});return e=>n.format(e,t.range||`day`)}),list:n((e,t)=>{let n=new Intl.ListFormat(e,{...t});return e=>n.format(e)})}}add(e,t){this.formats[e.toLowerCase().trim()]=t}addCached(e,t){this.formats[e.toLowerCase().trim()]=z(t)}format(e,t,n,r={}){if(!t||e==null)return e;let i=t.split(this.formatSeparator),a=[];for(let e=0;e<i.length;e++){let t=i[e];for(;t.indexOf(`(`)>-1&&!t.includes(`)`)&&e+1<i.length;)t=`${t}${this.formatSeparator}${i[++e]}`;a.push(t)}return a.reduce((e,t)=>{let{formatName:i,formatOptions:a}=te(t);if(this.formats[i]){let t=e;try{let o=r?.formatParams?.[r.interpolationkey]||{},s=o.locale||o.lng||r.locale||r.lng||n;t=this.formats[i](e,s,{...a,...r,...o})}catch(e){this.logger.warn(e)}return t}else this.logger.warn(`there was no format function for ${i}`);return e},e)}},ie=(e,t)=>{e.pending[t]!==void 0&&(delete e.pending[t],e.pendingCount--)},ae=class extends w{constructor(e,t,n,r={}){super(),this.backend=e,this.store=t,this.services=n,this.languageUtils=n.languageUtils,this.options=r,this.logger=C.create(`backendConnector`),this.waitingReads=[],this.maxParallelReads=r.maxParallelReads||10,this.readingCalls=0,this.maxRetries=r.maxRetries>=0?r.maxRetries:5,this.retryTimeout=r.retryTimeout>=1?r.retryTimeout:350,this.state={},this.queue=[],this.backend?.init?.(n,r.backend,r)}queueLoad(e,t,n,r){let i={},a={},o={},s={};return e.forEach(e=>{let r=!0;t.forEach(t=>{let o=`${e}|${t}`;!n.reload&&this.store.hasResourceBundle(e,t)?this.state[o]=2:this.state[o]<0||(this.state[o]===1?a[o]===void 0&&(a[o]=!0):(this.state[o]=1,r=!1,a[o]===void 0&&(a[o]=!0),i[o]===void 0&&(i[o]=!0),s[t]===void 0&&(s[t]=!0)))}),r||(o[e]=!0)}),(Object.keys(i).length||Object.keys(a).length)&&this.queue.push({pending:a,pendingCount:Object.keys(a).length,loaded:{},errors:[],callback:r}),{toLoad:Object.keys(i),pending:Object.keys(a),toLoadLanguages:Object.keys(o),toLoadNamespaces:Object.keys(s)}}loaded(e,t,n){let r=e.split(`|`),i=r[0],a=r[1];t&&this.emit(`failedLoading`,i,a,t),!t&&n&&this.store.addResourceBundle(i,a,n,void 0,void 0,{skipCopy:!0}),this.state[e]=t?-1:2,t&&n&&(this.state[e]=0);let o={};this.queue.forEach(n=>{l(n.loaded,[i],a),ie(n,e),t&&n.errors.push(t),n.pendingCount===0&&!n.done&&(Object.keys(n.loaded).forEach(e=>{o[e]||(o[e]={});let t=n.loaded[e];t.length&&t.forEach(t=>{o[e][t]===void 0&&(o[e][t]=!0)})}),n.done=!0,n.errors.length?n.callback(n.errors):n.callback())}),this.emit(`loaded`,o),this.queue=this.queue.filter(e=>!e.done)}read(e,t,n,r=0,i=this.retryTimeout,a){if(!e.length)return a(null,{});if(this.readingCalls>=this.maxParallelReads){this.waitingReads.push({lng:e,ns:t,fcName:n,tried:r,wait:i,callback:a});return}this.readingCalls++;let o=(o,s)=>{if(this.readingCalls--,this.waitingReads.length>0){let e=this.waitingReads.shift();this.read(e.lng,e.ns,e.fcName,e.tried,e.wait,e.callback)}if(o&&s&&r<this.maxRetries){setTimeout(()=>{this.read(e,t,n,r+1,i*2,a)},i);return}a(o,s)},s=this.backend[n].bind(this.backend);if(s.length===2){try{let n=s(e,t);n&&typeof n.then==`function`?n.then(e=>o(null,e)).catch(o):o(null,n)}catch(e){o(e)}return}return s(e,t,o)}prepareLoading(t,n,r={},i){if(!this.backend)return this.logger.warn(`No backend was added via i18next.use. Will not load resources.`),i&&i();e(t)&&(t=this.languageUtils.toResolveHierarchy(t)),e(n)&&(n=[n]);let a=this.queueLoad(t,n,r,i);if(!a.toLoad.length)return a.pending.length||i(),null;a.toLoad.forEach(e=>{this.loadOne(e)})}load(e,t,n){this.prepareLoading(e,t,{},n)}reload(e,t,n){this.prepareLoading(e,t,{reload:!0},n)}loadOne(e,t=``){let n=e.split(`|`),r=n[0],i=n[1];this.read(r,i,`read`,void 0,void 0,(n,a)=>{n&&this.logger.warn(`${t}loading namespace ${i} for language ${r} failed`,n),!n&&a&&this.logger.log(`${t}loaded namespace ${i} for language ${r}`,a),this.loaded(e,n,a)})}saveMissing(e,t,n,r,i,a={},o=()=>{}){if(this.services?.utils?.hasLoadedNamespace&&!this.services?.utils?.hasLoadedNamespace(t)){this.logger.warn(`did not save key "${n}" as the namespace "${t}" was not yet loaded`,`This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!`);return}if(!(n==null||n===``)){if(this.backend?.create){let s={...a,isUpdate:i},c=this.backend.create.bind(this.backend);if(c.length<6)try{let i;i=c.length===5?c(e,t,n,r,s):c(e,t,n,r),i&&typeof i.then==`function`?i.then(e=>o(null,e)).catch(o):o(null,i)}catch(e){o(e)}else c(e,t,n,r,o,s)}!e||!e[0]||this.store.addResource(e[0],t,n,r)}}},B=()=>({debug:!1,initAsync:!0,ns:[`translation`],defaultNS:[`translation`],fallbackLng:[`dev`],fallbackNS:!1,supportedLngs:!1,nonExplicitSupportedLngs:!1,load:`all`,preload:!1,keySeparator:`.`,nsSeparator:`:`,pluralSeparator:`_`,contextSeparator:`_`,enableSelector:!1,partialBundledLanguages:!1,saveMissing:!1,updateMissing:!1,saveMissingTo:`fallback`,saveMissingPlurals:!0,missingKeyHandler:!1,missingInterpolationHandler:!1,postProcess:!1,postProcessPassResolved:!1,returnNull:!1,returnEmptyString:!0,returnObjects:!1,joinArrays:!1,returnedObjectHandler:!1,parseMissingKeyHandler:!1,appendNamespaceToMissingKey:!1,appendNamespaceToCIMode:!1,overloadTranslationOptionHandler:t=>{let n={};if(typeof t[1]==`object`&&(n=t[1]),e(t[1])&&(n.defaultValue=t[1]),e(t[2])&&(n.tDescription=t[2]),typeof t[2]==`object`||typeof t[3]==`object`){let e=t[3]||t[2];Object.keys(e).forEach(t=>{n[t]=e[t]})}return n},interpolation:{escapeValue:!0,prefix:`{{`,suffix:`}}`,formatSeparator:`,`,unescapePrefix:`-`,nestingPrefix:`$t(`,nestingSuffix:`)`,nestingOptionsSeparator:`,`,maxReplaces:1e3,skipOnVariables:!0},cacheInBuiltFormats:!0}),V=t=>(e(t.ns)&&(t.ns=[t.ns]),e(t.fallbackLng)&&(t.fallbackLng=[t.fallbackLng]),e(t.fallbackNS)&&(t.fallbackNS=[t.fallbackNS]),t.supportedLngs&&!t.supportedLngs.includes(`cimode`)&&(t.supportedLngs=t.supportedLngs.concat([`cimode`])),t),H=()=>{},oe=e=>{Object.getOwnPropertyNames(Object.getPrototypeOf(e)).forEach(t=>{typeof e[t]==`function`&&(e[t]=e[t].bind(e))})},U=class n extends w{constructor(e={},t){if(super(),this.options=V(e),this.services={},this.logger=C,this.modules={external:[]},oe(this),t&&!this.isInitialized&&!e.isClone){if(!this.options.initAsync)return this.init(e,t),this;setTimeout(()=>{this.init(e,t)},0)}}init(n={},r){this.isInitializing=!0,typeof n==`function`&&(r=n,n={}),n.defaultNS==null&&n.ns&&(e(n.ns)?n.defaultNS=n.ns:n.ns.includes(`translation`)||(n.defaultNS=n.ns[0]));let i=B();this.options={...i,...this.options,...V(n)},this.options.interpolation={...i.interpolation,...this.options.interpolation},n.keySeparator!==void 0&&(this.options.userDefinedKeySeparator=n.keySeparator),n.nsSeparator!==void 0&&(this.options.userDefinedNsSeparator=n.nsSeparator),typeof this.options.overloadTranslationOptionHandler!=`function`&&(this.options.overloadTranslationOptionHandler=i.overloadTranslationOptionHandler);let a=e=>e?typeof e==`function`?new e:e:null;if(!this.options.isClone){this.modules.logger?C.init(a(this.modules.logger),this.options):C.init(null,this.options);let e;e=this.modules.formatter?this.modules.formatter:re;let t=new N(this.options);this.store=new E(this.options.resources,this.options);let n=this.services;n.logger=C,n.resourceStore=this.store,n.languageUtils=t,n.pluralResolver=new ee(t,{prepend:this.options.pluralSeparator}),e&&(n.formatter=a(e),n.formatter.init&&n.formatter.init(n,this.options),this.options.interpolation.format=n.formatter.format.bind(n.formatter)),n.interpolator=new R(this.options),n.utils={hasLoadedNamespace:this.hasLoadedNamespace.bind(this)},n.backendConnector=new ae(a(this.modules.backend),n.resourceStore,n,this.options),n.backendConnector.on(`*`,(e,...t)=>{this.emit(e,...t)}),this.modules.languageDetector&&(n.languageDetector=a(this.modules.languageDetector),n.languageDetector.init&&n.languageDetector.init(n,this.options.detection,this.options)),this.modules.i18nFormat&&(n.i18nFormat=a(this.modules.i18nFormat),n.i18nFormat.init&&n.i18nFormat.init(this)),this.translator=new M(this.services,this.options),this.translator.on(`*`,(e,...t)=>{this.emit(e,...t)}),this.modules.external.forEach(e=>{e.init&&e.init(this)})}if(this.format=this.options.interpolation.format,r||=H,this.options.fallbackLng&&!this.services.languageDetector&&!this.options.lng){let e=this.services.languageUtils.getFallbackCodes(this.options.fallbackLng);e.length>0&&e[0]!==`dev`&&(this.options.lng=e[0])}!this.services.languageDetector&&!this.options.lng&&this.logger.warn(`init: no languageDetector is used and no lng is defined`),[`getResource`,`hasResourceBundle`,`getResourceBundle`,`getDataByLanguage`].forEach(e=>{this[e]=(...t)=>this.store[e](...t)}),[`addResource`,`addResources`,`addResourceBundle`,`removeResourceBundle`].forEach(e=>{this[e]=(...t)=>(this.store[e](...t),this)});let o=t(),s=()=>{let e=(e,t)=>{this.isInitializing=!1,this.isInitialized&&!this.initializedStoreOnce&&this.logger.warn(`init: i18next is already initialized. You should call init just once!`),this.isInitialized=!0,this.options.isClone||this.logger.log(`initialized`,this.options),this.emit(`initialized`,this.options),o.resolve(t),r(e,t)};if((this.languages||this.isLanguageChangingTo)&&!this.isInitialized)return e(null,this.t.bind(this));this.changeLanguage(this.options.lng,e)};return this.options.resources||!this.options.initAsync?s():setTimeout(s,0),o}loadResources(t,n=H){let r=n,i=e(t)?t:this.language;if(typeof t==`function`&&(r=t),!this.options.resources||this.options.partialBundledLanguages){if(i?.toLowerCase()===`cimode`&&(!this.options.preload||this.options.preload.length===0))return r();let e=[],t=t=>{t&&t!==`cimode`&&this.services.languageUtils.toResolveHierarchy(t).forEach(t=>{t!==`cimode`&&(e.includes(t)||e.push(t))})};i?t(i):this.services.languageUtils.getFallbackCodes(this.options.fallbackLng).forEach(e=>t(e)),this.options.preload?.forEach?.(e=>t(e)),this.services.backendConnector.load(e,this.options.ns,e=>{!e&&!this.resolvedLanguage&&this.language&&this.setResolvedLanguage(this.language),r(e)})}else r(null)}reloadResources(e,n,r){let i=t();return typeof e==`function`&&(r=e,e=void 0),typeof n==`function`&&(r=n,n=void 0),e||=this.languages,n||=this.options.ns,r||=H,this.services.backendConnector.reload(e,n,e=>{i.resolve(),r(e)}),i}use(e){if(!e)throw Error(`You are passing an undefined module! Please check the object you are passing to i18next.use()`);if(!e.type)throw Error(`You are passing a wrong module! Please check the object you are passing to i18next.use()`);return e.type===`backend`&&(this.modules.backend=e),(e.type===`logger`||e.log&&e.warn&&e.error)&&(this.modules.logger=e),e.type===`languageDetector`&&(this.modules.languageDetector=e),e.type===`i18nFormat`&&(this.modules.i18nFormat=e),e.type===`postProcessor`&&D.addPostProcessor(e),e.type===`formatter`&&(this.modules.formatter=e),e.type===`3rdParty`&&this.modules.external.push(e),this}setResolvedLanguage(e){if(!(!e||!this.languages)&&![`cimode`,`dev`].includes(e)){for(let e=0;e<this.languages.length;e++){let t=this.languages[e];if(![`cimode`,`dev`].includes(t)&&this.store.hasLanguageSomeTranslations(t)){this.resolvedLanguage=t;break}}!this.resolvedLanguage&&!this.languages.includes(e)&&this.store.hasLanguageSomeTranslations(e)&&(this.resolvedLanguage=e,this.languages.unshift(e))}}changeLanguage(n,r){this.isLanguageChangingTo=n;let i=t();this.emit(`languageChanging`,n);let a=e=>{this.language=e,this.languages=this.services.languageUtils.toResolveHierarchy(e),this.resolvedLanguage=void 0,this.setResolvedLanguage(e)},o=(e,t)=>{t?this.isLanguageChangingTo===n&&(a(t),this.translator.changeLanguage(t),this.isLanguageChangingTo=void 0,this.emit(`languageChanged`,t),this.logger.log(`languageChanged`,t)):this.isLanguageChangingTo=void 0,i.resolve((...e)=>this.t(...e)),r&&r(e,(...e)=>this.t(...e))},s=t=>{!n&&!t&&this.services.languageDetector&&(t=[]);let r=e(t)?t:t&&t[0],i=this.store.hasLanguageSomeTranslations(r)?r:this.services.languageUtils.getBestMatchFromCodes(e(t)?[t]:t);i&&(this.language||a(i),this.translator.language||this.translator.changeLanguage(i),this.services.languageDetector?.cacheUserLanguage?.(i)),this.loadResources(i,e=>{o(e,i)})};return!n&&this.services.languageDetector&&!this.services.languageDetector.async?s(this.services.languageDetector.detect()):!n&&this.services.languageDetector&&this.services.languageDetector.async?this.services.languageDetector.detect.length===0?this.services.languageDetector.detect().then(s):this.services.languageDetector.detect(s):s(n),i}getFixedT(t,n,r,i){let a=i?.scopeNs,o=(e,t,...n)=>{let i;i=typeof t==`object`?{...t}:this.options.overloadTranslationOptionHandler([e,t].concat(n)),i.lng=i.lng||o.lng,i.lngs=i.lngs||o.lngs;let s=i.ns!==void 0&&i.ns!==null;i.ns=i.ns||o.ns,i.keyPrefix!==``&&(i.keyPrefix=i.keyPrefix||r||o.keyPrefix);let c={...this.options,...i};Array.isArray(a)&&!s&&(c.ns=a),typeof i.keyPrefix==`function`&&(i.keyPrefix=A(i.keyPrefix,c));let l=this.options.keySeparator||`.`,u;return i.keyPrefix&&Array.isArray(e)?u=e.map(e=>(typeof e==`function`&&(e=A(e,c)),`${i.keyPrefix}${l}${e}`)):(typeof e==`function`&&(e=A(e,c)),u=i.keyPrefix?`${i.keyPrefix}${l}${e}`:e),this.t(u,i)};return e(t)?o.lng=t:o.lngs=t,o.ns=n,o.keyPrefix=r,o}t(...e){return this.translator?.translate(...e)}exists(...e){return this.translator?.exists(...e)}setDefaultNamespace(e){this.options.defaultNS=e}hasLoadedNamespace(e,t={}){if(!this.isInitialized)return this.logger.warn(`hasLoadedNamespace: i18next was not initialized`,this.languages),!1;if(!this.languages||!this.languages.length)return this.logger.warn(`hasLoadedNamespace: i18n.languages were undefined or empty`,this.languages),!1;let n=t.lng||this.resolvedLanguage||this.languages[0],r=this.options?this.options.fallbackLng:!1,i=this.languages[this.languages.length-1];if(n.toLowerCase()===`cimode`)return!0;let a=(e,t)=>{let n=this.services.backendConnector.state[`${e}|${t}`];return n===-1||n===0||n===2};if(t.precheck){let e=t.precheck(this,a);if(e!==void 0)return e}return!!(this.hasResourceBundle(n,e)||!this.services.backendConnector.backend||this.options.resources&&!this.options.partialBundledLanguages||a(n,e)&&(!r||a(i,e)))}loadNamespaces(n,r){let i=t();return this.options.ns?(e(n)&&(n=[n]),n.forEach(e=>{this.options.ns.includes(e)||this.options.ns.push(e)}),this.loadResources(e=>{i.resolve(),r&&r(e)}),i):(r&&r(),Promise.resolve())}loadLanguages(n,r){let i=t();e(n)&&(n=[n]);let a=this.options.preload||[],o=n.filter(e=>!a.includes(e)&&this.services.languageUtils.isSupportedCode(e));return o.length?(this.options.preload=a.concat(o),this.loadResources(e=>{i.resolve(),r&&r(e)}),i):(r&&r(),Promise.resolve())}dir(e){if(e||=this.resolvedLanguage||(this.languages?.length>0?this.languages[0]:this.language),!e)return`rtl`;try{let t=new Intl.Locale(e);if(t&&t.getTextInfo){let e=t.getTextInfo();if(e&&e.direction)return e.direction}}catch{}let t=`ar.shu.sqr.ssh.xaa.yhd.yud.aao.abh.abv.acm.acq.acw.acx.acy.adf.ads.aeb.aec.afb.ajp.apc.apd.arb.arq.ars.ary.arz.auz.avl.ayh.ayl.ayn.ayp.bbz.pga.he.iw.ps.pbt.pbu.pst.prp.prd.ug.ur.ydd.yds.yih.ji.yi.hbo.men.xmn.fa.jpr.peo.pes.prs.dv.sam.ckb`.split(`.`),n=this.services?.languageUtils||new N(B());return e.toLowerCase().indexOf(`-latn`)>1?`ltr`:t.includes(n.getLanguagePartFromCode(e))||e.toLowerCase().indexOf(`-arab`)>1?`rtl`:`ltr`}static createInstance(e={},t){let r=new n(e,t);return r.createInstance=n.createInstance,r}cloneInstance(e={},t=H){let r=e.forkResourceStore;r&&delete e.forkResourceStore;let i={...this.options,...e,isClone:!0},a=new n(i);if((e.debug!==void 0||e.prefix!==void 0)&&(a.logger=a.logger.clone(e)),[`store`,`services`,`language`].forEach(e=>{a[e]=this[e]}),a.services={...this.services},a.services.utils={hasLoadedNamespace:a.hasLoadedNamespace.bind(a)},r&&(a.store=new E(Object.keys(this.store.data).reduce((e,t)=>(e[t]={...this.store.data[t]},e[t]=Object.keys(e[t]).reduce((n,r)=>(n[r]={...e[t][r]},n),e[t]),e),{}),i),a.services.resourceStore=a.store),e.interpolation){let t={...B().interpolation,...this.options.interpolation,...e.interpolation},n={...i,interpolation:t};a.services.interpolator=new R(n)}return a.translator=new M(a.services,i),a.translator.on(`*`,(e,...t)=>{a.emit(e,...t)}),a.init(i,t),a.translator.options=i,a.translator.backendConnector.services.utils={hasLoadedNamespace:a.hasLoadedNamespace.bind(a)},a}toJSON(){return{options:this.options,store:this.store,language:this.language,languages:this.languages,resolvedLanguage:this.resolvedLanguage}}}.createInstance();U.createInstance,U.dir,U.init,U.loadResources,U.reloadResources,U.use,U.changeLanguage,U.getFixedT;var se=U.t;U.exists,U.setDefaultNamespace,U.hasLoadedNamespace,U.loadNamespaces,U.loadLanguages;var{slice:ce,forEach:le}=[];function ue(e){return le.call(ce.call(arguments,1),t=>{if(t)for(let n in t)e[n]===void 0&&(e[n]=t[n])}),e}function de(e){return typeof e==`string`?[/<\s*script.*?>/i,/<\s*\/\s*script\s*>/i,/<\s*img.*?on\w+\s*=/i,/<\s*\w+\s*on\w+\s*=.*?>/i,/javascript\s*:/i,/vbscript\s*:/i,/expression\s*\(/i,/eval\s*\(/i,/alert\s*\(/i,/document\.cookie/i,/document\.write\s*\(/i,/window\.location/i,/innerHTML/i].some(t=>t.test(e)):!1}var W=/^[\u0009\u0020-\u007e\u0080-\u00ff]+$/,fe=function(e,t){let n=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{path:`/`},r=`${e}=${encodeURIComponent(t)}`;if(n.maxAge>0){let e=n.maxAge-0;if(Number.isNaN(e))throw Error(`maxAge should be a Number`);r+=`; Max-Age=${Math.floor(e)}`}if(n.domain){if(!W.test(n.domain))throw TypeError(`option domain is invalid`);r+=`; Domain=${n.domain}`}if(n.path){if(!W.test(n.path))throw TypeError(`option path is invalid`);r+=`; Path=${n.path}`}if(n.expires){if(typeof n.expires.toUTCString!=`function`)throw TypeError(`option expires is invalid`);r+=`; Expires=${n.expires.toUTCString()}`}if(n.httpOnly&&(r+=`; HttpOnly`),n.secure&&(r+=`; Secure`),n.sameSite)switch(typeof n.sameSite==`string`?n.sameSite.toLowerCase():n.sameSite){case!0:r+=`; SameSite=Strict`;break;case`lax`:r+=`; SameSite=Lax`;break;case`strict`:r+=`; SameSite=Strict`;break;case`none`:r+=`; SameSite=None`;break;default:throw TypeError(`option sameSite is invalid`)}return n.partitioned&&(r+=`; Partitioned`),r},G={create(e,t,n,r){let i=arguments.length>4&&arguments[4]!==void 0?arguments[4]:{path:`/`,sameSite:`strict`};n&&(i.expires=new Date,i.expires.setTime(i.expires.getTime()+n*60*1e3)),r&&(i.domain=r),document.cookie=fe(e,t,i)},read(e){let t=`${e}=`,n=document.cookie.split(`;`);for(let e=0;e<n.length;e++){let r=n[e];for(;r.charAt(0)===` `;)r=r.substring(1,r.length);if(r.indexOf(t)===0)return r.substring(t.length,r.length)}return null},remove(e,t){this.create(e,``,-1,t)}},pe={name:`cookie`,lookup(e){let{lookupCookie:t}=e;if(t&&typeof document<`u`)return G.read(t)||void 0},cacheUserLanguage(e,t){let{lookupCookie:n,cookieMinutes:r,cookieDomain:i,cookieOptions:a}=t;n&&typeof document<`u`&&G.create(n,e,r,i,a)}},me={name:`querystring`,lookup(e){let{lookupQuerystring:t}=e,n;if(typeof window<`u`){let{search:e}=window.location;!window.location.search&&window.location.hash?.indexOf(`?`)>-1&&(e=window.location.hash.substring(window.location.hash.indexOf(`?`)));let r=e.substring(1).split(`&`);for(let e=0;e<r.length;e++){let i=r[e].indexOf(`=`);i>0&&r[e].substring(0,i)===t&&(n=r[e].substring(i+1))}}return n}},he={name:`hash`,lookup(e){let{lookupHash:t,lookupFromHashIndex:n}=e,r;if(typeof window<`u`){let{hash:e}=window.location;if(e&&e.length>2){let i=e.substring(1);if(t){let e=i.split(`&`);for(let n=0;n<e.length;n++){let i=e[n].indexOf(`=`);i>0&&e[n].substring(0,i)===t&&(r=e[n].substring(i+1))}}if(r)return r;if(!r&&n>-1){let t=e.match(/\/([a-zA-Z-]*)/g);return Array.isArray(t)?t[typeof n==`number`?n:0]?.replace(`/`,``):void 0}}}return r}},K=null,q=()=>{if(K!==null)return K;try{if(K=typeof window<`u`&&window.localStorage!==null,!K)return!1;let e=`i18next.translate.boo`;window.localStorage.setItem(e,`foo`),window.localStorage.removeItem(e)}catch{K=!1}return K},ge={name:`localStorage`,lookup(e){let{lookupLocalStorage:t}=e;if(t&&q())return window.localStorage.getItem(t)||void 0},cacheUserLanguage(e,t){let{lookupLocalStorage:n}=t;n&&q()&&window.localStorage.setItem(n,e)}},J=null,Y=()=>{if(J!==null)return J;try{if(J=typeof window<`u`&&window.sessionStorage!==null,!J)return!1;let e=`i18next.translate.boo`;window.sessionStorage.setItem(e,`foo`),window.sessionStorage.removeItem(e)}catch{J=!1}return J},_e={name:`sessionStorage`,lookup(e){let{lookupSessionStorage:t}=e;if(t&&Y())return window.sessionStorage.getItem(t)||void 0},cacheUserLanguage(e,t){let{lookupSessionStorage:n}=t;n&&Y()&&window.sessionStorage.setItem(n,e)}},ve={name:`navigator`,lookup(e){let t=[];if(typeof navigator<`u`){let{languages:e,userLanguage:n,language:r}=navigator;if(e)for(let n=0;n<e.length;n++)t.push(e[n]);n&&t.push(n),r&&t.push(r)}return t.length>0?t:void 0}},ye={name:`htmlTag`,lookup(e){let{htmlTag:t}=e,n,r=t||(typeof document<`u`?document.documentElement:null);return r&&typeof r.getAttribute==`function`&&(n=r.getAttribute(`lang`)),n}},X={name:`path`,lookup(e){let{lookupFromPathIndex:t}=e;if(typeof window>`u`)return;let n=window.location.pathname.match(/\/([a-zA-Z-]*)/g);if(Array.isArray(n))return n[typeof t==`number`?t:0]?.replace(`/`,``)}},be={name:`subdomain`,lookup(e){let{lookupFromSubdomainIndex:t}=e,n=typeof t==`number`?t+1:1,r=typeof window<`u`&&window.location?.hostname?.match(/^(\w{2,5})\.(([a-z0-9-]{1,63}\.[a-z]{2,6})|localhost)/i);if(r)return r[n]}},Z=!1;try{document.cookie,Z=!0}catch{}var Q=[`querystring`,`cookie`,`localStorage`,`sessionStorage`,`navigator`,`htmlTag`];Z||Q.splice(1,1);var xe=()=>({order:Q,lookupQuerystring:`lng`,lookupCookie:`i18next`,lookupLocalStorage:`i18nextLng`,lookupSessionStorage:`i18nextLng`,caches:[`localStorage`],excludeCacheFor:[`cimode`],convertDetectedLanguage:e=>e}),$=class{constructor(e){let t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};this.type=`languageDetector`,this.detectors={},this.init(e,t)}init(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{languageUtils:{}},t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},n=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};this.services=e,this.options=ue(t,this.options||{},xe()),typeof this.options.convertDetectedLanguage==`string`&&this.options.convertDetectedLanguage.indexOf(`15897`)>-1&&(this.options.convertDetectedLanguage=e=>e.replace(`-`,`_`)),this.options.lookupFromUrlIndex&&(this.options.lookupFromPathIndex=this.options.lookupFromUrlIndex),this.i18nOptions=n,this.addDetector(pe),this.addDetector(me),this.addDetector(ge),this.addDetector(_e),this.addDetector(ve),this.addDetector(ye),this.addDetector(X),this.addDetector(be),this.addDetector(he)}addDetector(e){return this.detectors[e.name]=e,this}detect(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:this.options.order,t=[];return e.forEach(e=>{if(this.detectors[e]){let n=this.detectors[e].lookup(this.options);n&&typeof n==`string`&&(n=[n]),n&&(t=t.concat(n))}}),t=t.filter(e=>e!=null&&!de(e)).map(e=>this.options.convertDetectedLanguage(e)),this.services&&this.services.languageUtils&&this.services.languageUtils.getBestMatchFromCodes?t:t.length>0?t[0]:null}cacheUserLanguage(e){let t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:this.options.caches;t&&(this.options.excludeCacheFor&&this.options.excludeCacheFor.indexOf(e)>-1||t.forEach(t=>{this.detectors[t]&&this.detectors[t].cacheUserLanguage(e,this.options)}))}};$.type=`languageDetector`;export{U as n,se as r,$ as t};