| Device ID | +{{ data.ext.device_id }} | +
| Access Token | +{{ data.ext.access_token }} | +
| {{ $t('types.wg.privKey') }} | +{{ data.private_key }} | +
| {{ $t('types.wg.localIp') }} | +{{ data.address.join(',') }} | +
|
+ |
+ |
| {{ $t('types.wg.pubKey') }} | +{{ data.peers[0].public_key }} | +
| {{ $t('types.wg.allowedIp') }} | +{{ data.peers[0].allowed_ips.join(',') }} | +
| Reserved | +[{{ data.peers[0].reserved.join(',') }}] | +
{{ item.obj }}{message}
", + errNoArraysFetched: 'در JSON دریافتشده «rules» یا «rule_set» یافت نشد.', + errFetch: "خطای دریافت:{message}
", + errNoFile: "فایلی انتخاب نشده است.", + errNoArraysInFile: 'در فایل «rules» یا «rule_set» یافت نشد.', + }, + }, + ruleset: { + add: "ایجاد مجموعه", + format: "فرمت دادهها", + interval: "بازه بروزرسانیها", + remote: "راه دور", + local: "محلی", + }, + dns: { + add: "ایجاد سرور DNS", + title: "سرورهای DNS", + final: "سرور نهایی", + server: "سرور", + firstServer: "سرور نخست", + cacheCapacity: "ظرفیت cache", + disableCache: "غیرفعالسازی cache", + disableExpire: "بدون انقضا", + independentCache: "استقلال cache", + reverseMapping: "نگاشت معکوس", + domainStrategy: "استراتژی دامنه", + local: { preferGo: "ترجیح Go" }, + rule: { + add: "ایجاد قانون DNS", + title: "قوانین DNS", + inet4Range: "محدوده IPv4", + inet6Range: "محدوده IPv6", + acceptDefault: "پذیرش پیشفرض", + action: { + title: "عملیات", + route: "مسیریابی", + routeOptions: "گزینههای مسیریابی", + reject: "رد کردن", + predefined: "پیش تعریف شده", + rewriteTtl: "بازنویسی TTL", + clientSubnet: "زیر شبکه کاربر", + rcode: "کد جواب", + rcodes: { + noError: "درست", + formerr: "درخواست نامعتبر", + servFail: "خطای سرور", + nxDomain: "یافت نشد", + refused: "رد شده", + notImp: "پیادهسازی نشده", + }, + answer: "پاسخها", + ns: "سرورهای دامنه", + extra: "اضافی", + }, + }, + }, + basic: { + log: { + title: "گزارشها", + level: "سطح", + output: "خروجی", + timestamp: "فعالسازی ثبت زمان", + }, + routing: { + title: "مسیریابی", + defaultOut: "خروجی پیشفرض", + defaultIf: "کارت شبکه پیشفرض", + defaultRm: "Routing Mark پیشفرض", + defaultDns: "DNS پیشفرض", + autoBind: "انتخاب اتوماتیک کارت شبکه", + }, + exp: { + storeFakeIp: "ذخیره آدرسهای نامعتبر", + extController: "کنترلگر خارجی", + extUi: "رابطکاربری خارجی", + extUiDownloadUrl: "آدرس دانلود رابطکاربری", + extUiDownloadDetour: "خروجی دانلود رابطکاربری", + secret: "رمز", + defaultMode: "حالت پیشفرض", + allowOrigin: "اجازه از مبدا", + allowPrivate: "اجازه شبکه خصوصی", + }, + }, + tls: { + enable: "فعالسازی رمزنگاری", + usePath: "مسیر فایل", + useText: "متن گواهی", + certPath: "مسیر فایل گواهی", + keyPath: "مسیر فایل کلید", + cert: "گواهی", + key: "کلید", + options: "گزینههای رمزنگاری", + minVer: "کمینه نسخه", + maxVer: "بیشینه نسخه", + cs: "مدلهای رمزنگاری", + privKey: "کلید خصوصی", + pubKey: "کلید عمومی", + disableSni: "غیرفعالسازی SNI", + insecure: "تایید ارتباط ناامن", + fragment: "تکهبندی", + fragmentDelay: "تاخیر تکهبندی جایگزین", + recordFragment: "تکهبندی چندگانه", + store: "انبار ریشه", + ktls: "TLS هسته", + kernelTx: "ارسال", + kernelRx: "دریافت", + queryServerName: "نام سرور ECH برای جستجو", + acme: { + options: "گزینههای ACME", + dataDir: "مسیر دادهها", + defaultDomain: "دامنه پیشفرض", + disableChallenges: "بستن چالشها", + httpChallenge: "بستن چالش HTTP", + tlsChallenge: "بستن چالش TLS", + altPorts: "پورتهای جایگزین", + altHport: "پورت جایگزین HTTP", + altTport: "پورت جایگزین TLS", + caProvider: "فراهم کننده گواهی", + customCa: "فراهم کننده دیگر", + extAcc: "حساب خارجی", + dns01: "چالش DNS01", + dns01Provider: "فراهم کننده چالش DNS01", + dns01Params: { + api_token: "توکن API", + zone_token: "توکن زون", + access_key_id: "شناسه کلید دسترسی", + access_key_secret: "رمز کلید دسترسی", + region_id: "شناسه منطقه", + security_token: "توکن امنیتی", + username: "نام کاربری", + password: "رمز عبور", + subdomain: "زیردامنه", + server_url: "آدرس سرور", + }, + }, + }, + stats: { + upload: "آپلود", + download: "دانلود", + volume: "حجم", + usage: "استفاده", + enable: "فعال سازی گزارش ترافیک", + graphTitle: "نمودار ترافیک", + B: "ب", + KB: "کب", + MB: "مب", + GB: "گب", + TB: "تب", + PB: "پب", + p: "پ", + Kp: "کپ", + Mp: "مپ", + Gp: "گپ", + Mbps: "مب/ث", + }, + date: { + expiry: "انقضا", + expired: "منقضی", + d: "ر", + h: "س", + m: "د", + s: "ث", + ms: "مث", + } +} diff --git a/frontend/src/locales/index.ts b/frontend/src/locales/index.ts new file mode 100644 index 0000000..9005f59 --- /dev/null +++ b/frontend/src/locales/index.ts @@ -0,0 +1,42 @@ +import { createI18n } from 'vue-i18n' +import en from './en' +import fa from './fa' +import vi from './vi' +import zhcn from './zhcn' +import zhtw from './zhtw' +import ru from './ru' + +export const i18n = createI18n({ + legacy: false, + locale: localStorage.getItem("locale") ?? 'en', + fallbackLocale: 'en', + messages: { + en: en, + fa: fa, + vi: vi, + zhHans: zhcn, + zhHant: zhtw, + ru: ru + }, +}) + +export const locale = (() => { + const l = i18n.global.locale.value + switch (l) { + case "zhHans": + return "zh-cn" + case "zhHant": + return "zh-tw" + default: + return l + } +})() + +export const languages = [ + { title: 'English', value: 'en' }, + { title: 'فارسی', value: 'fa' }, + { title: 'Tiếng Việt', value: 'vi' }, + { title: '简体中文', value: 'zhHans' }, + { title: '繁體中文', value: 'zhHant' }, + { title: 'Русский', value: 'ru' }, +] diff --git a/frontend/src/locales/ru.ts b/frontend/src/locales/ru.ts new file mode 100644 index 0000000..2029e43 --- /dev/null +++ b/frontend/src/locales/ru.ts @@ -0,0 +1,640 @@ +export default { + success: "успех", + failed: "ошибка", + enable: "Включить", + disable: "Отключить", + none: "Никакие", + all: "Все", + loading: "Загрузка...", + confirm: "Вы уверены?", + yes: "да", + no: "нет", + unlimited: "бесконечный", + type: "Тип", + protocol: "Протокол", + submit: "Отправить", + reset: "Сбросить", + now: "Сейчас", + network: "Сеть", + copyToClipboard: "Копировать в буфер обмена", + noData: "Нет данных!", + invalidLogin: "Неверный логин!", + online: "В сети", + version: "Версия", + email: "Электронная почта", + commaSeparated: "(разделено запятыми)", + count: "Количество", + template: "Шаблон", + editor: "Редактор", + error: { + dplData: "Дублирующие данные", + core: "Ошибка Sing-Box", + invalidData: "Неверные данные", + }, + theme: { + light: "Светлый", + dark: "Темный", + system: "Система", + }, + pages: { + login: "Вход", + home: "Главная", + inbounds: "Входящие", + outbounds: "Исходящие", + services: "Устройства", + endpoints: "Эндпоинты", + clients: "Клиенты", + rules: "Правила", + tls: "Настройки TLS", + basics: "Основы", + dns: "DNS", + admins: "Администраторы", + settings: "Настройки", + }, + main: { + tiles: "Плитки", + gauges: "Датчики", + charts: "Графики", + infos: "Информация", + gauge: { + cpu: "Загрузка ЦП", + mem: "Загрузка ОЗУ", + dsk: "Загрузка диска", + swp: "Загрузка Swap", + }, + chart: { + cpu: "Мониторинг ЦП", + mem: "Мониторинг ОЗУ", + net: "Сетевой трафик", + pnet: "Сетевые пакеты", + dio: "Мониторинг диска", + }, + info: { + sys: "Информация о системе", + sbd: "Информация о Sing-Box", + host: "Хост", + cpu: "ЦП", + core: "Ядро", + uptime: "Время работы", + startupTime: "Время запуска", + threads: "Потоки", + memory: "Память", + running: "Работает" + }, + backup: { + title: "Резервное копирование и восстановление", + backup: "Скачать резервную копию", + restore: "Восстановить резервную копию", + exclStats: "Исключить графики", + exclChanges: "Исключить изменения", + sbConfig: "Скачать конфигурацию Sing-Box", + }, + stats: { + title: "Использование и количество", + totalUsage: "Общее использование", + }, + }, + objects: { + inbound: "Входящий", + client: "Клиент", + outbound: "Исходящий", + endpoint: "Точка входа", + config: "Настройки", + rule: "Правило", + ruleset: "Набор правил", + service: "Устройство", + dnsserver: "DNS сервер", + dnsrule: "Правило DNS", + user: "Пользователь", + tag: "Тег", + listen: "Прослушивание", + dial: "Звонок", + tls: "TLS", + multiplex: "Мультиплекс", + transport: "Транспорт", + headers: "Заголовки", + key: "Ключ", + value: "Значение", + }, + actions: { + action: "Действие", + add: "Добавить", + addbulk: "Добавить пакетно", + editbulk: "Редактировать пакетно", + delbulk: "Удалить пакетно", + new: "Новый", + edit: "Редактировать", + del: "Удалить", + clone: "Клонировать", + test: "Тест", + testAll: "Тестировать все", + save: "Сохранить", + update: "Обновить", + submit: "Отправить", + set: "Установить", + generate: "Генерировать", + disable: "Отключить", + close: "Закрыть", + restartApp: "Перезапустить приложение", + restartSb: "Перезапустить Singbox", + apply: "Применить", + }, + login: { + title: "Вход", + username: "Имя пользователя", + unRules: "Имя пользователя не может быть пустым", + password: "Пароль", + pwRules: "Пароль не может быть пустым", + }, + menu: { + logout: "Выйти", + }, + admin: { + changeCred: "Изменить учетные данные", + oldPass: "Текущий пароль", + newUname: "Новое имя пользователя", + newPass: "Новый пароль", + lastLogin: "Последний вход", + date: "Дата", + time: "Время", + changes: "Изменения", + actor: "Исполнитель", + key: "Ключ", + action: "Действие", + api: { + title: "Токены API", + msg: "Пожалуйста, скопируйте токен ниже и сохраните его в безопасном месте. Он не будет показан заново.", + token: "Токен", + }, + }, + setting: { + interface: "Интерфейс", + sub: "Подписка", + addr: "Адрес", + port: "Порт", + webPath: "Базовый URI", + domain: "Домен", + sslKey: "Путь к SSL ключу", + sslCert: "Путь к SSL сертификату", + webUri: "URI панели", + sessionAge: "Максимальная длительность сессии", + trafficAge: "Максимальная длительность трафика", + timeLoc: "Часовой пояс", + subEncode: "Включить кодирование", + subInfo: "Включить информацию о клиенте", + path: "Путь по умолчанию", + update: "Время автоматического обновления", + subUri: "URI подписки", + jsonSub: "JSON подписка", + toDirect: "Маршрутизация на Direct", + toBlock: "Маршрутизация на Block", + timestamp: "Метка времени", + globalDns: "Глобальный DNS", + directDns: "Прямой DNS", + toDirectDns: "Маршрутизация на Direct DNS", + jsonSubOptions: "Другие параметры", + excludePkg: "Исключить пакеты", + clashSub: "Clash подписка", + mixedPort: "Смешанный порт", + tun: "Tun инбоунд", + }, + client: { + name: "Имя", + desc: "Описание", + group: "Группа", + inboundTags: "Теги входящих", + basics: "Основы", + config: "Конфигурация", + links: "Ссылки", + external: "Внешняя ссылка", + sub: "Внешняя подписка", + delayStart: "Отложенный старт", + autoReset: "Авто сброс", + resetDays: "Дней до сброса", + nextReset: "Следующий сброс", + }, + bulk: { + order: "Порядок", + random: "Случайный", + changeLimits: "Изменить лимиты", + addInbounds: "Добавить входящие", + removeInbounds: "Удалить входящие", + addDays: "Добавить дни", + addVolume: "Добавить объём", + }, + types: { + un: "Имя пользователя", + pw: "Пароль", + direct: { + overrideAddr: "Переопределить адрес", + overridePort: "Переопределить порт", + }, + hy: { + obfs: "Обфусцированный пароль", + auth: "Пароль аутентификации", + hyOptions: "Параметры Hysteria", + hy2Options: "Параметры Hysteria2", + ignoreBw: "Игнорировать пропускную способность клиента", + }, + shdwTls: { + hs: "Сервер рукопожатий", + addHS: "Добавить сервер рукопожатий", + }, + ssh: { + passphrase: "Парольная фраза", + hostKey: "Ключи хоста", + algorithm: "Алгоритмы ключей", + clientVer: "Версия клиента", + options: "Параметры SSH", + }, + tor: { + execPath: "Путь к исполняемому файлу", + dataDir: "Каталог данных", + extArgs: "Дополнительные аргументы", + }, + tuic: { + congControl: "Контроль перегрузок", + authTimeout: "Таймаут аутентификации", + hb: "Сердцебиение", + }, + tun: { + addr: "Адреса", + ifName: "Имя интерфейса", + excludeMptcp: "Исключить MPTCP", + fallbackRuleIndex: "Индекс правила iproute2 fallback", + }, + vless: { + flow: "Поток", + udpEnc: "Кодирование UDP пакетов", + }, + vmess: { + security: "Безопасность", + globalPadding: "Глобальная подкладка", + authLen: "Длина шифрования", + }, + wg: { + privKey: "Приватный ключ", + pubKey: "Публичный ключ пира", + psk: "Предварительно разделенный ключ", + localIp: "Локальные IP", + worker: "Работники", + ifName: "Имя интерфейса", + sysIf: "Системный интерфейс", + options: "Параметры Wireguard", + allowedIp: "Разрешенные IP", + peer: "Пир", + peers: "Пиры", + }, + lb: { + defaultOut: "Исходящий по умолчанию", + interruptConn: "Прервать существующие соединения", + testUrl: "Тестовый URL", + interval: "Интервал", + tolerance: "Толерантность", + urlTestOptions: "Параметры URLTest" + }, + ts: { + options: "Параметры Tailscale", + stateDir: "Каталог состояния", + authKey: "Ключ аутентификации", + relayServer: "Сервер ретрансляции", + relayServerPort: "Порт сервера ретрансляции", + relayEndpoints: "Статические точки ретрансляции", + systemInterface: "Системный интерфейс", + sysIfName: "Имя интерфейса", + sysIfMtu: "MTU интерфейса", + controlUrl: "URL управления", + ephemeral: "Эфемерный", + hostname: "Имя хоста", + acceptRoutes: "Принять маршруты", + exitNode: "Выходной узел", + allowLanAccess: "Разрешить доступ LAN", + advRoutes: "Рекламируемые маршруты", + advExitNode: "Рекламируемый выходной узел", + udpTimeout: "Таймаут UDP", + }, + ocm: { + credentialPath: "Путь к учетным данным", + usagesPath: "Путь к статистике", + users: "Пользователи", + userName: "Имя", + userToken: "Токен", + }, + ccm: { + credentialPath: "Путь к учетным данным", + usagesPath: "Путь к статистике", + users: "Пользователи", + userName: "Имя", + userToken: "Токен", + }, + derp: { + configPath: "Путь к конфигурации", + verifyClientEndpoint: "Проверить конечную точку клиента", + verifyClientUrl: "Проверить URL клиента", + meshWith: "Сеть с", + meshPsk: "Предварительно разделенный ключ", + meshPskFile: "Файл предварительно разделенного ключа", + stun: "Сервер STUN", + options: "Параметры DERP", + }, + naive: { + insecureConcurrency: "Небезопасная параллельность", + quic: "QUIC", + quicCongestion: "Управление перегрузкой QUIC", + udpOverTcp: "UDP через TCP", + }, + anytls: { + idleInterval: "Интервал проверки неактивных сессий", + idleTimeout: "Тайм-аут неактивной сессии", + minIdle: "Минимум неактивных сессий" + }, + }, + in: { + addr: "Адрес", + port: "Порт", + ssMethod: "Метод", + ssManageable: "Управляемый", + sSide: "Сторона сервера", + cSide: "Сторона клиента", + multiDomain: "Мультидомен", + remark: "Примечание", + mdOption: "Параметры мультидомена", + }, + listen: { + options: "Параметры прослушивания", + tcpOptions: "Параметры TCP", + udpOptions: "Параметры UDP", + detour: "Обход", + detourText: "Переадресация на входящий", + disableTcpKeepAlive: "Отключить TCP Keep Alive", + tcpKeepAlive: "TCP Keep Alive", + tcpKeepAliveInterval: "Интервал TCP Keep Alive", + }, + dial: { + bindIf: "Привязка к сетевому интерфейсу", + bindIp4: "Привязка к IPv4", + bindIp6: "Привязка к IPv6", + bindNoPort: "Привязка адреса без порта", + reuseAddr: "Повторное использование адреса слушателя", + connTimeout: "Таймаут подключения", + disableTcpKeepAlive: "Отключить TCP Keep Alive", + tcpKeepAlive: "TCP Keep Alive", + tcpKeepAliveInterval: "Интервал TCP Keep Alive", + domainResolver: "Разрешение домена", + options: "Параметры вызова", + detourText: "Переадресация на исходящий", + }, + transport: { + enable: "Включить транспорт", + host: "Хост", + hosts: "Хосты", + path: "Путь", + httpMethod: "Метод запроса", + idleTimeout: "Таймаут бездействия", + pingTimeout: "Таймаут пинга", + grpcServiceName: "Имя службы", + grpcPws: "Разрешить без потока", + }, + mux: { + enable: "Включить мультиплекс", + maxConn: "Максимальное количество соединений", + minStr: "Минимальное количество потоков", + maxStr: "Максимальное количество потоков", + padding: "Только подкладка", + enableBrutal: "Включить Brutal", + }, + out: { + addr: "Адрес сервера", + port: "Порт сервера", + addUrlTest: "Добавить URLTest", + delay: "Задержка", + }, + rule: { + add: "Добавить правило", + simple: "Простое", + logical: "Логическое", + mode: "Режим", + invert: "Инвертировать", + ipVer: "Версия IP", + domain: "Домены", + domainSufix: "Суффиксы доменов", + domainKw: "Ключевые слова домена", + domainRgx: "Регулярные выражения домена", + ip: "CIDR IP", + privateIp: "Недействительные диапазоны IP", + port: "Порты", + portRange: "Диапазоны портов", + srcCidr: "CIDR исходного IP", + srcPrivateIp: "Недействительные исходные IP", + srcPort: "Исходные порты", + srcPortRange: "Диапазоны исходных портов", + ruleset: "Наборы правил", + rulesetMatchSrc: "Набор правил для соответствия источника IPcidr", + preferredBy: "Предпочтительный исходящий", + interfaceAddr: "Адрес интерфейса", + options: "Параметры правила", + domainRules: "Домен/IP", + srcIpRules: "Источник IP", + srcPortRules: "Источник порта", + udpDisableDomainUnmapping: "Отключить перенос доменных имен", + udpConnect: "Подключение UDP", + udpTimeout: "Таймаут UDP", + method: "Метод", + noDrop: "Не сбрасывать", + sniffer: "Обнаружение", + timeout: "Таймаут", + strategy: "Стратегия", + etaHint: "По одной записи в строке. Пустые строки и дубликаты игнорируются.", + import: { + title: "Массовый импорт наборов правил", + rulesTitle: "Импорт правил", + urlsHint: "По одному URL в строке. Тег определяется из имени файла без расширения.", + fileHint: "Загрузите .txt файл с URL-адресами, по одному в строке.", + jsonHint: "Вставьте JSON-объект с массивами rules и/или rule_set. Можно вставить весь блок \"route\": {'{'}...{'}'} или только его содержимое.", + fileJsonHint: "Загрузите .json файл с блоком route.", + urlHint: "Укажите прямую ссылку на JSON-файл (например, raw-ссылку GitHub).", + preview: "Предпросмотр", + skipped: "уже существуют, показаны серым", + conflict: "Обнаружены конфликты", + merge: "Объединить — добавить импортируемые правила (пропустить дубликаты тегов наборов)", + replace: "Заменить — удалить существующие правила и наборы, импортировать заново", + pasteUrls: "Вставить URL", + uploadTxt: "Загрузить .txt", + uploadFile: "Загрузить файл", + fromUrl: "По ссылке", + selectTxt: "Выберите .txt файл", + selectJson: "Выберите .json файл", + parse: "Разобрать", + conflictDesc: "В конфиге уже есть {rules} правил и {rulesets} наборов правил. Выберите действие:", + finalOutbound: "Outbound по умолчанию (final)", + applyFinal: "Применить как outbound по умолчанию", + errNoArrays: 'Массивы "rules" или "rule_set" не найдены.', + errJsonParse: "Ошибка разбора JSON: {message}", + errNoArraysFetched: 'В полученном JSON нет "rules" или "rule_set".', + errFetch: "Ошибка загрузки: {message}", + errNoFile: "Файл не выбран.", + errNoArraysInFile: 'В файле нет "rules" или "rule_set".', + }, + }, + ruleset: { + add: "Добавить набор правил", + format: "Формат данных", + interval: "Интервалы обновления", + remote: "Удаленный", + local: "Локальный", + }, + dns: { + add: "Добавить DNS сервер", + title: "DNS серверы", + final: "Итоговый", + server: "Сервер", + firstServer: "Первый сервер", + cacheCapacity: "Вместимость кэша", + disableCache: "Отключить кэш", + disableExpire: "Отключить истечение", + independentCache: "Независимый кэш", + reverseMapping: "Обратное отображение", + domainStrategy: "Стратегия домена", + local: { preferGo: "Предпочитать Go" }, + rule: { + add: "Добавить правило DNS", + title: "Правила DNS", + inet4Range: "Диапазон IPv4", + inet6Range: "Диапазон IPv6", + acceptDefault: "Принять резолверы по умолчанию", + action: { + title: "Действие", + route: "Маршрутизация", + routeOptions: "Параметры маршрутизации", + reject: "Отклонить", + predefined: "Предопределенные", + rewriteTtl: "Перезаписать TTL", + clientSubnet: "Подсеть клиента", + rcode: "Код ответа", + rcodes: { + noError: "ОК", + formerr: "Неверный запрос", + servFail: "Сбой сервера", + nxDomain: "Не найдено", + refused: "Отклонено", + notImp: "Не реализовано" + }, + answer: "Ответ", + ns: "Серверы имён", + extra: "Дополнительные", + }, + } + }, + basic: { + log: { + title: "Журналы", + level: "Уровень", + output: "Вывод", + timestamp: "Включить метку времени", + }, + routing: { + title: "Маршрутизация", + defaultOut: "Исходящий по умолчанию", + defaultIf: "Сетевой интерфейс по умолчанию", + defaultRm: "Маршрут по умолчанию", + defaultDns: "DNS по умолчанию", + autoBind: "Автопривязка сетевого интерфейса", + }, + exp: { + storeFakeIp: "Хранить поддельный IP", + extController: "Внешний контроллер", + extUi: "Внешний интерфейс", + extUiDownloadUrl: "URL загрузки интерфейса", + extUiDownloadDetour: "Обход загрузки интерфейса", + secret: "Секрет", + defaultMode: "Режим по умолчанию", + allowOrigin: "Разрешить источник", + allowPrivate: "Разрешить частную сеть" + }, + }, + tls: { + enable: "Включить TLS", + usePath: "Использовать путь", + useText: "Использовать текст", + certPath: "Путь к файлу сертификата", + keyPath: "Путь к файлу ключа", + cert: "Сертификат", + key: "Ключ", + options: "Параметры TLS", + minVer: "Минимальная версия", + maxVer: "Максимальная версия", + cs: "Наборы шифров", + privKey: "Приватный ключ", + pubKey: "Публичный ключ", + disableSni: "Отключить SNI", + insecure: "Разрешить небезопасное", + fragment: "Фрагментация", + fragmentDelay: "Задержка фрагментации", + recordFragment: "Фрагментация записей", + store: "Корневое хранилище", + ktls: "Ядро TLS", + kernelTx: "TX", + kernelRx: "RX", + queryServerName: "ECH имя сервера для запроса", + acme: { + options: "Параметры ACME", + dataDir: "Каталог данных", + defaultDomain: "Домен по умолчанию", + disableChallenges: "Отключить вызовы", + httpChallenge: "Отключить HTTP вызов", + tlsChallenge: "Отключить TLS вызов", + altPorts: "Альтернативные порты", + altHport: "Альтернативный HTTP порт", + altTport: "Альтернативный TLS порт", + caProvider: "Поставщик CA", + customCa: "Пользовательский поставщик CA", + extAcc: "Внешний аккаунт", + dns01: "DNS01 вызов", + dns01Provider: "Поставщик DNS01 вызова", + dns01Params: { + api_token: "API Токен", + zone_token: "Токен зоны", + access_key_id: "ID ключа доступа", + access_key_secret: "Секрет ключа доступа", + region_id: "ID региона", + security_token: "Токен безопасности", + username: "Имя пользователя", + password: "Пароль", + subdomain: "Поддомен", + server_url: "URL сервера", + }, + }, + }, + stats: { + upload: "Загрузка", + download: "Скачивание", + volume: "Объем", + usage: "Использование", + enable: "Включить статистику", + graphTitle: "График трафика", + B: "Б", + KB: "КБ", + MB: "МБ", + GB: "ГБ", + TB: "ТБ", + PB: "ПБ", + p: "п", + Kp: "Кп", + Mp: "Мп", + Gp: "Гп", + Mbps: "Мб/с", + }, + date: { + expiry: "Срок действия", + expired: "Истек", + d: "д", + h: "ч", + m: "м", + s: "с", + ms: "мс", + }, +} + + + + diff --git a/frontend/src/locales/vi.ts b/frontend/src/locales/vi.ts new file mode 100644 index 0000000..11cbaa2 --- /dev/null +++ b/frontend/src/locales/vi.ts @@ -0,0 +1,635 @@ +export default { + success: "Thành công", + failed: "Thất bại", + enable: "Kích hoạt", + disable: "Vô hiệu hóa", + none: "Không", + all: "Tất cả", + loading: "Đang tải...", + confirm: "Bạn chắc chắn chứ?", + yes: "có", + no: "không", + unlimited: "vô hạn", + type: "Loại", + protocol: "Giao thức", + submit: "Gửi", + reset: "Đặt lại", + now: "Hiện tại", + network: "Mạng", + copyToClipboard: "Sao chép vào clipboard", + noData: "Không có dữ liệu!", + invalidLogin: "Đăng nhập không hợp lệ!", + online: "Trực tuyến", + version: "Phiên bản", + email: "Email", + commaSeparated: "(được phân tách bằng dấu phẩy)", + count: "Đếm", + template: "Mẫu", + editor: "Bản sử dụng", + error: { + dplData: "Dữ liệu trùng lặp", + core: "Lỗi Sing-Box", + invalidData: "Dữ liệu khỏ hợp lệ", + }, + theme: { + light: "Nhật", + dark: "Xanh", + system: "Phòng bán", + }, + pages: { + login: "Đăng nhập", + home: "Trang chủ", + inbounds: "Đầu Vào", + outbounds: "Đầu ra", + services: "Thiết bị", + endpoints: "Câu hình", + clients: "Khách hàng", + rules: "Quy tắc", + tls: "Cài đặt TLS", + basics: "Cơ bản", + dns: "DNS", + admins: "Quản trị viên", + settings: "Cài đặt", + }, + main: { + tiles: "OHB", + gauges: "Đồng hồ đo", + charts: "Biểu đồ", + infos: "Thông tin", + gauge: { + cpu: "Đồng hồ CPU", + mem: "Đồng hồ RAM", + dsk: "Đồng hồ Disk", + swp: "Đồng hồ Swap", + }, + chart: { + cpu: "Máy theo dõi CPU", + mem: "Máy theo dõi RAM", + net: "Băng thông mạng", + pnet: "Gói mạng", + dio: "Disk I/O", + }, + info: { + sys: "Thông tin hệ thống", + sbd: "Thông tin Sing-Box", + host: "Máy chủ", + cpu: "CPU", + core: "Nhân", + uptime: "Thời gian hoạt động", + startupTime: "Thời gian khởi động", + threads: "Luồng", + memory: "Bộ nhớ", + running: "Đang chạy" + }, + backup: { + title: "Sao lưu và khôi phục", + backup: "Tải xuống bản sao lưu", + restore: "Khôi phục bản sao lưu", + exclStats: "Loại trừ các biểu đồ", + exclChanges: "Loại trừ các thay đổi", + sbConfig: "Tải xuống cấu hình Sing-Box", + }, + stats: { + title: "Sử dụng và số lượng", + totalUsage: "Tổng sử dụng", + }, + }, + objects: { + inbound: "Đầu Vào", + client: "Máy Khách hàng", + outbound: "Đầu Ra", + endpoint: "Điểm cuối", + config: "Câu hình", + rule: "Quy tắc", + ruleset: "Bộ quy tắc", + service: "Dịch vụ", + dnsserver: "Máy chủ DNS", + dnsrule: "Quy tắc DNS", + user: "Người dùng", + tag: "Thẻ", + listen: "Nghe", + dial: "Quay số", + tls: "TLS", + multiplex: "Ghép đa truyền thông ", + transport: "Giao thông", + headers: "Tiêu đề", + key: "Chìa khóa", + value: "Giá trị", + }, + actions: { + action: "Hành động", + add: "Thêm", + addbulk: "Thêm Hàng loạt", + editbulk: "Chỉnh sửa hàng loạt", + delbulk: "Xóa hàng loạt", + new: "Mới", + edit: "Chỉnh sửa", + del: "Xóa", + clone: "Nhân bản", + test: "Kiểm tra", + testAll: "Kiểm tra tất cả", + save: "Lưu", + update: "Cập nhật", + submit: "Gửi", + set: "Đặt", + generate: "Tạo ra", + disable: "Vô hiệu hóa", + close: "Đóng", + restartApp: "Khởi động lại ứng dụng", + restartSb: "Khởi động lại Singbox", + }, + login: { + title: "Đăng nhập", + username: "Tên người dùng", + unRules: "Tên người dùng không thể trống", + password: "Mật khẩu", + pwRules: "Mật khẩu không thể trống", + }, + menu: { + logout: "Đăng xuất", + }, + admin: { + changeCred: "Thay đổi thông tin đăng nhập", + oldPass: "Mật khẩu hiện tại", + newUname: "Tên người dùng mới", + newPass: "Mật khẩu mới", + lastLogin: "Lân đăng nhập cuôi", + date: "Ngày", + time: "Thời gian", + changes: "Thay đổi", + actor: "Diễn viên", + key: "Khóa", + action: "Hành động", + api: { + title: "Mã thông báo API", + msg: "Vui lòng sao chép mã thông báo bên dưới và lưu trữ nó ở nơi an toàn. Nó sẽ không được hiển thị lại.", + token: "Mã thông báo" + }, + }, + setting: { + interface: "Giao diện", + sub: "Đăng ký", + addr: "Địa chỉ", + port: "Cổng", + webPath: "Đường dẫn gốc", + domain: "Miền", + sslKey: "Đường dẫn khóa SSL", + sslCert: "Đường dẫn chứng chỉ SSL", + webUri: "URI bảng điều khiển", + sessionAge: "Tuổi tối đa của phiên", + trafficAge: "Tuổi lưu thông tối đa", + timeLoc: "Vị trí múi giờ", + subEncode: "Kích hoạt mã hóa", + subInfo: "Kích hoạt thông tin khách hàng", + path: "Đường dẫn mặc định", + update: "Thời gian cập nhật tự động", + subUri: "URI đăng ký", + jsonSub: "Đăng ký JSON", + toDirect: "Chuyển hướng tới Trực tiếp", + toBlock: "Chuyển hướng tới Chặn", + timestamp: "Dấu thời gian", + globalDns: "DNS Toàn cầu", + directDns: "DNS Trực tiếp", + toDirectDns: "Chuyển hướng tới DNS Trực tiếp", + jsonSubOptions: "Tùy chọn Khác", + excludePkg: "Loại trừ Gói", + clashSub: "Clash đăng ký", + mixedPort: "Cổng khóa", + tun: "Tun đăng ký", + }, + client: { + name: "Tên", + desc: "Mô tả", + group: "Nhóm", + inboundTags: "Thẻ đầu vào", + basics: "Cơ bản", + config: "Cấu hình", + links: "Liên kết", + external: "Liên kết bên ngoài", + sub: "Đăng ký bên ngoài", + delayStart: "Trì hoãn bắt đầu", + autoReset: "Tự động đặt lại", + resetDays: "Số ngày đặt lại", + nextReset: "Đặt lại lần sau", + }, + bulk: { + order: "Sắp xếp", + random: "Ngẫu nhiên", + changeLimits: "Thay đổi giới hạn", + addInbounds: "Thêm inbound", + removeInbounds: "Xóa inbound", + addDays: "Thêm ngày", + addVolume: "Thêm dung lượng", + }, + types: { + un: "Tên người dùng", + pw: "Mật khẩu", + direct: { + overrideAddr: "Ghi đè Địa chỉ", + overridePort: "Ghi đè Cổng", + }, + hy: { + obfs: "Mật khẩu đã được Ẩn", + auth: "Mật khẩu Xác thực", + hyOptions: "Tùy chọn Hysteria", + hy2Options: "Tùy chọn Hysteria2", + ignoreBw: "Bỏ qua Băng thông của Client", + }, + shdwTls: { + hs: "Máy chủ Handshake", + addHS: "Thêm Máy chủ Handshake", + }, + ssh: { + passphrase: "Cụm từ mật khẩu", + hostKey: "Khóa Máy chủ", + algorithm: "Thuật toán Khóa", + clientVer: "Phiên bản Client", + options: "Tùy chọn SSH", + }, + tor: { + execPath: "Đường dẫn File thực thi", + dataDir: "Thư mục Dữ liệu", + extArgs: "Đối số Bổ sung", + }, + tuic: { + congControl: "Kiểm soát Tắc nghẽn", + authTimeout: "Thời gian chờ Xác thực", + hb: "Nhịp tim", + }, + tun: { + addr: "Địa chỉ", + ifName: "Tên Giao diện", + excludeMptcp: "Loại trừ MPTCP", + fallbackRuleIndex: "Chỉ số quy tắc dự phòng iproute2", + }, + vless: { + flow: "Luồng", + udpEnc: "Mã hóa Gói UDP", + }, + vmess: { + security: "Bảo mật", + globalPadding: "Đệm Toàn cầu", + authLen: "Chiều dài Mã hóa", + }, + wg: { + privKey: "Khóa Riêng tư", + pubKey: "Khóa Công khai của Đối tác", + psk: "Khóa được Chia sẻ trước", + localIp: "IPs Cục bộ", + worker: "Công nhân", + ifName: "Tên Giao diện", + sysIf: "Giao diện Hệ thống", + options: "Tùy chọn Wireguard", + allowedIp: "IPs được Phép", + peer: "Đối tác", + peers: "Đối tác", + }, + lb: { + defaultOut: "Đầu ra Mặc định", + interruptConn: "Ngắt kết nối hiện tại", + testUrl: "URL Kiểm tra", + interval: "Khoảng thời gian", + tolerance: "Sự dung hòa", + urlTestOptions: "Tùy chọn URLTest", + }, + ts: { + options: "Tùy chọn Tailscale", + stateDir: "Thư mục Trạng thái", + authKey: "Khóa Xác thực", + relayServer: "Máy chủ chuyển tiếp", + relayServerPort: "Cổng máy chủ chuyển tiếp", + relayEndpoints: "Điểm cuối tĩnh chuyển tiếp", + systemInterface: "Giao diện hệ thống", + sysIfName: "Tên giao diện", + sysIfMtu: "MTU giao diện", + controlUrl: "URL Cấu hình", + ephemeral: "Tạm thời", + hostname: "Tên máy chủ", + acceptRoutes: "Chấp nhận Đường dẫn", + exitNode: "Nút thoát", + allowLanAccess: "Cho phép Truy cập LAN", + advRoutes: "Quảng bá Đường dẫn", + advExitNode: "Quảng bá Nút thoát", + udpTimeout: "Thời gian Chờ UDP", + }, + ocm: { + credentialPath: "Đường dẫn Thông tin xác thực", + usagesPath: "Đường dẫn Thống kê", + users: "Người dùng", + userName: "Tên", + userToken: "Token", + }, + ccm: { + credentialPath: "Đường dẫn Thông tin xác thực", + usagesPath: "Đường dẫn Thống kê", + users: "Người dùng", + userName: "Tên", + userToken: "Token", + }, + derp: { + configPath: "Đường dẫn Cấu hình", + verifyClientEndpoint: "Điểm cuối Xác minh Máy khách", + verifyClientUrl: "URL Xác minh Máy khách", + meshWith: "Kết nối Mesh với", + meshPsk: "Khóa PSK Mesh", + meshPskFile: "Tệp Khóa PSK Mesh", + stun: "Máy chủ STUN", + options: "Tùy chọn DERP", + }, + naive: { + insecureConcurrency: "Đồng thời không an toàn", + quic: "QUIC", + quicCongestion: "Điều khiển tắc nghẽn QUIC", + udpOverTcp: "UDP qua TCP", + }, + anytls: { + idleInterval: "Khoảng kiểm tra phiên nhàn rỗi", + idleTimeout: "Thời gian chờ phiên nhàn rỗi", + minIdle: "Số phiên nhàn rỗi tối thiểu" + }, + }, + in: { + addr: "Địa chỉ", + port: "Cổng", + ssMethod: "Phương thức", + ssManageable: "Quản lý được", + sSide: "Phía Máy chủ", + cSide: "Phía Khách hàng", + multiDomain: "Nhiều Tên miền", + remark: "Ghi chú", + mdOption: "Tùy chọn Nhiều Tên miền", + }, + listen: { + options: "Tùy chọn Nghe", + tcpOptions: "Tùy chọn TCP", + udpOptions: "Tùy chọn UDP", + detour: "Lạc đạo", + detourText: "Chuyển tiếp tới đầu vào", + disableTcpKeepAlive: "Tắt TCP Keep Alive", + tcpKeepAlive: "TCP Keep Alive", + tcpKeepAliveInterval: "Khoảng thời gian TCP Keep Alive", + }, + dial: { + bindIf: "Ràng buộc tới Giao diện Mạng", + bindIp4: "Ràng buộc tới IPv4", + bindIp6: "Ràng buộc tới IPv6", + bindNoPort: "Ràng buộc địa chỉ không cổng", + reuseAddr: "Sử dụng lại Địa chỉ Nghe", + connTimeout: "Thời gian Chờ Kết nối", + disableTcpKeepAlive: "Tắt TCP Keep Alive", + tcpKeepAlive: "TCP Keep Alive", + tcpKeepAliveInterval: "Khoảng thời gian TCP Keep Alive", + domainResolver: "Trình phân giải Tên miền", + options: "Tùy chọn Gọi", + detourText: "Chuyển tiếp tới thư đi", + }, + transport: { + enable: "Kích hoạt vận chuyển", + host: "Máy chủ", + hosts: "Máy chủ", + path: "Đường dẫn", + httpMethod: "Phương thức Yêu cầu", + idleTimeout: "Thời gian Chờ Chờ đợi", + pingTimeout: "Thời gian Chờ Ping", + grpcServiceName: "Tên Dịch vụ", + grpcPws: "Cho phép mà không Có Luồng", + }, + mux: { + enable: "Bật Multiplex", + maxConn: "Số kết nối Tối đa", + minStr: "Số Luồng Tối thiểu", + maxStr: "Số Luồng Tối đa", + padding: "Chỉ đệm", + enableBrutal: "Bật Brutal", + }, + out: { + addr: "Địa chỉ Máy chủ", + port: "Cổng Máy chủ", + addUrlTest: "Thêm URLTest", + delay: "Độ trễ", + }, + rule: { + add: "Thêm Quy tắc", + simple: "Đơn giản", + logical: "Logic", + mode: "Chế độ", + invert: "Nghịch đảo", + ipVer: "Phiên bản IP", + domain: "Tên miền", + domainSufix: "Hậu tố Miền", + domainKw: "Từ khóa Miền", + domainRgx: "Regex Miền", + ip: "CIDRs IP", + privateIp: "Dải IP Không hợp lệ", + port: "Cổng", + portRange: "Dải Cổng", + srcCidr: "CIDRs IP Nguồn", + srcPrivateIp: "IP Nguồn Không hợp lệ", + srcPort: "Cổng Nguồn", + srcPortRange: "Dải Cổng Nguồn", + ruleset: "Bộ quy tắc", + rulesetMatchSrc: "Bộ quy tắc IPcidr Phù hợp Nguồn", + preferredBy: "Ưu tiên theo đầu ra", + interfaceAddr: "Địa chỉ giao diện", + options: "Tùy chọn Quy tắc", + domainRules: "Tên miền/IP", + srcIpRules: "IP Nguồn", + srcPortRules: "Cổng Nguồn", + udpDisableDomainUnmapping: "Không màm mạng tiền lập tên miền", + udpConnect: "Kết nối UDP", + udpTimeout: "Thời gian Chờ UDP", + method: "Phương pháp", + noDrop: "Không Tháp", + sniffer: "Kiểm tra Sniffer", + timeout: "Thời gian Chờ Sniffing", + strategy: "Chiến lệ", + etaHint: "Mỗi dòng một mục. Dòng trống và mục trùng lặp sẽ bị bỏ qua.", + import: { + title: "Nhập hàng loạt bộ quy tắc", + rulesTitle: "Nhập quy tắc", + urlsHint: "Một URL mỗi dòng. Thẻ được lấy từ tên tệp (không gồm phần mở rộng).", + fileHint: "Tải lên tệp .txt chứa URL, mỗi dòng một URL.", + jsonHint: "Dán đối tượng JSON có mảng rules và/hoặc rule_set. Có thể dán cả khối \"route\": {'{'}...{'}'} hoặc chỉ nội dung bên trong.", + fileJsonHint: "Tải lên tệp .json có khối route.", + urlHint: "Nhập liên kết trực tiếp tới tệp JSON (ví dụ liên kết raw GitHub).", + preview: "Xem trước", + skipped: "đã tồn tại, hiển thị màu xám", + conflict: "Phát hiện xung đột", + merge: "Gộp — thêm quy tắc đã nhập (bỏ qua thẻ bộ trùng)", + replace: "Thay thế — xóa quy tắc và bộ hiện có, nhập lại", + pasteUrls: "Dán URL", + uploadTxt: "Tải lên .txt", + uploadFile: "Tải lên tệp", + fromUrl: "Theo URL", + selectTxt: "Chọn tệp .txt", + selectJson: "Chọn tệp .json", + parse: "Phân tích", + conflictDesc: "Cấu hình đã có {rules} quy tắc và {rulesets} bộ quy tắc. Chọn thao tác:", + finalOutbound: "Outbound mặc định (final)", + applyFinal: "Áp dụng làm outbound mặc định", + errNoArrays: 'Không tìm thấy mảng "rules" hoặc "rule_set".', + errJsonParse: "Lỗi phân tích JSON: {message}", + errNoArraysFetched: 'Không tìm thấy "rules" hoặc "rule_set" trong JSON đã tải.', + errFetch: "Lỗi tải: {message}", + errNoFile: "Chưa chọn tệp.", + errNoArraysInFile: 'Không tìm thấy "rules" hoặc "rule_set" trong tệp.', + }, + }, + ruleset: { + add: "Thêm Bộ quy tắc", + format: "Định dạng Dữ liệu", + interval: "Khoảng cách Cập nhật", + remote: "Xa", + local: "Cục bộ", + }, + dns: { + add: "Thêm Máy chủ DNS", + title: "Máy chủ DNS", + final: "Cuối cùng", + server: "Máy chủ", + firstServer: "Máy chủ Đầu tiên", + cacheCapacity: "Nội dung bộ nhớ", + disableCache: "Vô hiệu hóa bộ nhớ đệm", + disableExpire: "Vô hiệu hóa hệ thống", + independentCache: "Bộ nhớ rẽ", + reverseMapping: "Màm mạng tên lập", + domainStrategy: "Chiến lược Domain", + local: { preferGo: "Ưu tiên Go" }, + rule: { + add: "Thêm Quy tắc DNS", + title: "Quy tắc DNS", + inet4Range: "Dải CIDR IPv4", + inet6Range: "Dải CIDR IPv6", + acceptDefault: "Chấp nhận Mặc định", + action: { + title: "Hành động", + route: "Định tuyến", + routeOptions: "Tùy chọn định tuyến", + reject: "Từ chối", + predefined: "Định nghĩa sẵn", + rewriteTtl: "Ghi đè TTL", + clientSubnet: "Subnet của máy khách", + rcode: "Máy chủ tên", + rcodes: { + noError: "OK", + formerr: "Yêu cầu không hợp lệ", + servFail: "Lỗi máy chủ", + nxDomain: "Không tìm thấy", + refused: "Bị từ chối", + notImp: "Chưa được hỗ trợ" + }, + answer: "Câu trả lời", + ns: "Máy chủ tên", + extra: "Bổ sung" + } + } + }, + basic: { + log: { + title: "Nhật ký", + level: "Mức độ", + output: "Đầu ra", + timestamp: "Bật Dấu thời gian", + }, + routing: { + title: "Định tuyến", + defaultOut: "Ra ngoài Mặc định", + defaultIf: "NIC Mặc định", + defaultRm: "Đánh dấu Định tuyến Mặc định", + defaultDns: "Máy chủ DNS Mặc định", + autoBind: "Tự động Ràng buộc NIC", + }, + exp: { + storeFakeIp: "Lưu IP Giả mạo", + extController: "Trình điều khiển bên ngoài", + extUi: "Giao diện người dùng bên ngoài", + extUiDownloadUrl: "URL tải giao diện", + extUiDownloadDetour: "Chuyển hướng tải giao diện", + secret: "Mã bí mật", + defaultMode: "Chế độ mặc định", + allowOrigin: "Cho phép nguồn gốc", + allowPrivate: "Cho phép mạng riêng", + }, + }, + tls : { + enable: "Kích hoạt TLS", + usePath: "Sử dụng đường dẫn", + useText: "Sử dụng văn bản", + certPath: "Đường dẫn tệp chứng chỉ", + keyPath: "Đường dẫn tệp khóa", + cert: "Chứng chỉ", + key: "Khóa", + options: "Tùy chọn TLS", + minVer: "Phiên bản Tối thiểu", + maxVer: "Phiên bản Tối đa", + cs: "Các bộ mã hóa", + privKey: "Khóa riêng", + pubKey: "Khóa Công khai", + disableSni: "Tắt SNI", + insecure: "Cho phép Không an toàn", + fragment: "Kiểm tra hệ thống", + fragmentDelay: "Khoảng thời gian hệ thống", + recordFragment: "Kiểm tra bộ nhớ", + store: "Kho lưu trữ gốc", + ktls: "TLS nhân", + kernelTx: "TX", + kernelRx: "RX", + queryServerName: "Tên máy chủ truy vấn ECH", + acme: { + options: "Tùy chọn ACME", + dataDir: "Thư mục Dữ liệu", + defaultDomain: "Tên miền Mặc định", + disableChallenges: "Vô hiệu hóa Thách thức", + httpChallenge: "Vô hiệu hóa Thách thức HTTP", + tlsChallenge: "Vô hiệu hóa Thách thức TLS", + altPorts: "Cổng Thay thế", + altHport: "Cổng HTTP Thay thế", + altTport: "Cổng TLS Thay thế", + caProvider: "Nhà cung cấp CA", + customCa: "Nhà cung cấp CA Tùy chỉnh", + extAcc: "Tài khoản Bên ngoài", + dns01: "Thách thức DNS01", + dns01Provider: "Nhà cung cấp Thách thức DNS01", + dns01Params: { + api_token: "Mã API", + zone_token: "Mã Vùng", + access_key_id: "ID Khóa Truy cập", + access_key_secret: "Bí mật Khóa Truy cập", + region_id: "ID Khu vực", + security_token: "Mã Bảo mật", + username: "Tên đăng nhập", + password: "Mật khẩu", + subdomain: "Tên miền phụ", + server_url: "URL Máy chủ", + }, + }, + }, + stats: { + upload: "Tải lên", + download: "Tải xuống", + volume: "Thể tích", + usage: "Sử dụng", + enable: "Kích hoạt thống kê", + graphTitle: "Biểu đồ lưu lượng", + B: "B", + KB: "KB", + MB: "MB", + GB: "GB", + TB: "TB", + PB: "PB", + p: "ph", + Kp: "Kph", + Mp: "Mph", + Gp: "Gph", + Mbps: "Mbps", + }, + date: { + expiry: "Hết hạn", + expired: "Đã hết hạn", + d: "ng", + h: "g", + m: "p", + s: "s", + ms: "ms", + }, +} diff --git a/frontend/src/locales/zhcn.ts b/frontend/src/locales/zhcn.ts new file mode 100644 index 0000000..0a9f012 --- /dev/null +++ b/frontend/src/locales/zhcn.ts @@ -0,0 +1,635 @@ +export default { + success: "成功", + failed: "失败", + enable: "启用", + disable: "禁用", + none: "无", + all: "全部", + loading: "加载中...", + confirm: "是否确定?", + yes: "确认", + no: "取消", + unlimited: "无限", + type: "类型", + protocol: "协议", + submit: "提交", + reset: "重置", + now: "当前", + network: "网络", + copyToClipboard: "复制到剪贴板", + noData: "无数据!", + invalidLogin: "登录无效!", + online: "在线", + version: "版本", + email: "电子邮件", + commaSeparated: "(逗号分隔)", + count: "计数", + template: "模板", + editor: "编辑器", + error: { + dplData: "重复数据", + core: "Sing-Box 错误", + invalidData: "无效数据", + }, + theme: { + light: "浅色", + dark: "深色", + system: "跟随系统", + }, + pages: { + login: "登录", + home: "主页", + inbounds: "入站管理", + outbounds: "出站管理", + services: "服务管理", + endpoints: "节点管理", + clients: "用户管理", + rules: "路由列表", + tls: "TLS 设置", + basics: "基础信息", + dns: "DNS", + admins: "管理员", + settings: "设置", + }, + main: { + tiles: "信息卡", + gauges: "仪表板", + charts: "图表", + infos: "信息", + gauge: { + cpu: "CPU 仪表", + mem: "RAM 仪表", + dsk: "Disk 仪表", + swp: "Swap 仪表", + }, + chart: { + cpu: "CPU 监视器", + mem: "RAM 监视器", + net: "网络带宽", + pnet: "网络数据包", + dio: "Disk I/O", + }, + info: { + sys: "系统信息", + sbd: "运行信息", + host: "主机", + cpu: "CPU", + core: "核心", + uptime: "运行时间", + startupTime: "启动时间", + threads: "线程", + memory: "内存", + running: "运行状态" + }, + backup: { + title: "备份与恢复", + backup: "下载备份", + restore: "恢复备份", + exclStats: "排除图表数据", + exclChanges: "排除变更数据", + sbConfig: "下载 Sing-Box 配置", + }, + stats: { + title: "使用量与统计", + totalUsage: "总用量", + }, + }, + objects: { + inbound: "入站", + client: "客户端", + outbound: "出站", + endpoint: "节点", + config: "配置", + rule: "规则", + ruleset: "规则集", + service: "服务", + dnsserver: "DNS 服务器", + dnsrule: "DNS规则", + user: "用户", + tag: "标签", + listen: "监听", + dial: "拨号", + tls: "TLS", + multiplex: "多路复用", + transport: "传输", + headers: "标头", + key: "键", + value: "值", + }, + actions: { + action: "操作", + add: "添加", + addbulk: "批量添加", + editbulk: "批量编辑", + delbulk: "批量删除", + new: "新建", + edit: "编辑", + del: "删除", + clone: "克隆", + test: "测试", + testAll: "测试全部", + save: "保存", + update: "更新", + submit: "提交", + set: "设置", + generate: "生成", + disable: "禁用", + close: "关闭", + restartApp: "重启面板", + restartSb: "重启 Singbox", + }, + login: { + title: "登录", + username: "用户名", + unRules: "用户名不能为空", + password: "密码", + pwRules: "密码不能为空", + }, + menu: { + logout: "退出登录", + }, + admin: { + changeCred: "更改凭据", + oldPass: "当前密码", + newUname: "新用户名", + newPass: "新密码", + lastLogin: "上次登录", + date: "日期", + time: "时间", + changes: "更改", + actor: "执行者", + key: "键", + action: "操作", + api: { + title: "API 令牌", + msg: "请复制令牌并保存到安全的地方。它将不再显示。", + token: "令牌", + }, + }, + setting: { + interface: "界面", + sub: "订阅", + addr: "地址", + port: "端口", + webPath: "面板路径", + domain: "域名", + sslKey: "SSL 密钥 (Key) 路径", + sslCert: "SSL 证书 (cert) 路径", + webUri: "面板 URI", + sessionAge: "会话超时时限", + trafficAge: "流量过期时限", + timeLoc: "时区", + subEncode: "启用 Base64 编码", + subInfo: "启用用户信息", + path: "默认路径", + update: "自动更新时间", + subUri: "订阅 URI", + jsonSub: "JSON 订阅", + toDirect: "路由到直连", + toBlock: "路由到阻止", + timestamp: "时间戳", + globalDns: "全局 DNS", + directDns: "直连 DNS", + toDirectDns: "路由到直连 DNS", + jsonSubOptions: "其他选项", + excludePkg: "排除包", + clashSub: "Clash 订阅", + mixedPort: "混合入站端口", + tun: "Tun 入站", + }, + client: { + name: "名称", + desc: "描述", + group: "组", + inboundTags: "入站标签", + basics: "基础", + config: "配置", + links: "链接", + external: "外部链接", + sub: "外部订阅", + delayStart: "延迟启动", + autoReset: "自动重置", + resetDays: "重置天数", + nextReset: "下次重置", + }, + bulk: { + order: "排序", + random: "随机", + changeLimits: "更改限制", + addInbounds: "添加入站", + removeInbounds: "移除入站", + addDays: "增加天数", + addVolume: "增加流量", + }, + types: { + un: "用户名", + pw: "密码", + direct: { + overrideAddr: "覆盖地址", + overridePort: "覆盖端口", + }, + hy: { + obfs: "混淆密码", + auth: "认证密码", + hyOptions: "Hysteria 选项", + hy2Options: "Hysteria2 选项", + ignoreBw: "忽略客户端带宽", + }, + shdwTls: { + hs: "握手服务器", + addHS: "添加握手服务器", + }, + ssh: { + passphrase: "密码短语", + hostKey: "主机密钥", + algorithm: "密钥算法", + clientVer: "客户端版本", + options: "SSH 选项", + }, + tor: { + execPath: "可执行文件路径", + dataDir: "数据目录", + extArgs: "额外参数", + }, + tuic: { + congControl: "拥塞控制", + authTimeout: "认证超时", + hb: "心跳包", + }, + tun: { + addr: "地址", + ifName: "接口名称", + excludeMptcp: "排除 MPTCP", + fallbackRuleIndex: "iproute2 回退规则索引", + }, + vless: { + flow: "流控", + udpEnc: "UDP 数据包编码", + }, + vmess: { + security: "安全性", + globalPadding: "全局填充", + authLen: "加密长度", + }, + wg: { + privKey: "私钥", + pubKey: "对等方公钥", + psk: "预共享密钥", + localIp: "本地 IP 地址", + worker: "工作线程", + ifName: "接口名称", + sysIf: "系统接口", + options: "WireGuard 选项", + allowedIp: "允许的 IP 地址", + peer: "对等体", + peers: "对等体", + }, + lb: { + defaultOut: "默认出站", + interruptConn: "中断现有连接", + testUrl: "测试 URL", + interval: "间隔", + tolerance: "容错", + urlTestOptions: "URLTest 选项", + }, + ts: { + options: "Tailscale 选项", + stateDir: "状态目录", + authKey: "认证密钥", + relayServer: "中继服务器", + relayServerPort: "中继服务器端口", + relayEndpoints: "中继静态端点", + systemInterface: "系统接口", + sysIfName: "接口名称", + sysIfMtu: "接口 MTU", + controlUrl: "控制 URL", + ephemeral: "临时节点", + hostname: "主机名", + acceptRoutes: "接受路由", + exitNode: "出口节点", + allowLanAccess: "允许 LAN 访问", + advRoutes: "广告路由", + advExitNode: "广告出口节点", + udpTimeout: "UDP 超时", + }, + ocm: { + credentialPath: "凭据路径", + usagesPath: "用量统计路径", + users: "用户", + userName: "名称", + userToken: "令牌", + }, + ccm: { + credentialPath: "凭据路径", + usagesPath: "用量统计路径", + users: "用户", + userName: "名称", + userToken: "令牌", + }, + derp: { + configPath: "配置路径", + verifyClientEndpoint: "验证客户端端点", + verifyClientUrl: "验证客户端 URL", + meshWith: "与其他 DERP 节点网格连接", + meshPsk: "网格预共享密钥", + meshPskFile: "网格预共享密钥文件", + stun: "STUN 服务器", + options: "DERP 选项", + }, + naive: { + insecureConcurrency: "不安全并发数", + quic: "QUIC", + quicCongestion: "QUIC 拥塞控制", + udpOverTcp: "UDP over TCP", + }, + anytls: { + idleInterval: "空闲会话检查间隔", + idleTimeout: "空闲会话超时", + minIdle: "最小空闲会话数" + }, + }, + in: { + addr: "地址", + port: "端口", + ssMethod: "方法", + ssManageable: "可管理的", + sSide: "服务器端", + cSide: "客户端", + multiDomain: "多域名", + remark: "备注", + mdOption: "多域名选项", + }, + listen: { + options: "监听选项", + tcpOptions: "TCP 选项", + udpOptions: "UDP 选项", + detour: "转发", + detourText: "转发到入站", + disableTcpKeepAlive: "禁用 TCP Keep Alive", + tcpKeepAlive: "TCP Keep Alive", + tcpKeepAliveInterval: "TCP Keep Alive 间隔", + }, + dial: { + bindIf: "绑定到网络接口", + bindIp4: "绑定到 IPv4", + bindIp6: "绑定到 IPv6", + bindNoPort: "绑定地址不占端口", + reuseAddr: "重用监听地址", + connTimeout: "连接超时", + disableTcpKeepAlive: "禁用 TCP Keep Alive", + tcpKeepAlive: "TCP Keep Alive", + tcpKeepAliveInterval: "TCP Keep Alive 间隔", + domainResolver: "域名解析器", + options: "拨号选项", + detourText: "转发至出站", + }, + transport: { + enable: "启用传输", + host: "主机域名", + hosts: "主机域名列表", + path: "HTTP 请求路径", + httpMethod: "HTTP 请求方法", + idleTimeout: "空闲超时", + pingTimeout: "Ping 超时", + grpcServiceName: "gRPC 服务名称", + grpcPws: "允许无流时保持连接", + }, + mux: { + enable: "启用多路复用", + maxConn: "最大连接数", + minStr: "最小流数", + maxStr: "最大流数", + padding: "仅允许填充连接", + enableBrutal: "启用 TCP Brutal", + }, + out: { + addr: "服务器地址", + port: "服务器端口", + addUrlTest: "添加 URLTest", + delay: "延迟", + }, + rule: { + add: "添加规则", + simple: "简单", + logical: "逻辑", + mode: "模式", + invert: "反选结果", + ipVer: "IP 版本", + domain: "域名", + domainSufix: "域名后缀", + domainKw: "域名关键词", + domainRgx: "域名正则表达式", + ip: "IP CIDR", + privateIp: "匹配非公开 IP", + port: "端口", + portRange: "端口范围", + srcCidr: "源 IP CIDR", + srcPrivateIp: "匹配非公开源 IP", + srcPort: "源端口", + srcPortRange: "源端口范围", + ruleset: "规则集", + rulesetMatchSrc: "规则集 IP CIDR 匹配源 IP", + preferredBy: "优选出站", + interfaceAddr: "接口地址", + options: "规则选项", + domainRules: "域名/IP", + srcIpRules: "源 IP", + srcPortRules: "源端口", + udpDisableDomainUnmapping: "禁用域名解析映射", + udpConnect: "启用 UDP 连接", + udpTimeout: "UDP 超时", + method: "方法", + noDrop: "不丢弃", + sniffer: "嗅探", + timeout: "超时", + strategy: "策略", + etaHint: "每行一项。空行和重复项将被忽略。", + import: { + title: "批量导入规则集", + rulesTitle: "导入规则", + urlsHint: "每行一个 URL。标签由文件名(不含扩展名)决定。", + fileHint: "上传包含 URL 的 .txt 文件,每行一个。", + jsonHint: "粘贴包含 rules 和/或 rule_set 数组的 JSON 对象。可粘贴整个 \"route\" 块:{'{'}...{'}'} 或仅其内容。", + fileJsonHint: "上传包含 route 块的 .json 文件。", + urlHint: "填写 JSON 文件的直链(例如 GitHub raw 链接)。", + preview: "预览", + skipped: "已存在,以灰色显示", + conflict: "检测到冲突", + merge: "合并 — 添加导入的规则(跳过重复的规则集标签)", + replace: "替换 — 删除现有规则与规则集后重新导入", + pasteUrls: "粘贴 URL", + uploadTxt: "上传 .txt", + uploadFile: "上传文件", + fromUrl: "通过链接", + selectTxt: "选择 .txt 文件", + selectJson: "选择 .json 文件", + parse: "解析", + conflictDesc: "配置中已有 {rules} 条规则和 {rulesets} 个规则集。请选择操作:", + finalOutbound: "默认出站(final)", + applyFinal: "设为默认出站", + errNoArrays: '未找到 "rules" 或 "rule_set" 数组。', + errJsonParse: "JSON 解析错误:{message}", + errNoArraysFetched: '获取的 JSON 中未找到 "rules" 或 "rule_set"。', + errFetch: "获取失败:{message}", + errNoFile: "未选择文件。", + errNoArraysInFile: '文件中未找到 "rules" 或 "rule_set"。', + }, + }, + ruleset: { + add: "添加规则集", + format: "数据格式", + interval: "更新间隔", + remote: "远程", + local: "本地", + }, + dns: { + add: "添加 DNS 服务器", + title: "DNS 服务器", + final: "最终", + server: "服务器", + firstServer: "首选服务器", + cacheCapacity: "缓存容量", + disableCache: "禁用缓存", + disableExpire: "禁用过期", + independentCache: "独立缓存", + reverseMapping: "反向映射", + domainStrategy: "域名解析策略", + local: { preferGo: "优先使用 Go" }, + rule: { + add: "添加 DNS 规则", + title: "DNS 规则", + inet4Range: "IPv4 范围", + inet6Range: "IPv6 范围", + acceptDefault: "接受默认", + action: { + title: "操作", + route: "路由", + routeOptions: "路由选项", + reject: "拒绝", + predefined: "预定义", + rewriteTtl: "重写 TTL", + clientSubnet: "客户端子网", + rcode: "响应码", + rcodes: { + noError: "正常", + formerr: "请求错误", + servFail: "服务器故障", + nxDomain: "未找到", + refused: "被拒绝", + notImp: "未实现" + }, + answer: "回答", + ns: "名称服务器", + extra: "附加" + } + } + }, + basic: { + log: { + title: "日志", + level: "级别", + output: "输出", + timestamp: "启用时间戳", + }, + routing: { + title: "路由", + defaultOut: "默认出站", + defaultIf: "默认网卡", + defaultRm: "默认路由标记", + defaultDns: "默认 DNS 解析器", + autoBind: "自动绑定网卡", + }, + exp: { + storeFakeIp: "持久化 Fake-IP", + extController: "外部控制器", + extUi: "外部界面", + extUiDownloadUrl: "界面下载地址", + extUiDownloadDetour: "界面下载绕行", + secret: "密钥", + defaultMode: "默认模式", + allowOrigin: "允许来源", + allowPrivate: "允许私有网络", + }, + }, + tls : { + enable: "启用 TLS", + usePath: "使用外部路径", + useText: "使用文件内容", + certPath: "证书文件路径", + keyPath: "私钥文件路径", + cert: "证书文件内容", + key: "私钥文件内容", + options: "TLS 选项", + minVer: "最低版本", + maxVer: "最高版本", + cs: "密码套件", + privKey: "私钥", + pubKey: "公钥", + disableSni: "禁用 SNI", + insecure: "允许不安全", + fragment: "启用", + fragmentDelay: "启用后延迟", + recordFragment: "启用", + store: "根证书库", + ktls: "内核 TLS", + kernelTx: "发送", + kernelRx: "接收", + queryServerName: "ECH 查询服务器名称", + acme: { + options: "ACME 选项", + dataDir: "数据目录", + defaultDomain: "默认域名", + disableChallenges: "禁用挑战", + httpChallenge: "禁用 HTTP 挑战", + tlsChallenge: "禁用 TLS 挑战", + altPorts: "替代端口", + altHport: "替代 HTTP 端口", + altTport: "替代 TLS 端口", + caProvider: "CA 提供商", + customCa: "自定义 CA 提供商", + extAcc: "外部账户", + dns01: "DNS01 挑战", + dns01Provider: "DNS01 挑战提供商", + dns01Params: { + api_token: "API 令牌", + zone_token: "Zone 令牌", + access_key_id: "访问密钥 ID", + access_key_secret: "访问密钥密文", + region_id: "区域 ID", + security_token: "安全令牌", + username: "用户名", + password: "密码", + subdomain: "子域名", + server_url: "服务器 URL", + }, + }, + }, + stats: { + upload: "上传", + download: "下载", + volume: "流量", + usage: "已用", + enable: "启用统计", + graphTitle: "流量图表", + B: "B", + KB: "KB", + MB: "MB", + GB: "GB", + TB: "TB", + PB: "PB", + p: "p", + Kp: "Kp", + Mp: "Mp", + Gp: "Gp", + Mbps: "Mbps", + }, + date: { + expiry: "到期", + expired: "已到期", + d: "天", + h: "时", + m: "分", + s: "秒", + ms: "毫秒", + }, +} diff --git a/frontend/src/locales/zhtw.ts b/frontend/src/locales/zhtw.ts new file mode 100644 index 0000000..727cef3 --- /dev/null +++ b/frontend/src/locales/zhtw.ts @@ -0,0 +1,635 @@ +export default { + success: "成功", + failed: "失敗", + enable: "啟用", + disable: "禁用", + none: "無", + all: "全部", + loading: "加載中...", + confirm: "是否確定?", + yes: "確認", + no: "取消", + unlimited: "無限", + type: "類型", + protocol: "協定", + submit: "提交", + reset: "重置", + now: "當前", + network: "網絡", + copyToClipboard: "復製到剪貼板", + noData: "無數據!", + invalidLogin: "登錄無效!", + online: "在線", + version: "版本", + email: "電子郵件", + commaSeparated: "(逗號分隔)", + count: "計數", + template: "模板", + editor: "編輯器", + error: { + dplData: "重複數據", + core: "Sing-Box 錯誤", + invalidData: "無效數據", + }, + theme: { + light: "明亮", + dark: "暗黑", + system: "系統", + }, + pages: { + login: "登錄", + home: "主頁", + inbounds: "入站管理", + outbounds: "出站管理", + services: "服務管理", + endpoints: "端點管理", + clients: "用戶管理", + rules: "路由列表", + tls: "TLS 設置", + basics: "基礎信息", + dns: "DNS", + admins: "管理員", + settings: "設置", + }, + main: { + tiles: "信息卡", + gauges: "儀表板", + charts: "圖表", + infos: "信息", + gauge: { + cpu: "CPU 儀表", + mem: "RAM 儀表", + dsk: "Disk 儀表", + swp: "Swap 儀表", + }, + chart: { + cpu: "CPU 監視器", + mem: "RAM 監視器", + net: "網絡帶寬", + pnet: "網絡數據包", + dio: "Disk I/O", + }, + info: { + sys: "系統信息", + sbd: "運行信息", + host: "主機", + cpu: "CPU", + core: "核心", + uptime: "運行時間", + startupTime: "啟動時間", + threads: "線程", + memory: "內存", + running: "運行狀態" + }, + backup: { + title: "備份與恢復", + backup: "下載備份", + restore: "恢復備份", + exclStats: "排除圖表記錄", + exclChanges: "排除更改記錄", + sbConfig: "下載 Sing-Box 配置", + }, + stats: { + title: "使用量與統計", + totalUsage: "總用量", + }, + }, + objects: { + inbound: "入站", + client: "客戶端", + outbound: "出站", + endpoint: "端點", + config: "配置", + rule: "規則", + ruleset: "規則集", + service: "服務", + dnsserver: "DNS 服務器", + dnsrule: "DNS 規則", + user: "用戶", + tag: "標簽", + listen: "聽", + dial: "撥號", + tls: "TLS", + multiplex: "多路復用", + transport: "傳輸", + headers: "方法", + key: "鑰匙", + value: "價值", + }, + actions: { + action: "操作", + add: "添加", + addbulk: "批量添加", + editbulk: "批量編輯", + delbulk: "批量刪除", + new: "新建", + edit: "編輯", + del: "刪除", + clone: "克隆", + test: "測試", + testAll: "測試全部", + save: "保存", + update: "更新", + submit: "提交", + set: "設置", + generate: "生成", + disable: "禁用", + close: "關閉", + restartApp: "重啟面板", + restartSb: "重啟 Singbox", + }, + login: { + title: "登錄", + username: "用戶名", + unRules: "用戶名不能為空", + password: "密碼", + pwRules: "密碼不能為空", + }, + menu: { + logout: "退出登錄", + }, + admin: { + changeCred: "更改憑據", + oldPass: "當前密碼", + newUname: "新用戶名", + newPass: "新密碼", + lastLogin: "上次登入", + date: "日期", + time: "時間", + changes: "更改", + actor: "執行者", + key: "鍵", + action: "操作", + api: { + title: "API 憑據", + msg: "請複製下面的憑據並儲存在安全的地方。它將不再顯示。", + token: "憑據", + }, + }, + setting: { + interface: "界面", + sub: "訂閱", + addr: "地址", + port: "端口", + webPath: "基本 URI", + domain: "域名", + sslKey: "SSL 密鑰 (Key) 路徑", + sslCert: "SSL 證書 (cert) 路徑", + webUri: "面板 URI", + sessionAge: "會話最大連接數", + trafficAge: "流量最大年齡", + timeLoc: "時區", + subEncode: "啟用編碼", + subInfo: "啟用用戶信息", + path: "默認路徑", + update: "自動更新時間", + subUri: "訂閱 URL", + jsonSub: "JSON 訂閱", + toDirect: "路由到直連", + toBlock: "路由到阻止", + timestamp: "時間戳", + globalDns: "全局 DNS", + directDns: "直連 DNS", + toDirectDns: "路由到直連 DNS", + jsonSubOptions: "其他選項", + excludePkg: "排除包", + clashSub: "Clash 訂閱", + mixedPort: "混合入站端口", + tun: "Tun 入站", + }, + client: { + name: "名稱", + desc: "描述", + group: "組", + inboundTags: "入站標簽", + basics: "基礎", + config: "配置", + links: "鏈接", + external: "外部鏈接", + sub: "外部訂閱", + delayStart: "延遲啟動", + autoReset: "自動重置", + resetDays: "重置天數", + nextReset: "下次重置", + }, + bulk: { + order: "排序", + random: "隨機", + changeLimits: "變更限制", + addInbounds: "添加入站", + removeInbounds: "移除入站", + addDays: "增加天數", + addVolume: "增加流量", + }, + types: { + un: "用戶名", + pw: "密碼", + direct: { + overrideAddr: "覆蓋地址", + overridePort: "覆蓋端口", + }, + hy: { + obfs: "混淆密碼", + auth: "驗證密碼", + hyOptions: "Hysteria 選項", + hy2Options: "Hysteria2 選項", + ignoreBw: "忽略客戶端帶寬", + }, + shdwTls: { + hs: "握手服務器", + addHS: "添加握手服務器", + }, + ssh: { + passphrase: "密語", + hostKey: "主機密鑰", + algorithm: "密鑰算法", + clientVer: "客戶端版本", + options: "SSH 選項", + }, + tor: { + execPath: "可執行文件路徑", + dataDir: "數據目錄", + extArgs: "額外參數", + }, + tuic: { + congControl: "擁塞控制", + authTimeout: "身份驗證超時", + hb: "心跳", + }, + tun: { + addr: "地址", + ifName: "介面名稱", + excludeMptcp: "排除 MPTCP", + fallbackRuleIndex: "iproute2 回退規則索引", + }, + vless: { + flow: "流量", + udpEnc: "UDP 封包編碼", + }, + vmess: { + security: "安全性", + globalPadding: "全局填充", + authLen: "加密長度", + }, + wg: { + privKey: "私鑰", + pubKey: "對等方公鑰", + psk: "預共享密鑰", + localIp: "本地 IP", + worker: "工作線程", + ifName: "介面名稱", + sysIf: "系統介面", + options: "Wireguard 選項", + allowedIp: "允許的 IP", + peer: "對等方", + peers: "對等方", + }, + lb: { + defaultOut: "默認外部", + interruptConn: "中斷現有連接", + testUrl: "測試 URL", + interval: "間隔", + tolerance: "容忍度", + urlTestOptions: "URLTest 選項" + }, + ts: { + options: "Tailscale 選項", + stateDir: "狀態目錄", + authKey: "授權密鑰", + relayServer: "轉發伺服器", + relayServerPort: "轉發伺服器端口", + relayEndpoints: "轉發靜態端點", + systemInterface: "系統介面", + sysIfName: "介面名稱", + sysIfMtu: "介面 MTU", + controlUrl: "控制 URL", + ephemeral: "臨時節點", + hostname: "主機名", + acceptRoutes: "接受路由", + exitNode: "出口節點", + allowLanAccess: "允許 LAN 訪問", + advRoutes: "廣告路由", + advExitNode: "廣告出口節點", + udpTimeout: "UDP 超時", + }, + ocm: { + credentialPath: "憑證路徑", + usagesPath: "用量統計路徑", + users: "用戶", + userName: "名稱", + userToken: "令牌", + }, + ccm: { + credentialPath: "憑證路徑", + usagesPath: "用量統計路徑", + users: "用戶", + userName: "名稱", + userToken: "令牌", + }, + derp: { + configPath: "配置路徑", + verifyClientEndpoint: "驗證客戶端端點", + verifyClientUrl: "驗證客戶端 URL", + meshWith: "網狀連接", + meshPsk: "網狀 PSK", + meshPskFile: "網狀 PSK 文件", + stun: "STUN 服務器", + options: "DERP 選項", + }, + naive: { + insecureConcurrency: "不安全並發數", + quic: "QUIC", + quicCongestion: "QUIC 擁塞控制", + udpOverTcp: "UDP over TCP", + }, + anytls: { + idleInterval: "閒置會話檢查間隔", + idleTimeout: "閒置會話逾時", + minIdle: "最小閒置會話數" + }, + }, + in: { + addr: "地址", + port: "端口", + ssMethod: "方法", + ssManageable: "可管理的", + sSide: "服務器端", + cSide: "客戶端", + multiDomain: "多域名", + remark: "備註", + mdOption: "多域名選項", + }, + listen: { + options: "監聽選項", + tcpOptions: "TCP 選項", + udpOptions: "UDP 選項", + detour: "繞道", + detourText: "轉發到入站", + disableTcpKeepAlive: "停用 TCP Keep Alive", + tcpKeepAlive: "TCP Keep Alive", + tcpKeepAliveInterval: "TCP Keep Alive 間隔", + }, + dial: { + bindIf: "綁定到網路接口", + bindIp4: "綁定到 IPv4", + bindIp6: "綁定到 IPv6", + bindNoPort: "綁定地址不佔用端口", + reuseAddr: "重用監聽地址", + connTimeout: "連接超時", + disableTcpKeepAlive: "停用 TCP Keep Alive", + tcpKeepAlive: "TCP Keep Alive", + tcpKeepAliveInterval: "TCP Keep Alive 間隔", + domainResolver: "域名解析器", + options: "撥號選項", + detourText: "轉寄至出站", + }, + transport: { + enable: "啟用傳輸", + host: "主機", + hosts: "主機列表", + path: "路徑", + httpMethod: "請求方法", + idleTimeout: "閒置超時", + pingTimeout: "Ping 超時", + grpcServiceName: "服務名稱", + grpcPws: "允許無流", + }, + mux: { + enable: "啟用多路徑", + maxConn: "最大連接數", + minStr: "最小串流數", + maxStr: "最大串流數", + padding: "僅填充", + enableBrutal: "啟用暴力", + }, + out: { + addr: "伺服器地址", + port: "伺服器端口", + addUrlTest: "新增 URLTest", + delay: "延遲", + }, + rule: { + add: "添加規則", + simple: "簡單", + logical: "邏輯", + mode: "模式", + invert: "反轉", + ipVer: "IP 版本", + domain: "域名", + domainSufix: "域名後綴", + domainKw: "域名關鍵詞", + domainRgx: "域名正則表達式", + ip: "IP CIDR", + privateIp: "無效 IP 範圍", + port: "端口", + portRange: "端口範圍", + srcCidr: "源 IP CIDR", + srcPrivateIp: "無效源 IP", + srcPort: "源端口", + srcPortRange: "源端口範圍", + ruleset: "規則集", + rulesetMatchSrc: "規則集 IP 範圍匹配源", + preferredBy: "優選出站", + interfaceAddr: "介面地址", + options: "規則選項", + domainRules: "域名/IP", + srcIpRules: "源 IP", + srcPortRules: "源端口", + udpDisableDomainUnmapping: "禁用域名解析映射", + udpConnect: "啟用 UDP 連接", + udpTimeout: "UDP 超時", + method: "方法", + noDrop: "不丟弃", + sniffer: "嗅探", + timeout: "超時", + strategy: "策略", + etaHint: "每行一項。空白行與重複項目將被略過。", + import: { + title: "批次匯入規則集", + rulesTitle: "匯入規則", + urlsHint: "每行一個 URL。標籤由檔名(不含副檔名)決定。", + fileHint: "上傳含 URL 的 .txt 檔案,每行一個。", + jsonHint: "貼上含 rules 及/或 rule_set 陣列的 JSON 物件。可貼上整個 \"route\" 區塊:{'{'}...{'}'} 或僅其內容。", + fileJsonHint: "上傳含 route 區塊的 .json 檔案。", + urlHint: "填寫 JSON 檔案的直連(例如 GitHub raw 連結)。", + preview: "預覽", + skipped: "已存在,以灰色顯示", + conflict: "偵測到衝突", + merge: "合併 — 加入匯入的規則(略過重複的規則集標籤)", + replace: "取代 — 刪除現有規則與規則集後重新匯入", + pasteUrls: "貼上 URL", + uploadTxt: "上傳 .txt", + uploadFile: "上傳檔案", + fromUrl: "透過連結", + selectTxt: "選擇 .txt 檔案", + selectJson: "選擇 .json 檔案", + parse: "解析", + conflictDesc: "設定中已有 {rules} 條規則與 {rulesets} 個規則集。請選擇操作:", + finalOutbound: "預設出站(final)", + applyFinal: "設為預設出站", + errNoArrays: '找不到 "rules" 或 "rule_set" 陣列。', + errJsonParse: "JSON 解析錯誤:{message}", + errNoArraysFetched: '取得的 JSON 中找不到 "rules" 或 "rule_set"。', + errFetch: "取得失敗:{message}", + errNoFile: "未選擇檔案。", + errNoArraysInFile: '檔案中找不到 "rules" 或 "rule_set"。', + }, + }, + ruleset: { + add: "添加規則集", + format: "數據格式", + interval: "更新間隔", + remote: "遠端", + local: "本地", + }, + dns: { + add: "添加 DNS 服務器", + title: "DNS 服務器", + final: "最終", + server: "服務器", + firstServer: "首選服務器", + cacheCapacity: "快取容量", + disableCache: "停用快取", + disableExpire: "停用過期", + independentCache: "獨立快取", + reverseMapping: "反向映射", + domainStrategy: "域名策略", + local: { preferGo: "優先使用 Go" }, + rule: { + add: "添加 DNS 規則", + title: "DNS 規則", + inet4Range: "IPv4 範圍", + inet6Range: "IPv6 範圍", + acceptDefault: "接受默認", + action: { + title: "操作", + route: "路由", + routeOptions: "路由選項", + reject: "拒絕", + predefined: "預設", + rewriteTtl: "重寫 TTL", + clientSubnet: "用戶端子網", + rcode: "回應碼", + rcodes: { + noError: "正常", + formerr: "請求錯誤", + servFail: "伺服器故障", + nxDomain: "未找到", + refused: "被拒絕", + notImp: "尚未實作" + }, + answer: "回應", + ns: "名稱伺服器", + extra: "額外資訊" + } + }, + }, + basic: { + log: { + title: "日誌", + level: "級別", + output: "輸出", + timestamp: "啟用時間戳記", + }, + routing: { + title: "路由", + defaultOut: "默認外部", + defaultIf: "默認網卡", + defaultRm: "默認路由標記", + defaultDns: "默認 DNS 解析器", + autoBind: "自動綁定網卡", + }, + exp: { + storeFakeIp: "存儲假 IP", + extController: "外部控制器", + extUi: "外部介面", + extUiDownloadUrl: "介面下載網址", + extUiDownloadDetour: "介面下載繞行", + secret: "密鑰", + defaultMode: "預設模式", + allowOrigin: "允許來源", + allowPrivate: "允許私人網路", + }, + }, + tls : { + enable: "啟用 TLS", + usePath: "使用外部路徑", + useText: "使用文件內容", + certPath: "證書文件路徑", + keyPath: "私鑰文件路徑", + cert: "證書文件內容", + key: "私鑰文件內容", + options: "TLS 選項", + minVer: "最低版本", + maxVer: "最高版本", + cs: "加密套件", + privKey: "私鑰", + pubKey: "公鑰", + disableSni: "停用 SNI", + insecure: "允許不安全連線", + fragment: "分段", + fragmentDelay: "分段回應延遲", + recordFragment: "多筆記錄分段", + store: "根憑證庫", + ktls: "內核 TLS", + kernelTx: "發送", + kernelRx: "接收", + queryServerName: "ECH 查詢伺服器名稱", + acme: { + options: "ACME 選項", + dataDir: "數據目錄", + defaultDomain: "默認域名", + disableChallenges: "禁用挑戰", + httpChallenge: "禁用 HTTP 挑戰", + tlsChallenge: "禁用 TLS 挑戰", + altPorts: "替代端口", + altHport: "替代 HTTP 端口", + altTport: "替代 TLS 端口", + caProvider: "CA 提供商", + customCa: "自定義 CA 提供商", + extAcc: "外部賬戶", + dns01: "DNS01 挑戰", + dns01Provider: "DNS01 挑戰提供商", + dns01Params: { + api_token: "API 令牌", + zone_token: "Zone 令牌", + access_key_id: "存取金鑰 ID", + access_key_secret: "存取金鑰密文", + region_id: "區域 ID", + security_token: "安全令牌", + username: "用戶名", + password: "密碼", + subdomain: "子網域", + server_url: "伺服器 URL", + }, + }, + }, + stats: { + upload: "上傳", + download: "下載", + volume: "流量", + usage: "已用", + enable: "啟用統計", + graphTitle: "流量圖表", + B: "B", + KB: "KB", + MB: "MB", + GB: "GB", + TB: "TB", + PB: "PB", + p: "p", + Kp: "Kp", + Mp: "Mp", + Gp: "Gp", + Mbps: "Mbps", + }, + date: { + expiry: "到期", + expired: "已到期", + d: "天", + h: "時", + m: "分", + s: "秒", + ms: "毫秒", + }, +} diff --git a/frontend/src/main.ts b/frontend/src/main.ts new file mode 100644 index 0000000..fb7d25d --- /dev/null +++ b/frontend/src/main.ts @@ -0,0 +1,55 @@ +/** + * main.ts + * + * Bootstraps Vuetify and other plugins then mounts the App` + */ + +// Composables +import { createApp, ref } from 'vue' + +// Components +import App from './App.vue' + +// Use router +import router from './router' + +// Store +import store from './store' + +// Plugins +import { registerPlugins } from '@/plugins' + +// Locale +import { i18n } from '@/locales' +import Vue3PersianDatetimePicker from 'vue3-persian-datetime-picker' + +// Notivue +import { createNotivue } from 'notivue' +import 'notivue/notification.css' +import 'notivue/animations.css' +const notivue = createNotivue({ + position: 'bottom-center', + limit: 4, + enqueue: false, + avoidDuplicates: true, + notifications: { + global: { + duration: 3000 + } + }, +}) + +const loading = ref(false) + +const app = createApp(App) +app.provide('loading', loading) + +registerPlugins(app) + +app + .use(router) + .use(store) + .use(i18n) + .use(notivue) + .component('DatePicker', Vue3PersianDatetimePicker) + .mount('#app') diff --git a/frontend/src/plugins/api.ts b/frontend/src/plugins/api.ts new file mode 100644 index 0000000..52b9e81 --- /dev/null +++ b/frontend/src/plugins/api.ts @@ -0,0 +1,57 @@ +import axios from 'axios' + +axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8' +axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest' + +axios.defaults.baseURL = "./" +const pendingRequests = new Map() + +axios.interceptors.request.use( + (config) => { + // Generate a unique key for the request + const requestKey = `${config.method}:${config.url}` + + // Check if there is already a pending request with the same key + if (pendingRequests.has(requestKey)) { + const cancelSource = pendingRequests.get(requestKey) + cancelSource.cancel('Duplicate request cancelled') + } + + // Create a new cancel token for the request + const cancelSource = axios.CancelToken.source() + config.cancelToken = cancelSource.token + + // Store the cancel token in the pending requests map + pendingRequests.set(requestKey, cancelSource) + + if (config.data instanceof FormData) { + config.headers['Content-Type'] = 'multipart/form-data' + } + return config + }, + (error) => Promise.reject(error), +) + +axios.interceptors.response.use( + (response) => { + // Remove the request from the pending requests map + const requestKey = `${response.config.method}:${response.config.url}` + pendingRequests.delete(requestKey) + return response + }, + (error) => { + if (axios.isCancel(error)) { + // Handle duplicate request cancellation here if needed + console.warn(error.message) + } else { + // Remove the request from the pending requests map on error + const requestKey = `${error.config.method}:${error.config.url}` + pendingRequests.delete(requestKey) + } + return Promise.reject(error) + } +) + +const api = axios.create() + +export default api diff --git a/frontend/src/plugins/httputil.ts b/frontend/src/plugins/httputil.ts new file mode 100644 index 0000000..99f0b52 --- /dev/null +++ b/frontend/src/plugins/httputil.ts @@ -0,0 +1,88 @@ +import api from './api' +import { i18n } from '@/locales' +import router from '@/router' +import { push } from 'notivue' + +export interface Msg { + success: boolean + msg: string + obj: any | null +} + +function _handleMsg(msg: any): void { + if (!isMsg(msg)) { + return + } + if(msg.msg){ + if (!msg.success && msg.msg == "Invalid login") { + push.error({ + title: i18n.global.t('invalidLogin'), + }) + logout() + return + } + if (msg.success) { + push.success({ + message: i18n.global.t('success') + ": " + i18n.global.t('actions.' + msg.msg), + }) + } else { + push.error({ + title: i18n.global.t('failed'), + message: msg.msg + }) + } + } +} + +export const logout = async () => { + const response = await HttpUtils.get('api/logout') + if(response.success){ + router.push('/login') + } +} + +function _respToMsg(resp: any): Msg { + const data = resp.data + if (data == null) { + return { success: true, msg: "", obj: null } + } else if (isMsg(data)) { + if (data.hasOwnProperty('success')) { + return { success: data.success, msg: data.msg, obj: data.obj || null } + } else { + return data + } + } else { + return { success: false, msg: `unknown data: ${data}`, obj: null } + } +} + +function isMsg(obj: any): obj is Msg { + return Object.hasOwn(obj,'success') && Object.hasOwn(obj,'msg') && Object.hasOwn(obj, 'obj') +} + +const HttpUtils = { + async get(url: string, data: object = {}, options: any[] = []): Promise